rino 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/Rakefile +1 -1
  2. data/ext/extconf.rb +1 -24
  3. data/ext/libinchi.so +0 -0
  4. data/ext/src/aux2atom.h +120 -39
  5. data/ext/src/comdef.h +3 -3
  6. data/ext/src/dispstru.c +2547 -0
  7. data/ext/src/dispstru.h +73 -0
  8. data/ext/src/extr_ct.h +5 -2
  9. data/ext/src/ichi.h +27 -11
  10. data/ext/src/ichi_bns.c +1800 -254
  11. data/ext/src/ichi_bns.h +205 -4
  12. data/ext/src/ichican2.c +197 -86
  13. data/ext/src/ichicano.c +8 -13
  14. data/ext/src/ichicano.h +2 -2
  15. data/ext/src/ichicans.c +11 -6
  16. data/ext/src/ichicant.h +2 -2
  17. data/ext/src/ichicomn.h +2 -2
  18. data/ext/src/ichicomp.h +19 -4
  19. data/ext/src/ichidrp.h +9 -5
  20. data/ext/src/ichierr.h +5 -3
  21. data/ext/src/ichiisot.c +2 -2
  22. data/ext/src/ichimain.c +461 -0
  23. data/ext/src/ichimain.h +23 -15
  24. data/ext/src/ichimak2.c +6 -6
  25. data/ext/src/ichimake.c +843 -42
  26. data/ext/src/ichimake.h +4 -2
  27. data/ext/src/ichimap1.c +5 -5
  28. data/ext/src/ichimap2.c +2 -2
  29. data/ext/src/ichimap4.c +34 -21
  30. data/ext/src/ichinorm.c +11 -5
  31. data/ext/src/ichinorm.h +3 -2
  32. data/ext/src/ichiparm.c +2 -2
  33. data/ext/src/ichiparm.h +232 -30
  34. data/ext/src/ichiprt1.c +35 -11
  35. data/ext/src/ichiprt2.c +78 -7
  36. data/ext/src/ichiprt3.c +300 -120
  37. data/ext/src/ichiqueu.c +17 -2
  38. data/ext/src/ichiread.c +6932 -0
  39. data/ext/src/ichiring.c +3 -2
  40. data/ext/src/ichiring.h +2 -2
  41. data/ext/src/ichirvr1.c +4891 -0
  42. data/ext/src/ichirvr2.c +6344 -0
  43. data/ext/src/ichirvr3.c +5499 -0
  44. data/ext/src/ichirvr4.c +3177 -0
  45. data/ext/src/ichirvr5.c +1166 -0
  46. data/ext/src/ichirvr6.c +1287 -0
  47. data/ext/src/ichirvr7.c +2319 -0
  48. data/ext/src/ichirvrs.h +882 -0
  49. data/ext/src/ichisize.h +2 -2
  50. data/ext/src/ichisort.c +5 -5
  51. data/ext/src/ichister.c +281 -86
  52. data/ext/src/ichister.h +9 -3
  53. data/ext/src/ichitaut.c +208 -9
  54. data/ext/src/ichitaut.h +13 -11
  55. data/ext/src/ichitime.h +16 -2
  56. data/ext/src/inchicmp.h +107 -0
  57. data/ext/src/inpdef.h +6 -3
  58. data/ext/src/libinchi_wrap.c +912 -0
  59. data/ext/src/lreadmol.h +34 -31
  60. data/ext/src/mode.h +244 -7
  61. data/ext/src/mol2atom.c +1060 -0
  62. data/ext/src/mol2atom.h +31 -0
  63. data/ext/src/readinch.c +239 -0
  64. data/ext/src/readmol.c +28 -0
  65. data/ext/src/{e_readmol.h → readmol.h} +7 -9
  66. data/ext/src/runichi.c +251 -177
  67. data/ext/src/strutil.c +444 -238
  68. data/ext/src/strutil.h +150 -11
  69. data/ext/src/util.c +176 -118
  70. data/ext/src/util.h +15 -3
  71. data/lib/rino.rb +71 -3
  72. data/test/test.rb +33 -4
  73. metadata +22 -34
  74. data/ext/ruby_inchi_main.so +0 -0
  75. data/ext/src/e_0dstereo.c +0 -3014
  76. data/ext/src/e_0dstereo.h +0 -31
  77. data/ext/src/e_comdef.h +0 -57
  78. data/ext/src/e_ctl_data.h +0 -147
  79. data/ext/src/e_ichi_io.c +0 -498
  80. data/ext/src/e_ichi_io.h +0 -40
  81. data/ext/src/e_ichi_parms.c +0 -37
  82. data/ext/src/e_ichi_parms.h +0 -41
  83. data/ext/src/e_ichicomp.h +0 -50
  84. data/ext/src/e_ichierr.h +0 -40
  85. data/ext/src/e_ichimain.c +0 -593
  86. data/ext/src/e_ichisize.h +0 -43
  87. data/ext/src/e_inchi_atom.c +0 -75
  88. data/ext/src/e_inchi_atom.h +0 -33
  89. data/ext/src/e_inpdef.h +0 -41
  90. data/ext/src/e_mode.h +0 -706
  91. data/ext/src/e_mol2atom.c +0 -649
  92. data/ext/src/e_readinch.c +0 -58
  93. data/ext/src/e_readmol.c +0 -54
  94. data/ext/src/e_readstru.c +0 -251
  95. data/ext/src/e_readstru.h +0 -33
  96. data/ext/src/e_util.c +0 -284
  97. data/ext/src/e_util.h +0 -61
  98. data/ext/src/ichilnct.c +0 -286
  99. data/ext/src/inchi_api.h +0 -670
  100. data/ext/src/inchi_dll.c +0 -1480
  101. data/ext/src/inchi_dll.h +0 -34
  102. data/ext/src/inchi_dll_main.c +0 -23
  103. data/ext/src/inchi_dll_main.h +0 -31
  104. data/ext/src/ruby_inchi_main.c +0 -558
@@ -0,0 +1,73 @@
1
+ /*
2
+ * International Union of Pure and Applied Chemistry (IUPAC)
3
+ * International Chemical Identifier (InChI)
4
+ * Version 1
5
+ * Software version 1.01
6
+ * July 21, 2006
7
+ * Developed at NIST
8
+ */
9
+
10
+ #ifndef __DISP_STRU_H__
11
+ #define __DISP_STRU_H__
12
+ #include <windows.h>
13
+
14
+ /* local types */
15
+ /*****************************************************/
16
+ typedef struct tagInternalDrawParms {
17
+ double xmin, xmax, ymin, ymax;
18
+ int max_label_width_char;
19
+ int max_left_label_width_pix;
20
+ int max_right_label_width_pix;
21
+ int bInit;
22
+ } INT_DRAW_PARMS; /* internal: saved for redisplaying one structure */
23
+
24
+
25
+ /*****************************************************
26
+ * Window data
27
+ */
28
+ typedef struct tagWindowData {
29
+
30
+ inp_ATOM *at0; /* [MAX_ATOMS]; */
31
+ inp_ATOM *at1; /* [MAX_ATOMS]; */
32
+ INF_ATOM_DATA inf_at_data;
33
+ /*inf_ATOM *inf_at;*/ /* [MAX_ATOMS]; */
34
+ int num_at;
35
+ int bOrigAtom;
36
+ int bHighlight;
37
+ int bEsc;
38
+ int bUserIntervened;
39
+ UINT nTimerId;
40
+
41
+ unsigned long ulDisplTime;
42
+ int nFontSize;
43
+ RECT rc; /* window rectangle size for saving */
44
+ INT_DRAW_PARMS idp; /* structure geom. parameters for redrawing */
45
+ TBL_DRAW_PARMS tdp; /* table data for displaying */
46
+ char *szTitle; /* for INCHI_LIB printing */
47
+
48
+ /* component equivalence info */
49
+ AT_NUMB *nEquLabels; /* num_at elements or NULL */
50
+ AT_NUMB nNumEquSets; /* number of equivalent sets or 0 */
51
+ AT_NUMB nCurEquLabel; /* in range 0..nNumEquSets; 0=>do not display equivalent components */
52
+
53
+ AT_NUMB nNewEquLabel; /* non-zero only if DISPLAY_EQU_COMPONENTS==1 */
54
+
55
+ } MY_WINDOW_DATA;
56
+
57
+ #ifndef INCHI_ALL_CPP
58
+ #ifdef __cplusplus
59
+ extern "C" {
60
+ #endif
61
+ #endif
62
+
63
+ void FreeWinData( MY_WINDOW_DATA* pWinData );
64
+ int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel );
65
+
66
+ #ifndef INCHI_ALL_CPP
67
+ #ifdef __cplusplus
68
+ }
69
+ #endif
70
+ #endif
71
+
72
+
73
+ #endif
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -175,6 +175,9 @@ typedef struct tagAtom {
175
175
  #define BOND_MARK_ALT12NS 0x50 /* 1 or 2, non-stereo */
176
176
  #define BOND_MARK_MASK 0x70
177
177
 
178
+ #define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\
179
+ ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE))
180
+
178
181
 
179
182
  #define BITS_PARITY 0x07 /* mask to retrieve half-bond parity */
180
183
  #define MASK_CUMULENE_LEN 0x38 /* mask to retrieve (cumulene chain length - 1)*MULT_STEREOBOND */
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -12,8 +12,8 @@
12
12
 
13
13
  #include "comdef.h"
14
14
 
15
- #define REQ_MODE_BASIC 0x000001 /* B */
16
- #define REQ_MODE_TAUT 0x000002 /* T */
15
+ #define REQ_MODE_BASIC 0x000001 /* B include Fixed-H layer */
16
+ #define REQ_MODE_TAUT 0x000002 /* T include Mobile-H layer */
17
17
  #define REQ_MODE_ISO 0x000004 /* I */
18
18
  #define REQ_MODE_NON_ISO 0x000008 /* NI */
19
19
  #define REQ_MODE_STEREO 0x000010 /* S */
@@ -45,7 +45,7 @@
45
45
  /****************** chemical identifier member definitions *************/
46
46
  typedef struct tagINChI_IsotopicAtom {
47
47
  AT_NUMB nAtomNumber; /* Canonical atom number */
48
- NUM_H nIsoDifference; /* 0=non-isotopic; 1=only most abundant */
48
+ NUM_H nIsoDifference; /* 0=non-isotopic; 1=rounded avg. atomic mass */
49
49
  NUM_H nNum_H; /* number of 1H isotopic atoms attached */
50
50
  NUM_H nNum_D; /* number of 2H isotopic atoms attached */
51
51
  NUM_H nNum_T; /* number of 3H isotopic atoms attached */
@@ -98,6 +98,15 @@ typedef struct tagINChI_Stereo { /* [N] = allocated length */
98
98
  #define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */
99
99
  #define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */
100
100
  #define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */
101
+ #define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */
102
+ #define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */
103
+
104
+ #define INCHI_OUT_PRINT_OPTIONS (INCHI_OUT_EMBED_REC | \
105
+ INCHI_OUT_XML | \
106
+ INCHI_OUT_PLAIN_TEXT | \
107
+ INCHI_OUT_PLAIN_TEXT_COMMENTS | \
108
+ INCHI_OUT_XML_TEXT_COMMENTS)
109
+
101
110
 
102
111
  /*******REQ_MODE_SB_IGN_ALL_UU*************** chemical identifier definition *****************/
103
112
  typedef struct tagINChI { /* [N] = allocated length */
@@ -131,6 +140,7 @@ typedef struct tagINChI { /* [N] = allocated length */
131
140
  int nNumberOfIsotopicAtoms;
132
141
  INChI_IsotopicAtom *IsotopicAtom; /* [nNumberOfIsotopicAtoms] */
133
142
  int nNumberOfIsotopicTGroups;
143
+ /* in reversing InChI keeps a pointer to stolen from AuxInfo coordinates */
134
144
  INChI_IsotopicTGroup *IsotopicTGroup; /* [nNumberOfIsotopicAtoms] */
135
145
  /* ---- stereo layer */
136
146
  INChI_Stereo *Stereo;
@@ -144,7 +154,9 @@ typedef struct tagINChI { /* [N] = allocated length */
144
154
  #if( bRELEASE_VERSION == 0 )
145
155
  int bExtract;
146
156
  #endif
147
-
157
+ #if( READ_INCHI_STRING == 1 )
158
+ int nLink; /* negative: ignore InChI; positive: index of (Reconnected component) + 1 linked to it */
159
+ #endif
148
160
  } INChI;
149
161
 
150
162
  typedef INChI *PINChI2[TAUT_NUM];
@@ -185,11 +197,12 @@ typedef struct tagINChI_Aux { /* [N] = allocated length */
185
197
  MOL_COORD *szOrigCoord;
186
198
  NUM_H nNumRemovedProtons;
187
199
  NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered
188
- randomly distributed, including removed protons */
200
+ randomly distributed, including removed protons;
201
+ order: 0=>1H, 1=>D, 2=>T */
189
202
  int bDeleted;
190
- INCHI_MODE bTautFlags;
191
- INCHI_MODE bTautFlagsDone;
192
- INCHI_MODE bNormalizationFlags;
203
+ INCHI_MODE bTautFlags; /* t_group_info->bTautFlags */
204
+ INCHI_MODE bTautFlagsDone; /* t_group_info->bTautFlagsDone */
205
+ INCHI_MODE bNormalizationFlags; /* t_group_info->tni.bNormalizationFlags */
193
206
  int nCanonFlags;
194
207
  } INChI_Aux;
195
208
 
@@ -199,7 +212,10 @@ typedef INChI_Aux *PINChI_Aux2[TAUT_NUM];
199
212
  typedef struct tagINChIforSort {
200
213
  INChI *pINChI[TAUT_NUM];
201
214
  INChI_Aux *pINChI_Aux[TAUT_NUM];
202
- int ord_number; /* for stable sort */
215
+ short ord_number; /* for stable sort */
216
+ short n1; /* points to the original; used in structure reconstruction only */
217
+ short n2; /* points to the original; used in structure reconstruction only */
218
+ short n3; /* points to the original; used in structure reconstruction only */
203
219
  }INCHI_SORT;
204
220
 
205
221
 
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -28,8 +28,6 @@
28
28
 
29
29
  #define BNS_MARK_ONLY_BLOCKS 1 /* 1 => find only blocks, do not search for ring systems */
30
30
  #define ALLOW_ONLY_SIMPLE_ALT_PATH 0 /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */
31
- #define MAX_BOND_EDGE_CAP 2 /* triple bond */
32
- #define AROM_BOND_EDGE_CAP 1
33
31
 
34
32
  #define CHECK_TG_ALT_PATH 0 /* 1=> when chacking alt path of a tautomeric atom modify
35
33
  t-group, not the atom */
@@ -37,9 +35,7 @@
37
35
 
38
36
  #define FIX_CPOINT_BOND_CAP 1 /* 1=> fix bug in case of double bond from neutral cpoint */
39
37
 
40
- #define ADD_CAPACITY_RADICAL 1 /* add capacity to radical */
41
-
42
- #define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "dge->forbidden &= pBNS->edge_forbidden_mask" */
38
+ #define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */
43
39
  #if ( RESET_EDGE_FORBIDDEN_MASK == 1 )
44
40
  #define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN)
45
41
  #else
@@ -219,23 +215,6 @@ typedef struct tagProtonRemovalMaskAndType {
219
215
  #define AA_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS)
220
216
  #define AA_HARD_TYP_H (ATTYP_N | ATTYP_OS)
221
217
 
222
- /*********************************************************************************/
223
- #if( ADD_CAPACITY_RADICAL == 1 ) /* { */
224
- /* -- do not treat triplets as moving dots -- 2004-02-18 --
225
- #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\
226
- ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\
227
- (((X).radical==RADICAL_DOUBLET)+2*((X).radical==RADICAL_TRIPLET)):0))
228
- */
229
- #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\
230
- ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\
231
- (((X).radical==RADICAL_DOUBLET)/*+2*((X).radical==RADICAL_TRIPLET)*/):0))
232
-
233
-
234
- #else /* } ADD_CAPACITY_RADICAL { */
235
-
236
- #define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)
237
-
238
- #endif /* } ADD_CAPACITY_RADICAL */
239
218
 
240
219
  /*********************************************************************************/
241
220
 
@@ -247,9 +226,6 @@ typedef struct tagProtonRemovalMaskAndType {
247
226
  #define FIRST_INDX 1
248
227
  */
249
228
 
250
- #define NO_VERTEX -2
251
- #define BLOSSOM_BASE -1
252
-
253
229
 
254
230
  #define TREE_NOT_IN_M 0 /* not in T or T' */
255
231
  #define TREE_IN_2 1 /* in T' and not s-reachable */
@@ -261,83 +237,6 @@ typedef struct tagProtonRemovalMaskAndType {
261
237
  /* #define TREE_IS_ON_SCANQ(X) (Tree[X] != TREE_NOT_IN_M) */
262
238
  #define TREE_MARK(X, MARK) do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0)
263
239
 
264
-
265
- #define BNS_ADD_ATOMS 2 /* max. number of fictitious atoms to add (except t-gtoups) */
266
- #define BNS_ADD_EDGES 1 /* max. number of edges to add to each atom (except edges to a t-group or c-group) */
267
- #define BNS_ADD_SUPER_TGROUP 1 /* reserve one more edge for a t-group to connect to a single super-t-group */
268
- #define NUM_KINDS_OF_GROUPS 2 /* 1 accounts for t-group kind, one more 1 accounts for c-group kind */
269
-
270
- #define BNS_VERT_TYPE_ATOM 0x0001
271
- #define BNS_VERT_TYPE_ENDPOINT 0x0002
272
- #define BNS_VERT_TYPE_TGROUP 0x0004
273
- #define BNS_VERT_TYPE_C_POINT 0x0008
274
- #define BNS_VERT_TYPE_C_GROUP 0x0010
275
- #define BNS_VERT_TYPE_SUPER_TGROUP 0x0020
276
- #define BNS_VERT_TYPE_TEMP 0x0040
277
- #define BNS_VERT_TYPE_C_NEGATIVE 0x0080 /* attribute, should be used with BNS_VERT_TYPE_C_GROUP */
278
- #define BNS_VERT_TYPE_ACID 0x0100 /* only for this type are allowed paths: t_group-atom-c_group_neg (path_TACN) */
279
- #define BNS_VERT_TYPE_ANY_GROUP (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_SUPER_TGROUP)
280
-
281
- /*********************************************************************************
282
- bChangeFlow:
283
- 1 => change flow inside the BNS search
284
- 3 => change flow inside the BNS search and undo the flow change in the BNS structure here
285
- 4 => change bonds in the structure according to the flow
286
- 8 => make altern. bonds in the structure
287
-
288
- Note: (bChangeFlow & 1) == 1 is needed for multiple runs
289
- **********************************************************************************/
290
-
291
- /* "EF" = "Edge Flow" */
292
- #define BNS_EF_CHNG_FLOW 1 /* change Balanced Network (BN) flow inside the BNS search */
293
- #define BNS_EF_RSTR_FLOW 2 /* undo BN flow changes after BNS */
294
- #define BNS_EF_CHNG_RSTR (BNS_EF_CHNG_FLOW | BNS_EF_RSTR_FLOW)
295
- #define BNS_EF_CHNG_BONDS 4 /* change bonds in the structure according to the BN flow */
296
- #define BNS_EF_ALTR_BONDS 8 /* make altern. bonds in the structure if the flow has changed */
297
- #define BNS_EF_UPD_RAD_ORI 16 /* update BN flow0 & Atom radical values:
298
- flow0 := flow, radical:=st_cap - st_flow */
299
- #define BNS_EF_SET_NOSTEREO 32 /* in combination with BNS_EF_ALTR_BONDS only:
300
- ALT12 bond cannot be stereogenic */
301
- #define BNS_EF_UPD_H_CHARGE 64 /* update charges and H-counts according to change flow to c- and t-group vertices */
302
-
303
- #define BNS_EF_SAVE_ALL (BNS_EF_CHNG_FLOW | BNS_EF_CHNG_BONDS | BNS_EF_UPD_RAD_ORI)
304
- #define BNS_EF_ALTR_NS (BNS_EF_ALTR_BONDS | BNS_EF_SET_NOSTEREO)
305
-
306
- /* edge to s or t */
307
- #define EDGE_FLOW_ST_MASK 0x3fff /* mask for flow */
308
- #define EDGE_FLOW_ST_PATH 0x4000 /* mark: the edge belongs to the augmenting path */
309
- /* edges between other vertices */
310
- #define EDGE_FLOW_MASK 0x3f /* mask for flow */
311
- #define EDGE_FLOW_PATH 0x40 /* mark: the edge belongs to the augmenting path */
312
-
313
-
314
- typedef enum tagAltPathConst {
315
- iALTP_MAX_LEN, /* 0 */
316
- iALTP_FLOW, /* 1 */
317
- iALTP_PATH_LEN, /* 2 */
318
- iALTP_START_ATOM, /* 3 */
319
- iALTP_END_ATOM, /* 4 */
320
- iALTP_NEIGHBOR, /* 5 */
321
- iALTP_HDR_LEN = iALTP_NEIGHBOR
322
- } ALT_CONST;
323
-
324
- #define ALTP_PATH_LEN(altp) (altp)[iALTP_PATH_LEN].number /* number of bonds = number of atoms-1*/
325
- #define ALTP_END_ATOM(altp) (altp)[iALTP_END_ATOM].number
326
- #define ALTP_START_ATOM(altp) (altp)[iALTP_START_ATOM].number
327
- #define ALTP_THIS_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[0] /* 0 <= X < path_len */
328
- #define ALTP_NEXT_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[1]
329
- #define ALTP_CUR_THIS_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[0] /* 0 <= X < path_len */
330
- #define ALTP_CUR_NEXT_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[1]
331
- #define ALTP_NEXT(altp) (++ALTP_PATH_LEN(altp))
332
- #define ALTP_PREV(altp) (--ALTP_PATH_LEN(altp))
333
- #define ALTP_MAY_ADD(altp) (iALTP_NEIGHBOR + (altp)[iALTP_PATH_LEN].number < (altp)[iALTP_MAX_LEN].number)
334
- #define ALTP_ALLOCATED_LEN(altp) (altp)[iALTP_MAX_LEN].number
335
- #define ALTP_DELTA(altp) (altp)[iALTP_FLOW].flow[0]
336
- #define ALTP_OVERFLOW(altp) (altp)[iALTP_FLOW].flow[1]
337
-
338
- #define Vertex_s 0
339
- #define Vertex_t 1
340
-
341
240
  /**********************************************************************************
342
241
  * store changes done to check whether an alternating path exists
343
242
  * (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath)
@@ -357,20 +256,8 @@ typedef struct tagAltPathChanges {
357
256
 
358
257
  /* local functions */
359
258
 
360
- BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *num_changed_bonds );
361
- BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS );
362
- int ReInitBnStructAltPaths( BN_STRUCT *pBNS );
363
- /*
364
- int ReInitBnStructAddGroups( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi, C_GROUP_INFO *cgi );
365
- */
366
- int ReInitBnStructForMoveableAltBondTest( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
367
-
368
- void ClearAllBnDataVertices( Vertex *v, Vertex value, int size );
369
- void ClearAllBnDataEdges( Edge *e, Vertex value, int size );
370
- BN_DATA *DeAllocateBnData( BN_DATA *pBD );
371
- BN_DATA *AllocateAndInitBnData( int max_num_vertices );
372
- int ReInitBnData( BN_DATA *pBD );
373
-
259
+ int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at );
260
+ int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 );
374
261
  int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms );
375
262
  int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 );
376
263
  int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond );
@@ -383,9 +270,9 @@ int CompTGroupNumber( const void *tg1, const void *tg2 );
383
270
  int CompCGroupNumber( const void *cg1, const void *cg2 );
384
271
 
385
272
  /* Rings, Blocks, Non-stereo bonds */
386
- int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
387
- int MarkRingSystemsAltBns( BN_STRUCT* pBNS );
388
- int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
273
+ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo );
274
+ int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo );
275
+ int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo );
389
276
 
390
277
  /* called from BalancedNetworkSearch */
391
278
 
@@ -406,10 +293,6 @@ Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize,
406
293
  int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow );
407
294
  int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta );
408
295
 
409
- /* main function: find augmenting path */
410
- int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow );
411
-
412
- int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow );
413
296
 
414
297
  /*
415
298
  int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow );
@@ -432,8 +315,7 @@ int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTes
432
315
  int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest );
433
316
  int bIsAltBond( int bond_type );
434
317
  /* fix bonds */
435
- int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
436
- int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
318
+ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask );
437
319
  int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
438
320
  int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms );
439
321
 
@@ -444,6 +326,7 @@ int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bC
444
326
  Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type);
445
327
  BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type);
446
328
  int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots );
329
+ int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow );
447
330
  int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors );
448
331
 
449
332
  static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms);
@@ -479,11 +362,20 @@ int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG,
479
362
  Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv );
480
363
  int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v );
481
364
  int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge );
365
+ int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v );
366
+ int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge );
482
367
 
483
368
  int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge );
484
369
  int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType );
485
370
  int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType );
486
371
 
372
+ #if ( BNS_RAD_SEARCH == 1 )
373
+ int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u);
374
+ int cmp_rad_endpoints( const void *a1, const void *a2 );
375
+ int cmp_endpoints_rad( const void *a1, const void *a2 );
376
+ #endif
377
+
378
+ int bHasChargedNeighbor( inp_ATOM *at, int iat );
487
379
  /*********************************************************************************/
488
380
 
489
381
  /**** prim(v) is v' *****/
@@ -502,7 +394,6 @@ int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType );
502
394
  /* returns value > 0 if a bond has been changed */
503
395
  int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow )
504
396
  {
505
-
506
397
  /*flow1 = edge->flow;*/ /* output from BNS */
507
398
  switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) {
508
399
  case 0: /* the flow has not been permitted to change inside the BNS */
@@ -550,6 +441,8 @@ int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, i
550
441
  flow2 = edge->flow; /* output from BNS, the changed (new) value */
551
442
  flow1 = edge->flow0; /* the original flow (old) value before the BNS */
552
443
  break;
444
+ default:
445
+ return 0; /* added 2006-03-21 */
553
446
  }
554
447
 
555
448
  if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && (bChangeFlow & BNS_EF_ALTR_NS) !=BNS_EF_ALTR_NS ) {
@@ -745,7 +638,12 @@ int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_
745
638
  for ( i = 0, num = 0; i < num_atoms; i ++ ) {
746
639
  if ( mark[i] ) {
747
640
  mark[i] = 0;
641
+ #if( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
642
+ /* add ignoring adjacent charges */
643
+ at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 );
644
+ #else
748
645
  at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 );
646
+ #endif
749
647
  num ++;
750
648
  }
751
649
  }
@@ -1014,7 +912,11 @@ int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms,
1014
912
  if ( bSubtract ) {
1015
913
  if ( !mark[v1] ) {
1016
914
  /* first time the atom has been encountered: subtract */
915
+ #if( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
916
+ type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 2 );
917
+ #else
1017
918
  type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 1 );
919
+ #endif
1018
920
  ret ++; /* number of changed atoms */
1019
921
  mark[v1] ++;
1020
922
  }
@@ -1410,14 +1312,68 @@ int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond
1410
1312
  }
1411
1313
  return 1;
1412
1314
  }
1413
-
1315
+ /***********************************************************************************/
1316
+ int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds )
1317
+ {
1318
+ int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0;
1319
+ for ( j = 0; j < at->valence; j ++ ) {
1320
+ bond_type = at->bond_type[j] & BOND_TYPE_MASK;
1321
+ switch( bond_type ) {
1322
+ case 0: /* for structure from InChI reconstruction */
1323
+ case BOND_SINGLE:
1324
+ case BOND_DOUBLE:
1325
+ case BOND_TRIPLE:
1326
+ nBondsValence += bond_type;
1327
+ break;
1328
+ case BOND_ALTERN:
1329
+ nAltBonds ++;
1330
+ break;
1331
+ default:
1332
+ nNumWrong ++;
1333
+ }
1334
+ }
1335
+ switch ( nAltBonds ) {
1336
+ case 0:
1337
+ break;
1338
+ case 1:
1339
+ nBondsValence += 1; /* 1 or greater than 3 is wrong */
1340
+ nNumWrong ++;
1341
+ break;
1342
+ default:
1343
+ nBondsValence += nAltBonds+1;
1344
+ break;
1345
+ }
1346
+ if ( nNumAltBonds ) *nNumAltBonds = nAltBonds;
1347
+ if ( nNumWrongBonds ) *nNumWrongBonds = nNumWrong;
1348
+ return nBondsValence;
1349
+ }
1414
1350
  /***************************************************************************************/
1415
1351
  /* if radical or has aromatic bonds then augment to the lowest "multiplicity" */
1416
1352
  int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms )
1417
1353
  {
1418
- int bError, nOrigDelta, ret, num_removed;
1419
- bError = 0;
1420
- nOrigDelta = 0;
1354
+ int bError=0, nOrigDelta=0, ret, num_removed;
1355
+
1356
+ #if( CHECK_AROMBOND2ALT == 1 )
1357
+ char *pcValMinusBondsVal = NULL;
1358
+ int i, nValMinusBondsVal, nAltBonds, bIgnore;
1359
+
1360
+ /* find valence excess (it may only be due to aromatic bonds) */
1361
+ for ( i = 0; i < num_atoms; i ++ ) {
1362
+ nValMinusBondsVal = (int)at[i].chem_bonds_valence - nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore );
1363
+ bIgnore += (nAltBonds > 3);
1364
+ if ( !bIgnore && nValMinusBondsVal > 0 ) {
1365
+ if ( !pcValMinusBondsVal &&
1366
+ !(pcValMinusBondsVal = (char *)inchi_calloc(num_atoms, sizeof(pcValMinusBondsVal[0])))) {
1367
+ bError = BNS_OUT_OF_RAM;
1368
+ goto exit_function;
1369
+ }
1370
+ /* mark atoms that have extra unsatisfied valence due to aromatic bonds */
1371
+ pcValMinusBondsVal[i] = nValMinusBondsVal + (at[i].radical == RADICAL_DOUBLET);
1372
+ }
1373
+ }
1374
+ #endif /* CHECK_AROMBOND2ALT */
1375
+
1376
+ /* match bonds to valences */
1421
1377
  do {
1422
1378
  num_removed = 0;
1423
1379
  ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW );
@@ -1440,6 +1396,27 @@ int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_
1440
1396
  ReInitBnStructAltPaths( pBNS );
1441
1397
  }
1442
1398
  } while ( num_removed && num_removed == pBNS->max_altp && !bError );
1399
+
1400
+ #if( CHECK_AROMBOND2ALT == 1 )
1401
+ /* check whether aromatic bonds have been replaces with alternating bonds */
1402
+ if ( !bError && pcValMinusBondsVal ) {
1403
+ for ( i = 0; i < num_atoms; i ++ ) {
1404
+ if ( !pcValMinusBondsVal[i] )
1405
+ continue;
1406
+ nValMinusBondsVal = (int)at[i].chem_bonds_valence - nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore );
1407
+ if ( bIgnore ||
1408
+ 1 != (int)pcValMinusBondsVal[i] - (nValMinusBondsVal + (at[i].radical == RADICAL_DOUBLET)) ) {
1409
+ /* radical excess has not been reduced */
1410
+ bError = BNS_ALTBOND_ERR;
1411
+ break;
1412
+ }
1413
+ }
1414
+ }
1415
+
1416
+ exit_function:
1417
+ if ( pcValMinusBondsVal ) inchi_free( pcValMinusBondsVal );
1418
+ #endif /* CHECK_AROMBOND2ALT */
1419
+
1443
1420
  return bError? bError : nOrigDelta;
1444
1421
  }
1445
1422
 
@@ -1551,7 +1528,7 @@ static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms)
1551
1528
  }
1552
1529
  }
1553
1530
  /***************************************************************************************/
1554
- int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1531
+ int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask )
1555
1532
  {
1556
1533
  static U_CHAR el_number_O;
1557
1534
  static U_CHAR el_number_C;
@@ -1559,9 +1536,10 @@ int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1559
1536
 
1560
1537
  int i, j, neigh, num_found;
1561
1538
  BNS_IEDGE iedge;
1562
- S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;
1539
+ /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/
1540
+ S_CHAR edge_forbidden_mask = forbidden_mask;
1563
1541
 
1564
- pBNS->edge_forbidden_mask |= edge_forbidden_mask;
1542
+ pBNS->edge_forbidden_mask |= forbidden_mask;
1565
1543
 
1566
1544
  if ( !el_number_C ) {
1567
1545
  el_number_O = (U_CHAR)get_periodic_table_number( "O" );
@@ -1623,7 +1601,7 @@ int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1623
1601
  }
1624
1602
  }
1625
1603
  #if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 )
1626
- num_found += fix_special_bonds( pBNS, at, num_atoms );
1604
+ num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask );
1627
1605
  #endif
1628
1606
  #if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
1629
1607
  num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms );
@@ -1696,12 +1674,16 @@ int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1696
1674
  #endif
1697
1675
  /************************************************************************/
1698
1676
  /* fixes bonds set by remove_ion_pairs() in strutil.c */
1699
- int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1677
+ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask )
1700
1678
  {
1701
1679
  int num_changes = 0;
1702
1680
 
1703
1681
  /* 0 1 2 3 4 5 6 7 8 9 8 9 */
1682
+ #if( FIX_REM_ION_PAIRS_Si_BUG == 1 )
1683
+ static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */
1684
+ #else
1704
1685
  static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */
1686
+ #endif
1705
1687
  static char en[12]; /* same number: 8 elements */
1706
1688
  static int ne=0; /* will be 8 and 10 */
1707
1689
 
@@ -1722,7 +1704,8 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1722
1704
  int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N;
1723
1705
 
1724
1706
  BNS_IEDGE iedge;
1725
- S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;
1707
+ /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/
1708
+ S_CHAR edge_forbidden_mask = forbidden_mask;
1726
1709
 
1727
1710
  pBNS->edge_forbidden_mask |= edge_forbidden_mask;
1728
1711
 
@@ -1735,7 +1718,8 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1735
1718
  elname[len] = '\0';
1736
1719
  en[ne++] = get_periodic_table_number( elname );
1737
1720
  }
1738
- en[ne] = '\0';
1721
+ en[ne] = '\0';
1722
+ en[ne+1] = '\0';
1739
1723
  }
1740
1724
  for ( i = 0, a = at; i < num_atoms; i ++, a++ ) {
1741
1725
  if ( !a->charge && !a->radical &&
@@ -1747,15 +1731,25 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1747
1731
  if ( 2 == nNoMetalNumBonds(at, i) ) {
1748
1732
  /* #N= */
1749
1733
  /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */
1734
+ #if( FIX_N_V_METAL_BONDS_GPF == 1 )
1735
+ if ( 0 > (i1 = nNoMetalNeighIndex( at, i )) ||
1736
+ 0 > (i2 = nNoMetalOtherNeighIndex( at, i,
1737
+ n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ) ) {
1738
+ /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */
1739
+ continue; /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */
1740
+ }
1741
+ #else
1750
1742
  i1 = nNoMetalNeighIndex( at, i );
1751
1743
  n1 = a->neighbor[i1]; /* non-metal neighbor #1 */
1752
1744
  i2 = nNoMetalOtherNeighIndex( at, i, n1 );
1745
+ #endif
1753
1746
  n2 = a->neighbor[i2]; /* non-metal neighbor #2 */
1754
1747
  /* forbid all edges to non-metals */
1755
1748
  iedge = pBNS->vert[i].iedge[i1];
1756
1749
  pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */
1757
1750
  iedge = pBNS->vert[i].iedge[i2];
1758
1751
  pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */
1752
+ num_changes ++; /* added 11-15-2005 */
1759
1753
  /* i n3 */
1760
1754
  /* forbid single bond edge beyond the neighboring =N- as in #N=N- */
1761
1755
  if ( (at[i].bond_type[i1] & BOND_TYPE_MASK) == BOND_TYPE_DOUBLE ) {
@@ -1823,7 +1817,7 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1823
1817
  } else
1824
1818
  if ( 4 == nNoMetalNumBonds(at, i) ) {
1825
1819
  /* | */
1826
- /* found -N= */
1820
+ /* found -N=N- */
1827
1821
  /* | */
1828
1822
  /* locate non-metal neighbor connected by a double bond;
1829
1823
  * if it is =N- then fix the double bond and the single bond beyond the neighbor
@@ -1852,7 +1846,6 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
1852
1846
  iedge = pBNS->vert[i].iedge[i2];
1853
1847
  pBNS->edge[iedge].forbidden |= edge_forbidden_mask;
1854
1848
  num_changes ++;
1855
- num_changes ++;
1856
1849
  }
1857
1850
  }
1858
1851
  } else
@@ -2152,8 +2145,6 @@ int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
2152
2145
  }
2153
2146
  }
2154
2147
  }
2155
-
2156
-
2157
2148
  return num_changes;
2158
2149
  }
2159
2150
 
@@ -2267,7 +2258,12 @@ int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMas
2267
2258
  static U_CHAR el_number_I = 0;
2268
2259
 
2269
2260
  inp_ATOM *at = atom + at_no;
2261
+ #if( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
2262
+ 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 */
2263
+ int bNoAdjIon = (bSubtract==0 || bSubtract==1);
2264
+ #else
2270
2265
  int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract? -1 : 1;
2266
+ #endif
2271
2267
  int bUnsatNHasTerminalO = 0;
2272
2268
  if ( !el_number_C ) {
2273
2269
  el_number_C = (U_CHAR)get_periodic_table_number( "C" );
@@ -2339,9 +2335,23 @@ int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMas
2339
2335
  /* check neighbors */
2340
2336
  for ( i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i ++ ) {
2341
2337
  neigh = at->neighbor[i];
2338
+ #if( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
2339
+ if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 ) {
2340
+ goto exit_function; /* neighboring charge */
2341
+ }
2342
+ if ( atom[neigh].charge && at->charge ) {
2343
+ if ( bNoAdjIon ) {
2344
+ goto exit_function; /* neighboring charge */
2345
+ }
2346
+ type = ATT_NONE;
2347
+ mask = 0;
2348
+ goto count_mask_bits;
2349
+ }
2350
+ #else
2342
2351
  if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge ) {
2343
2352
  goto exit_function; /* neighboring charge */
2344
2353
  }
2354
+ #endif
2345
2355
  if ( detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical,
2346
2356
  atom[neigh].chem_bonds_valence, atom[neigh].num_H,
2347
2357
  atom[neigh].valence ) ) {
@@ -2634,7 +2644,11 @@ int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GR
2634
2644
  AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, t_group_info );
2635
2645
  /*at[i].num_H --;*/
2636
2646
  num_removed ++;
2637
- type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* add changed at[i] */
2647
+ #if( FIX_NORM_BUG_ADD_ION_PAIR == 1 )
2648
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
2649
+ #else
2650
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* bug: subtract instead of add */
2651
+ #endif
2638
2652
  /*
2639
2653
  if ( nAtTypeTotals ) {
2640
2654
  nAtTypeTotals[ATTOT_NUM_NP_Proton] --;
@@ -2876,9 +2890,9 @@ int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms,
2876
2890
  for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) {
2877
2891
  int iedge = vertex_cpoint->iedge[k];
2878
2892
  VertexFlow nNewCap = vertex_cpoint->st_edge.cap;
2893
+ centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point);
2879
2894
  if ( !pBNS->edge[iedge].cap ) {
2880
2895
  /* single bond, possibly between c_point and centerpoint */
2881
- centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point);
2882
2896
  if ( centerpoint < pBNS->num_atoms &&
2883
2897
  pBNS->vert[centerpoint].st_edge.cap >= 1 ) {
2884
2898
  nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap );
@@ -2886,6 +2900,13 @@ int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms,
2886
2900
  pBNS->edge[iedge].cap = nNewCap;
2887
2901
  }
2888
2902
  }
2903
+ #if( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */
2904
+ else
2905
+ if ( centerpoint < pBNS->num_atoms &&
2906
+ edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) {
2907
+ pBNS->edge[iedge].cap ++;
2908
+ }
2909
+ #endif
2889
2910
  }
2890
2911
  #endif /* } FIX_CPOINT_BOND_CAP */
2891
2912
  /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */
@@ -3451,7 +3472,11 @@ int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int nu
3451
3472
  }
3452
3473
 
3453
3474
  if ( nNumCanceledCharges ) {
3475
+ #if( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
3476
+ *nNumCanceledCharges += 2*nNumNeutralized;
3477
+ #else
3454
3478
  *nNumCanceledCharges = 2*nNumNeutralized;
3479
+ #endif
3455
3480
  }
3456
3481
 
3457
3482
  return nNumMoved2AcidH;
@@ -3604,7 +3629,11 @@ int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2a
3604
3629
  }
3605
3630
 
3606
3631
  if ( nNumCanceledCharges ) {
3632
+ #if( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
3633
+ *nNumCanceledCharges += 2*nNumNeutralized;
3634
+ #else
3607
3635
  *nNumCanceledCharges = 2*nNumNeutralized;
3636
+ #endif
3608
3637
  }
3609
3638
 
3610
3639
  return nNumMoved2AcidMinus;
@@ -3622,6 +3651,9 @@ int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, in
3622
3651
  int tg_H = 0;
3623
3652
  #if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 )
3624
3653
  int cg_PlusP = 0;
3654
+ #endif
3655
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3656
+ int nPrevRemovedProtons, nCurrRemovedProtons;
3625
3657
  #endif
3626
3658
  int ret = 0, ret2;
3627
3659
  int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges;
@@ -3664,15 +3696,59 @@ int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, in
3664
3696
  tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H );
3665
3697
 
3666
3698
  if ( tg_H >= num_atoms && cg_Plus >= num_atoms ) {
3699
+
3700
+ #if( FIX_N_MINUS_NORN_BUG == 1 )
3701
+ /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */
3702
+ if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms &&
3703
+ pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) {
3704
+ do {
3705
+ nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
3706
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3707
+ nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3708
+ #endif
3709
+ ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms,
3710
+ cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON );
3711
+ if ( IS_BNS_ERROR( ret ) ) {
3712
+ return ret;
3713
+ }
3714
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3715
+ nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3716
+ if ( nCurrRemovedProtons != nPrevRemovedProtons ) {
3717
+ return BNS_RADICAL_ERR;
3718
+ }
3719
+ #endif
3720
+ if ( ret & 1 ) {
3721
+ nDelta = (ret & ~3) >> 2;
3722
+ nNumChanges += (0 != (ret & 2));
3723
+ if ( nDelta ) {
3724
+ /* radical pair has disappeared */
3725
+ ; /* goto quick_exit;*/
3726
+ }
3727
+ if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) {
3728
+ nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2;
3729
+ }
3730
+ }
3731
+ } while ( ret & 1 );
3732
+ }
3733
+ #endif
3667
3734
  /* find alt path to remove one proton */
3668
3735
  do {
3669
3736
  /* remove a proton */
3670
3737
  nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
3738
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3739
+ nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3740
+ #endif
3671
3741
  ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms,
3672
3742
  tg_H /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON );
3673
3743
  if ( IS_BNS_ERROR( ret ) ) {
3674
3744
  return ret;
3675
3745
  }
3746
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3747
+ nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3748
+ if ( nCurrRemovedProtons != nPrevRemovedProtons + (ret & 1) ) {
3749
+ return BNS_RADICAL_ERR;
3750
+ }
3751
+ #endif
3676
3752
  if ( ret & 1 ) {
3677
3753
  nDelta = (ret & ~3) >> 2;
3678
3754
  nNumChanges += (0 != (ret & 2));
@@ -3693,11 +3769,20 @@ int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, in
3693
3769
  pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) {
3694
3770
  do {
3695
3771
  nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES];
3772
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3773
+ nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3774
+ #endif
3696
3775
  ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms,
3697
3776
  cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON );
3698
3777
  if ( IS_BNS_ERROR( ret ) ) {
3699
3778
  return ret;
3700
3779
  }
3780
+ #if ( FIX_REM_PROTON_COUNT_BUG == 1 )
3781
+ nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons;
3782
+ if ( nCurrRemovedProtons != nPrevRemovedProtons ) {
3783
+ return BNS_RADICAL_ERR;
3784
+ }
3785
+ #endif
3701
3786
  if ( ret & 1 ) {
3702
3787
  nDelta = (ret & ~3) >> 2;
3703
3788
  nNumChanges += (0 != (ret & 2));
@@ -3761,7 +3846,11 @@ int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, in
3761
3846
  }
3762
3847
 
3763
3848
  if ( nNumCanceledCharges ) {
3849
+ #if( FIX_CANCEL_CHARGE_COUNT_BUG == 1 )
3850
+ *nNumCanceledCharges += 2*nNumNeutralized;
3851
+ #else
3764
3852
  *nNumCanceledCharges = 2*nNumNeutralized;
3853
+ #endif
3765
3854
  }
3766
3855
 
3767
3856
  return nNumRemovedProtons;
@@ -3986,7 +4075,7 @@ int mark_alt_bonds_and_taut_groups ( inp_ATOM *at, inp_ATOM *at_fixed_bonds_out,
3986
4075
  /*
3987
4076
  again:
3988
4077
  */
3989
- /* allocate Balanced Network Data Strucures */
4078
+ /* allocate Balanced Network Data Strucures; replace Alternating bonds with Single */
3990
4079
  if ( (pBNS = AllocateAndInitBnStruct( at, num_atoms, BNS_ADD_ATOMS, BNS_ADD_EDGES, max_altp, &num_changed_bonds )) &&
3991
4080
  (pBD = AllocateAndInitBnData( pBNS->max_vertices )) ) {
3992
4081
 
@@ -3995,7 +4084,7 @@ again:
3995
4084
 
3996
4085
  #if( BNS_PROTECT_FROM_TAUT == 1 )
3997
4086
  /* protect bonds to acetyl and nitro */
3998
- SetForbiddenEdges( pBNS, at, num_atoms );
4087
+ SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK );
3999
4088
  #endif
4000
4089
  /* set bonds in case of input "aromatic" bonds or multiple radicals */
4001
4090
  ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
@@ -4042,6 +4131,11 @@ again:
4042
4131
  #if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
4043
4132
  pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP;
4044
4133
  #endif
4134
+ /***********************************************************/
4135
+ /* */
4136
+ /* ( D E ) P R O T O N A T I O N */
4137
+ /* */
4138
+ /***********************************************************/
4045
4139
  ret = RemoveNPProtonsAndAcidCharges( at, num_atoms, pAATG, pBNS, pBD );
4046
4140
  #if ( RESET_EDGE_FORBIDDEN_MASK == 0 )
4047
4141
  pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP;
@@ -4088,6 +4182,8 @@ again:
4088
4182
  *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE;
4089
4183
  }
4090
4184
  } while ( ret > 0 );
4185
+ #if( BNS_RAD_SEARCH == 1 )
4186
+ #else
4091
4187
  /* moveable charges may allow to cancel radicals -- check it */
4092
4188
  if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) {
4093
4189
  ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
@@ -4108,6 +4204,7 @@ again:
4108
4204
  goto exit_function;
4109
4205
  }
4110
4206
  }
4207
+ #endif
4111
4208
  }
4112
4209
  /************************************************************************/
4113
4210
  /******** test bonds for bond tautomerism **************/
@@ -4156,6 +4253,8 @@ again:
4156
4253
  bError = ret;
4157
4254
  goto exit_function;
4158
4255
  }
4256
+ #if( BNS_RAD_SEARCH == 1 )
4257
+ #else
4159
4258
  /* discovered moveable charges and H-atoms may allow to cancel radicals */
4160
4259
  if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) {
4161
4260
  ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms );
@@ -4176,7 +4275,7 @@ again:
4176
4275
  goto exit_function;
4177
4276
  }
4178
4277
  }
4179
-
4278
+ #endif
4180
4279
  /****************** update bonds normalization ***************/
4181
4280
  if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) {
4182
4281
  /******************* find moveable charges ***************/
@@ -4346,9 +4445,16 @@ again:
4346
4445
  * and mark non-ring alt bonds non-stereogenic
4347
4446
  ************************************************/
4348
4447
 
4349
- ReInitBnStructForAltBns( pBNS, at, num_atoms );
4350
- MarkRingSystemsAltBns( pBNS );
4351
- MarkNonStereoAltBns( pBNS, at, num_atoms );
4448
+ ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 );
4449
+ MarkRingSystemsAltBns( pBNS, 0 );
4450
+ MarkNonStereoAltBns( pBNS, at, num_atoms, 0 );
4451
+ #if( FIX_EITHER_DB_AS_NONSTEREO == 1 )
4452
+ /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */
4453
+ /* stereobonds bonds that lost stereo get "Either" stereo_type */
4454
+ ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 );
4455
+ MarkRingSystemsAltBns( pBNS, 1 );
4456
+ MarkNonStereoAltBns( pBNS, at, num_atoms, 1 );
4457
+ #endif
4352
4458
  } else {
4353
4459
  bError = BNS_OUT_OF_RAM;
4354
4460
  /*printf("BNS_OUT_OF_RAM-3\n");*/
@@ -4812,7 +4918,9 @@ int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, in
4812
4918
  if ( (pBNS->vert[vlast].iedge - pBNS->iedge) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges ) {
4813
4919
  return BNS_VERT_EDGE_OVFL; /* iedges overflow */
4814
4920
  }
4815
-
4921
+ if ( pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0 ) {
4922
+ return BNS_VERT_EDGE_OVFL; /* neighbors overflow */
4923
+ }
4816
4924
  /* fill out the new edge, set its cap and flow, connect */
4817
4925
  /* memset( pEdge, 0, sizeof(*pEdge) ); */
4818
4926
  pEdge->cap = pEdge->cap0 = nCap;
@@ -4827,12 +4935,13 @@ int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, in
4827
4935
  pNewVert->num_adj_edges = 0;
4828
4936
  pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap;
4829
4937
  pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow;
4938
+ pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */
4830
4939
  pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges;
4831
4940
  pNewVert->type = BNS_VERT_TYPE_TEMP;
4832
4941
  *nDots += nCap - nFlow;
4833
4942
 
4834
- pEdge->neigh_ord[0] = pVert2->num_adj_edges;
4835
- pEdge->neigh_ord[1] = pNewVert->num_adj_edges;
4943
+ pEdge->neigh_ord[v2>vnew] = pVert2->num_adj_edges;
4944
+ pEdge->neigh_ord[v2<vnew] = pNewVert->num_adj_edges;
4836
4945
 
4837
4946
  /* connect new edge to v2 */
4838
4947
  pVert2->iedge[pVert2->num_adj_edges ++] = iedge;
@@ -4852,6 +4961,48 @@ int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, in
4852
4961
 
4853
4962
  return vnew;
4854
4963
  }
4964
+
4965
+ /*****************************************************************************************************/
4966
+ int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow )
4967
+ {
4968
+ int ip1 = p1 - pBNS->vert;
4969
+ int ip2 = p2 - pBNS->vert;
4970
+ int ie = pBNS->num_edges;
4971
+ BNS_EDGE *e = pBNS->edge + ie;
4972
+ /* debug: check bounds */
4973
+ if ( ip1 >= pBNS->max_vertices || ip1 < 0 ||
4974
+ ip2 >= pBNS->max_vertices || ip2 < 0 ||
4975
+ ie >= pBNS->max_edges || ie < 0 ||
4976
+ (p1->iedge - pBNS->iedge) < 0 ||
4977
+ (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges ||
4978
+ (p2->iedge - pBNS->iedge) < 0 ||
4979
+ (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges ||
4980
+ p1->num_adj_edges >= p1->max_adj_edges ||
4981
+ p2->num_adj_edges >= p2->max_adj_edges ) {
4982
+ return BNS_VERT_EDGE_OVFL;
4983
+ }
4984
+ /* clear the edge */
4985
+ memset( e, 0, sizeof(*e) );
4986
+ /* connect */
4987
+ e->neighbor1 = inchi_min( ip1, ip2 );
4988
+ e->neighbor12 = ip1 ^ ip2;
4989
+ p1->iedge[p1->num_adj_edges] = ie;
4990
+ p2->iedge[p2->num_adj_edges] = ie;
4991
+ e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++;
4992
+ e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++;
4993
+ e->cap = e->cap0 = nEdgeCap;
4994
+ e->flow = e->flow0 = nEdgeFlow;
4995
+ p1->st_edge.flow += nEdgeFlow;
4996
+ p2->st_edge.flow += nEdgeFlow;
4997
+ if ( p1->st_edge.cap < p1->st_edge.flow ) {
4998
+ p1->st_edge.cap = p1->st_edge.flow;
4999
+ }
5000
+ if ( p2->st_edge.cap < p2->st_edge.flow ) {
5001
+ p2->st_edge.cap = p2->st_edge.flow;
5002
+ }
5003
+ pBNS->num_edges ++;
5004
+ return ie;
5005
+ }
4855
5006
  /**********************************************************************************/
4856
5007
  BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type)
4857
5008
  {
@@ -5630,60 +5781,684 @@ int bIsBnsEndpoint( BN_STRUCT *pBNS, int v )
5630
5781
  return 0;
5631
5782
  }
5632
5783
  /**********************************************************************************/
5633
- /* Return value ret bits if not IS_BNS_ERROR(ret):
5634
-
5635
- ret & 1 => Success
5636
- ret & 2 => Bonds changed to Alt
5637
- (ret & ~3) >> 2 => nDelta: number of removed dots
5638
- */
5639
- int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at, int num_atoms,
5640
- int nVertDoubleBond, int nVertSingleBond, int path_type )
5784
+ #if ( BNS_RAD_SEARCH == 1 )
5785
+ /**********************************************************************************/
5786
+ int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 )
5641
5787
  {
5642
- ALT_PATH_CHANGES apc;
5643
- int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1;
5644
- AT_NUMB type;
5645
- BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1];
5646
- ENDPOINT_INFO eif;
5647
-
5648
- /* initialize */
5649
- switch( path_type ) {
5650
- case ALT_PATH_MODE_TAUTOM:
5651
- /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
5652
- type = BNS_VERT_TYPE_ENDPOINT;
5653
- bChangeFlow = BNS_EF_CHNG_RSTR;
5654
- if ( !at[nVertSingleBond].endpoint &&
5655
- (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) )
5656
- return 0;
5657
- if ( !at[nVertDoubleBond].endpoint &&
5658
- (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) )
5659
- return 0;
5788
+
5789
+ EdgeIndex iuv;
5790
+ Vertex v_O, v_ChgOrH;
5791
+ /* the previous atom along the path: should be a terminal atom */
5792
+ if ( v_1 == NO_VERTEX ) {
5793
+ v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv );
5794
+ }
5795
+ v_O = v_1 / 2 - 1;
5796
+ if ( v_O < 0 || v_O >= pBNS->num_atoms ) {
5797
+ return 0;
5798
+ }
5799
+ /* make sure v_O is a terminal atom: its second neighbor is not an atom */
5800
+ if ( pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM ) {
5801
+ return 0;
5802
+ }
5803
+ /* the next to previous vertex vertex along the path: should be a Charge or Taut group vertex */
5804
+ if ( v_2 == NO_VERTEX ) {
5805
+ v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv );
5806
+ }
5807
+ v_ChgOrH = v_2 / 2 - 1;
5808
+ if ( v_ChgOrH < pBNS->num_atoms ) {
5809
+ return 0;
5810
+ }
5811
+ /* make sure v_ChgOrH is a charge or taut_group */
5812
+ if ( pBNS->vert[v_ChgOrH].type & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP) )
5813
+ return 1;
5814
+ return 0;
5815
+ }
5816
+ /**********************************************************************************/
5817
+ int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u)
5818
+ {
5819
+ EdgeIndex iuv;
5820
+ int i, num_found;
5821
+ Vertex v, w;
5822
+ Vertex u_last, v2;
5823
+ switch( pBD->bRadSrchMode ) {
5824
+ case RAD_SRCH_NORM:
5825
+ /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */
5826
+ /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */
5827
+ /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms */
5828
+ num_found = 0;
5829
+ while ( u > Vertex_t && (u % 2 || u/2 > pBNS->num_atoms ) ) {
5830
+ u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv );
5831
+ }
5832
+ w = u/2 - 1; /* Check whether u is a radical endpoint */
5833
+ if ( Vertex_t < u && w < pBNS->num_atoms &&
5834
+ pBNS->vert[w].st_edge.cap == (pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK) ) {
5835
+ /* u is an atom; it is not a radical atom */
5836
+ /* now search for the starting radical atom by following the path back from u */
5837
+ v = u_last = u;
5838
+ while( v > Vertex_t ) {
5839
+ u = v;
5840
+ v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */
5841
+ }
5842
+ /* check whether u is a radical atom */
5843
+ if ( !(u%2) && Vertex_t < u &&
5844
+ (u = u/2 - 1) < pBNS->num_atoms &&
5845
+ pBNS->vert[u].st_edge.cap > (pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK) ) {
5846
+ /* at pBNS->vert[u] we have found the radical that originated the path */
5847
+ /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */
5848
+ /* to which the radical may be moved (farthest reachable atom) */
5849
+
5850
+ /* add *all* atoms that may receive radical from u_rad */
5851
+ /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */
5852
+
5853
+ for ( v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ) ) {
5854
+ if ( !(v%2) && (v2 = v/2 - 1) < pBNS->num_atoms &&
5855
+ pBNS->vert[v2].st_edge.cap == (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) ) {
5856
+ /* check exception */
5857
+ if ( pBNS->type_TACN &&
5858
+ bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ) ) {
5859
+ continue;
5860
+ }
5861
+ /* add */
5862
+ for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) {
5863
+ /* check whether this pair, (u,w), has already been saved */
5864
+ if ( u == pBD->RadEndpoints[i] &&
5865
+ v2 == pBD->RadEndpoints[i+1] ) {
5866
+ break;
5867
+ }
5868
+ }
5869
+ if ( i >= pBD->nNumRadEndpoints ) {
5870
+ /* add new (u,w) pair */
5871
+ if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) {
5872
+ /* add */
5873
+ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */
5874
+ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = v2; /* endpoint */
5875
+ num_found ++;
5876
+ /*return 1;*/ /* registered */
5877
+ } else {
5878
+ return BNS_VERT_EDGE_OVFL;
5879
+ }
5880
+ }
5881
+ }
5882
+ }
5883
+ if ( num_found ) {
5884
+ return 1;
5885
+ }
5886
+ }
5887
+ }
5660
5888
  break;
5661
5889
 
5662
- case ALT_PATH_MODE_CHARGE:
5663
- /* Find alt path allowing to move (+). Purpose: establish "charge groups",
5664
- mark alt. bonds due to (+) charge movement */
5665
- type = BNS_VERT_TYPE_C_POINT;
5666
- bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS);
5890
+ case RAD_SRCH_FROM_FICT:
5891
+ /* find nearest atom accessible from a fictitious vertex */
5892
+ /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */
5893
+ v = u;
5894
+ w = NO_VERTEX; /* the nearest atom -- radical-endpoint */
5895
+ u = NO_VERTEX; /* fictitious vertex carrying a radical */
5896
+ while ( v > Vertex_t ) {
5897
+ u = v;
5898
+ if ( !(v % 2) && v/2 <= pBNS->num_atoms &&
5899
+ pBNS->vert[v/2-1].st_edge.cap - pBNS->vert[v/2-1].st_edge.flow < 2 ) {
5900
+ w = v; /* vertex w is atom that may be singlet or doublet but not triplet */
5901
+ }
5902
+ v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv );
5903
+ }
5904
+ v = u/2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */
5905
+ if ( w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms ||
5906
+ pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow ||
5907
+ (w = w/2 - 1) >= pBNS->num_atoms ) {
5908
+ break; /* reject */
5909
+ }
5910
+ u = v;
5911
+ /* at pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */
5912
+ for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) {
5913
+ if ( u == pBD->RadEndpoints[i] &&
5914
+ w == pBD->RadEndpoints[i+1] ) {
5915
+ break; /* this pair has already been stored */
5916
+ }
5917
+ }
5918
+ if ( i >= pBD->nNumRadEndpoints ) {
5919
+ /* a new pair has been found */
5920
+ if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) {
5921
+ /* add */
5922
+ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */
5923
+ pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = w; /* endpoint */
5924
+ return 1; /* registered */
5925
+ } else {
5926
+ return BNS_VERT_EDGE_OVFL;
5927
+ }
5928
+ }
5667
5929
  break;
5930
+ }
5668
5931
 
5669
- case ALT_PATH_MODE_4_SALT:
5670
- case ALT_PATH_MODE_4_SALT2:
5671
- /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms".
5672
- Purpose: mark alt bonds due to this "long range" tautomerism. */
5673
- type = BNS_VERT_TYPE_ENDPOINT;
5674
- bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS);
5675
- if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ &&
5676
- (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) )
5677
- return 0;
5678
- if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ &&
5679
- (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) )
5680
- return 0;
5681
- memset( &apc, 0, sizeof(apc) );
5682
- break;
5932
+ return 0; /* rejected */
5933
+ }
5934
+ /**********************************************************************************/
5935
+ int cmp_rad_endpoints( const void *a1, const void *a2 )
5936
+ {
5937
+ /* Vertex radical_vertex, radical_endpoint */
5938
+ const Vertex *p1 = (const Vertex *)a1;
5939
+ const Vertex *p2 = (const Vertex *)a2;
5940
+ if ( p1[0] < p2[0] )
5941
+ return -1;
5942
+ if ( p1[0] > p2[0] )
5943
+ return 1;
5944
+ if ( p1[1] < p2[1] )
5945
+ return -1;
5946
+ if ( p1[1] > p2[1] )
5947
+ return 1;
5948
+ return 0;
5949
+ }
5950
+ /**********************************************************************************/
5951
+ int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
5952
+ {
5953
+ BNS_EDGE *e;
5954
+ EdgeIndex ie;
5955
+ BNS_VERTEX *p1, *p2;
5956
+ Vertex v1, v2;
5957
+ int i, delta, rad;
5958
+ for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) {
5959
+ ie = pBD->RadEdges[i];
5960
+ if ( ie < 0 || ie >= pBNS->num_edges ) {
5961
+ goto error_exit;
5962
+ }
5963
+ e = pBNS->edge + ie;
5964
+ v1 = e->neighbor1;
5965
+ v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */
5966
+ if ( ie + 1 != pBNS->num_edges ||
5967
+ v1 < 0 || v1 >= pBNS->num_vertices ||
5968
+ v2 < 0 || v2 >= pBNS->num_vertices ) {
5969
+ goto error_exit;
5970
+ }
5971
+ p1 = pBNS->vert + v1;
5972
+ p2 = pBNS->vert + v2;
5973
+
5974
+ if ( p2->iedge[p2->num_adj_edges-1] != ie ||
5975
+ p1->iedge[p1->num_adj_edges-1] != ie ) {
5976
+ goto error_exit;
5977
+ }
5978
+ p2->num_adj_edges --;
5979
+ p1->num_adj_edges --;
5980
+ p2->iedge[p2->num_adj_edges] = 0;
5981
+ p1->iedge[p1->num_adj_edges] = 0;
5982
+ p2->st_edge.flow -= e->flow;
5983
+ p1->st_edge.flow -= e->flow;
5984
+
5985
+ if ( !p2->num_adj_edges && v2 >= pBNS->num_atoms ) {
5986
+ if ( v2+1 != pBNS->num_vertices ) {
5987
+ goto error_exit;
5988
+ }
5989
+ memset( p2, 0, sizeof(*p2) );
5990
+ pBNS->num_vertices --;
5991
+ }
5992
+ if ( !p1->num_adj_edges && v1 >= pBNS->num_atoms ) {
5993
+ if ( v1+1 != pBNS->num_vertices ) {
5994
+ goto error_exit;
5995
+ }
5996
+ memset( p1, 0, sizeof(*p1) );
5997
+ pBNS->num_vertices --;
5998
+ }
5999
+ if ( at && v1 < pBNS->num_atoms ) {
6000
+ delta = p1->st_edge.cap - p1->st_edge.flow;
6001
+ rad = at[v1].radical;
6002
+ switch( delta ) {
6003
+ case 0:
6004
+ if ( rad == RADICAL_DOUBLET )
6005
+ rad = 0;
6006
+ break;
6007
+ case 1:
6008
+ if ( rad != RADICAL_DOUBLET )
6009
+ rad = RADICAL_DOUBLET;
6010
+ }
6011
+ at[v1].radical = rad;
6012
+ }
6013
+ memset( e, 0, sizeof(*e) );
6014
+ pBNS->num_edges --;
6015
+ }
6016
+ pBD->nNumRadEdges = 0;
6017
+ pBD->nNumRadicals = 0;
6018
+ pBD->bRadSrchMode = RAD_SRCH_NORM;
6019
+ return 0;
6020
+ error_exit:
6021
+ return BNS_PROGRAM_ERR;
6022
+ }
6023
+ /**********************************************************************************/
6024
+ int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
6025
+ {
6026
+ BNS_EDGE *e;
6027
+ EdgeIndex ie;
6028
+ BNS_VERTEX *p1, *p2;
6029
+ Vertex v1, v2;
6030
+ int i, delta, rad;
6031
+ int p1_num_adj_edges, p2_num_adj_edges;
6032
+
6033
+ for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) {
6034
+ ie = pBD->RadEdges[i];
6035
+ if ( ie < 0 || ie >= pBNS->num_edges ) {
6036
+ goto error_exit;
6037
+ }
6038
+ e = pBNS->edge + ie;
6039
+ v1 = e->neighbor1; /* atom */
6040
+ v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */
6041
+ if ( v1 < 0 || v1 >= pBNS->num_atoms ||
6042
+ v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices ) {
6043
+ goto error_exit;
6044
+ }
6045
+ p1 = pBNS->vert + v1;
6046
+ p2 = pBNS->vert + v2;
6047
+
6048
+ p1_num_adj_edges = e->neigh_ord[0];
6049
+ p2_num_adj_edges = e->neigh_ord[1];
6050
+
6051
+ if ( p2->iedge[p2_num_adj_edges] != ie ||
6052
+ p1->iedge[p1_num_adj_edges] != ie ) {
6053
+ goto error_exit;
6054
+ }
6055
+
6056
+ if ( at && v1 < pBNS->num_atoms ) {
6057
+ delta = p1->st_edge.cap - p1->st_edge.flow + e->flow;
6058
+ rad = at[v1].radical;
6059
+ switch( delta ) {
6060
+ case 0:
6061
+ if ( rad == RADICAL_DOUBLET )
6062
+ rad = 0;
6063
+ break;
6064
+ case 1:
6065
+ if ( rad != RADICAL_DOUBLET )
6066
+ rad = RADICAL_DOUBLET;
6067
+ }
6068
+ at[v1].radical = rad;
6069
+ }
6070
+ }
6071
+ return 0;
6072
+ error_exit:
6073
+ return BNS_PROGRAM_ERR;
6074
+ }
6075
+ /**********************************************************************************/
6076
+ int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
6077
+ {
6078
+ int ret, i, j, k, num_new_edges, delta;
6079
+ BNS_VERTEX *pRad, *pEndp;
6080
+ Vertex wRad, vRad, vEndp, nNumRadicals;
6081
+ int nDots=0 /* added initialization, 2006-03 */, nNumEdges;
6082
+ if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) {
6083
+ return 0;
6084
+ }
6085
+ pBD->nNumRadEndpoints = 0;
6086
+ pBD->nNumRadEdges = 0;
6087
+ pBD->bRadSrchMode = bRadSrchMode;
6088
+ pBNS->alt_path = pBNS->altp[0];
6089
+ pBNS->bChangeFlow = 0;
6090
+ ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH );
6091
+ ReInitBnData( pBD );
6092
+ ReInitBnStructAltPaths( pBNS );
6093
+ if ( !ret && pBD->nNumRadEndpoints >= 2 ) {
6094
+ /* sort by radical locations */
6095
+ qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints );
6096
+ num_new_edges = 0;
6097
+ nNumRadicals = 0;
6098
+ /* create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */
6099
+ /* connecting the new vertices radical vertices */
6100
+ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) {
6101
+ wRad = pBD->RadEndpoints[i];
6102
+ pRad = pBNS->vert + wRad;
6103
+ delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK);
6104
+ if ( delta <= 0 ) {
6105
+ delta = 1;
6106
+ }
6107
+ nNumEdges = 0;
6108
+ for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) {
6109
+ nNumEdges ++;
6110
+ }
6111
+ /* add new aux vertex to the radical atom/vertex */
6112
+ vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots );
6113
+ if ( IS_BNS_ERROR( vRad ) ) {
6114
+ ret = vRad;
6115
+ goto error_exit;
6116
+ }
6117
+ pRad = pBNS->vert + vRad;
6118
+ pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1];
6119
+ /* replace references to vertex wRad with vRad */
6120
+ for ( k = i, nNumEdges = 0; k < j; k += 2 ) {
6121
+ pBD->RadEndpoints[k] = vRad;
6122
+ }
6123
+ nNumRadicals ++;
6124
+ }
6125
+ /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */
6126
+ /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */
6127
+ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) {
6128
+ vRad = pBD->RadEndpoints[i];
6129
+ pRad = pBNS->vert + vRad;
6130
+ for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) {
6131
+ /* connect vew vertex pRad to radical endpoints */
6132
+ vEndp = pBD->RadEndpoints[j+1];
6133
+ pEndp = pBNS->vert + vEndp;
6134
+ ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 );
6135
+ if ( IS_BNS_ERROR( ret ) ) {
6136
+ goto error_exit;
6137
+ }
6138
+ pBD->RadEdges[pBD->nNumRadEdges ++] = ret;
6139
+ }
6140
+ }
6141
+ pBD->nNumRadicals = nNumRadicals;
6142
+ return nNumRadicals; /* done */
6143
+ }
6144
+ return 0; /* nothing to do */
5683
6145
 
5684
- case ALT_PATH_MODE_REM2H_CHG:
5685
- bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */
5686
- case ALT_PATH_MODE_REM2H_TST:
6146
+ error_exit:
6147
+ RemoveRadEndpoints( pBNS, pBD, NULL );
6148
+ return ret;
6149
+
6150
+ }
6151
+ /**********************************************************************************/
6152
+ #define MAX_NUM_RAD 256
6153
+ /***************************************************************************/
6154
+ int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
6155
+ {
6156
+ int ret = 0, i, j, k, n, num_new_edges, delta = 1;
6157
+ BNS_VERTEX *pRad, *pEndp;
6158
+ Vertex wRad, vRad, vEndp, nNumRadicals;
6159
+ Vertex vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD];
6160
+ int nNumRad = 0;
6161
+ int edge_flow;
6162
+ int nDots=0 /* added initialization, 2006-03 */, nNumEdges;
6163
+ NodeSet VertSet;
6164
+ if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) {
6165
+ return 0;
6166
+ }
6167
+ /* find all radicals: their vertices have st_cap-st_flow=delta */
6168
+ /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */
6169
+ for ( i = 0; i < pBNS->num_atoms; i ++ ) {
6170
+ if ( pBNS->vert[i].st_edge.cap - delta == (pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK) ) {
6171
+ if ( nNumRad < MAX_NUM_RAD ) {
6172
+ pBNS->vert[i].st_edge.cap -= delta;
6173
+ pBNS->tot_st_cap -= delta;
6174
+ vRadList[nNumRad] = i; /* radical position; i > j <=> vRadList[i] > vRadList[j] */
6175
+ vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable
6176
+ * atoms in common with this radical atom
6177
+ * always keep vRadEqul[nNumRad] <= nNumRad */
6178
+ nNumRad ++;
6179
+ }
6180
+ }
6181
+ }
6182
+ if ( pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad ) {
6183
+ return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */
6184
+ }
6185
+ memset( &VertSet, 0, sizeof(VertSet) );
6186
+ /* find reachable atoms by enabling each radical separately */
6187
+ for ( j = 0; j < nNumRad; j ++ ) {
6188
+ i = vRadList[j];
6189
+ pBD->nNumRadEndpoints = 0;
6190
+ pBD->nNumRadEdges = 0;
6191
+ pBD->bRadSrchMode = bRadSrchMode;
6192
+ pBNS->alt_path = pBNS->altp[0];
6193
+ pBNS->bChangeFlow = 0;
6194
+ pBNS->vert[i].st_edge.cap += delta; /* enable single radical */
6195
+ pBNS->tot_st_cap += delta;
6196
+ ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */
6197
+ ReInitBnData( pBD );
6198
+ ReInitBnStructAltPaths( pBNS );
6199
+ pBD->bRadSrchMode = RAD_SRCH_NORM;
6200
+ pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */
6201
+ pBNS->tot_st_cap -= delta;
6202
+ if ( IS_BNS_ERROR( ret ) ) {
6203
+ goto error_exit;
6204
+ } else
6205
+ if ( ret ) {
6206
+ ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */
6207
+ goto error_exit;
6208
+ }
6209
+ if ( !ret && pBD->nNumRadEndpoints >= 2 ) {
6210
+ /* sort by: primary_key=radical locations, secondary_key=radical endoint */
6211
+ qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints );
6212
+ if ( pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints-2] != i ) {
6213
+ ret = BNS_RADICAL_ERR; /* more than one radical vertex */
6214
+ goto error_exit;
6215
+ }
6216
+ if ( nNumRad > 1 ) {
6217
+ /* if more than one radical then save reachable atoms in bitmaps to allow */
6218
+ /* faster finding whether same atoms are reachable by two or more radicals */
6219
+ /* Later merge such sets */
6220
+ if ( NULL == VertSet.bitword ) {
6221
+ SetBitCreate( );
6222
+ if ( !NodeSetCreate( &VertSet, pBNS->num_atoms, nNumRad ) ) {
6223
+ ret = BNS_OUT_OF_RAM; /* out of RAM */
6224
+ goto error_exit;
6225
+ }
6226
+ }
6227
+ NodeSetFromRadEndpoints( &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints);
6228
+ /* do not allow any radical center be treated as a reachable atom: */
6229
+ RemoveFromNodeSet( &VertSet, j, vRadList, nNumRad );
6230
+ }
6231
+ }
6232
+ }
6233
+ /* restore radical st_cap so that st_cap-st_flow=delta */
6234
+ for ( j = 0; j < nNumRad; j ++ ) {
6235
+ i = vRadList[j];
6236
+ pBNS->vert[i].st_edge.cap += delta;
6237
+ pBNS->tot_st_cap += delta;
6238
+ }
6239
+ /* merge lists that have common radical endpoints */
6240
+ /* defect: if vertex sets i and j do not intersect they will be compared 2 times */
6241
+ /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */
6242
+ if ( nNumRad > 1 ) {
6243
+ for ( i = 0; i < nNumRad; i ++ ) {
6244
+ if ( vRadEqul[i] != i )
6245
+ continue;
6246
+ do {
6247
+ n = 0;
6248
+ for ( j = i+1; j < nNumRad; j ++ ) {
6249
+ if ( vRadEqul[j] != j )
6250
+ continue;
6251
+ if ( DoNodeSetsIntersect( &VertSet, i, j) ) {
6252
+ AddNodeSet2ToNodeSet1( &VertSet, i, j);
6253
+ vRadEqul[j] = i; /* Set j was copied to set i; i < j */
6254
+ n ++;
6255
+ }
6256
+ }
6257
+ } while( n );
6258
+ }
6259
+ /* fill out pBD->RadEndpoints[] */
6260
+ for ( i = 0, n = 0; i < nNumRad; i ++ ) {
6261
+ if ( i == vRadEqul[i] ) {
6262
+ if ( !IsNodeSetEmpty( &VertSet, i) ) {
6263
+ /* store equivalent radicals */
6264
+ for ( j = i+1; j < nNumRad; j ++ ) {
6265
+ if (i == vRadEqul[j] ) {
6266
+ pBD->RadEndpoints[n++] = vRadList[i];
6267
+ pBD->RadEndpoints[n++] = -vRadList[j]-2; /* equivalent radical, alvays not zero */
6268
+ }
6269
+ }
6270
+ /* store endpoints */
6271
+ n = AddNodesToRadEndpoints( &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv );
6272
+ if ( n < 0 ) {
6273
+ ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */
6274
+ goto error_exit;
6275
+ }
6276
+ } else {
6277
+ pBD->RadEndpoints[n++] = vRadList[i];
6278
+ pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */
6279
+ }
6280
+ }
6281
+ }
6282
+ pBD->nNumRadEndpoints = n;
6283
+ NodeSetFree( &VertSet );
6284
+ } else
6285
+ if ( nNumRad == 1 && !pBD->nNumRadEndpoints ) {
6286
+ /* 2006-07-30: a single radical; no possible endpoint found */
6287
+ for ( i = 0, n = 0; i < nNumRad; i ++ ) {
6288
+ pBD->RadEndpoints[n++] = vRadList[i];
6289
+ pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */
6290
+ }
6291
+ pBD->nNumRadEndpoints = n;
6292
+ }
6293
+
6294
+ if ( !ret && pBD->nNumRadEndpoints >= 2 ) {
6295
+ /* already sorted by radical locations */
6296
+ num_new_edges = 0;
6297
+ nNumRadicals = 0;
6298
+ /**************************************************************************
6299
+ * create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1
6300
+ * connecting the new vertices radical vertices
6301
+ *
6302
+ *
6303
+ * Original structure: atom A is a radical center A==B--C*--D==E
6304
+ * A*--B==C--D==E atoms C and E are reachable: A==B--C===D--E*
6305
+ *
6306
+ * Resultant temporary structure:
6307
+ * A---B==C--D==E
6308
+ * || / /
6309
+ * || / / The additional new vertex (*) and its
6310
+ * || / / 3 edges replace the radical with alternating
6311
+ * || / / circuits that allow same bond changes
6312
+ * || / / as moving the radical to atoms C or E.
6313
+ * ||// "Double bonds" here have edge cap=1, flow=1
6314
+ * (*) "Single bonds" have edge cap=1, flow=0
6315
+ *
6316
+ * The "equivalent radical centers" (which have at least one reachable atom
6317
+ * in common) are connected to (*) with "double bonds" (edge cap=1, flow=1).
6318
+ * Reachable non-radical atoms are connected by edges with cap=1, flow=0
6319
+ * After running BNS to find alt.path a "double bond" from (*) may move
6320
+ * to another atom thus muving the radical.
6321
+ *
6322
+ * Number of additional (*) vertices = number of sets of
6323
+ * "equivalent radical centers".
6324
+ * Each such a set may include one or more radical centers.
6325
+ *
6326
+ * The radicals will be re-created in RemoveRadEndpoints()
6327
+ ***************************************************************************/
6328
+ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) {
6329
+ wRad = pBD->RadEndpoints[i];
6330
+ pRad = pBNS->vert + wRad;
6331
+ delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK);
6332
+ if ( delta <= 0 ) {
6333
+ delta = 1;
6334
+ }
6335
+ nNumEdges = 0;
6336
+ for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) {
6337
+ nNumEdges += (pBD->RadEndpoints[j+1] != -1); /* immobile radicals have one edge only */
6338
+ }
6339
+ /* add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */
6340
+ /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */
6341
+ vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots );
6342
+ if ( IS_BNS_ERROR( vRad ) ) {
6343
+ ret = vRad;
6344
+ goto error_exit;
6345
+ }
6346
+ pRad = pBNS->vert + vRad;
6347
+ pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1];
6348
+ /* replace references to vertex wRad with vRad */
6349
+ for ( k = i, nNumEdges = 0; k < j; k += 2 ) {
6350
+ pBD->RadEndpoints[k] = vRad;
6351
+ }
6352
+ nNumRadicals ++;
6353
+ }
6354
+ /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */
6355
+ /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */
6356
+ for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) {
6357
+ vRad = pBD->RadEndpoints[i];
6358
+ pRad = pBNS->vert + vRad;
6359
+ for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) {
6360
+ /* connect vew vertex pRad to radical endpoints */
6361
+ vEndp = pBD->RadEndpoints[j+1];
6362
+ if ( vEndp == -1 )
6363
+ continue;
6364
+ if ( vEndp < 0 ) {
6365
+ edge_flow = 1;
6366
+ vEndp = -vEndp - 2; /* equivalent radical centers */
6367
+ } else {
6368
+ edge_flow = 0;
6369
+ }
6370
+ pEndp = pBNS->vert + vEndp;
6371
+ ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow );
6372
+ if ( IS_BNS_ERROR( ret ) ) {
6373
+ goto error_exit;
6374
+ }
6375
+ pBD->RadEdges[pBD->nNumRadEdges ++] = ret;
6376
+ }
6377
+ }
6378
+ pBD->nNumRadicals = nNumRadicals;
6379
+ return nNumRadicals; /* done */
6380
+ }
6381
+ return 0; /* nothing to do */
6382
+
6383
+ error_exit:
6384
+ RemoveRadEndpoints( pBNS, pBD, NULL );
6385
+ NodeSetFree( &VertSet );
6386
+ return ret;
6387
+
6388
+ }
6389
+
6390
+
6391
+ #else
6392
+ /**********************************************************************************/
6393
+ int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
6394
+ {
6395
+ return 0;
6396
+ }
6397
+ int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at )
6398
+ {
6399
+ return 0;
6400
+ }
6401
+ int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode )
6402
+ {
6403
+ return 0;
6404
+ }
6405
+ #endif
6406
+ /**********************************************************************************/
6407
+ /* Return value ret bits if not IS_BNS_ERROR(ret):
6408
+
6409
+ ret & 1 => Success
6410
+ ret & 2 => Bonds changed to Alt
6411
+ (ret & ~3) >> 2 => nDelta: number of removed dots
6412
+ */
6413
+ int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at, int num_atoms,
6414
+ int nVertDoubleBond, int nVertSingleBond, int path_type )
6415
+ {
6416
+ ALT_PATH_CHANGES apc;
6417
+ int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1;
6418
+ int bAdjustRadicals = 0;
6419
+ AT_NUMB type;
6420
+ BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1];
6421
+ ENDPOINT_INFO eif;
6422
+
6423
+ /* initialize */
6424
+ switch( path_type ) {
6425
+ case ALT_PATH_MODE_TAUTOM:
6426
+ /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */
6427
+ type = BNS_VERT_TYPE_ENDPOINT;
6428
+ bChangeFlow = BNS_EF_CHNG_RSTR;
6429
+ if ( !at[nVertSingleBond].endpoint &&
6430
+ (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) )
6431
+ return 0;
6432
+ if ( !at[nVertDoubleBond].endpoint &&
6433
+ (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) )
6434
+ return 0;
6435
+ break;
6436
+
6437
+ case ALT_PATH_MODE_CHARGE:
6438
+ /* Find alt path allowing to move (+). Purpose: establish "charge groups",
6439
+ mark alt. bonds due to (+) charge movement */
6440
+ type = BNS_VERT_TYPE_C_POINT;
6441
+ bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS);
6442
+ break;
6443
+
6444
+ case ALT_PATH_MODE_4_SALT:
6445
+ case ALT_PATH_MODE_4_SALT2:
6446
+ /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms".
6447
+ Purpose: mark alt bonds due to this "long range" tautomerism. */
6448
+ type = BNS_VERT_TYPE_ENDPOINT;
6449
+ bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS);
6450
+ if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ &&
6451
+ (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) )
6452
+ return 0;
6453
+ if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ &&
6454
+ (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) )
6455
+ return 0;
6456
+ memset( &apc, 0, sizeof(apc) );
6457
+ break;
6458
+
6459
+ case ALT_PATH_MODE_REM2H_CHG:
6460
+ bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */
6461
+ case ALT_PATH_MODE_REM2H_TST:
5687
6462
  bChangeFlow |= BNS_EF_CHNG_RSTR;
5688
6463
  type = BNS_VERT_TYPE_ENDPOINT;
5689
6464
  /* allow non-tautomeric donors or any tautomeric atom */
@@ -5727,27 +6502,37 @@ int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at,
5727
6502
  bSuccess = 0;
5728
6503
  nDelta = 0;
5729
6504
 
6505
+ ret = SetRadEndpoints2( pBNS, pBD, RAD_SRCH_NORM );
6506
+ if ( IS_BNS_ERROR( ret ) ) {
6507
+ return ret;
6508
+ }
6509
+
5730
6510
  /* set BNS to check alt path */
5731
6511
  ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots );
5732
6512
  switch( ret ) {
5733
6513
  case BNS_CHK_ALTP_NO_ALTPATH:
5734
- return 0;
6514
+ ret = RemoveRadEndpoints( pBNS, pBD, NULL );
6515
+ return ret;
5735
6516
  case BNS_CHK_ALTP_SAME_TGROUP:
5736
6517
  bSuccess = 1;
5737
6518
  goto reinit_BNS;
5738
6519
  case BNS_CHK_ALTP_SAME_VERTEX:
5739
- return 1; /* very strange ... set a breakpoint here */
6520
+ ret = RemoveRadEndpoints( pBNS, pBD, NULL );
6521
+ return ret? ret : 1; /* very strange ... set a breakpoint here */
5740
6522
  case BNS_CHK_ALTP_SET_SUCCESS:
5741
6523
  break; /* actually check the existence of the altpath */
5742
6524
  case BNS_CANT_SET_BOND:
5743
6525
  goto reinit_BNS;
5744
6526
  default:
6527
+ ret_val = RemoveRadEndpoints( pBNS, pBD, NULL );
5745
6528
  if ( IS_BNS_ERROR( ret ) ) {
5746
6529
  return ret;
5747
6530
  }
5748
6531
  return BNS_PROGRAM_ERR;
5749
6532
  }
5750
6533
 
6534
+ bAdjustRadicals = ( (bChangeFlow & BNS_EF_UPD_RAD_ORI) && !(bChangeFlow & BNS_EF_RSTR_FLOW) );
6535
+
5751
6536
  /*****************************************************************
5752
6537
  * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges)
5753
6538
  * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater
@@ -5769,9 +6554,12 @@ int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at,
5769
6554
  if ( pAATG && pAATG->nMarkedAtom ) {
5770
6555
  if ( pAATG->nAtTypeTotals && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) {
5771
6556
  memset( pAATG->nMarkedAtom, 0, num_atoms*sizeof(pAATG->nMarkedAtom[0]) );
5772
- /* subtract */
6557
+ /* mark atoms that have charge or H changed, check their input types (that is, before changes),
6558
+ and subtract their input charge/H from nAtTypeTotals */
5773
6559
  SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 );
5774
- /* change */
6560
+ /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */
6561
+ /* Atom types will be checked and nAtTypeTotals will be changed in
6562
+ AddChangedAtHChargeBNS() later */
5775
6563
  SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 );
5776
6564
  } else
5777
6565
  if ( !pAATG->nAtTypeTotals ){
@@ -5805,6 +6593,15 @@ int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at,
5805
6593
  bError = BNS_BOND_ERR;
5806
6594
  }
5807
6595
  if ( !bError && pAATG && pAATG->nMarkedAtom && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) {
6596
+ /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */
6597
+ if ( bAdjustRadicals ) {
6598
+ ret_val = RestoreRadicalsOnly( pBNS, pBD, at );
6599
+ if ( IS_BNS_ERROR( ret_val ) ) {
6600
+ bError = ret_val;
6601
+ }
6602
+ }
6603
+ /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */
6604
+ /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */
5808
6605
  AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom );
5809
6606
  if ( bChangeFlow & BNS_EF_CHNG_FLOW ) {
5810
6607
  /* eliminate ambiguities in already changed flow:
@@ -5823,8 +6620,9 @@ reinit_BNS:
5823
6620
  /* --- reinitialize to repeat the calculations --- */
5824
6621
  bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE );
5825
6622
  bRestoreFlowAfterCheckOneBond( pBNS, fcd );
6623
+ ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals? at : NULL );
5826
6624
  ReInitBnStructAltPaths( pBNS );
5827
- return bError? bError : (bSuccess + 4*nDelta);
6625
+ return bError? bError : ret_val? ret_val : (bSuccess + 4*nDelta);
5828
6626
  }
5829
6627
 
5830
6628
 
@@ -5840,12 +6638,23 @@ BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtom
5840
6638
  int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond;
5841
6639
  int tot_st_cap, tot_st_flow;
5842
6640
  int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp;
6641
+ #if( BNS_RAD_SEARCH == 1 )
6642
+ int num_rad = 0;
5843
6643
 
5844
- max_tg = num_atoms / 2;
6644
+ nMaxAddEdges += 1;
6645
+ #endif
6646
+ #if( FIX_NUM_TG == 1 )
6647
+ max_tg = inchi_max( num_atoms / 2, 5);
6648
+ #else
6649
+ max_tg = num_atoms;
6650
+ #endif
5845
6651
  num_changed_bonds = 0;
5846
6652
 
5847
6653
  for ( i = 0, num_bonds = 0; i < num_atoms; i ++ ) {
5848
6654
  num_bonds += at[i].valence;
6655
+ #if( BNS_RAD_SEARCH == 1 )
6656
+ num_rad += (at[i].radical == RADICAL_DOUBLET);
6657
+ #endif
5849
6658
  }
5850
6659
  /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */
5851
6660
  /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */
@@ -5855,6 +6664,12 @@ BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtom
5855
6664
  max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1;
5856
6665
  /* +max_tg for edges between t-groups and super-tautomeric group */
5857
6666
  max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices + max_tg;
6667
+ #if( BNS_RAD_SEARCH == 1 )
6668
+ if ( num_rad ) {
6669
+ max_vertices *= 2;
6670
+ max_edges *= 2;
6671
+ }
6672
+ #endif
5858
6673
  max_iedges = 2*max_edges;
5859
6674
  len_alt_path = max_vertices+iALTP_HDR_LEN+1; /* may overflow if an edge is traversed in 2 directions */
5860
6675
 
@@ -5942,8 +6757,9 @@ BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtom
5942
6757
  }
5943
6758
  bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK);
5944
6759
  bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK);
5945
- if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE ) {
5946
- /* make unknown bonds single */
6760
+ if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE &&
6761
+ bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/ ) {
6762
+ /* make Unknown or Alternating bonds single */
5947
6763
  bond_type = 1;
5948
6764
  at[i].bond_type[j] = bond_mark | bond_type;
5949
6765
  num_changed_bonds ++;
@@ -6429,15 +7245,22 @@ int AddCGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, C_GROUP_I
6429
7245
  for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) {
6430
7246
  int iedge = vertex_cpoint->iedge[k];
6431
7247
  VertexFlow nNewCap = vertex_cpoint->st_edge.cap;
7248
+ centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point);
6432
7249
  if ( !pBNS->edge[iedge].cap ) {
6433
7250
  /* single bond, possibly between c_point and centerpoint */
6434
- centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point);
6435
7251
  if ( centerpoint < pBNS->num_atoms &&
6436
7252
  pBNS->vert[centerpoint].st_edge.cap >= 1 ) {
6437
7253
  nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap );
6438
7254
  nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP );
6439
7255
  pBNS->edge[iedge].cap = nNewCap;
6440
7256
  }
7257
+ #if( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */
7258
+ else
7259
+ if ( centerpoint < pBNS->num_atoms &&
7260
+ edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) {
7261
+ pBNS->edge[iedge].cap ++;
7262
+ }
7263
+ #endif
6441
7264
  }
6442
7265
  }
6443
7266
  #endif /* } FIX_CPOINT_BOND_CAP */
@@ -6495,6 +7318,14 @@ BN_DATA *DeAllocateBnData( BN_DATA *pBD )
6495
7318
  inchi_free( pBD->Pu );
6496
7319
  if ( pBD->Pv )
6497
7320
  inchi_free( pBD->Pv );
7321
+ #if( BNS_RAD_SEARCH == 1 )
7322
+ if ( pBD->RadEndpoints ) {
7323
+ inchi_free( pBD->RadEndpoints );
7324
+ }
7325
+ if ( pBD->RadEdges ) {
7326
+ inchi_free( pBD->RadEdges );
7327
+ }
7328
+ #endif
6498
7329
  inchi_free( pBD );
6499
7330
  }
6500
7331
  return NULL;
@@ -6507,12 +7338,17 @@ BN_DATA *AllocateAndInitBnData( int max_num_vertices )
6507
7338
  int max_len_Pu_Pv;
6508
7339
  max_num_vertices = 2*max_num_vertices+2;
6509
7340
  max_len_Pu_Pv = max_num_vertices/2+1;
7341
+ max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */
6510
7342
  if ( !(pBD = (BN_DATA *) inchi_calloc( 1, sizeof(BN_DATA) ) ) ||
6511
7343
  !(pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) ||
6512
7344
  !(pBD->SwitchEdge = (Edge *) inchi_calloc( max_num_vertices, sizeof(Edge ) ) ) ||
6513
7345
  !(pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof(S_CHAR) ) ) ||
6514
7346
  !(pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) ||
6515
7347
  !(pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) ||
7348
+ #if( BNS_RAD_SEARCH == 1 )
7349
+ !(pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) ||
7350
+ !(pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof(EdgeIndex) ) ) ||
7351
+ #endif
6516
7352
  !(pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) )
6517
7353
  ) {
6518
7354
  pBD = DeAllocateBnData( pBD );
@@ -6524,6 +7360,9 @@ BN_DATA *AllocateAndInitBnData( int max_num_vertices )
6524
7360
  pBD->QSize = -1;
6525
7361
  pBD->max_len_Pu_Pv = max_len_Pu_Pv;
6526
7362
  pBD->max_num_vertices = max_num_vertices;
7363
+ #if( BNS_RAD_SEARCH == 1 )
7364
+ pBD->nNumRadEndpoints = 0;
7365
+ #endif
6527
7366
  }
6528
7367
  return pBD;
6529
7368
  }
@@ -6846,12 +7685,14 @@ int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv )
6846
7685
  return bBackward;
6847
7686
  }
6848
7687
  /********************************************************************************
7688
+ Get previous vertex in the searched path
6849
7689
  z is SwitchEdge_Vert2(y) != y. Go backward from z to y
6850
7690
  *********************************************************************************/
6851
7691
  Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv )
6852
7692
  {
6853
7693
  Vertex w, z, x2, y2, n;
6854
7694
  EdgeIndex iwy;
7695
+
6855
7696
  w = SwitchEdge_Vert1(y);
6856
7697
  z = SwitchEdge_Vert2(y);
6857
7698
  iwy = SwitchEdge_IEdge(y);
@@ -6868,7 +7709,8 @@ Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iu
6868
7709
  iwy = SwitchEdge_IEdge(y2);
6869
7710
  if ( w == x2 ) {
6870
7711
  *iuv = iwy;
6871
- return z;
7712
+ /*return z; */
7713
+ return (y + z)%2? z : prim(z);
6872
7714
  }
6873
7715
  n ++;
6874
7716
  #ifdef _DEBUG
@@ -6896,6 +7738,51 @@ Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iu
6896
7738
  along an alternating path from another heteroatom (t-group will be detected).
6897
7739
 
6898
7740
  **********************************************************************************/
7741
+ #if( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */
7742
+ /*********************************************************************************/
7743
+ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
7744
+ {
7745
+ #define TYPE_T 1 /* t-group [also called H-group] */
7746
+ #define TYPE_CN 2 /* (-)c-group */
7747
+ int i, degree, ret, u_is_taut=0, w_is_taut, num_allowed=0, num_found_groups=0;
7748
+ Vertex w;
7749
+ EdgeIndex ivw;
7750
+ if ( !pBNS->type_TACN || u <= 1 || v <= 1 ||
7751
+ (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) {
7752
+ return 0; /* add/remove H(+) is allowed for acidic atoms */
7753
+ }
7754
+ if ( !pBNS->type_T || !pBNS->type_CN )
7755
+ return 0; /* should not happen */
7756
+ u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T :
7757
+ ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0;
7758
+ if ( u_is_taut ) {
7759
+ /* u is either t-group vertex or (-) c-group */
7760
+ degree = GetVertexDegree( pBNS, v );
7761
+ for ( i = 0; i < degree; i ++ ) {
7762
+ /* v = vert[u].neighbor[i]; */
7763
+ w = GetVertexNeighbor( pBNS, v, i, &ivw );
7764
+ if ( w == NO_VERTEX || w <= 1 ) {
7765
+ continue; /* the atom has only single bonds or it is s or t, ignore it */
7766
+ }
7767
+ if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) {
7768
+ num_allowed ++;
7769
+ w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN :
7770
+ ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : 0;
7771
+ if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN) ) {
7772
+ num_found_groups ++;
7773
+ }
7774
+ }
7775
+ }
7776
+ if ( num_found_groups && num_allowed == 1 ) {
7777
+ return 1; /* reject */
7778
+ }
7779
+ }
7780
+ return 0;
7781
+ #undef TYPE_T
7782
+ #undef TYPE_CN
7783
+ }
7784
+ #else /* } FIX_TACN_POSSIBLE_BUG { */
7785
+ /*********************************************************************************/
6899
7786
  int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
6900
7787
  {
6901
7788
  int i, degree, ret, u_is_taut=0, num_allowed=0, num_found_groups=0;
@@ -6903,12 +7790,13 @@ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
6903
7790
  EdgeIndex ivw;
6904
7791
  if ( !pBNS->type_TACN || u <= 1 || v <= 1 ||
6905
7792
  (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) {
6906
- return 0;
7793
+ return 0; /* add/remove H(+) is allowed for acidic atoms */
6907
7794
  }
6908
7795
  if ( !pBNS->type_T || !pBNS->type_CN )
6909
7796
  return 0; /* should not happen */
6910
7797
  if ( (u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) ||
6911
7798
  ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN) ) {
7799
+ /* u is either t-group vertex or (-) c-group */
6912
7800
  degree = GetVertexDegree( pBNS, v );
6913
7801
  for ( i = 0; i < degree; i ++ ) {
6914
7802
  /* v = vert[u].neighbor[i]; */
@@ -6925,12 +7813,13 @@ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
6925
7813
  }
6926
7814
  }
6927
7815
  if ( num_found_groups && num_allowed == 1 ) {
6928
- return 1;
7816
+ return 1; /* reject */
6929
7817
  }
6930
7818
 
6931
7819
  }
6932
7820
  return 0;
6933
7821
  }
7822
+ #endif /* } FIX_TACN_POSSIBLE_BUG */
6934
7823
  /*********************************************************************************
6935
7824
  the purpose is to avoid paths
6936
7825
  (H-group)[u]---atom[v]---((-)-cgroup)[w], where
@@ -6940,6 +7829,55 @@ int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v )
6940
7829
  only one chemical bond. Only because of this an early rejection of
6941
7830
  the vertex v (before it gets on SCANQ) is possible.
6942
7831
  **********************************************************************************/
7832
+ #if( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */
7833
+ /*********************************************************************************/
7834
+ int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge )
7835
+ {
7836
+ #define TYPE_T 1 /* t-group [also called H-group] */
7837
+ #define TYPE_CN 2 /* (-)c-group */
7838
+ int u_is_taut=0, w_is_taut=0;
7839
+ Vertex u;
7840
+ EdgeIndex iuv;
7841
+ if ( v <= 1 || w <= 1 )
7842
+ return 0;
7843
+ #if ( CHECK_TACN == 1 )
7844
+ if ( !pBNS->type_TACN ||
7845
+ (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) {
7846
+ return 0;
7847
+ }
7848
+ if ( !pBNS->type_T || !pBNS->type_CN )
7849
+ return 0; /* should not happen */
7850
+ #endif
7851
+ u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv );
7852
+ /*
7853
+ u = SwitchEdge_Vert1(v);
7854
+ iuv = SwitchEdge_IEdge(v);
7855
+ */
7856
+ if ( u == NO_VERTEX || iuv < 0 )
7857
+ return 0; /* should not happen */
7858
+ /* check edge adjacency */
7859
+ if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 ||
7860
+ (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) {
7861
+ return 0; /* !!! should not happen !!! */
7862
+ }
7863
+
7864
+ #if ( CHECK_TACN == 1 )
7865
+ u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T :
7866
+ ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0;
7867
+ w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T :
7868
+ ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0;
7869
+ if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN ) ) {
7870
+ /* rescap must have already been checked */
7871
+ return 1;
7872
+ }
7873
+ #endif
7874
+
7875
+ return 0;
7876
+ #undef TYPE_T
7877
+ #undef TYPE_CN
7878
+ }
7879
+ #else /* } FIX_TACN_POSSIBLE_BUG { */
7880
+ /*********************************************************************************/
6943
7881
  int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge )
6944
7882
  {
6945
7883
  int u_is_taut=0, w_is_taut=0;
@@ -6981,6 +7919,131 @@ int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *Switc
6981
7919
 
6982
7920
  return 0;
6983
7921
  }
7922
+ #endif /* } FIX_TACN_POSSIBLE_BUG { */
7923
+
7924
+ #if( FIX_KEEP_H_ON_NH_ANION == 1 )
7925
+ /*********************************************************************************/
7926
+ /* detect an attempt to remove H from -NH(-) to make =N(-); */
7927
+ /* all taut atoma except N are 'acidic' */
7928
+ /*********************************************************************************/
7929
+ int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v )
7930
+ {
7931
+ int i, u2, v2, vat2;
7932
+ Vertex vtg, vat;
7933
+ BNS_VERTEX *pvAT, *pvCN;
7934
+ BNS_EDGE *pEdge;
7935
+ if ( !pBNS->type_TACN || u <= 1 || v <= 1 ||
7936
+ u%2 || !(v%2) /* the edge flow may only increase */ ) {
7937
+ return 0;
7938
+ }
7939
+ if ((pBNS->vert[u2 = u/2-1].type & pBNS->type_TACN) ||
7940
+ (pBNS->vert[v2 = v/2-1].type & pBNS->type_TACN) ) {
7941
+ return 0; /* add/remove H is allowed for acidic atoms */
7942
+ }
7943
+ if ( !pBNS->type_T || !pBNS->type_CN )
7944
+ return 0; /* should not happen */
7945
+ /* find which of u, v vertices is N and which is t-group */
7946
+ if ( ((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && v2 < pBNS->num_atoms ) {
7947
+ vtg = u;
7948
+ vat = v;
7949
+ } else
7950
+ if ( ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T ) && u2 < pBNS->num_atoms ) {
7951
+ vtg = v;
7952
+ vat = u;
7953
+ } else {
7954
+ return 0;
7955
+ }
7956
+ vat2 = vat/2-1;
7957
+ pvAT = pBNS->vert + vat2; /* atom */
7958
+ for ( i = pvAT->num_adj_edges-1; 0 <= i; i -- ) {
7959
+ pEdge = pBNS->edge + pvAT->iedge[i];
7960
+ pvCN = pBNS->vert + (pEdge->neighbor12 ^ vat2);
7961
+ if ( ((pvCN->type & pBNS->type_CN) == pBNS->type_CN) && pEdge->flow > 0 ) {
7962
+ return 1; /* detected */
7963
+ }
7964
+ }
7965
+ return 0;
7966
+ }
7967
+ #endif
7968
+ #if ( FIX_AVOID_ADP == 1 )
7969
+ /************************************************************************
7970
+ Detect (tg)-N=A-A=A-A=N-(tg)
7971
+ u v w
7972
+ k = 5 4 3 2 1 0 1 2
7973
+ ^
7974
+ odd number means ADP
7975
+ *************************************************************************/
7976
+ int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge )
7977
+ {
7978
+ #define TYPE_T 1 /* t-group [also called H-group] */
7979
+ #define TYPE_CN 2 /* (-)c-group */
7980
+ #define TYPE_AT 4
7981
+ int k, v2, u2, w2, u2_next, type0, type1, type2, type;
7982
+ Vertex u, u_next;
7983
+ EdgeIndex iuv;
7984
+ if ( v <= 1 || w <= 1 )
7985
+ return 0;
7986
+
7987
+ if ( !pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN )
7988
+ return 0; /* should not happen */
7989
+ v2 = v/2 - 1;
7990
+ w2 = w/2 - 1;
7991
+ if ( v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms )
7992
+ goto cross_edge;
7993
+
7994
+ if ( !((pBNS->vert[w2].type & pBNS->type_T) == pBNS->type_T ) &&
7995
+ !((pBNS->vert[w2].type & pBNS->type_CN) == pBNS->type_CN) )
7996
+ goto cross_edge;
7997
+ /* v ia an atom, w is a t-group, v != w' */
7998
+ for ( k = 0, u = v; 1 < (u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv )); k ++ ) {
7999
+ u2 = u/2 - 1;
8000
+ if ( u2 >= pBNS->num_atoms ) {
8001
+ /* moving backward along the alt path we have found a vertex
8002
+ that is not an atom. Possibly it is a t- or (-)c-group */
8003
+ if ( !( k % 2 ) ) {
8004
+ return 0; /* even vertex -- always okay */
8005
+ }
8006
+ if ( !((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) &&
8007
+ !((pBNS->vert[u2].type & pBNS->type_CN) == pBNS->type_CN) ) {
8008
+ /* not a t- or (-)c-group */
8009
+ return 0;
8010
+ }
8011
+ u2_next = u_next/2 - 1;
8012
+ if ( !(pBNS->vert[v2 ].type & pBNS->type_TACN) &&
8013
+ !(pBNS->vert[u2_next].type & pBNS->type_TACN) ) {
8014
+ /* none of the atoms at the ends are N */
8015
+ return 0;
8016
+ }
8017
+ return 1;
8018
+ }
8019
+ }
8020
+ return 0;
8021
+ cross_edge:
8022
+ /*****************************************************************************
8023
+ * v and w (v=w') are same vertex reached with opposite "phases".
8024
+ * w cannot be (t) because this would have been detected earlier -- ???
8025
+ * (t)-A=A-A=A-A=A-(t)
8026
+ * v
8027
+ * 3 2 1 0 1 2 3 4
8028
+ * kv kw
8029
+ * (kv + kw)%2 == 1 <==> aggressive deprotonation
8030
+ *****************************************************************************/
8031
+ if ( v == prim(w) ) {
8032
+ type0 = 0;
8033
+ if ( v2 >= pBNS->num_atoms ) {
8034
+ type0 = ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T )? TYPE_T :
8035
+ ((pBNS->vert[v2].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0;
8036
+ }
8037
+
8038
+
8039
+
8040
+ }
8041
+
8042
+
8043
+ return 0;
8044
+ }
8045
+ #endif
8046
+
6984
8047
  /*********************************************************************************/
6985
8048
  int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv )
6986
8049
  {
@@ -7107,12 +8170,20 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7107
8170
  Vertex *Pv = pBD->Pv;
7108
8171
  int max_len_Pu_Pv= pBD->max_len_Pu_Pv;
7109
8172
 
7110
-
7111
-
7112
8173
  /* added to translate into C */
7113
8174
  int i, k, degree, delta, ret = 0;
7114
8175
  Vertex u, b_u, v, b_v, w;
7115
8176
  EdgeIndex iuv;
8177
+ #if( BNS_RAD_SEARCH == 1 )
8178
+ int n, bRadSearch = (BNS_EF_RAD_SRCH & bChangeFlow) && pBD->RadEndpoints;
8179
+ BRS_MODE bRadSrchMode = RAD_SRCH_NORM;
8180
+ int bRadSearchPrelim = 0;
8181
+ if ( bRadSearch ) {
8182
+ pBD->nNumRadEndpoints = 0;
8183
+ bRadSrchMode = pBD->bRadSrchMode;
8184
+ bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM;
8185
+ }
8186
+ #endif
7116
8187
 
7117
8188
  /* -- Always --
7118
8189
  Vertex_s = FIRST_INDX;
@@ -7129,12 +8200,23 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7129
8200
  /* since u is on the queue, it has a blossom C(U) with base b_u */
7130
8201
  b_u = FindBase( u, BasePtr );
7131
8202
  degree = GetVertexDegree( pBNS, u );
8203
+ #if( BNS_RAD_SEARCH == 1 )
8204
+ n = 0;
8205
+ #endif
7132
8206
  for ( i = 0; i < degree; i ++ ) {
7133
8207
  /* v = vert[u].neighbor[i]; */
7134
8208
  v = GetVertexNeighbor( pBNS, u, i, &iuv );
7135
8209
  if ( v == NO_VERTEX ) {
7136
8210
  continue; /* the atom has only single bonds, ignore it */
7137
8211
  }
8212
+ #if( BNS_RAD_SEARCH == 1 )
8213
+ if ( !k && bRadSrchMode == RAD_SRCH_FROM_FICT && v/2 <= pBNS->num_atoms ) {
8214
+ continue; /* start from fict. vertices only */
8215
+ }
8216
+ if ( bRadSearchPrelim && v/2 > pBNS->num_atoms ) {
8217
+ continue; /* during initial add/remove H allow radical movement only through real atoms */
8218
+ }
8219
+ #endif
7138
8220
  if ( /* PrevPt[u] != v ** avoid edges of T */
7139
8221
  ( SwitchEdge_Vert1(u) != v || SwitchEdge_Vert2(u) != u ) /* avoid edges of T */
7140
8222
  && (ret = rescap(pBNS, u, v, iuv)) > 0 ) {
@@ -7147,6 +8229,17 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7147
8229
  if ( bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ) ) {
7148
8230
  continue;
7149
8231
  }
8232
+ #if( FIX_KEEP_H_ON_NH_ANION == 1 )
8233
+ if ( bIsRemovedHfromNHaion( pBNS, u, v ) ) {
8234
+ continue;
8235
+ }
8236
+ #endif
8237
+ #if ( FIX_AVOID_ADP == 1 )
8238
+ if ( bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ) ) {
8239
+ continue;
8240
+ }
8241
+ #endif
8242
+
7150
8243
  }
7151
8244
  /*----------------------------------------------------------------------*/
7152
8245
  b_v = FindBase(v, BasePtr); /* Notation: b_x is a base of x */
@@ -7217,14 +8310,23 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7217
8310
 
7218
8311
  BasePtr[prim(v)] = v;
7219
8312
  BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */
8313
+ #if( BNS_RAD_SEARCH == 1 )
8314
+ n ++;
8315
+ #endif
7220
8316
  } else
7221
8317
  if ( TREE_IS_S_REACHABLE(prim(v)) /*Is_s_Reachable(prim(v)*/
7222
8318
  /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */
7223
8319
  /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */
7224
8320
  && (SwitchEdge_Vert1(prim(u)) != prim(v) || SwitchEdge_Vert2(prim(u)) != prim(u)) /* avoid edges of T' */
7225
8321
  && b_u != b_v
7226
- && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge )) ) {
7227
-
8322
+ && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge ))
8323
+ #if( FIX_KEEP_H_ON_NH_ANION == 1 )
8324
+ && !(pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim(v), u ))
8325
+ #endif
8326
+ ) {
8327
+ #if( BNS_RAD_SEARCH == 1 )
8328
+ n ++;
8329
+ #endif
7228
8330
  /* there is now a valid sv-path via u avoiding b_v (unless v==b_v)
7229
8331
  => u, v, u', and v' now all become part of the same connected component of M[C] */
7230
8332
  w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree );
@@ -7239,9 +8341,8 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7239
8341
  delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */
7240
8342
  if ( IS_BNS_ERROR( delta ) ) {
7241
8343
  pBD->QSize = QSize;
7242
- return delta;
8344
+ return delta; /* error */
7243
8345
  }
7244
-
7245
8346
  #if( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 )
7246
8347
  if ( pBNS->bNotASimplePath || abs(delta) > 1 ) {
7247
8348
  delta = 0;
@@ -7258,11 +8359,19 @@ int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow )
7258
8359
  } else
7259
8360
  if ( IS_BNS_ERROR( ret ) ) {
7260
8361
  pBD->QSize = QSize;
7261
- return ret;
8362
+ return ret; /* error */
7262
8363
  }
7263
-
7264
-
7265
8364
  }
8365
+ #if( BNS_RAD_SEARCH == 1 )
8366
+ if ( bRadSearch && !n ) {
8367
+ /* the BNS stopped at u */
8368
+ n = RegisterRadEndpoint( pBNS, pBD, u);
8369
+ if ( IS_BNS_ERROR( n ) ) {
8370
+ pBD->QSize = QSize;
8371
+ return n;
8372
+ }
8373
+ }
8374
+ #endif
7266
8375
  k ++; /* advance ScanQ */
7267
8376
  } while( k <= QSize );
7268
8377
  /* if this point is reached, no valid augmenting path exists, ScanQ contains
@@ -7538,6 +8647,16 @@ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delt
7538
8647
  */
7539
8648
  Vertex w, z, iwz;
7540
8649
  int cap, delta2;
8650
+ static int level;
8651
+
8652
+ if ( level ++ > 50 ) {
8653
+ #ifdef _DEBUG
8654
+ int stop = 1;
8655
+ #else
8656
+ ;
8657
+ #endif
8658
+ }
8659
+
7541
8660
 
7542
8661
  w = SwitchEdge_Vert1(y);
7543
8662
  z = SwitchEdge_Vert2(y); /* wz is on the path P */
@@ -7547,6 +8666,7 @@ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delt
7547
8666
  cap = rescap_mark( pBNS, w, z, iwz );
7548
8667
 
7549
8668
  if ( IS_BNS_ERROR( cap ) ) {
8669
+ level --;
7550
8670
  return cap;
7551
8671
  }
7552
8672
  if ( cap < delta ) {
@@ -7561,6 +8681,7 @@ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delt
7561
8681
  delta2 = FindPathCap( pBNS, SwitchEdge, prim(y), prim(z), delta );
7562
8682
  delta = inchi_min( delta2, delta );
7563
8683
  }
8684
+ level --;
7564
8685
  return delta;
7565
8686
  }
7566
8687
 
@@ -7569,18 +8690,19 @@ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delt
7569
8690
  #define BT_ALTERN_BOND 1 /* 1-2, possibly stereo */
7570
8691
  #define BT_OTHER_ALTERN_BOND 2 /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */
7571
8692
 
7572
- #define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND)
7573
-
7574
8693
  #define BT_ALTERN_NS_BOND 4
7575
8694
 
7576
- #define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND)
7577
-
7578
8695
  #define BT_TAUTOM_BOND 8
7579
8696
 
8697
+ #define BT_ALTERN_UNKN_BOND 16
8698
+
7580
8699
  #define BT_IGNORE_BOND 0
7581
8700
 
7582
8701
  #define BT_NONSTEREO_MASK (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND)
7583
8702
 
8703
+ #define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND)
8704
+
8705
+ #define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND)
7584
8706
 
7585
8707
  /* BNS members redefinitions for finding non-stereo bonds */
7586
8708
  /* BNS_EDGE */
@@ -7603,7 +8725,7 @@ int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delt
7603
8725
 
7604
8726
 
7605
8727
  /********************************************************************************/
7606
- int MarkRingSystemsAltBns( BN_STRUCT* pBNS )
8728
+ int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo )
7607
8729
  {
7608
8730
  AT_NUMB *nStackAtom = NULL;
7609
8731
  int nTopStackAtom;
@@ -7627,10 +8749,10 @@ int MarkRingSystemsAltBns( BN_STRUCT* pBNS )
7627
8749
  nRingStack = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nRingStack[0]));
7628
8750
  nDfsNumber = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nDfsNumber[0]));
7629
8751
  nLowNumber = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nLowNumber[0]));
7630
- nBondStack = (AT_NUMB *)inchi_malloc(num_edges*sizeof(nBondStack[0]));
8752
+ nBondStack = (AT_NUMB *)(num_edges? inchi_malloc(num_edges*sizeof(nBondStack[0])):NULL); /* special case: no bonds 2006-03 */
7631
8753
  cNeighNumb = (S_CHAR *)inchi_malloc(num_atoms*sizeof(cNeighNumb[0]));
7632
8754
  /* check allocation */
7633
- if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack || !cNeighNumb
8755
+ if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack && num_edges || !cNeighNumb
7634
8756
  ) {
7635
8757
  nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* <BRKPT> */
7636
8758
  goto exit_function;
@@ -7693,7 +8815,10 @@ found_alt:
7693
8815
 
7694
8816
  do {
7695
8817
  /* advance */
7696
- while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) ) {
8818
+ /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/
8819
+ /* replaced due to missing sequence point */
8820
+ while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j )
8821
+ {
7697
8822
  cNeighNumb[i] ++;
7698
8823
  if ( !(bond[w=at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK) ) {
7699
8824
  continue;
@@ -7797,7 +8922,10 @@ found_alt2:
7797
8922
  do {
7798
8923
  /* advance */
7799
8924
  advance_ring:
7800
- if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) ) {
8925
+ /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/
8926
+ /* replaced due to missing sequence point */
8927
+ if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j )
8928
+ {
7801
8929
  cNeighNumb[i] ++;
7802
8930
  if ( !(bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND) ) {
7803
8931
  goto advance_ring;
@@ -7876,7 +9004,7 @@ exit_function:
7876
9004
  }
7877
9005
 
7878
9006
  /*********************************************************************************/
7879
- int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
9007
+ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo )
7880
9008
  {
7881
9009
  Vertex v, v2;
7882
9010
  int ret, bond_type, num_to_test, j;
@@ -7884,6 +9012,11 @@ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
7884
9012
  BNS_VERTEX *pAtom;
7885
9013
  /* strip all t-groups and c-groups */
7886
9014
  num_to_test = 0;
9015
+ if ( bUnknAltAsNoStereo ) {
9016
+ for ( j = 0; j < pBNS->num_edges; j ++ ) {
9017
+ pBNS->edge[j].pass = 0;
9018
+ }
9019
+ }
7887
9020
  ret = ReInitBnStruct( pBNS, at, num_atoms, 0 );
7888
9021
  if ( ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) {
7889
9022
  ret = BNS_REINIT_ERR;
@@ -7900,6 +9033,13 @@ int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
7900
9033
  if ( at[v].endpoint || at[v2].endpoint ) {
7901
9034
  bond_type = 0; /* any bond to an endpoint considered non-stereogenic */
7902
9035
  }
9036
+ #if( FIX_EITHER_DB_AS_NONSTEREO == 1 )
9037
+ if ( bUnknAltAsNoStereo ) {
9038
+ if ( bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER ) {
9039
+ bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */
9040
+ }
9041
+ }
9042
+ #endif
7903
9043
  switch ( bond_type ) {
7904
9044
 
7905
9045
  case BOND_ALTERN :
@@ -7954,7 +9094,7 @@ exit_function:
7954
9094
  }
7955
9095
 
7956
9096
  /************************************************************************************/
7957
- int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
9097
+ int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo )
7958
9098
  {
7959
9099
  int num_bonds = pBNS->num_bonds;
7960
9100
  int ret;
@@ -7968,27 +9108,53 @@ int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms )
7968
9108
  ret = BNS_REINIT_ERR;
7969
9109
  goto exit_function;
7970
9110
  }
7971
-
7972
- for ( ibond=0; ibond < num_bonds; ibond ++ ) {
7973
- pBond = pBNS->edge + ibond;
7974
- if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) {
7975
- continue;
9111
+ if ( bUnknAltAsNoStereo ) {
9112
+ for ( ibond=0; ibond < num_bonds; ibond ++ ) {
9113
+ pBond = pBNS->edge + ibond;
9114
+ if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) {
9115
+ continue;
9116
+ }
9117
+ iat1 = pBond->neighbor1;
9118
+ iat2 = pBond->neighbor12 ^ iat1;
9119
+ ib1 = pBond->neigh_ord[0];
9120
+ ib2 = pBond->neigh_ord[1];
9121
+ if ( /* alt bond non-adjacent to a taut. endpoint: */
9122
+ (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND &&
9123
+ pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ ||
9124
+ /* alt bond adjacent to a taut. endpoint: */
9125
+ (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND &&
9126
+ (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN )
9127
+ ) {
9128
+ if ( (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) {
9129
+ /* bond_type = BOND_ALT12NS; */
9130
+ at[iat1].bond_stereo[ib1] =
9131
+ at[iat2].bond_stereo[ib2] =STEREO_DBLE_EITHER;
9132
+ ret ++;
9133
+ }
9134
+ }
9135
+ }
9136
+ } else {
9137
+ for ( ibond=0; ibond < num_bonds; ibond ++ ) {
9138
+ pBond = pBNS->edge + ibond;
9139
+ if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) {
9140
+ continue;
9141
+ }
9142
+ iat1 = pBond->neighbor1;
9143
+ iat2 = pBond->neighbor12 ^ iat1;
9144
+ ib1 = pBond->neigh_ord[0];
9145
+ ib2 = pBond->neigh_ord[1];
9146
+ if ( /* alt bond non-adjacent to a taut. endpoint: */
9147
+ (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND &&
9148
+ pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ ||
9149
+ /* alt bond adjacent to a taut. endpoint: */
9150
+ (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND &&
9151
+ (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN )
9152
+ ) {
9153
+ at[iat1].bond_type[ib1] =
9154
+ at[iat2].bond_type[ib2] =BOND_ALT12NS;
9155
+ ret ++;
9156
+ }
7976
9157
  }
7977
- iat1 = pBond->neighbor1;
7978
- iat2 = pBond->neighbor12 ^ iat1;
7979
- ib1 = pBond->neigh_ord[0];
7980
- ib2 = pBond->neigh_ord[1];
7981
- if ( /* alt bond non-adjacent to a taut. endpoint: */
7982
- (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND &&
7983
- pBond->nNumAtInBlockAltBns <= 3 ) ||
7984
- /* alt bond adjacent to a taut. endpoint: */
7985
- (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND &&
7986
- (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN )
7987
- ) {
7988
- at[iat1].bond_type[ib1] =
7989
- at[iat2].bond_type[ib2] =BOND_ALT12NS;
7990
- ret ++;
7991
- }
7992
9158
  }
7993
9159
 
7994
9160
  exit_function:
@@ -7996,4 +9162,384 @@ exit_function:
7996
9162
  return ret;
7997
9163
  }
7998
9164
 
9165
+ #if( READ_INCHI_STRING == 1 )
9166
+ /*****************************************************************/
9167
+ #ifndef RI_ERR_ALLOC
9168
+ /* from ichirvrs.h */
9169
+ #define RI_ERR_ALLOC (-1)
9170
+ #define RI_ERR_SYNTAX (-2)
9171
+ #define RI_ERR_PROGR (-3)
9172
+ #endif
9173
+ /*****************************************************************/
9174
+ int bHasChargedNeighbor( inp_ATOM *at, int iat )
9175
+ {
9176
+ int i;
9177
+ for( i = 0; i < at[iat].valence; i ++ ) {
9178
+ if ( at[(int)at[iat].neighbor[i]].charge )
9179
+ return 1;
9180
+ }
9181
+ return 0;
9182
+ }
9183
+ /***********************************************************************************************
9184
+ *num_protons_to_add = nToBeRemovedByNormFromRevrs
9185
+
9186
+ nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be
9187
+ added by the Normalization of the Reconstructed Structure
9188
+ nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by
9189
+ the InChI Normalization of the Reconstructed Structure
9190
+
9191
+ OrigStruct -> NormOrig + n(orig)*H(+)
9192
+ RevrStruct -> NormRevr + n(revr)*H(+)
9193
+ nToBeRemovedByNormFromRevrs = n(orig) - n(revr) [each may be negative]
9194
+
9195
+ n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means:
9196
+ -----------------------------------------------------------
9197
+ - Too many protons were added by the Normalization to the Reconstructed Structure
9198
+ (a) n(revr) < 0 => protons were added while they should not have been added;
9199
+ Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms
9200
+ on the condition ADP cannot add in another way;
9201
+ (b) n(orig) > n(revr) => 0 => too few protons were removed
9202
+ Solution: (the easiest) attach H(+) to =O or -N< or -N=
9203
+ Solution: move (+) from N or OH to an atom adjacent to (-) charge or to
9204
+ an atom that is not N.
9205
+
9206
+ n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means:
9207
+ -----------------------------------------------------------
9208
+ - Too few protons were added by the Normalization to the Reconstructed Stucture
9209
+ (a) n(orig) < 0 => protons were not added while they should have been added;
9210
+ Solution: move (-) to O by replacing =O with -O(-)
9211
+ (b) 0 <= n(orig) < n(revr) => too many protons were removed
9212
+
9213
+ Note: it is critically important to takr into account cumbersome Normalization
9214
+ Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP
9215
+ However, if N(+) is present then ADP will always try to remove a proton
9216
+ *********************************************************************************************/
9217
+ int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add,
9218
+ int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags,
9219
+ int num_tg, int nChargeRevrs, int nChargeInChI )
9220
+ {
9221
+ int i, j, ret = 0;
9222
+ int nAtTypeTotals[ATTOT_ARRAY_LEN];
9223
+ int num_prot = *num_protons_to_add;
9224
+ int type, mask, bSuccess, nTotCharge, nNumSuccess = 0;
9225
+ int max_j_Aa=-1, max_j_Ar=-1;
9226
+
9227
+ /* for the reference:
9228
+
9229
+ #define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \
9230
+ FLAG_PROTON_NP_HARD_REMOVED | \
9231
+ FLAG_PROTON_AC_SIMPLE_ADDED | \
9232
+ FLAG_PROTON_AC_SIMPLE_REMOVED | \
9233
+ FLAG_PROTON_AC_HARD_REMOVED | \
9234
+ FLAG_PROTON_AC_HARD_ADDED | \
9235
+ FLAG_PROTON_SINGLE_REMOVED | \
9236
+ FLAG_PROTON_CHARGE_CANCEL )
9237
+
9238
+ #define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \
9239
+ FLAG_PROTON_AC_HARD_REMOVED | \
9240
+ FLAG_PROTON_AC_HARD_ADDED )
9241
+
9242
+ */
9243
+ /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal
9244
+ a typical case is (=) on N or O instead of C(-)
9245
+
9246
+ if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition
9247
+ */
9248
+
9249
+ mark_at_type( at, num_atoms, nAtTypeTotals );
9250
+ for ( i = nTotCharge = 0; i < num_atoms; i ++ ) {
9251
+ nTotCharge += at[i].charge;
9252
+ }
9253
+ /* size for SimpleAddAcidicProtons() */
9254
+ for ( max_j_Aa = 0; AaTypMask[2*max_j_Aa]; max_j_Aa ++ )
9255
+ ;
9256
+ /* size for SimpleRemoveAcidicProtons */
9257
+ for ( max_j_Ar = 0; ArTypMask[2*max_j_Ar]; max_j_Ar ++ )
9258
+ ;
9259
+ if ( num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) {
9260
+ /* remove proton(s) */
9261
+ /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */
9262
+ for ( i = 0; i < num_atoms && num_prot; i ++ ) {
9263
+ /* choose an atom */
9264
+ if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge ||
9265
+ !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ) ) {
9266
+ continue;
9267
+ }
9268
+ /* try to remove a proton and check whether InChI would add it back */
9269
+ at[i].charge --;
9270
+ at[i].num_H --;
9271
+ type = GetAtomChargeType( at, i, NULL, &mask, 0 );
9272
+ at[i].charge ++;
9273
+ at[i].num_H ++;
9274
+
9275
+ if ( type ) {
9276
+ for ( bSuccess = 0, j = 0; j < max_j_Aa; j ++ ) {
9277
+ if ( bSuccess = (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) {
9278
+ break; /* the proton may be added to this atom */
9279
+ }
9280
+ }
9281
+ if ( bSuccess ) {
9282
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
9283
+ at[i].charge --;
9284
+ at[i].num_H --;
9285
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
9286
+ num_prot ++; /* success */
9287
+ nNumSuccess ++;
9288
+ }
9289
+ }
9290
+ }
9291
+ }
9292
+ if ( num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) {
9293
+ /* alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */
9294
+ int endp2, centp, k, i0, k0;
9295
+ for ( i = 0; i < num_atoms; i ++ ) {
9296
+ /* choose an atom */
9297
+ if ( !at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity ||
9298
+ at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ) ) {
9299
+ continue;
9300
+ }
9301
+ /* looking for tautomeric =O */
9302
+ if ( 1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H ||
9303
+ 2 != get_endpoint_valence( at[i].el_number ) ) {
9304
+ continue;
9305
+ }
9306
+ centp = at[i].neighbor[0];
9307
+ if ( at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ) ) {
9308
+ continue;
9309
+ }
9310
+ /* found a possible centerpoint, looking for -NH endpoint */
9311
+ for ( k = 0; k < at[centp].valence; k ++ ) {
9312
+ if ( at[centp].bond_type[k] != BOND_TYPE_SINGLE ) {
9313
+ continue;
9314
+ }
9315
+ endp2 = at[centp].neighbor[k];
9316
+ if ( at[endp2].endpoint != at[i].endpoint ||
9317
+ !at[endp2].num_H || at[endp2].charge ||
9318
+ at[endp2].sb_parity[0] || at[endp2].p_parity ||
9319
+ at[endp2].valence != at[endp2].chem_bonds_valence ||
9320
+ 3 != at[endp2].chem_bonds_valence + at[endp2].num_H ||
9321
+ 3 != get_endpoint_valence( at[endp2].el_number ) ) {
9322
+ continue;
9323
+ }
9324
+ /* find bonds in reciprocal ajacency lists */
9325
+ for ( i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0 ++ )
9326
+ ;
9327
+ for ( k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0 ++ )
9328
+ ;
9329
+ if ( i0 == at[centp].valence || k0 == at[endp2].valence ) {
9330
+ return RI_ERR_PROGR;
9331
+ }
9332
+ /* -NH has been found */
9333
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
9334
+ type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */
9335
+
9336
+ at[i].bond_type[0] --;
9337
+ at[centp].bond_type[i0] --;
9338
+ at[i].chem_bonds_valence --;
9339
+ at[i].charge --;
9340
+
9341
+ at[endp2].bond_type[k0] ++;
9342
+ at[centp].bond_type[k] ++;
9343
+ at[endp2].chem_bonds_valence ++;
9344
+ at[endp2].num_H --;
9345
+
9346
+ num_prot ++;
9347
+ nNumSuccess ++;
9348
+
9349
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */
9350
+ type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */
9351
+ }
9352
+ }
9353
+ }
9354
+ if ( num_prot > 0 ) {
9355
+ /* add protons */
9356
+ /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */
9357
+ for ( i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr >= 0; i ++ ) {
9358
+ /* choose an atom */
9359
+ if ( at[i].sb_parity[0] || at[i].p_parity || at[i].num_H ||
9360
+ at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ) ) {
9361
+ continue;
9362
+ }
9363
+ /* try to add a proton and check whether InChI would remove it back */
9364
+ at[i].charge ++;
9365
+ at[i].num_H ++;
9366
+ type = GetAtomChargeType( at, i, NULL, &mask, 0 );
9367
+ at[i].charge --;
9368
+ at[i].num_H --;
9369
+
9370
+ if ( type ) {
9371
+ for ( bSuccess = 0, j = 0; j < max_j_Ar; j ++ ) {
9372
+ if ( bSuccess = (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) {
9373
+ break;
9374
+ }
9375
+ }
9376
+ if ( bSuccess ) {
9377
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
9378
+ at[i].charge ++;
9379
+ at[i].num_H ++;
9380
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
9381
+ num_prot --; /* success */
9382
+ nNumSuccess ++;
9383
+ }
9384
+ }
9385
+ }
9386
+ /* 2. Use test from SimpleRemoveHplusNPO() */
9387
+ for ( i = 0; i < num_atoms && num_prot; i ++ ) {
9388
+ /* choose an atom */
9389
+ if ( at[i].sb_parity[0] || at[i].p_parity ||
9390
+ at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ) ) {
9391
+ continue;
9392
+ }
9393
+ /* try to add a proton and check whether InChI would remove it back */
9394
+ at[i].num_H ++;
9395
+ at[i].charge ++;
9396
+ bSuccess = (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) &&
9397
+ (PR_SIMPLE_MSK & mask );
9398
+ at[i].num_H --; /* failed */
9399
+ at[i].charge --;
9400
+ if ( bSuccess ) {
9401
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
9402
+ at[i].num_H ++;
9403
+ at[i].charge ++;
9404
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
9405
+ num_prot --; /* succeeded */
9406
+ nNumSuccess ++;
9407
+ }
9408
+ }
9409
+ }
9410
+
9411
+ if ( num_prot < 0 && (bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED) && 1 == num_tg &&
9412
+ nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) {
9413
+ /* try to remove protons from tautomeric N (specific ADP must be present) */
9414
+ int nNumAcceptors_DB_O=0, nNumDonors_SB_NH=0, num_max, num_success;
9415
+ for ( i = 0; i < num_atoms; i ++ ) {
9416
+ /* choose an atom */
9417
+ if ( !at[i].endpoint || at[i].radical ||
9418
+ at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) {
9419
+ continue;
9420
+ }
9421
+ type = GetAtomChargeType( at, i, NULL, &mask, 0 );
9422
+ if ( (type & AA_HARD_TYP_CO) && (mask & AA_HARD_MSK_CO) ) {
9423
+ nNumAcceptors_DB_O ++;
9424
+ } else
9425
+ if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge &&
9426
+ at[i].valence == at[i].chem_bonds_valence ) {
9427
+ nNumDonors_SB_NH ++;
9428
+ }
9429
+ }
9430
+ num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH );
9431
+ for ( i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i ++ ) {
9432
+ /* choose an atom */
9433
+ if ( !at[i].endpoint|| at[i].radical || at[i].sb_parity[0] ||
9434
+ at[i].p_parity || bHasChargedNeighbor( at, i ) ) {
9435
+ continue;
9436
+ }
9437
+ type = GetAtomChargeType( at, i, NULL, &mask, 0 );
9438
+ if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge &&
9439
+ at[i].valence == at[i].chem_bonds_valence ) {
9440
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */
9441
+ at[i].num_H --;
9442
+ at[i].charge --;
9443
+ type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */
9444
+ num_prot ++;
9445
+ num_success ++;
9446
+ nNumSuccess ++;
9447
+ }
9448
+ }
9449
+ }
9450
+ /*exit_function:*/
9451
+ *num_protons_to_add = num_prot;
9452
+ return ret<0? ret : nNumSuccess;
9453
+ }
9454
+ /*****************************************************************/
9455
+ int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg )
9456
+ {
9457
+ int i, j, k, n, ret = 0;
9458
+ int nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H;
9459
+ int iCurIso; /* 0=> 1H, 1=> D, 2=> T */
9460
+ int iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */
9461
+ static U_CHAR el_number_H = 0;
9462
+
9463
+ /* distribute isotopes from heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */
9464
+ iCurMode1 = 0;
9465
+ iCurMode2 = num_tg ? 1 : 0;
9466
+ if ( !el_number_H ) {
9467
+ el_number_H = (U_CHAR) get_periodic_table_number( "H" );
9468
+ }
9469
+ for ( iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode ++ ) {
9470
+ for ( iCurIso = 2; 0 <= iCurIso; iCurIso -- ) {
9471
+ /* check for isotopic H to add */
9472
+ if ( !num_protons_to_add[iCurIso] ) {
9473
+ continue;
9474
+ }
9475
+ if ( 0 > num_protons_to_add[iCurIso] ) {
9476
+ ret = RI_ERR_PROGR;
9477
+ goto exit_function;
9478
+ }
9479
+ /* limits for atom scanning */
9480
+ min_at = 0;
9481
+ max_at = num_atoms;
9482
+ /* cycle withio the limits */
9483
+ for ( i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i ++ ) {
9484
+ /* pick an atom */
9485
+ if ( iCurMode ) {
9486
+ if ( at[i].endpoint )
9487
+ j = i; /* atom number */
9488
+ else
9489
+ continue;
9490
+ } else
9491
+ if ( !at[i].endpoint &&
9492
+ 1 == bHeteroAtomMayHaveXchgIsoH( at, i ) ) { /* atom number */
9493
+ j = i;
9494
+ } else
9495
+ if ( at[i].el_number == el_number_H && at[i].charge == 1 &&
9496
+ !at[i].valence && !at[i].radical && !at[i].iso_atw_diff ) {
9497
+ /* proton, not isotopic; make it isotopic */
9498
+ at[i].iso_atw_diff = 1 + iCurIso;
9499
+ num_protons_to_add[iCurIso] --;
9500
+ nNumSuccess ++;
9501
+ continue;
9502
+ } else {
9503
+ continue;
9504
+ }
9505
+ /* j is the atom number */
9506
+ /* count implicit H */
9507
+ num_H = at[j].num_H;
9508
+ num_iso_H = NUM_ISO_H(at,j);
9509
+ while ( num_H > 0 && num_protons_to_add[iCurIso] > 0 ) {
9510
+ /* substitute one implicit H with an isotopic atom H */
9511
+ at[j].num_iso_H[iCurIso] ++;
9512
+ at[j].num_H --;
9513
+ num_protons_to_add[iCurIso] --;
9514
+ num_H --;
9515
+ num_iso_H ++;
9516
+ nNumSuccess ++;
9517
+ }
9518
+ /* count explicit H */
9519
+ num_expl_H = num_expl_iso_H = 0;
9520
+ for ( k = 0; k < at[j].valence && num_atoms <= (n=at[j].neighbor[k]); k ++ ) {
9521
+ num_expl_H += (0 == at[n].iso_atw_diff);
9522
+ num_expl_iso_H += (0 != at[n].iso_atw_diff);
9523
+ }
9524
+ while ( num_expl_H > 0 && num_protons_to_add[iCurIso] > 0 ) {
9525
+ /* substitute one explicit H with an isotopic atom H */
9526
+ n = at[j].neighbor[num_expl_H];
9527
+ if ( at[n].iso_atw_diff ) {
9528
+ ret = RI_ERR_PROGR;
9529
+ goto exit_function;
9530
+ }
9531
+ at[n].iso_atw_diff = 1 + iCurIso;
9532
+ num_expl_H --;
9533
+ num_expl_iso_H ++;
9534
+ num_protons_to_add[iCurIso] --;
9535
+ nNumSuccess ++;
9536
+ }
9537
+ }
9538
+ }
9539
+ }
9540
+ exit_function:
9541
+ return ret<0? ret : nNumSuccess;
9542
+ }
9543
+
9544
+ #endif
7999
9545