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,3177 @@
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
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+
14
+ /*#define CHECK_WIN32_VC_HEAP*/
15
+ #include "mode.h"
16
+
17
+ #if( READ_INCHI_STRING == 1 )
18
+
19
+ #include "ichi.h"
20
+ #include "ichitime.h"
21
+
22
+ #include "inpdef.h"
23
+ #include "ichimain.h"
24
+ #include "ichierr.h"
25
+ #include "comdef.h"
26
+ #include "ichiring.h"
27
+ #include "extr_ct.h"
28
+ #include "ichitaut.h"
29
+ #include "ichinorm.h"
30
+ #include "util.h"
31
+
32
+ #include "ichicomp.h"
33
+ #include "ichister.h"
34
+
35
+ #include "ichi_bns.h"
36
+
37
+ #include "strutil.h"
38
+
39
+ #include "ichirvrs.h"
40
+
41
+
42
+ /********************** Forbid carbon charge edges ***********************************/
43
+ int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask )
44
+ {
45
+ #define MAX_NUM_CARBON_CHARGE_EDGES 2
46
+ int nType, i, k, ret;
47
+ BNS_EDGE *pEdge;
48
+ if ( ret = AllocEdgeList( pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES ) ) {
49
+ goto exit_function;
50
+ }
51
+ pCarbonChargeEdges->num_edges = 0;
52
+ for ( i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i ++ ) {
53
+ switch( i ) {
54
+ case 0:
55
+ nType = TCG_Plus_C0;
56
+ break;
57
+ case 1:
58
+ nType = TCG_Minus_C0;
59
+ break;
60
+ default:
61
+ ret = RI_ERR_PROGR;
62
+ goto exit_function;
63
+ }
64
+ if ( (k = pTCGroups->nGroup[nType]) >= 0 ) {
65
+ k = pTCGroups->pTCG[k].nForwardEdge;
66
+ if ( k > 0 ) {
67
+ pEdge = pBNS->edge + k;
68
+ if ( !(pEdge->forbidden & forbidden_edge_mask) ) {
69
+ pEdge->forbidden |= forbidden_edge_mask;
70
+ if ( ret = AddToEdgeList( pCarbonChargeEdges, k, 0 ) ) {
71
+ goto exit_function;
72
+ }
73
+ }
74
+ } else {
75
+ ret = RI_ERR_PROGR;
76
+ goto exit_function;
77
+ }
78
+ }
79
+ }
80
+ ret = pCarbonChargeEdges->num_edges;
81
+ exit_function:
82
+ return ret;
83
+ #undef MAX_NUM_CARBON_CHARGE_EDGES
84
+ }
85
+ /******************************************************************************************************/
86
+ int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at,
87
+ VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups,
88
+ EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask )
89
+ {
90
+ int i, j, ret;
91
+ BNS_EDGE *e;
92
+
93
+ ret = 0;
94
+ /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */
95
+ for ( i = 0; i < num_at; i ++ ) {
96
+ if ( at[i].valence == 2 &&
97
+ !at[i].num_H && !at[i].endpoint &&
98
+ pVA[i].cNumValenceElectrons == 5 &&
99
+ pVA[i].cPeriodicRowNumber == 1 &&
100
+ !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 &&
101
+ pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MNP &&
102
+ pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size ) {
103
+
104
+ e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1);
105
+ if ( !(e->forbidden & forbidden_edge_mask) ) {
106
+ e->forbidden |= forbidden_edge_mask;
107
+ if ( ret = AddToEdgeList( pNplus2BondsEdges, j, 128 ) ) {
108
+ goto exit_function;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ret = 0;
114
+ exit_function:
115
+ return ret;
116
+ }
117
+
118
+ /*************************************************************************************************
119
+ Problem: Formula in InChI from the reversed structure has less H than in the input InChI
120
+ Solutions:
121
+
122
+ (a) | |
123
+ -B(-)-NH-=..-=N(+)< => -B(-)-NH(+)=-..=-N< (H is not removed from the ion pair)
124
+ | |
125
+
126
+ | |
127
+ (b) >N(+)=-=...-=N-NH => >N-=-...=-N(+)-NH (charge from onium cannot be moved to remove H+)
128
+ | |
129
+ *************************************************************************************************/
130
+ int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at,
131
+ inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
132
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
133
+ {
134
+ int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh;
135
+ EDGE_LIST NewlyFixedEdges;
136
+ int ret, i, j;
137
+ int num_at = pStruct->num_atoms;
138
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
139
+ /* for RunBnsTestOnce */
140
+ Vertex vPathStart, vPathEnd;
141
+ int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
142
+
143
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
144
+ if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) {
145
+ goto exit_function;
146
+ }
147
+ for ( i = 0; i < num_at; i ++ ) {
148
+ if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) {
149
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
150
+ goto exit_function;
151
+ }
152
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
153
+ }
154
+ if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) {
155
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
156
+ goto exit_function;
157
+ }
158
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
159
+ }
160
+ }
161
+ /* extra H has been removed; check non-tautomeric atoms */
162
+ for ( i = 0; i < num_at; i ++ ) {
163
+ if ( !at2[i].endpoint && !pVA[i].cMetal &&
164
+ pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 &&
165
+ at2[i].num_H == atf[i].num_H + 1) {
166
+ /* H was removed from N */
167
+ iNH = i;
168
+ break;
169
+ }
170
+ }
171
+ if ( 0 <= iNH && iNH < num_at ) {
172
+ /* check neighbors for | |
173
+ (a) -B(+)- or (b) =N-
174
+ | |
175
+ */
176
+ for ( j = 0; j < at2[i].valence; j ++ ) {
177
+ neigh = at2[iNH].neighbor[j];
178
+ if ( at2[neigh].valence == 4 ) {
179
+ if ( at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 &&
180
+ !at2[neigh].radical && !at[neigh].num_H ) {
181
+ iBPlus = neigh;
182
+ }
183
+ }
184
+ }
185
+ }
186
+ if ( 0 <= iNH && iNH < num_at ) {
187
+ int bond_type_at2;
188
+ int bond_type_atf;
189
+ int num_bonds_in_path = 0;
190
+ int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus;
191
+ /* the changed bond to the dehydrogenated atom H should have greater order */
192
+ /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */
193
+ nxt_is_NPlus = 0;
194
+ do {
195
+ i = nxt;
196
+ nxt = NO_VERTEX;
197
+ delta = -delta;
198
+ for ( j = 0; j < at2[i].valence; j ++ ) {
199
+ bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */
200
+ bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */
201
+ nxt_is_NPlus = 0;
202
+ if ( (bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) &&
203
+ BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE &&
204
+ !at2[(int)at2[i].neighbor[j]].cFlags ) {
205
+ prv = i;
206
+ nxt = at2[i].neighbor[j];
207
+ nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 &&
208
+ pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1;
209
+ at2[i].cFlags |= 1; /* avoid cycling */
210
+ num_bonds_in_path ++;
211
+ if ( delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 &&
212
+ !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 &&
213
+ pVA[prv].nCPlusGroupEdge > 0 ) {
214
+ iNV = prv;
215
+ }
216
+ if ( at2[nxt].charge != atf[nxt].charge ) {
217
+ if ( (at2[nxt].charge == 1 || atf[nxt].charge == 1) &&
218
+ pVA[nxt].nCPlusGroupEdge > 0 ) {
219
+ pBNS->edge[pVA[nxt].nCPlusGroupEdge-1].forbidden &= inv_forbidden_edge_mask;
220
+ }
221
+ if ( (at2[nxt].charge == -1 || atf[nxt].charge == -1) &&
222
+ pVA[nxt].nCMinusGroupEdge > 0 ) {
223
+ pBNS->edge[pVA[nxt].nCMinusGroupEdge-1].forbidden &= inv_forbidden_edge_mask;
224
+ }
225
+ }
226
+ break; /* found */
227
+ }
228
+ }
229
+ } while ( nxt >= 0 && !( nxt_is_NPlus && delta == -1 ) );
230
+ for ( i = 0; i < num_at; i ++ ) {
231
+ at2[i].cFlags = 0;
232
+ }
233
+ if ( nxt >= 0 && nxt_is_NPlus && delta == -1 ) {
234
+ /* a simple alt path from NH-= to =N(+) has been found */
235
+ if ( iBPlus || iNV ) {
236
+ /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */
237
+ if ( iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ||
238
+ iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ) {
239
+ int ieFlower;
240
+ BNS_EDGE *pe = pBNS->edge + j, *peFlower = NULL;
241
+ Vertex v1 = pe->neighbor1;
242
+ Vertex v2 = v1 ^ pe->neighbor12;
243
+ BNS_VERTEX *pv1 = pBNS->vert + v1;
244
+ BNS_VERTEX *pv2 = pBNS->vert + v2;
245
+
246
+ delta = 1;
247
+ /* prevent conversion of >N(+)= into N(V) neutral */
248
+ ieFlower = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[nxt].nCPlusGroupEdge-1 );
249
+ if ( ieFlower >= 0 ) {
250
+ peFlower = pBNS->edge + ieFlower;
251
+ if ( peFlower->flow == delta ) {
252
+ peFlower->forbidden |= forbidden_edge_mask;
253
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, ieFlower, 0 )) {
254
+ goto exit_function;
255
+ }
256
+ }
257
+ }
258
+ pe->forbidden |= forbidden_edge_mask;
259
+ pe->flow -= delta;
260
+ pv1->st_edge.flow -= delta;
261
+ pv2->st_edge.flow -= delta;
262
+ pBNS->tot_st_flow -= 2*delta;
263
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
264
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
265
+ if ( ret < 0 ) {
266
+ goto exit_function;
267
+ }
268
+ if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
269
+ vPathEnd == v2 && vPathStart == v1) &&
270
+ nDeltaCharge <= 0 /* charge moving to this atom disappers*/ ) {
271
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
272
+ (*pnNumRunBNS) ++;
273
+ if ( ret < 0 ) {
274
+ goto exit_function;
275
+ } else
276
+ if ( ret == 1 ) {
277
+ *pnTotalDelta += ret;
278
+ } else {
279
+ ret = RI_ERR_PROGR;
280
+ goto exit_function;
281
+ }
282
+ } else {
283
+ ret = 0;
284
+ pe->flow += delta;
285
+ pv1->st_edge.flow += delta;
286
+ pv2->st_edge.flow += delta;
287
+ pBNS->tot_st_flow += 2*delta;
288
+ }
289
+
290
+ }
291
+
292
+ }
293
+ }
294
+ }
295
+ exit_function:
296
+ /* remove bond fixation */
297
+ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
298
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
299
+ return ret;
300
+ }
301
+ /***********************************************************************************************
302
+
303
+
304
+ X=Y-O(-) => X(-)-Y=O
305
+
306
+
307
+ ************************************************************************************************/
308
+ int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at,
309
+ inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
310
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
311
+ {
312
+ int iNH = NO_VERTEX, neigh, neigh2;
313
+ EDGE_LIST NewlyFixedEdges;
314
+ int ret, i, j, k, k2, delta;
315
+ int num_at = pStruct->num_atoms;
316
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
317
+ Vertex v1, v2;
318
+ /* for RunBnsTestOnce */
319
+ Vertex vPathStart, vPathEnd;
320
+ int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
321
+ BNS_EDGE *pe, *pe2;
322
+
323
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
324
+ if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) {
325
+ goto exit_function;
326
+ }
327
+ /* fix all charges */
328
+ for ( i = 0; i < num_at; i ++ ) {
329
+ if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) {
330
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
331
+ goto exit_function;
332
+ }
333
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
334
+ }
335
+ if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) {
336
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
337
+ goto exit_function;
338
+ }
339
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
340
+ }
341
+ }
342
+
343
+ /* H(+) has been added to -O(-); check non-tautomeric atoms */
344
+ for ( i = 0; i < num_at; i ++ ) {
345
+ if ( !(pStruct->bMobileH? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal &&
346
+ at2[i].num_H + 1 == atf[i].num_H && /* normalization added H ??? What would happen in Fixed-H case?*/
347
+ (k = pVA[i].nCMinusGroupEdge-1) >= 0 &&
348
+ pBNS->edge[k].flow == 1 && /* atom had (-) charge before preprocessing */
349
+ at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */
350
+ at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */
351
+ pVA[i].cNumValenceElectrons == 6 && /* atom is O, S, Se, Te */
352
+ at2[neigh=at2[i].neighbor[0]].chem_bonds_valence > at2[neigh].valence
353
+ /* atom's single neighbor has multiple bond(s)*/
354
+ ) {
355
+ /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */
356
+ iNH = i;
357
+ for ( j = 0; j < at2[neigh].valence; j ++ ) {
358
+ neigh2 = at2[neigh].neighbor[j];
359
+ if ( neigh2 != iNH && !at2[neigh2].endpoint &&
360
+ !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden &&
361
+ 4 <= pVA[neigh2].cNumValenceElectrons &&
362
+ pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */
363
+ (k2 = pVA[neigh2].nCMinusGroupEdge-1) >= 0 &&
364
+ !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */ ) {
365
+ break;
366
+ }
367
+ }
368
+ if ( j < at2[neigh].valence ) {
369
+ delta = 1;
370
+ pe = pBNS->edge + k; /* -O(-) negative charge edge; flow = 1 */
371
+ pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */
372
+ v1 = pe->neighbor1;
373
+ v2 = pe->neighbor12 ^ v1;
374
+ pe->flow -= delta;
375
+ pBNS->vert[v1].st_edge.flow -= delta;
376
+ pBNS->vert[v2].st_edge.flow -= delta;
377
+ pBNS->tot_st_flow -= 2*delta;
378
+ pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */
379
+
380
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
381
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
382
+ if ( ret < 0 ) {
383
+ goto exit_function;
384
+ }
385
+ if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
386
+ vPathEnd == v2 && vPathStart == v1) &&
387
+ nDeltaCharge <= 1 ) {
388
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
389
+ (*pnNumRunBNS) ++;
390
+ if ( ret < 0 ) {
391
+ goto exit_function;
392
+ } else
393
+ if ( ret ) {
394
+ *pnTotalDelta += ret;
395
+ } else {
396
+ ret = RI_ERR_PROGR;
397
+ }
398
+ break;
399
+ } else {
400
+ /* the attempt has failed; restore the flow */
401
+ ret = 0;
402
+ pe->flow += delta;
403
+ pBNS->vert[v1].st_edge.flow += delta;
404
+ pBNS->vert[v2].st_edge.flow += delta;
405
+ pBNS->tot_st_flow += 2*delta;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ exit_function:
411
+ /* remove bond fixation */
412
+ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
413
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
414
+ return ret;
415
+ }
416
+ #if( FIX_ADD_PROTON_FOR_ADP == 1 )
417
+ /******************************************************************************************************/
418
+ int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at,
419
+ inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr,
420
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
421
+ {
422
+ int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2;
423
+ EDGE_LIST NewlyFixedEdges;
424
+ int ret, i, j, k, k2, delta;
425
+ int num_at = pStruct->num_atoms;
426
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
427
+ Vertex v1, v2;
428
+ /* for RunBnsTestOnce */
429
+ Vertex vPathStart, vPathEnd;
430
+ int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
431
+ BNS_EDGE *pe, *pe2;
432
+
433
+ ret = 0;
434
+ /*
435
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
436
+
437
+ for ( i = 0; i < num_at; i ++ ) {
438
+ if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) {
439
+ pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;
440
+ ret = 1;
441
+ break;
442
+ }
443
+ }
444
+ */
445
+ return ret;
446
+ }
447
+ #endif
448
+ /******************************************************************************************************
449
+ OH OH
450
+ / /
451
+ -NH => -NH(+) to eliminate false tautomerism. S(IV) or N(V) or P(V) may be a centerpoint
452
+ \\ \
453
+ O O(-)
454
+ *******************************************************************************************************/
455
+ int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at,
456
+ inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr,
457
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask )
458
+ {
459
+ EDGE_LIST NewlyFixedEdges;
460
+ int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2;
461
+ int num_at = pStruct->num_atoms;
462
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
463
+ Vertex v1, v2;
464
+ /* for RunBnsTestOnce */
465
+ Vertex vPathStart, vPathEnd;
466
+ int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
467
+ BNS_EDGE *pe, *pe2;
468
+
469
+ ret = 0;
470
+
471
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
472
+ if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) {
473
+ goto exit_function;
474
+ }
475
+ /* fix all charges */
476
+ for ( i = 0; i < num_at; i ++ ) {
477
+ if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) {
478
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
479
+ goto exit_function;
480
+ }
481
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
482
+ }
483
+ if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) {
484
+ if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) {
485
+ goto exit_function;
486
+ }
487
+ pBNS->edge[j].forbidden |= forbidden_edge_mask;
488
+ }
489
+ }
490
+
491
+ for ( i = 0; i < picr->num_endp_in1_only; i ++ ) {
492
+ endpoint1 = picr->endp_in1_only[i]-1;
493
+ if ( at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence ||
494
+ pVA[endpoint1].nCMinusGroupEdge <= 0 ) {
495
+ continue;
496
+ }
497
+ /* find centerpoint */
498
+ for ( j = 0; j < at2[endpoint1].valence; j ++ ) {
499
+ if ( BOND_TYPE_DOUBLE == ( BOND_TYPE_MASK & at2[endpoint1].bond_type[j] ) ) {
500
+ centerpoint = at2[endpoint1].neighbor[j];
501
+ if ( at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 ||
502
+ !is_centerpoint_elem( at2[centerpoint].el_number ) ) {
503
+ continue;
504
+ }
505
+ /* -- the centerpoint as depicted has no ChargeStruct flower ---
506
+ m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 );
507
+ if ( m < 0 || pBNS->edge[m].flow ) {
508
+ continue;
509
+ }
510
+ */
511
+ /* find 2nd endpoint */
512
+ for ( k = 0; k < at2[centerpoint].valence; k ++ ) {
513
+ if ( BOND_TYPE_SINGLE != ( BOND_TYPE_MASK & at2[centerpoint].bond_type[k] ) ) {
514
+ continue;
515
+ }
516
+ endpoint2 = at2[centerpoint].neighbor[k];
517
+ if ( !at2[endpoint2].endpoint && atn[endpoint2].endpoint ) {
518
+ break;
519
+ }
520
+ }
521
+ if ( k == at2[centerpoint].valence ) {
522
+ continue;
523
+ }
524
+ /* the centerpoint and two extra endpoints have been found */
525
+ pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1;
526
+ if ( !pe->flow ) {
527
+ continue;
528
+ }
529
+ pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1;
530
+ if ( pe2->flow ) {
531
+ continue;
532
+ }
533
+ delta = 1;
534
+ v1 = pe->neighbor1;
535
+ v2 = pe->neighbor12 ^ v1;
536
+ pe->flow -= delta;
537
+ pBNS->vert[v1].st_edge.flow -= delta;
538
+ pBNS->vert[v2].st_edge.flow -= delta;
539
+ pBNS->tot_st_flow -= 2*delta;
540
+ pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */
541
+
542
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
543
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
544
+ if ( ret < 0 ) {
545
+ goto exit_function;
546
+ }
547
+ if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
548
+ vPathEnd == v2 && vPathStart == v1) &&
549
+ nDeltaCharge <= 1 ) {
550
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
551
+ (*pnNumRunBNS) ++;
552
+ if ( ret < 0 ) {
553
+ goto exit_function;
554
+ } else
555
+ if ( ret ) {
556
+ *pnTotalDelta += ret;
557
+ } else {
558
+ ret = RI_ERR_PROGR;
559
+ }
560
+ goto exit_function;
561
+ } else {
562
+ ret = 0;
563
+ pe->flow += delta;
564
+ pBNS->vert[v1].st_edge.flow += delta;
565
+ pBNS->vert[v2].st_edge.flow += delta;
566
+ pBNS->tot_st_flow += 2*delta;
567
+ pe2->forbidden |= forbidden_edge_mask;
568
+ }
569
+ }
570
+ }
571
+ }
572
+
573
+ exit_function:
574
+ /* remove bond fixation */
575
+ RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
576
+ AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
577
+ return ret;
578
+ }
579
+ /*************************************************************************/
580
+ int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct )
581
+ {
582
+ int i, j, k, len, ret = 0;
583
+ AT_NUMB *pNum;
584
+ for ( i = 0; i < TAUT_NUM; i ++ ) {
585
+ if ( pStruct->pOneINChI_Aux[i] ) {
586
+ pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd &&
587
+ pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0])?
588
+ pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd:
589
+ (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd &&
590
+ pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0])?
591
+ pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL;
592
+ } else {
593
+ pNum = NULL;
594
+ }
595
+ if ( pNum ) {
596
+ len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]);
597
+ if ( !pStruct->nCanon2Atno[i] &&
598
+ !(pStruct->nCanon2Atno[i] = (AT_NUMB *)inchi_malloc( len )) ||
599
+ !pStruct->nAtno2Canon[i] &&
600
+ !(pStruct->nAtno2Canon[i] = (AT_NUMB *)inchi_malloc( len ))) {
601
+ ret = RI_ERR_ALLOC;
602
+ goto exit_function;
603
+ }
604
+
605
+ INCHI_HEAPCHK
606
+
607
+ memcpy( pStruct->nCanon2Atno[i], pNum, len ); /* ??? the next for(...) fills it out */
608
+
609
+ INCHI_HEAPCHK
610
+
611
+ for ( j = 0; j < pStruct->num_atoms; j ++ ) {
612
+ k = pNum[j]-1; /* atom number */
613
+ pStruct->nCanon2Atno[i][j] = (AT_NUMB)k;
614
+ pStruct->nAtno2Canon[i][k] = (AT_NUMB)j;
615
+ INCHI_HEAPCHK
616
+ }
617
+ } else
618
+ if ( !i ) {
619
+ ret = RI_ERR_PROGR;
620
+ goto exit_function;
621
+ } else {
622
+ if ( pStruct->nCanon2Atno[i] ) {
623
+ inchi_free( pStruct->nCanon2Atno[i] );
624
+ pStruct->nCanon2Atno[i] = NULL;
625
+ }
626
+ INCHI_HEAPCHK
627
+ if ( pStruct->nAtno2Canon[i] ) {
628
+ inchi_free( pStruct->nAtno2Canon[i] );
629
+ pStruct->nAtno2Canon[i] = NULL;
630
+ }
631
+ INCHI_HEAPCHK
632
+ }
633
+ }
634
+
635
+ exit_function:
636
+ return ret;
637
+ }
638
+ /*************************************************************************/
639
+ int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] )
640
+ {
641
+ int ret = 0;
642
+ /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/
643
+ if ( NULL == pStruct->endpoint ) {
644
+ pStruct->endpoint = (AT_NUMB *)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0]));
645
+ } else {
646
+ memset( pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0] ) );
647
+ }
648
+ if ( NULL == pStruct->fixed_H ) {
649
+ pStruct->fixed_H = (S_CHAR *)inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0]));
650
+ }
651
+ if ( !pStruct->endpoint || !pStruct->fixed_H ) {
652
+ ret = RI_ERR_ALLOC;
653
+ goto exit_function;
654
+ }
655
+ /*--- fill out Mobile/Fixed-H data from the input InChI ---*/
656
+ GetTgroupInfoFromInChI( &pStruct->ti, NULL, pStruct->endpoint, pInChI[1] );
657
+ if ( pInChI[0]->nNum_H_fixed ) {
658
+ memcpy( pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) );
659
+ } else {
660
+ memset( pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) );
661
+ }
662
+
663
+ exit_function:
664
+ return ret;
665
+ }
666
+ /***********************************************************************************************/
667
+ int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i )
668
+ {
669
+ int ret = 0, i, j;
670
+ int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted;
671
+ inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] &&
672
+ pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL;
673
+ S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL;
674
+ /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */
675
+ /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */
676
+ AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0];
677
+ S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H :
678
+ (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL;
679
+ S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)?
680
+ pStruct->pOneINChI[1]->nNum_H :
681
+ (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)?
682
+ pStruct->pOneINChI[0]->nNum_H : NULL;
683
+ int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs;
684
+ memset( pc2i, 0, sizeof(*pc2i) );
685
+ pc2i->nNumTgInChI = pStruct->ti.num_t_groups;
686
+ pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups;
687
+ pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs;
688
+
689
+ pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI;
690
+ pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons;
691
+ pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;
692
+
693
+ pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists;
694
+ pc2i->bHasDifference |= !bFixHRevrsExists;
695
+
696
+ for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) {
697
+ nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
698
+ nNumTgMInChI = pStruct->ti.t_group[i].num[1];
699
+ nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
700
+ nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
701
+
702
+ pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs;
703
+ pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs;
704
+
705
+ if ( pStruct->ti.t_group[i].nNumEndpoints ==
706
+ pStruct->One_ti.t_group[i].nNumEndpoints ) {
707
+
708
+ if ( nNumTgHInChI != nNumTgHRevrs ) {
709
+ pc2i->nNumTgDiffH ++;
710
+ }
711
+ if ( nNumTgMInChI != nNumTgMRevrs ) {
712
+ pc2i->nNumTgDiffMinus ++;
713
+ }
714
+ }
715
+ pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints !=
716
+ pStruct->One_ti.t_group[i].nNumEndpoints;
717
+
718
+ pc2i->nNumTgHInChI += nNumTgHInChI;
719
+ pc2i->nNumTgMInChI += nNumTgMInChI;
720
+ pc2i->nNumTgHRevrs += nNumTgHRevrs;
721
+ pc2i->nNumTgMRevrs += nNumTgMRevrs;
722
+
723
+ }
724
+ for ( ; i < pStruct->ti.num_t_groups; i ++ ) {
725
+ nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
726
+ nNumTgMInChI = pStruct->ti.t_group[i].num[1];
727
+ pc2i->nNumTgHInChI += nNumTgHInChI;
728
+ pc2i->nNumTgMInChI += nNumTgMInChI;
729
+ pc2i->bHasDifference |= 1;
730
+ }
731
+ for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) {
732
+ nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
733
+ nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
734
+ pc2i->nNumTgHRevrs += nNumTgHRevrs;
735
+ pc2i->nNumTgMRevrs += nNumTgMRevrs;
736
+ pc2i->bHasDifference |= 1;
737
+
738
+ }
739
+ for ( i = j = 0; i < pStruct->num_atoms; i ++ ) {
740
+ /* i = original InChI canonical number - 1 */
741
+ /* k = atom number from InChI created out of restored Fixed-H structure */
742
+ int iCanonRevrs = nAtno2CanonRevrs[i];
743
+ int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */
744
+ int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0;
745
+ int nFixHInChI = pStruct->fixed_H[i];
746
+ int nFixHRevrs = num_Fixed_H_Revrs? num_Fixed_H_Revrs[iCanonRevrs]:0;
747
+ int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0;
748
+ int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0;
749
+ if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs ) ||
750
+ (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) {
751
+ /* in InChI or reversed InChI atom[i] is not tautomeric */
752
+ /* and number of fixed-H on the atom[i] differs */
753
+ if ( j >= MAX_DIFF_FIXH ) {
754
+ ret = RI_ERR_PROGR;
755
+ goto exit_function;
756
+ }
757
+ pc2i->c2at[j].endptInChI = endptInChI;
758
+ pc2i->c2at[j].endptRevrs = endptRevrs;
759
+ pc2i->bHasDifference |= !endptInChI != !endptRevrs;
760
+ pc2i->c2at[j].atomNumber = i;
761
+ pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons;
762
+ pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber;
763
+ pc2i->c2at[j].nFixHInChI = nFixHInChI;
764
+ pc2i->c2at[j].nFixHRevrs = nFixHRevrs;
765
+ pc2i->bHasDifference |= nFixHInChI != nFixHRevrs;
766
+ pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] :
767
+ pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0;
768
+ pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)?
769
+ pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] :
770
+ (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)?
771
+ pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0;
772
+ pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI);
773
+ pc2i->bHasDifference |= nMobHInChI != nMobHRevrs;
774
+ pc2i->c2at[j].nNumHRevrs = at2[i].num_H;
775
+ pc2i->c2at[j].nAtChargeRevrs = at2[i].charge;
776
+ j ++;
777
+ }
778
+ pc2i->nNumEndpInChI += (endptInChI != 0);
779
+ pc2i->nNumEndpRevrs += (endptRevrs != 0);
780
+
781
+ if ( !pVA[i].cMetal ) {
782
+ pc2i->nChargeFixHRevrsNonMetal += at2[i].charge;
783
+ pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].charge : 0;
784
+ }
785
+
786
+ /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
787
+ }
788
+ pc2i->nChargeFixHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0;
789
+ pc2i->nChargeMobHInChI = pInChI[1]? pInChI[1]->nTotalCharge : 0;
790
+
791
+ pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1]? pStruct->pOneINChI[1]->nTotalCharge :
792
+ pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0;
793
+ pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0;
794
+
795
+ pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs;
796
+ pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs;
797
+
798
+ exit_function:
799
+ pc2i->len_c2at = j;
800
+
801
+ return ret;
802
+ }
803
+ /***********************************************************************************************/
804
+ int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2,
805
+ VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i )
806
+ {
807
+ int ret = 0, i, j, iat;
808
+ int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted;
809
+ inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] &&
810
+ pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL;
811
+ /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */
812
+ /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */
813
+ AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0];
814
+ AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0];
815
+ S_CHAR *pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL;
816
+ S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)?
817
+ pStruct->pOneINChI[0]->nNum_H : NULL;
818
+ int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs;
819
+ memset( pc2i, 0, sizeof(*pc2i) );
820
+ pc2i->nNumTgInChI = pStruct->ti.num_t_groups;
821
+ pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups;
822
+ pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs;
823
+
824
+ pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI;
825
+ pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons;
826
+ /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/
827
+
828
+ pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists;
829
+ /*pc2i->bHasDifference |= !bFixHRevrsExists;*/
830
+
831
+ for ( i = 0; i < pStruct->ti.num_t_groups; i ++ ) {
832
+ int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos;
833
+ int jNum = pStruct->ti.t_group[i].nNumEndpoints;
834
+ int is_N, is_O;
835
+ for ( j = 0; j < jNum; j ++ ) {
836
+ iat = pStruct->ti.nEndpointAtomNumber[jFst + j];
837
+ is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1;
838
+ is_O = pVA[iat].cNumValenceElectrons == 6;
839
+ if ( is_N + is_O != 1 ) {
840
+ return RI_ERR_SYNTAX;
841
+ }
842
+ pc2i->nNumTgNInChI += is_N;
843
+ pc2i->nNumTgOInChI += is_O;
844
+ if ( at2[iat].chem_bonds_valence == at2[iat].valence ) {
845
+ /* donor */
846
+ if ( is_N ) {
847
+ /* N */
848
+ pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
849
+ pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2;
850
+ pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
851
+ pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1;
852
+ } else {
853
+ /* O, S, Se, Te */
854
+ pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
855
+ pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
856
+ }
857
+ } else
858
+ if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) {
859
+ /* donor */
860
+ if ( is_N ) {
861
+ /* N */
862
+ pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1;
863
+ pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0;
864
+ pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0;
865
+ } else {
866
+ /* O, S, Se, Te */
867
+ pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0;
868
+ }
869
+ }
870
+ }
871
+ }
872
+ for ( i = 0; i < pStruct->One_ti.num_t_groups; i ++ ) {
873
+ int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos;
874
+ int jNum = pStruct->One_ti.t_group[i].nNumEndpoints;
875
+ int is_N, is_O;
876
+ for ( j = 0; j < jNum; j ++ ) {
877
+ iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]];
878
+ is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1;
879
+ is_O = pVA[iat].cNumValenceElectrons == 6;
880
+ if ( is_N + is_O != 1 ) {
881
+ return RI_ERR_PROGR;
882
+ }
883
+ pc2i->nNumTgNRevrs += is_N;
884
+ pc2i->nNumTgORevrs += is_O;
885
+ if ( at2[iat].chem_bonds_valence == at2[iat].valence ) {
886
+ /* donor */
887
+ if ( is_N ) {
888
+ /* N */
889
+ pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
890
+ pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2;
891
+ pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
892
+ pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1;
893
+ } else {
894
+ /* O, S, Se, Te */
895
+ pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
896
+ pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
897
+ }
898
+ } else
899
+ if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) {
900
+ /* donor */
901
+ if ( is_N ) {
902
+ /* N */
903
+ pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1;
904
+ pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0;
905
+ pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0;
906
+ } else {
907
+ /* O, S, Se, Te */
908
+ pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0;
909
+ }
910
+ }
911
+ }
912
+ }
913
+
914
+ for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) {
915
+ nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
916
+ nNumTgMInChI = pStruct->ti.t_group[i].num[1];
917
+ nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
918
+ nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
919
+
920
+ pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs;
921
+ pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs;
922
+
923
+ if ( pStruct->ti.t_group[i].nNumEndpoints ==
924
+ pStruct->One_ti.t_group[i].nNumEndpoints ) {
925
+
926
+ if ( nNumTgHInChI != nNumTgHRevrs ) {
927
+ pc2i->nNumTgDiffH ++;
928
+ }
929
+ if ( nNumTgMInChI != nNumTgMRevrs ) {
930
+ pc2i->nNumTgDiffMinus ++;
931
+ }
932
+ }
933
+ pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints !=
934
+ pStruct->One_ti.t_group[i].nNumEndpoints;
935
+
936
+ pc2i->nNumTgHInChI += nNumTgHInChI;
937
+ pc2i->nNumTgMInChI += nNumTgMInChI;
938
+ pc2i->nNumTgHRevrs += nNumTgHRevrs;
939
+ pc2i->nNumTgMRevrs += nNumTgMRevrs;
940
+
941
+ }
942
+ for ( ; i < pStruct->ti.num_t_groups; i ++ ) {
943
+ nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1];
944
+ nNumTgMInChI = pStruct->ti.t_group[i].num[1];
945
+ pc2i->nNumTgHInChI += nNumTgHInChI;
946
+ pc2i->nNumTgMInChI += nNumTgMInChI;
947
+ pc2i->bHasDifference |= 1;
948
+ }
949
+ for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) {
950
+ nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1];
951
+ nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1];
952
+ pc2i->nNumTgHRevrs += nNumTgHRevrs;
953
+ pc2i->nNumTgMRevrs += nNumTgMRevrs;
954
+ pc2i->bHasDifference |= 1;
955
+
956
+ }
957
+ for ( i = j = 0; i < pStruct->num_atoms; i ++ ) {
958
+ /* i = original InChI canonical number - 1 */
959
+ /* k = atom number from InChI created out of restored Fixed-H structure */
960
+ int iCanonRevrs = nAtno2CanonRevrs[i];
961
+ int endptInChI = at2[i].endpoint; /* endpoint in InChI */
962
+ int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0;
963
+ int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0;
964
+ int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0;
965
+ if ( (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) {
966
+ /* in InChI or reversed InChI atom[i] is not tautomeric */
967
+ /* and number of fixed-H on the atom[i] differs */
968
+ if ( j >= MAX_DIFF_FIXH ) {
969
+ ret = RI_ERR_PROGR;
970
+ goto exit_function;
971
+ }
972
+ pc2i->c2at[j].endptInChI = endptInChI;
973
+ pc2i->c2at[j].endptRevrs = endptRevrs;
974
+ pc2i->bHasDifference |= !endptInChI != !endptRevrs;
975
+ pc2i->c2at[j].atomNumber = i;
976
+ pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons;
977
+ pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber;
978
+ pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] :
979
+ pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0;
980
+ pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)?
981
+ pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] :
982
+ (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)?
983
+ pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0;
984
+
985
+ pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI);
986
+ pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs);
987
+ pc2i->c2at[j].nNumHRevrs = at2[i].num_H;
988
+ pc2i->c2at[j].nAtChargeRevrs = at2[i].charge;
989
+ j ++;
990
+ }
991
+ pc2i->nNumEndpInChI += (endptInChI != 0);
992
+ pc2i->nNumEndpRevrs += (endptRevrs != 0);
993
+
994
+ if ( !pVA[i].cMetal ) {
995
+ pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint)? at_Mobile_H_Revrs[i].charge : 0;
996
+ }
997
+
998
+
999
+ /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
1000
+ }
1001
+ pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge;
1002
+
1003
+ pc2i->nChargeMobHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0;
1004
+
1005
+ pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0;
1006
+
1007
+ pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs;
1008
+
1009
+ exit_function:
1010
+ pc2i->len_c2at = j;
1011
+
1012
+ return ret;
1013
+ }
1014
+ /******************************************************************************************************/
1015
+ int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD,
1016
+ StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA,
1017
+ ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH,
1018
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask)
1019
+ {
1020
+ int i;
1021
+ int err;
1022
+ ICR icr, icr2;
1023
+ int num_norm_endpoints, num_endpoints, num_norm_t_groups, num_mobile, num_norm_mobile, ret = 0;
1024
+ #if( bRELEASE_VERSION == 0 )
1025
+ #ifndef INCHI_LIBRARY
1026
+ const char *szCurHdr = (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue : "???";
1027
+ int iComponent = pTCGroups->iComponent;
1028
+ #endif
1029
+ #endif
1030
+ T_GROUP_INFO *t_group_info = NULL;
1031
+ inp_ATOM *at_norm = NULL; /* normalized */
1032
+ inp_ATOM *at_prep = NULL; /* preprocessed */
1033
+ INCHI_MODE cmpInChI, cmpInChI2;
1034
+ int nDeltaPrev, nDeltaCur;
1035
+ int iOrigInChI, iRevrInChI;
1036
+
1037
+
1038
+ /***********************************************************/
1039
+ /* normalize and create one component InChI */
1040
+ /***********************************************************/
1041
+ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1042
+ &t_group_info, &at_norm, &at_prep );
1043
+ if ( ret < 0 ) {
1044
+ #if( bRELEASE_VERSION == 0 )
1045
+ #ifndef INCHI_LIBRARY
1046
+ fprintf( stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1047
+ szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret);
1048
+ #endif
1049
+ #endif
1050
+ goto exit_function;
1051
+ }
1052
+ if ( pStruct->bMobileH == TAUT_NON ) {
1053
+ /* these indexes are used to compare Mobile-H InChI */
1054
+ iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0;
1055
+ iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0;
1056
+ } else {
1057
+ iOrigInChI = 0;
1058
+ iRevrInChI = 0;
1059
+ }
1060
+
1061
+ /************************************************************/
1062
+ /* compare */
1063
+ /************************************************************/
1064
+ if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) {
1065
+ goto exit_function;
1066
+ }
1067
+ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err );
1068
+ if ( cmpInChI & IDIF_PROBLEM ) {
1069
+ ret = RI_ERR_PROGR; /* severe restore problem */
1070
+ goto exit_function;
1071
+ }
1072
+ if ( err ) {
1073
+ ret = RI_ERR_ALLOC;
1074
+ goto exit_function;
1075
+ }
1076
+ /********** InChI from restored structure has LESS hydrogen atoms ******************************/
1077
+ if ( (cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1) ) {
1078
+ do {
1079
+ ret = FixLessHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups,
1080
+ pnNumRunBNS, pnTotalDelta, forbidden_edge_mask );
1081
+ if ( ret < 0 ) {
1082
+ goto exit_function;
1083
+ }
1084
+ if ( ret ) {
1085
+ /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1086
+ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1087
+ &t_group_info, &at_norm, &at_prep );
1088
+ if ( ret < 0 ) {
1089
+ #if( bRELEASE_VERSION == 0 )
1090
+ #ifndef INCHI_LIBRARY
1091
+ fprintf( stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1092
+ szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret);
1093
+ #endif
1094
+ #endif
1095
+ goto exit_function;
1096
+ }
1097
+ /* compare new InChI to the original InChI */
1098
+ if ( pStruct->bMobileH == TAUT_NON ) {
1099
+ iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0;
1100
+ } else {
1101
+ iRevrInChI = 0;
1102
+ }
1103
+ if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) {
1104
+ goto exit_function;
1105
+ }
1106
+ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err );
1107
+ nDeltaPrev = nDeltaCur;
1108
+ nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1;
1109
+ } else {
1110
+ break;
1111
+ }
1112
+ } while( (cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev );
1113
+ }
1114
+ /********** InChI from restored structure has MORE hydrogen atoms ******************************/
1115
+ if ( (cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2) ) {
1116
+ do {
1117
+ ret = FixMoreHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups,
1118
+ pnNumRunBNS, pnTotalDelta, forbidden_edge_mask );
1119
+ if ( ret < 0 ) {
1120
+ goto exit_function;
1121
+ }
1122
+ if ( ret ) {
1123
+ /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1124
+ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1125
+ &t_group_info, &at_norm, &at_prep );
1126
+ if ( ret < 0 ) {
1127
+ #if( bRELEASE_VERSION == 0 )
1128
+ #ifndef INCHI_LIBRARY
1129
+ fprintf( stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1130
+ szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret);
1131
+ #endif
1132
+ #endif
1133
+ goto exit_function;
1134
+ }
1135
+ /* compare new InChI to the original InChI */
1136
+ if ( pStruct->bMobileH == TAUT_NON ) {
1137
+ iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0;
1138
+ } else {
1139
+ iRevrInChI = 0;
1140
+ }
1141
+ if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) {
1142
+ goto exit_function;
1143
+ }
1144
+ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err );
1145
+ nDeltaPrev = nDeltaCur;
1146
+ nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2;
1147
+ } else {
1148
+ break;
1149
+ }
1150
+ } while( (cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev );
1151
+ }
1152
+ /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/
1153
+ if ( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only) ) {
1154
+ do {
1155
+ ret = FixRemoveExtraTautEndpoints( pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr,
1156
+ pnNumRunBNS, pnTotalDelta, forbidden_edge_mask );
1157
+ if ( ret < 0 ) {
1158
+ goto exit_function;
1159
+ }
1160
+ if ( ret ) {
1161
+ /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */
1162
+ ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups,
1163
+ &t_group_info, &at_norm, &at_prep );
1164
+ if ( ret < 0 ) {
1165
+ #if( bRELEASE_VERSION == 0 )
1166
+ #ifndef INCHI_LIBRARY
1167
+ fprintf( stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
1168
+ szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret);
1169
+ #endif
1170
+ #endif
1171
+ goto exit_function;
1172
+ }
1173
+ /* compare new InChI to the original InChI */
1174
+ if ( pStruct->bMobileH == TAUT_NON ) {
1175
+ iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0;
1176
+ } else {
1177
+ iRevrInChI = 0;
1178
+ }
1179
+ if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) {
1180
+ goto exit_function;
1181
+ }
1182
+ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err );
1183
+ nDeltaPrev = nDeltaCur;
1184
+ nDeltaCur = icr.num_endp_in1_only;
1185
+ } else {
1186
+ break;
1187
+ }
1188
+ } while( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev );
1189
+ }
1190
+ /************************ case of Fixed-H ******************************************************/
1191
+
1192
+ if ( pStruct->bMobileH == TAUT_NON ) {
1193
+ int num_tries = 0;
1194
+ do {
1195
+ if ( 0 > (ret = FixFixedHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1196
+ &t_group_info, &at_norm, &at_prep, pInChI,
1197
+ num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1198
+ forbidden_stereo_edge_mask) ) ) {
1199
+ goto exit_function;
1200
+ }
1201
+ } while( num_tries ++ < 2 && ret > 0 );
1202
+ }
1203
+ /************************ case of Fixed-H ******************************************************/
1204
+ if ( pStruct->bMobileH == TAUT_YES ) {
1205
+ if ( 0 > (ret = FixMobileHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1206
+ &t_group_info, &at_norm, &at_prep, pInChI,
1207
+ num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1208
+ forbidden_stereo_edge_mask) ) ) {
1209
+ goto exit_function;
1210
+ }
1211
+ }
1212
+ /**********************************************************************************************/
1213
+ /* stereo */
1214
+ cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err );
1215
+ if ( cmpInChI & IDIF_PROBLEM ) {
1216
+ ret = RI_ERR_PROGR; /* severe restore problem */
1217
+ goto exit_function;
1218
+ }
1219
+ if ( err ) {
1220
+ ret = RI_ERR_ALLOC;
1221
+ goto exit_function;
1222
+ }
1223
+ cmpInChI2 = 0;
1224
+ memset ( &icr2, 0, sizeof(icr2) );
1225
+ if ( iRevrInChI || iOrigInChI ) {
1226
+ /* additional mobile-H compare in case of Fixed-H */
1227
+ cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err );
1228
+ if ( cmpInChI & IDIF_PROBLEM ) {
1229
+ ret = RI_ERR_PROGR; /* severe restore problem */
1230
+ goto exit_function;
1231
+ }
1232
+ if ( err ) {
1233
+ ret = RI_ERR_ALLOC;
1234
+ goto exit_function;
1235
+ }
1236
+ }
1237
+ ret = FixRestoredStructureStereo( cmpInChI, &icr, cmpInChI2, &icr2,
1238
+ ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups,
1239
+ &t_group_info, &at_norm, &at_prep, pInChI,
1240
+ num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask,
1241
+ forbidden_stereo_edge_mask);
1242
+
1243
+ if ( ret < 0 ) {
1244
+ goto exit_function;
1245
+ }
1246
+ #if( FIX_ADD_PROTON_FOR_ADP == 1 )
1247
+ /************************ check and fix ADP by adding a proton (dummy) *************************/
1248
+ if ( cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow ) {
1249
+ ret = FixAddProtonForADP( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr,
1250
+ pnNumRunBNS, pnTotalDelta, forbidden_edge_mask );
1251
+ if ( ret < 0 ) {
1252
+ goto exit_function;
1253
+ }
1254
+ }
1255
+ #endif
1256
+ /* moved to MakeOneInChIOutOfStrFromINChI():
1257
+ pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0;
1258
+ */
1259
+
1260
+ /* count endpoints */
1261
+ num_endpoints = 0;
1262
+ num_norm_endpoints = 0;
1263
+ num_norm_t_groups = 0;
1264
+ num_mobile = 0;
1265
+ num_norm_mobile = 0;
1266
+ at_norm = pStruct->pOne_norm_data[0]->at;
1267
+ for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) {
1268
+ num_endpoints += pTCGroups->pTCG[i].num_edges;
1269
+ num_mobile += pTCGroups->pTCG[i].tg_num_H + pTCGroups->pTCG[i].tg_num_Minus;
1270
+ }
1271
+
1272
+ if ( t_group_info ) {
1273
+ /* after canonicalization, t_group_info->t_group[i].num[0] = number of H */
1274
+ /* t_group_info->t_group[i].num[1] = number of (-) */
1275
+ for ( i = 0; i < t_group_info->num_t_groups; i ++ ) {
1276
+ if ( t_group_info->t_group[i].num[0] ) {
1277
+ num_norm_t_groups ++;
1278
+ num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints;
1279
+ num_norm_mobile += t_group_info->t_group[i].num[0]+t_group_info->t_group[i].num[1];
1280
+ }
1281
+ }
1282
+ }
1283
+ #if( bRELEASE_VERSION == 0 )
1284
+ #ifndef INCHI_LIBRARY
1285
+ if ( num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints ) {
1286
+ /* need aggressive (de)protonation */
1287
+ /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */
1288
+ fprintf( stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n",
1289
+ (*ip).pSdfValue, (*pTCGroups).iComponent,
1290
+ pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F',
1291
+ pTCGroups->num_tgroups, num_norm_t_groups,
1292
+ num_endpoints, num_norm_endpoints );
1293
+
1294
+ }
1295
+ #endif
1296
+ #endif
1297
+
1298
+ exit_function:
1299
+
1300
+ for( i = 0; i < TAUT_NUM; i ++ ) {
1301
+ Free_INChI( &pStruct->pOneINChI[i] );
1302
+ Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] );
1303
+ FreeInpAtomData( pStruct->pOne_norm_data[i] );
1304
+ if ( pStruct->pOne_norm_data[i] ) {
1305
+ inchi_free( pStruct->pOne_norm_data[i] );
1306
+ pStruct->pOne_norm_data[i] = NULL;
1307
+ }
1308
+ }
1309
+ free_t_group_info( &pStruct->One_ti );
1310
+ return ret;
1311
+ }
1312
+ /******************************************************************************************************/
1313
+ /* Find A=X< where all bonds to X except A=X are marked as stereogenic; temporary allow stereobonds */
1314
+ /* change and make A=X bonds single */
1315
+ int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
1316
+ inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
1317
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
1318
+ {
1319
+ int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK;
1320
+ int inv_forbidden_edge_stereo = ~forbidden_edge_stereo;
1321
+
1322
+ int i, k, ne, j1, j2, num_wrong, num_fixed;
1323
+ int ret2, retBNS, ret;
1324
+ int num_at = pStruct->num_atoms;
1325
+ int num_deleted_H = pStruct->num_deleted_H;
1326
+ int len_at = num_at + num_deleted_H;
1327
+ EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges;
1328
+
1329
+ BNS_EDGE *pEdge;
1330
+ Vertex v1, v2;
1331
+ BNS_VERTEX *pv1, *pv2;
1332
+
1333
+ ret = 0;
1334
+
1335
+ /* to simplify, prepare new at[] from pBNS */
1336
+ memcpy( at2, at, len_at*sizeof(at2[0]));
1337
+ pStruct->at = at2;
1338
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
1339
+ pStruct->at = at;
1340
+ if ( ret2 < 0 ) {
1341
+ return ret;
1342
+ }
1343
+
1344
+ num_wrong = 0;
1345
+ /* find wrong double bonds */
1346
+ for ( i = 0; i < num_at; i ++ ) {
1347
+ if ( at2[i].valence == 3 &&
1348
+ at2[i].chem_bonds_valence - at2[i].valence == 1 &&
1349
+ at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
1350
+ (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1351
+ (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1352
+ j1 != j2 ) {
1353
+
1354
+ num_wrong ++;
1355
+ }
1356
+ }
1357
+ if ( !num_wrong ) {
1358
+ return 0;
1359
+ }
1360
+ num_fixed = 0;
1361
+ for ( i = 0; i < pBNS->num_bonds; i ++ ) {
1362
+ pEdge = pBNS->edge + i;
1363
+ if ( pEdge->forbidden & forbidden_edge_stereo ) {
1364
+ num_fixed ++;
1365
+ }
1366
+ }
1367
+
1368
+ /* there may be no fixed stereo bonds at all, see #87607 */
1369
+ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
1370
+ AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR );
1371
+ AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR );
1372
+
1373
+ /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */
1374
+
1375
+ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
1376
+ goto exit_function;
1377
+ }
1378
+ if ( (ret = AllocEdgeList( &FixedEdges, num_fixed )) ||
1379
+ (ret = AllocEdgeList( &WrongEdges, num_wrong )) ) {
1380
+ goto exit_function;
1381
+ }
1382
+ /* collect wrong double bonds and set flow=0 */
1383
+ for ( i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i ++ ) {
1384
+ if ( at2[i].valence == 3 &&
1385
+ at2[i].chem_bonds_valence - at2[i].valence == 1 &&
1386
+ at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] &&
1387
+ (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1388
+ (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE &&
1389
+ j1 != j2 ) {
1390
+ switch ( j1 + j2 ) {
1391
+ case 1: /* 0, 1 */
1392
+ k = 2;
1393
+ break;
1394
+ case 2: /* 0, 2 */
1395
+ k = 1;
1396
+ break;
1397
+ case 3: /* 1, 2 */
1398
+ k = 0;
1399
+ break;
1400
+ default:
1401
+ ret = RI_ERR_PROGR;
1402
+ goto exit_function;
1403
+ }
1404
+ ne = pBNS->vert[i].iedge[k];
1405
+ pEdge = pBNS->edge + ne;
1406
+ v1 = pEdge->neighbor1;
1407
+ v2 = pEdge->neighbor12 ^ v1;
1408
+ pv1 = pBNS->vert + v1;
1409
+ pv2 = pBNS->vert + v2;
1410
+
1411
+ if ( !pEdge->flow ) {
1412
+ ret = RI_ERR_PROGR;
1413
+ goto exit_function;
1414
+ }
1415
+ pEdge->flow --;
1416
+ pEdge->forbidden |= forbidden_edge_mask;
1417
+ pv1->st_edge.flow --;
1418
+ pv2->st_edge.flow --;
1419
+ pBNS->tot_st_flow -= 2;
1420
+ if ( ret = AddToEdgeList( &WrongEdges, ne, 0 )) {
1421
+ goto exit_function;
1422
+ }
1423
+ }
1424
+ }
1425
+ /* remove forbidden mark from stereo bonds (unfix stereo bonds) */
1426
+ for ( i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i ++ ) {
1427
+ pEdge = pBNS->edge + i;
1428
+ if ( pEdge->forbidden & forbidden_edge_stereo ) {
1429
+ pEdge->forbidden &= inv_forbidden_edge_stereo;
1430
+ FixedEdges.pnEdges[FixedEdges.num_edges ++] = i;
1431
+ }
1432
+ }
1433
+ /* Run BNS to move charges and rearrange bond orders */
1434
+ retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1435
+ (*pnNumRunBNS) ++;
1436
+ if ( retBNS < 0 ) {
1437
+ goto exit_function;
1438
+ } else
1439
+ if ( retBNS > 0 ) {
1440
+ *pnTotalDelta += retBNS;
1441
+ }
1442
+ /* remove forbidden_edge_mask and set forbidden_edge_stereo */
1443
+ RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask );
1444
+ /* allow carbon charges to change */
1445
+ RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
1446
+ /* fix previously unfixed stereo bonds */
1447
+ SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo );
1448
+ /* Run BNS again in case not all edge flows are maximal */
1449
+ ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1450
+ (*pnNumRunBNS) ++;
1451
+ if ( ret2 < 0 ) {
1452
+ goto exit_function;
1453
+ } else
1454
+ if ( ret2 > 0 ) {
1455
+ *pnTotalDelta += retBNS;
1456
+ }
1457
+ ret = retBNS;
1458
+
1459
+ exit_function:
1460
+
1461
+ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
1462
+ AllocEdgeList( &FixedEdges, EDGE_LIST_FREE );
1463
+ AllocEdgeList( &WrongEdges, EDGE_LIST_FREE );
1464
+
1465
+ return ret;
1466
+ }
1467
+ /******************************************************************************************************/
1468
+ /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */
1469
+ int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
1470
+ inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
1471
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
1472
+ {
1473
+ int i, j, neigh, num_endpoints, num_success;
1474
+ int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh;
1475
+ int ret2, ret_forbid_edges, ret, delta;
1476
+ int num_at = pStruct->num_atoms;
1477
+ int num_deleted_H = pStruct->num_deleted_H;
1478
+ int len_at = num_at + num_deleted_H;
1479
+ int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST;
1480
+ int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups);
1481
+ S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL];
1482
+ int numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh, numOtherDoubleBondOAcceptors=0;
1483
+ EDGE_LIST ChargeListAllExcept_DB_O;
1484
+
1485
+
1486
+ BNS_EDGE *pEdgeMinus, *pe;
1487
+ Vertex v1m, v2m;
1488
+ BNS_VERTEX *pv1m, *pv2m;
1489
+ ret = 0;
1490
+ num_success = 0;
1491
+
1492
+ /* count O(+)H, N(+)H */
1493
+
1494
+ /*
1495
+ if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) {
1496
+ goto exit_function;
1497
+ }
1498
+ */
1499
+ if ( ret = AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR ) ) {
1500
+ goto exit_function;
1501
+ }
1502
+
1503
+
1504
+ /* to simplify, prepare new at[] from pBNS */
1505
+ memcpy( at2, at, len_at*sizeof(at2[0]));
1506
+ pStruct->at = at2;
1507
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
1508
+ pStruct->at = at;
1509
+ if ( ret2 < 0 ) {
1510
+ ret = ret2;
1511
+ goto exit_function;
1512
+ }
1513
+ #if( FIND_RING_SYSTEMS == 1 )
1514
+ ret2 = MarkRingSystemsInp( at2, num_at );
1515
+ if ( ret2 < 0 ) {
1516
+ ret = ret2;
1517
+ goto exit_function;
1518
+ }
1519
+ #endif
1520
+ /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */
1521
+ ret_forbid_edges = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_test );
1522
+ if ( ret_forbid_edges < 0 ) {
1523
+ ret = ret_forbid_edges;
1524
+ goto exit_function;
1525
+ }
1526
+
1527
+ for ( i = 0; i < num_at; i ++ ) {
1528
+ if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */
1529
+ !(pVA[i].nTautGroupEdge || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i] ) &&
1530
+ !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 &&
1531
+ at2[i].valence < at2[i].chem_bonds_valence &&
1532
+ is_centerpoint_elem( at2[i].el_number ) ) {
1533
+
1534
+ is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal));
1535
+ /* look at the neighbors */
1536
+ numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0;
1537
+ num_donors_O = num_acceptors_O = 0;
1538
+ num_known_endpoints = num_wrong_neigh = 0;
1539
+ for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) {
1540
+ neigh = at2[i].neighbor[j];
1541
+ if ( (at2[neigh].endpoint || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh]) || at2[neigh].charge > 0 ) {
1542
+ num_known_endpoints ++;
1543
+ continue;
1544
+ }
1545
+ if ( pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test ) {
1546
+ continue;
1547
+ }
1548
+ bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK;
1549
+ if ( bond_type > BOND_TYPE_DOUBLE ) {
1550
+ num_wrong_neigh ++;
1551
+ continue;
1552
+ }
1553
+ if ( at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE ) {
1554
+ break; /* not this case */
1555
+ }
1556
+ if ( at2[neigh].chem_bonds_valence - at2[neigh].charge
1557
+ != get_endpoint_valence( at2[neigh].el_number ) ) {
1558
+ if ( bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6 ) {
1559
+ DoubleBondNotONeigh[numDoubleBondNotONeigh ++] = j;
1560
+ }
1561
+ continue;
1562
+ }
1563
+ if ( at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE &&
1564
+ (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge-1].flow != 1) ) {
1565
+ break;
1566
+ }
1567
+ switch( bond_type ) {
1568
+ case BOND_TYPE_SINGLE:
1569
+ if ( at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0 ) {
1570
+ num_wrong_neigh ++;
1571
+ continue;
1572
+ }
1573
+ num_donors ++;
1574
+ num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4);
1575
+ MobileChargeNeigh[numMobileChargeNeigh ++] = j;
1576
+ break;
1577
+ case BOND_TYPE_DOUBLE:
1578
+ if ( at2[neigh].charge ) {
1579
+ num_wrong_neigh ++;
1580
+ continue;
1581
+ }
1582
+ DoubleBondAcceptors[numDoubleBondAcceptors ++] = j;
1583
+ num_acceptors ++;
1584
+ num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4);
1585
+ }
1586
+ }
1587
+ if ( j != at2[i].valence || !num_donors || !num_acceptors ) {
1588
+ continue;
1589
+ }
1590
+ /* special case NOn(-) */
1591
+ if ( is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O) ) {
1592
+ continue;
1593
+ }
1594
+ if ( pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh ) {
1595
+ /* fix all charges except on =O */
1596
+ Vertex vPathStart, vPathEnd;
1597
+ int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
1598
+ int k, e, num_MovedCharges = 0;
1599
+
1600
+ if ( !ChargeListAllExcept_DB_O.num_edges ) {
1601
+ numOtherDoubleBondOAcceptors = 0;
1602
+ for ( k = 0; k < num_at; k ++ ) {
1603
+ if ( 1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow &&
1604
+ !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden &&
1605
+ !((e=pVA[k].nCMinusGroupEdge-1) >= 0 && pBNS->edge[e].flow) &&
1606
+ !((e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].flow) &&
1607
+ /* 0 == at2[k].charge && */
1608
+ pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal &&
1609
+ pStruct->endpoint && pStruct->endpoint[k] ||
1610
+ pStruct->fixed_H && pStruct->fixed_H[k] ) {
1611
+ numOtherDoubleBondOAcceptors ++; /* do not fix this minus edge */
1612
+ } else
1613
+ if ( (e=pVA[k].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].flow &&
1614
+ !pBNS->edge[e].forbidden &&
1615
+ ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) {
1616
+ goto exit_function;
1617
+ }
1618
+ if ( (e=pVA[k].nCPlusGroupEdge-1) >= 0 &&
1619
+ !pBNS->edge[e].forbidden &&
1620
+ ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) {
1621
+ goto exit_function;
1622
+ }
1623
+ }
1624
+ }
1625
+ /* fix double bonds to non-O neighbors connected by double bonds;
1626
+ we will try to make these bons single */
1627
+ for ( k = 0; k < numDoubleBondNotONeigh; k ++ ) {
1628
+ e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]];
1629
+ if ( !pBNS->edge[e].forbidden &&
1630
+ (ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 ))) {
1631
+ goto exit_function;
1632
+ }
1633
+ }
1634
+ /* attempt to make DoubleBondNotONeigh[] single */
1635
+ SetForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask);
1636
+ for ( k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k ++ ) {
1637
+ pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]];
1638
+ delta = 1;
1639
+ if ( pe->flow != delta )
1640
+ continue;
1641
+ pv1m = pBNS->vert + (v1m = pe->neighbor1);
1642
+ pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m);
1643
+ pv1m->st_edge.flow -= delta;
1644
+ pv2m->st_edge.flow -= delta;
1645
+ pe->flow -= delta;
1646
+ pBNS->tot_st_flow -= 2*delta;
1647
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
1648
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
1649
+ if ( ret < 0 ) {
1650
+ goto exit_function;
1651
+ }
1652
+ if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m ||
1653
+ vPathEnd == v2m && vPathStart == v1m) &&
1654
+ nDeltaCharge == 0 /* (-) moving from one to another atom*/ ) {
1655
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1656
+ (*pnNumRunBNS) ++;
1657
+ if ( ret < 0 ) {
1658
+ goto exit_function;
1659
+ } else
1660
+ if ( ret == 1 ) {
1661
+ *pnTotalDelta += ret;
1662
+ num_MovedCharges ++;
1663
+ } else {
1664
+ ret = RI_ERR_PROGR;
1665
+ goto exit_function;
1666
+ }
1667
+ } else {
1668
+ ret = 0;
1669
+ pv1m->st_edge.flow += delta;
1670
+ pv2m->st_edge.flow += delta;
1671
+ pe->flow += delta;
1672
+ pBNS->tot_st_flow += 2*delta;
1673
+ }
1674
+ }
1675
+ RemoveForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask);
1676
+ } else
1677
+ if ( !bPossiblyIgnore || !num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O) >=3 ) {
1678
+ /* remove negative charges from the neighbors */
1679
+ pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */
1680
+ pBNS->tot_st_cap += num_donors;
1681
+ pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */
1682
+ for ( j = 0; j < numMobileChargeNeigh; j ++ ) {
1683
+ neigh = at2[i].neighbor[ (int)MobileChargeNeigh[j] ];
1684
+ pEdgeMinus = pBNS->edge + (pVA[neigh].nCMinusGroupEdge-1);
1685
+ v1m = pEdgeMinus->neighbor1;
1686
+ v2m = pEdgeMinus->neighbor12 ^ v1m;
1687
+ pv1m = pBNS->vert + v1m;
1688
+ pv2m = pBNS->vert + v2m;
1689
+ delta = pEdgeMinus->flow;
1690
+ pv1m->st_edge.flow -= delta;
1691
+ pv2m->st_edge.flow -= delta;
1692
+ if ( IS_BNS_VT_C_GR( pv1m->type ) ) {
1693
+ /* irreversible change to ChargeStruct */
1694
+ pv1m->st_edge.cap -= delta;
1695
+ } else
1696
+ if ( IS_BNS_VT_C_GR( pv2m->type ) ) {
1697
+ /* irreversible change to ChargeStruct */
1698
+ pv2m->st_edge.cap -= delta;
1699
+ } else {
1700
+ ret = RI_ERR_PROGR;
1701
+ goto exit_function;
1702
+ }
1703
+ pBNS->tot_st_cap -= delta;
1704
+ pBNS->tot_st_flow -= 2*delta;
1705
+ pEdgeMinus->flow -= delta;
1706
+ }
1707
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1708
+ (*pnNumRunBNS) ++;
1709
+ if ( ret < 0 ) {
1710
+ goto exit_function;
1711
+ } else
1712
+ if ( ret == num_donors ) {
1713
+ *pnTotalDelta += ret;
1714
+ num_success ++;
1715
+ /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
1716
+ } else {
1717
+ ret = RI_ERR_PROGR;
1718
+ goto exit_function;
1719
+ }
1720
+ }
1721
+ }
1722
+ }
1723
+ if ( ret_forbid_edges ) {
1724
+ /* remove the marks */
1725
+ RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_test );
1726
+ }
1727
+ ret = num_success;
1728
+ exit_function:
1729
+ AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_FREE );
1730
+ return ret;
1731
+ }
1732
+ /******************************************************************************************************/
1733
+ /* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */
1734
+ int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
1735
+ inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
1736
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
1737
+ {
1738
+ int i;
1739
+
1740
+ int ret2, ret, pass;
1741
+ int num_at = pStruct->num_atoms;
1742
+ int num_deleted_H = pStruct->num_deleted_H;
1743
+ int len_at = num_at + num_deleted_H;
1744
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
1745
+
1746
+ int j, k;
1747
+ int cur_num_edges;
1748
+ BNS_EDGE *e;
1749
+ Vertex v1, v2;
1750
+
1751
+ EdgeIndex *pFixedEdges;
1752
+ int nNumEdgesToFix;
1753
+
1754
+ ret = 0;
1755
+
1756
+ /* to simplify, prepare new at[] from pBNS */
1757
+ memcpy( at2, at, len_at*sizeof(at2[0]));
1758
+ pStruct->at = at2;
1759
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
1760
+ pStruct->at = at;
1761
+ if ( ret2 < 0 ) {
1762
+ ret = ret2;
1763
+ goto exit_function;
1764
+ }
1765
+
1766
+ pFixedEdges = NULL;
1767
+
1768
+ nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */
1769
+ cur_num_edges = 0; /* count cur_num_edges only when pass==1; at the end they must be equal */
1770
+ for ( pass = 0; pass < 2; pass ++ ) {
1771
+ if ( pass ) {
1772
+ /* 2nd pass: allocate edge storage */
1773
+ if ( !nNumEdgesToFix ) {
1774
+ break; /* nothing to do */
1775
+ }
1776
+ pFixedEdges = (EdgeIndex *)inchi_malloc(nNumEdgesToFix * sizeof( pFixedEdges[0] ) );
1777
+ if ( !pFixedEdges ) {
1778
+ ret = RI_ERR_ALLOC;
1779
+ goto exit_function;
1780
+ }
1781
+ }
1782
+ for ( i = 0; i < num_at; i ++ ) {
1783
+ int neigh;
1784
+ if ( pVA[i].cMetal ) {
1785
+ for ( j = 0; j < at2[i].valence; j ++ ) {
1786
+ neigh = at2[i].neighbor[j];
1787
+ if ( pVA[neigh].cNumValenceElectrons == 4 &&
1788
+ pVA[neigh].cPeriodicRowNumber == 1 ) {
1789
+ continue; /* ignore carbon */
1790
+ }
1791
+ if ( at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge &&
1792
+ !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0 ) {
1793
+ int cnBits = at2[neigh].charge > 0? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) :
1794
+ MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0);
1795
+ int atBits = cnList[pVA[neigh].cnListIndex-1].bits;
1796
+ for ( k = 0; k < MAX_NUM_CN_BITS-1; k ++, atBits >>= cn_bits_shift ) { /* ??? */
1797
+ if ( (atBits & cnBits) == cnBits ) {
1798
+ break;
1799
+ }
1800
+ }
1801
+ if ( k == MAX_NUM_CN_BITS-1 ) {
1802
+ continue;
1803
+ }
1804
+ if ( pass == 0 ) {
1805
+ nNumEdgesToFix ++;
1806
+ } else {
1807
+ pFixedEdges[ cur_num_edges ++ ] = pBNS->vert[i].iedge[j];
1808
+ }
1809
+ }
1810
+ }
1811
+ }
1812
+ }
1813
+ }
1814
+
1815
+ /* restore the initial structures */
1816
+ memcpy( at2, at, (num_at + num_deleted_H)*sizeof(at2[0]));
1817
+
1818
+ if ( nNumEdgesToFix && pFixedEdges ) {
1819
+ if ( nNumEdgesToFix != cur_num_edges ) {
1820
+ ret = RI_ERR_PROGR;
1821
+ goto exit_function;
1822
+ }
1823
+ /* change edge flow, fix the edges, and run BNS */
1824
+ for ( i = 0; i < nNumEdgesToFix; i ++ ) {
1825
+ e = pBNS->edge + pFixedEdges[i];
1826
+ v1 = e->neighbor1;
1827
+ v2 = e->neighbor12 ^ v1;
1828
+ e->flow --;
1829
+ e->forbidden |= forbidden_edge_mask;
1830
+ pBNS->vert[v1].st_edge.flow --;
1831
+ pBNS->vert[v2].st_edge.flow --;
1832
+ pBNS->tot_st_flow -= 2;
1833
+ (*pnTotalDelta) -= 2;
1834
+ }
1835
+ /* Run BNS allowing to change any charges */
1836
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1837
+ (*pnNumRunBNS) ++;
1838
+ if ( ret < 0 ) {
1839
+ goto exit_function;
1840
+ } else {
1841
+ (*pnTotalDelta) += ret;
1842
+ }
1843
+ /* unfix the edges */
1844
+ for ( i = 0; i < nNumEdgesToFix; i ++ ) {
1845
+ e = pBNS->edge + pFixedEdges[i];
1846
+ e->forbidden &= inv_forbidden_edge_mask;
1847
+ }
1848
+ if ( ret < 2 * nNumEdgesToFix ) {
1849
+ /* not all fixes succeeded */
1850
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1851
+ (*pnNumRunBNS) ++;
1852
+ if ( ret < 0 ) {
1853
+ goto exit_function;
1854
+ } else {
1855
+ (*pnTotalDelta) += ret;
1856
+ }
1857
+ }
1858
+ }
1859
+ if ( pFixedEdges ) {
1860
+ inchi_free( pFixedEdges );
1861
+ pFixedEdges = NULL;
1862
+ }
1863
+
1864
+
1865
+ exit_function:
1866
+ return ret;
1867
+ }
1868
+ /**************************************************************************/
1869
+ /* In Reconnected structure change 'salt bonds' to 'coordination bonds */
1870
+ /* for example, M-O-C= -> M(+)-O(-)-C= */
1871
+ /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */
1872
+ /* However, in this release metal-organic compounds do not get much care */
1873
+ int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
1874
+ inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
1875
+ int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
1876
+ {
1877
+ int i;
1878
+
1879
+ int ret2, ret, cur_success;
1880
+ int num_at = pStruct->num_atoms;
1881
+ int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms;
1882
+ int num_deleted_H = pStruct->num_deleted_H;
1883
+ int len_at = num_at + num_deleted_H;
1884
+ int inv_forbidden_edge_mask = ~forbidden_edge_mask;
1885
+ EDGE_LIST AllChargeEdges;
1886
+
1887
+ int j, k, n;
1888
+ BNS_EDGE *pe, *pePlusMetal, *peMinusO;
1889
+ BNS_VERTEX *pv1, *pv2, *pvO, *pvM;
1890
+ Vertex v1, v2, vPlusMinus;
1891
+
1892
+ EdgeIndex ie, iePlusMetal, ieMinusO;
1893
+
1894
+ Vertex vPathStart, vPathEnd;
1895
+ int delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
1896
+
1897
+ ret = 0;
1898
+ cur_success = 0;
1899
+ AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
1900
+
1901
+ if ( pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder ) {
1902
+ goto exit_function;
1903
+ }
1904
+
1905
+ /* to simplify, prepare new at[] from pBNS */
1906
+ memcpy( at2, at, len_at*sizeof(at2[0]));
1907
+ pStruct->at = at2;
1908
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
1909
+ pStruct->at = at;
1910
+ if ( ret2 < 0 ) {
1911
+ ret = ret2;
1912
+ goto exit_function;
1913
+ }
1914
+ for ( i = 0; i < num_at; i ++ ) {
1915
+ if ( bIsMetalSalt( at2, i ) ) {
1916
+ if ( !AllChargeEdges.num_edges ) {
1917
+ /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/
1918
+ for ( j = 0; j < num_at; j ++ ) {
1919
+ /* all bonds */
1920
+ for ( k = 0; k < at2[j].valence; k ++ ) {
1921
+ n = at2[j].neighbor[k];
1922
+ if ( n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden &&
1923
+ ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) {
1924
+ goto exit_function;
1925
+ }
1926
+ }
1927
+ /* charge edges */
1928
+ if ( (ie=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden &&
1929
+ (ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) {
1930
+ goto exit_function;
1931
+ }
1932
+ if ( (ie=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden &&
1933
+ ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) {
1934
+ goto exit_function;
1935
+ }
1936
+ }
1937
+ /* taut group edges */
1938
+ for ( j = 0; j < pTCGroups->num_tgroups; j ++ ) {
1939
+ pv1 = pBNS->vert + (v1=pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */
1940
+ for ( k = 0; k < pv1->num_adj_edges; k ++ ) {
1941
+ /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */
1942
+ /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */
1943
+ pv2 = pBNS->vert + (pe = pBNS->edge + (ie=pv1->iedge[k]))->neighbor1;
1944
+ if ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) {
1945
+ goto exit_function;
1946
+ }
1947
+ }
1948
+ }
1949
+ /*---------------------------------------------------------------*/
1950
+ }
1951
+ /* replace all single bonds to neutral neighbors with zero-order bonds
1952
+ allow neighbor charge change to (-1) and metal atom charge increment +1 */
1953
+ for ( k = 0; k < at2[i].valence; k ++ ) {
1954
+ n = at2[i].neighbor[k];
1955
+ pe = pBNS->edge + pBNS->vert[i].iedge[k];
1956
+ if ( at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE ) {
1957
+ continue;
1958
+ }
1959
+ iePlusMetal = pVA[i].nCPlusGroupEdge-1;
1960
+ ieMinusO = pVA[n].nCMinusGroupEdge-1;
1961
+
1962
+ if ( pe->flow != 1 || pe->forbidden || iePlusMetal < 0 ) {
1963
+ continue;
1964
+ }
1965
+ pePlusMetal = pBNS->edge + iePlusMetal;
1966
+ if ( pePlusMetal->flow <= 0 ) {
1967
+ continue; /* to add (+) to metal this flow must be decremented */
1968
+ }
1969
+ if ( ieMinusO >= 0 ) {
1970
+ /* usually does not happen */
1971
+ peMinusO = pBNS->edge + ieMinusO;
1972
+
1973
+ if ( peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden ) {
1974
+ continue;
1975
+ }
1976
+
1977
+ /* decrement bond order to 0 */
1978
+ delta = 1;
1979
+ pv1 = pBNS->vert + (v1 = pe->neighbor1);
1980
+ pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1);
1981
+
1982
+ pe->flow -= delta;
1983
+ pv1->st_edge.flow -= delta;
1984
+ pv2->st_edge.flow -= delta;
1985
+ pBNS->tot_st_flow -= 2*delta;
1986
+
1987
+ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
1988
+ pePlusMetal->forbidden &= inv_forbidden_edge_mask;
1989
+ peMinusO->forbidden &= inv_forbidden_edge_mask;
1990
+
1991
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
1992
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
1993
+
1994
+ if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
1995
+ vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge > 0*/ ) {
1996
+ /* (+)charge was just moved, no change in number of charges */
1997
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1998
+ if ( ret > 0 ) {
1999
+ (*pnNumRunBNS) ++;
2000
+ cur_success ++; /* 01 */
2001
+ }
2002
+ } else {
2003
+ pe->flow += delta; /* roll back */
2004
+ pv1->st_edge.flow += delta;
2005
+ pv2->st_edge.flow += delta;
2006
+ pBNS->tot_st_flow += 2*delta;
2007
+ }
2008
+ RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
2009
+ } else
2010
+ if ( NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 ) ) ) {
2011
+ /* manually add (-) charge to O and (+) charge to metal */
2012
+ /* decrement bond order to 0 */
2013
+ /*---------------------------------------------------------------------------*/
2014
+ /* */
2015
+ /* (+/-)* (+/-) Result: */
2016
+ /* | || */
2017
+ /* | || - Added (+) to M */
2018
+ /* (+)super (+)super - Incremented bond M-O */
2019
+ /* || | */
2020
+ /* || => | To make this attachment H, */
2021
+ /* (Y) (Y) increment */
2022
+ /* | || pTCGroups->pTCG[itg].tg_num_H */
2023
+ /* | || */
2024
+ /* (+)metal (+)hetero Technical details: */
2025
+ /* \\ \ increase capacities of */
2026
+ /* M M(+) edges to (+/-) otherwise */
2027
+ /* | || flow may not be able to */
2028
+ /* -O* -O-O increase */
2029
+ /* */
2030
+ /* After that change M=O bond order from 2 to 0 */
2031
+ /*---------------------------------------------------------------------------*/
2032
+ int i1, j1, k1;
2033
+ delta = 1;
2034
+ pvO = pBNS->vert + n;
2035
+ pvM = pBNS->vert + i;
2036
+ /* Increment st_edge.cap on (+/-) vertex */
2037
+ pBNS->vert[vPlusMinus].st_edge.cap += delta;
2038
+ /* Increment st_edge.cap on O */
2039
+ pvO->st_edge.cap += delta;
2040
+ /* increment cap on M-O edge */
2041
+ pe->cap += delta;
2042
+ /* total cap count */
2043
+ pBNS->tot_st_cap += 2*delta;
2044
+
2045
+ v1 = vPlusMinus;
2046
+ v2 = n; /* atom O */
2047
+
2048
+ /* increase capacities of edges to Y */
2049
+ for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) {
2050
+ j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus;
2051
+ for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) {
2052
+ pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta;
2053
+ }
2054
+ }
2055
+ SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
2056
+ pePlusMetal->forbidden &= inv_forbidden_edge_mask;
2057
+ pe->forbidden &= inv_forbidden_edge_mask;
2058
+
2059
+ ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
2060
+ &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
2061
+ cur_success = 0;
2062
+ if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
2063
+ vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == 1*/ ) {
2064
+ /* Added (+)charge to -N< => nDeltaCharge == 1 */
2065
+ /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */
2066
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2067
+ if ( ret > 0 ) {
2068
+ (*pnNumRunBNS) ++;
2069
+ cur_success ++; /* 01 */
2070
+ }
2071
+ }
2072
+ if ( cur_success ) {
2073
+ /* set bond M=O order = 0 */
2074
+ if ( pe->flow != 2*delta ) {
2075
+ ret = RI_ERR_PROGR;
2076
+ goto exit_function;
2077
+ }
2078
+ /* reduce pe bond order by 2*delta */
2079
+ pe->flow -= 2*delta;
2080
+ pvO->st_edge.cap -= 2*delta;
2081
+ pvO->st_edge.flow -= 2*delta;
2082
+ pvM->st_edge.flow -= 2*delta;
2083
+ pvM->st_edge.cap -= 2*delta;
2084
+ pBNS->tot_st_cap -= 3*delta;
2085
+ pBNS->tot_st_flow -= 4*delta;
2086
+ /* fix M-O bond order to zero */
2087
+ pe->cap -= 2*delta;
2088
+ /* add fixed (-) charge to O */
2089
+ pVA[n].cInitCharge -= delta;
2090
+ } else {
2091
+ /* failed */
2092
+ pBNS->vert[vPlusMinus].st_edge.cap -= delta;
2093
+ pvO->st_edge.cap -= delta;
2094
+ /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */
2095
+ pBNS->tot_st_cap -= 2*delta;
2096
+ /* decrease capacities of edges to Y */
2097
+ for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) {
2098
+ j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus;
2099
+ for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) {
2100
+ pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta;
2101
+ }
2102
+ }
2103
+ }
2104
+ RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
2105
+ }
2106
+ }
2107
+ }
2108
+ }
2109
+
2110
+ exit_function:
2111
+ AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
2112
+ return ret;
2113
+ }
2114
+ #if ( KEEP_METAL_EDGE_FLOW == 1 )
2115
+ /******************************************************************************************************/
2116
+ int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA,
2117
+ ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask )
2118
+ {
2119
+
2120
+ int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0;
2121
+ BNS_VERTEX *pVert, *pNeigh;
2122
+ BNS_EDGE *pEdge;
2123
+
2124
+ /* count carbon-metal edges */
2125
+
2126
+ if ( pTCGroups->num_metal_atoms ) {
2127
+ fill_ForbiddenEdgesMetalCarbon:
2128
+ for ( i = 0; i < num_at; i ++ ) {
2129
+ if ( pVA[i].cMetal && pVA[i].cNumBondsToMetal ) {
2130
+ pVert = pBNS->vert + i;
2131
+ for ( j = 0; j < pVert->num_adj_edges; j ++ ) {
2132
+ pEdge = pBNS->edge + pVert->iedge[j];
2133
+ neigh = pEdge->neighbor12 ^ i;
2134
+ pNeigh = pBNS->vert + neigh;
2135
+ if ( !IS_BNS_VT_ATOM(pNeigh->type) )
2136
+ continue;
2137
+ if ( at[neigh].endpoint )
2138
+ continue;
2139
+ if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 &&
2140
+ pNeigh->st_edge.cap >= at[neigh].valence+1 ) {
2141
+ if ( pass ) {
2142
+ if ( ret = AddToEdgeList( pMetalCarbonEdges, pVert->iedge[j], 0 ) ) {
2143
+ goto exit_function;
2144
+ }
2145
+ pEdge->forbidden |= forbidden_edge_mask;
2146
+ } else {
2147
+ nNumEdgeMetalCarbon ++;
2148
+ }
2149
+ }
2150
+
2151
+ }
2152
+ }
2153
+ }
2154
+ if ( !pass && nNumEdgeMetalCarbon ) {
2155
+ if ( ret = AllocEdgeList( pMetalCarbonEdges, nNumEdgeMetalCarbon ) ) {
2156
+ goto exit_function;
2157
+ }
2158
+ pass ++;
2159
+ goto fill_ForbiddenEdgesMetalCarbon;
2160
+ }
2161
+ }
2162
+ exit_function:
2163
+ return ret;
2164
+ }
2165
+ #endif
2166
+ /******************************************************************************************************/
2167
+ int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD,
2168
+ StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[],
2169
+ long num_inp, int bHasSomeFixedH )
2170
+ {
2171
+ int nNumRunBNS = 0;
2172
+
2173
+ EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges;
2174
+
2175
+ int nTotalDelta = 0, ret = 0, tot_num_fixes;
2176
+ inp_ATOM *at = pStruct->at;
2177
+ inp_ATOM *at2 = NULL; /* restored structure */
2178
+ inp_ATOM *at3 = NULL; /* structure for calculating one InChI */
2179
+ int num_at = pStruct->num_atoms;
2180
+ int num_deleted_H = pStruct->num_deleted_H;
2181
+ #ifdef _DEBUG
2182
+ int ret2;
2183
+ #endif
2184
+
2185
+ #if ( KEEP_METAL_EDGE_FLOW == 1 )
2186
+ BNS_VERTEX *pVert, *pNeigh;
2187
+ int j, neigh;
2188
+ #endif
2189
+
2190
+ /* Edge lista initialization */
2191
+ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
2192
+ AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_CLEAR );
2193
+ AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_CLEAR );
2194
+
2195
+ if ( pStruct->iMobileH == TAUT_NON &&
2196
+ ( ret = FillOutExtraFixedHDataInChI( pStruct, pInChI ) ) ) {
2197
+ goto exit_function;
2198
+ }
2199
+
2200
+ if ( !at2 && !(at2 = (inp_ATOM *)inchi_malloc((num_at + num_deleted_H)*sizeof(at2[0]))) ||
2201
+ !at3 && !(at3 = (inp_ATOM *)inchi_malloc((num_at + num_deleted_H)*sizeof(at3[0])))) {
2202
+ return RI_ERR_ALLOC;
2203
+ }
2204
+
2205
+ if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ))) {
2206
+ goto exit_function;
2207
+ }
2208
+
2209
+ #if ( KEEP_METAL_EDGE_FLOW == 1 )
2210
+ /* count edges of -C(IV)< carbons connected to metals */
2211
+ if ( 0 > (ret = ForbidMetalCarbonEdges( pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ))) {
2212
+ goto exit_function;
2213
+ }
2214
+ #endif
2215
+ if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 6,
2216
+ pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) {
2217
+ goto exit_function;
2218
+ }
2219
+
2220
+ /*********** Run BNS #1: no charge on carbons and =N= ***************/
2221
+ if ( Nplus2BondsEdges.num_edges ) {
2222
+ /* Run BNS leaving carbon charges unchanged */
2223
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2224
+ nNumRunBNS ++;
2225
+ if ( ret < 0 ) {
2226
+ goto exit_function;
2227
+ } else {
2228
+ nTotalDelta += ret;
2229
+ }
2230
+ RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP );
2231
+ AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE );
2232
+ }
2233
+ #ifdef _DEBUG
2234
+ /* debug only */
2235
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2236
+ pStruct->at = at2;
2237
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2238
+ pStruct->at = at;
2239
+ #endif
2240
+ /*************************** extend min ring size to 8 ****************************/
2241
+ if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 8,
2242
+ pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) {
2243
+ goto exit_function;
2244
+ }
2245
+ if ( Nplus2BondsEdges.num_edges ) {
2246
+ /* Run BNS leaving carbon charges unchanged */
2247
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2248
+ nNumRunBNS ++;
2249
+ if ( ret < 0 ) {
2250
+ goto exit_function;
2251
+ } else {
2252
+ nTotalDelta += ret;
2253
+ }
2254
+ RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP );
2255
+ AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE );
2256
+ }
2257
+ #ifdef _DEBUG
2258
+ /* debug only */
2259
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2260
+ pStruct->at = at2;
2261
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2262
+ pStruct->at = at;
2263
+ #endif
2264
+ /*******************************************************************/
2265
+ if ( CarbonChargeEdges.num_edges > 0 ) {
2266
+ /* Run BNS leaving carbon charges unchanged */
2267
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2268
+ nNumRunBNS ++;
2269
+ if ( ret < 0 ) {
2270
+ goto exit_function;
2271
+ } else {
2272
+ nTotalDelta += ret;
2273
+ }
2274
+ RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP );
2275
+ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
2276
+ }
2277
+ #ifdef _DEBUG
2278
+ /* debug only */
2279
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2280
+ pStruct->at = at2;
2281
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2282
+ pStruct->at = at;
2283
+ #endif
2284
+ /*******************************************************************/
2285
+ if ( MetalCarbonEdges.num_edges > 0 ) {
2286
+ /* Run BNS leaving carbon charges unchanged */
2287
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2288
+ nNumRunBNS ++;
2289
+ if ( ret < 0 ) {
2290
+ goto exit_function;
2291
+ } else {
2292
+ nTotalDelta += ret;
2293
+ }
2294
+ RemoveForbiddenEdgeMask( pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP );
2295
+ AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE );
2296
+ }
2297
+ /*******************************************************************/
2298
+ /* Run BNS allowing to change any charges */
2299
+ ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2300
+ nNumRunBNS ++;
2301
+ if ( ret < 0 ) {
2302
+ goto exit_function;
2303
+ } else {
2304
+ nTotalDelta += ret;
2305
+ }
2306
+ #ifdef _DEBUG
2307
+ /* debug only */
2308
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2309
+ pStruct->at = at2;
2310
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2311
+ pStruct->at = at;
2312
+ #endif
2313
+
2314
+ #if ( BNS_RAD_SEARCH == 1 )
2315
+ /******************************************************************/
2316
+ /* move unfulfilled 'radicals' from ChargeStruct to atoms */
2317
+ /* and set change charges of affected atoms to fit total charge */
2318
+ ret = MoveRadToAtomsAddCharges( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP );
2319
+ if ( ret < 0 ) {
2320
+ goto exit_function;
2321
+ }
2322
+ #endif
2323
+ /**************************************************************/
2324
+ /**************************************************************/
2325
+ /***** fix restore inconsistencies *****/
2326
+ /**************************************************************/
2327
+ /**************************************************************/
2328
+ #ifdef _DEBUG
2329
+ /* debug only */
2330
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2331
+ pStruct->at = at2;
2332
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2333
+ pStruct->at = at;
2334
+ #endif
2335
+
2336
+ /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */
2337
+ ret = RearrangePlusMinusEdgesFlow( pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP );
2338
+ if ( ret < 0 ) {
2339
+ goto exit_function;
2340
+ }
2341
+
2342
+ /*****************************************************************/
2343
+ /* Increment zero order metal bonds to heteroatoms */
2344
+ /*****************************************************************/
2345
+ ret = IncrementZeroOrderBondsToHeteroat( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2346
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2347
+ if ( ret < 0 ) {
2348
+ goto exit_function;
2349
+ }
2350
+
2351
+ #ifdef _DEBUG
2352
+ /* debug only */
2353
+ memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0]));
2354
+ pStruct->at = at2;
2355
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2356
+ pStruct->at = at;
2357
+ #endif
2358
+
2359
+ #if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 )
2360
+ /*****************************************************************/
2361
+ /* move charges from heteroatoms to metal atoms */
2362
+ /*****************************************************************/
2363
+ ret = MoveChargeFromHeteroatomsToMetals( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2364
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2365
+ if ( ret < 0 ) {
2366
+ goto exit_function;
2367
+ }
2368
+ #endif
2369
+ /***********************************************************************
2370
+ NH2 NH2
2371
+ \ \
2372
+ C==S(+)- => C(+)-S- where NH2 are not tautomeric
2373
+ / /
2374
+ NH2 NH2
2375
+ ************************************************************************/
2376
+ ret = MovePlusFromS2DiaminoCarbon( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2377
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2378
+ if ( ret < 0 ) {
2379
+ goto exit_function;
2380
+ }
2381
+ /*****************************************************************/
2382
+ /* Avoid charge separation on heteroatoms */
2383
+ /*****************************************************************/
2384
+ ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2385
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0);
2386
+ if ( ret < 0 ) {
2387
+ goto exit_function;
2388
+ }
2389
+ if ( ret ) {
2390
+ /*charge separation remains; allow changes of stereobonds in a ring and try again */
2391
+ ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2392
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP,
2393
+ BNS_EDGE_FORBIDDEN_MASK);
2394
+ if ( ret < 0 ) {
2395
+ goto exit_function;
2396
+ }
2397
+ }
2398
+ /*****************************************************************/
2399
+ /* convert N#N(+)-N= into N(-)=N(+)=N- */
2400
+ /*****************************************************************/
2401
+ ret = RestoreNNNgroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2402
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2403
+ if ( ret < 0 ) {
2404
+ goto exit_function;
2405
+ }
2406
+ /*****************************************************************/
2407
+ /* convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change) */
2408
+ /*****************************************************************/
2409
+ ret = FixMetal_Nminus_Ominus( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2410
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2411
+ if ( ret < 0 ) {
2412
+ goto exit_function;
2413
+ }
2414
+ /*****************************************************************/
2415
+ /* convert N(-)=C= into N#C- - */
2416
+ /*****************************************************************/
2417
+ ret = RestoreCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2418
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2419
+ if ( ret < 0 ) {
2420
+ goto exit_function;
2421
+ }
2422
+ /*****************************************************************/
2423
+ /* convert C(+)#N(+)- into C(-)#N(+)- */
2424
+ /*****************************************************************/
2425
+ ret = RestoreIsoCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2426
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2427
+ if ( ret < 0 ) {
2428
+ goto exit_function;
2429
+ }
2430
+ /*****************************************************************/
2431
+ /* eliminate =N(V)= if possible */
2432
+ /* | */
2433
+ /*****************************************************************/
2434
+ ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2435
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2436
+ if ( ret < 0 ) {
2437
+ goto exit_function;
2438
+ }
2439
+
2440
+ /*****************************************************************/
2441
+ /* | | */
2442
+ /* convert -S- to =S= if possible */
2443
+ /* | | */
2444
+ /*****************************************************************/
2445
+ ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2446
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2447
+ if ( ret < 0 ) {
2448
+ goto exit_function;
2449
+ }
2450
+
2451
+ /*****************************************************************/
2452
+ /* =N(+)=O =N-O(-) */
2453
+ /* convert => if possible */
2454
+ /* Metal(q) Metal(q+2) */
2455
+ /*****************************************************************/
2456
+ ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2457
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2458
+ if ( ret < 0 ) {
2459
+ goto exit_function;
2460
+ }
2461
+
2462
+ /*****************************************************************/
2463
+ /* forbidden edges prevents required in InChI tautomerism */
2464
+ /* incorrectly restored mobile H mix separate tautomeric groups */
2465
+ /* because an edge may not become forbidden */
2466
+ /* note: removes this 'forbidden_edge' bit from ALL edges */
2467
+ /*****************************************************************/
2468
+ ret = MoveMobileHToAvoidFixedBonds( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2469
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2470
+
2471
+ if ( ret < 0 ) {
2472
+ goto exit_function;
2473
+ }
2474
+ /**************************************************************************/
2475
+ /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */
2476
+ tot_num_fixes = 0;
2477
+ if ( pStruct->iMobileH==TAUT_NON ) {
2478
+ ret = RemoveRadFromMobileHEndpointFixH( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2479
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2480
+ } else {
2481
+ ret = RemoveRadFromMobileHEndpoint( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2482
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2483
+ }
2484
+ if ( ret < 0 ) {
2485
+ goto exit_function;
2486
+ }
2487
+ tot_num_fixes += ret;
2488
+ /**************************************************************/
2489
+ /* make bonds between a charged heteroatom and a metal single */
2490
+ ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2491
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2492
+ if ( ret < 0 ) {
2493
+ goto exit_function;
2494
+ }
2495
+ /**************************************************************/
2496
+ /* move (+) charges to >N- and other centerpoints */
2497
+ ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2498
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2499
+ if ( ret < 0 ) {
2500
+ goto exit_function;
2501
+ }
2502
+
2503
+ /**************************************************************************/
2504
+ /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */
2505
+ ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2506
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2507
+ if ( ret < 0 ) {
2508
+ goto exit_function;
2509
+ }
2510
+ /**************************************************************************/
2511
+ /* Find A=X< where all bonds to X except A=X are marked as stereogenic */
2512
+ /* make bonds A=X single */
2513
+ ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2514
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2515
+ if ( ret < 0 ) {
2516
+ goto exit_function;
2517
+ }
2518
+ /**************************************************************************/
2519
+ /* In Reconnected structure change 'salt bonds' to 'coordination bonds */
2520
+ /* for example, M-O-C= -> M(+)-O(-)-C= */
2521
+ /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */
2522
+ /* However, in this release metal-organic compounds do not get much care */
2523
+ ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups,
2524
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP);
2525
+ if ( ret < 0 ) {
2526
+ goto exit_function;
2527
+ }
2528
+ /**************************************************************************/
2529
+ /* Normalize the structure and compare t-groups and stereobonds */
2530
+ ret = NormalizeAndCompare(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH,
2531
+ &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK);
2532
+ if ( ret < 0 ) {
2533
+ goto exit_function;
2534
+ }
2535
+ /**************************************************************************/
2536
+ /* Create InChI out of the restored structure */
2537
+
2538
+
2539
+ /*ret = nTotalDelta;*/
2540
+
2541
+ exit_function:
2542
+ pStruct->at = at;
2543
+ pStruct->at2 = at2;
2544
+ at2 = NULL;
2545
+ AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
2546
+ AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE );
2547
+ AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE );
2548
+ if ( at2 ) {
2549
+ inchi_free( at2 );
2550
+ }
2551
+ if ( at3 ) {
2552
+ inchi_free( at3 );
2553
+ }
2554
+
2555
+ return ret;
2556
+ }
2557
+
2558
+ /******************************************************************************************************/
2559
+ int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent,
2560
+ int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH )
2561
+ {
2562
+ int i, j, ret = 0, ret2;
2563
+ /*int nDelta, nTotalDelta;*/
2564
+ VAL_AT *pVA = NULL;
2565
+ VAL_AT va1;
2566
+ int num_at = pStruct->num_atoms;
2567
+ inp_ATOM *at = pStruct->at;
2568
+ ALL_TC_GROUPS TCGroups;
2569
+ ALL_TC_GROUPS *pTCGroups = &TCGroups;
2570
+ int nAddEdges2eachAtom = 2, nAddVertices = 0;
2571
+
2572
+ BFS_Q bfsq;
2573
+
2574
+ /* BNS creation */
2575
+ BN_STRUCT *pBNS = NULL;
2576
+ BN_DATA *pBD = NULL;
2577
+ int nNum_changed_bonds = 0;
2578
+ int bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals=0;
2579
+ int nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP;
2580
+
2581
+ memset( pTCGroups, 0, sizeof(pTCGroups[0]) );
2582
+ for ( i = 0; i < NUM_TCGROUP_TYPES; i ++ ) {
2583
+ pTCGroups->nGroup[i] = TCG_None; /* unassigned */
2584
+ }
2585
+ pTCGroups->iComponent = iComponent;
2586
+ pTCGroups->iAtNoOffset = iAtNoOffset;
2587
+
2588
+ if ( num_at == 1 ) {
2589
+ /* single atom -- no bonds to restore */
2590
+ inp_ATOM *at2 = (inp_ATOM *)inchi_malloc(sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H));
2591
+ inp_ATOM *at3 = (inp_ATOM *)inchi_malloc(sizeof(at3[0])*(pStruct->num_atoms+pStruct->num_deleted_H));
2592
+ pStruct->at2 = at2;
2593
+ at[0].charge = pInChI[0]->nTotalCharge;
2594
+ if ( at2 ) {
2595
+ memcpy( at2, at, sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H));
2596
+ }
2597
+ if ( !at2 || !at3 ) {
2598
+ if ( at3 ) inchi_free( at3 );
2599
+ return RI_ERR_ALLOC;
2600
+ }
2601
+ ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, pStruct->at2, at3, pTCGroups );
2602
+ /* clean up */
2603
+ for( i = 0; i < TAUT_NUM; i ++ ) {
2604
+ Free_INChI( &pStruct->pOneINChI[i] );
2605
+ Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] );
2606
+ FreeInpAtomData( pStruct->pOne_norm_data[i] );
2607
+ if ( pStruct->pOne_norm_data[i] ) {
2608
+ inchi_free( pStruct->pOne_norm_data[i] );
2609
+ pStruct->pOne_norm_data[i] = NULL;
2610
+ }
2611
+ }
2612
+ free_t_group_info( &pStruct->One_ti );
2613
+ inchi_free( at3 );
2614
+
2615
+ return ret;
2616
+ }
2617
+
2618
+ AllocBfsQueue( &bfsq, BFS_Q_CLEAR, 0 );
2619
+ if ( !(pVA = (VAL_AT *) inchi_calloc( num_at, sizeof( pVA[0] ) ) ) ) {
2620
+ ret = RI_ERR_ALLOC;
2621
+ goto exit_function;
2622
+ }
2623
+ pStruct->pVA = pVA;
2624
+ memset( &va1, 0, sizeof(va1) );
2625
+ pTCGroups->total_charge = pInChI[0]->nTotalCharge;
2626
+ if ( 0 > ( ret = AllocBfsQueue( &bfsq, num_at, 0 /* min ring size undefined */ ) ) ) {
2627
+ goto exit_function;
2628
+ }
2629
+ pStruct->pbfsq = &bfsq;
2630
+
2631
+ if ( pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 &&
2632
+ ( ret = FillOutpStructEndpointFromInChI( pInChI[1], &pStruct->endpoint )) ) {
2633
+ goto exit_function;
2634
+ }
2635
+
2636
+ /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */
2637
+ for ( i = 0; i < num_at; i ++ ) {
2638
+ pVA[i].cNumValenceElectrons = get_sp_element_type( at[i].el_number, &j );
2639
+ pVA[i].cPeriodicRowNumber = j;
2640
+ pVA[i].cPeriodicNumber = at[i].el_number;
2641
+ pVA[i].cNumValenceElectrons --; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1) */
2642
+
2643
+ if ( is_el_a_metal( at[i].el_number ) ) {
2644
+ if ( pStruct->pSrm->bStereoRemovesMetalFlag ) {
2645
+ /* treat metal as non-metal if it is stereogenic or has a stereobond */
2646
+ pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] );
2647
+ } else {
2648
+ pVA[i].cMetal = 1;
2649
+ }
2650
+ }
2651
+ if ( at[i].valence == 2 && !at[i].num_H ) {
2652
+ pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring( at, i, 0, bfsq.q, bfsq.nAtomLevel,
2653
+ bfsq.cSource, 99 /* max ring size */ );
2654
+ } else {
2655
+ pVA[i].cMinRingSize = 0;
2656
+ }
2657
+ }
2658
+ /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */
2659
+
2660
+ repeat_for_new_metals:
2661
+ /* set valences for the first time; find ChargeValence structures for each atom */
2662
+ for ( i = 0; i < num_at; i ++ ) {
2663
+ /* get additional fictitious atoms information */
2664
+ pVA[i].cInitFreeValences = 0;
2665
+ ret = GetAtomRestoreInfo( at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint );
2666
+ if ( ret < 0 ) {
2667
+ goto exit_function;
2668
+ }
2669
+ if ( ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal ) {
2670
+ if ( pStruct->pSrm->bStereoRemovesMetalFlag ) {
2671
+ /* treat metal as non-metal if it is stereogenic or has a stereobond */
2672
+ pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] );
2673
+ } else {
2674
+ pVA[i].cMetal = 1;
2675
+ }
2676
+ if ( pVA[i].cMetal ) {
2677
+ bTreatMoreAtomsAsMetals ++;
2678
+ }
2679
+ }
2680
+ pTCGroups->charge_on_atoms += pVA[i].cInitCharge;
2681
+ }
2682
+ if ( bTreatMoreAtomsAsMetals && !bSecondPassNewMetals ) {
2683
+ for ( i = 0; i < num_at; i ++ ) {
2684
+ /* clear all members of pVA[i] except two */
2685
+ pTCGroups->charge_on_atoms -= pVA[i].cInitCharge;
2686
+ va1.cMetal = pVA[i].cMetal;
2687
+ va1.cMinRingSize = pVA[i].cMinRingSize;
2688
+ va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons;
2689
+ va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber;
2690
+ va1.cPeriodicNumber = pVA[i].cPeriodicNumber;
2691
+ pVA[i] = va1;
2692
+ }
2693
+ bSecondPassNewMetals = 1;
2694
+ goto repeat_for_new_metals;
2695
+ }
2696
+
2697
+ /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */
2698
+ ret = nCountBnsSizes( at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti,
2699
+ pVA, pStruct->pSrm, pTCGroups );
2700
+ if ( ret < 0 ) {
2701
+ goto exit_function;
2702
+ }
2703
+
2704
+ /* find and count groups; add counts of all other vertices to be created */
2705
+ ret = nAddSuperCGroups( pTCGroups );
2706
+ if ( ret < 0 ) {
2707
+ goto exit_function;
2708
+ }
2709
+
2710
+ /* create the BNS and fill it with all real atoms */
2711
+ pBNS = AllocateAndInitTCGBnStruct( pStruct, pVA, pTCGroups,
2712
+ nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds );
2713
+ if ( !pBNS ) {
2714
+ ret = BNS_OUT_OF_RAM;
2715
+ goto exit_function;
2716
+ }
2717
+ /* add t-groups to the BNS */
2718
+ ret = AddTGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges );
2719
+ if ( ret < 0 ) {
2720
+ goto exit_function;
2721
+ }
2722
+
2723
+ /* add c-groups to the BNS; adjust charges */
2724
+ ret = AddCGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges );
2725
+ if ( ret < 0 ) {
2726
+ goto exit_function;
2727
+ }
2728
+
2729
+ /* allocate BNData */
2730
+ pBD = AllocateAndInitBnData( pBNS->max_vertices + pBNS->max_vertices/2 );
2731
+ if ( !pBD ) {
2732
+ ret = BNS_OUT_OF_RAM;
2733
+ goto exit_function;
2734
+ }
2735
+ CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 0 );
2736
+
2737
+ /* restore bonds & charges */
2738
+ ret = RunBnsRestore1( ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH );
2739
+ if ( ret < 0 ) {
2740
+ goto exit_function;
2741
+ }
2742
+
2743
+ ret = CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 1 );
2744
+ #if( bRELEASE_VERSION == 0 )
2745
+ #ifndef INCHI_LIBRARY
2746
+ if ( ret ) {
2747
+ fprintf( stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' );
2748
+ }
2749
+ if ( pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons ) {
2750
+ fprintf( stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" );
2751
+ /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/
2752
+ }
2753
+ if ( pStruct->bExtract & EXTRACT_STRUCT_NUMBER ) {
2754
+ fprintf( stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" );
2755
+ }
2756
+ #endif
2757
+ #endif
2758
+ { /* create the final structure in pStruct->at2 */
2759
+ inp_ATOM *at_tmp = pStruct->at;
2760
+ pStruct->at = pStruct->at2;
2761
+ memcpy( pStruct->at, at_tmp, sizeof(pStruct->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H) );
2762
+ ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2763
+ pStruct->at2 = pStruct->at;
2764
+ pStruct->at = at_tmp;
2765
+ if ( ret2 < 0 ) {
2766
+ ret = ret2;
2767
+ }
2768
+ }
2769
+
2770
+ exit_function:
2771
+
2772
+ pStruct->pbfsq = NULL;
2773
+ AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 );
2774
+
2775
+ pBD = DeAllocateBnData( pBD );
2776
+ pBNS = DeAllocateBnStruct( pBNS );
2777
+ /*
2778
+ if ( pVA ) inchi_free( pVA );
2779
+ */
2780
+ if ( pTCGroups->pTCG ) inchi_free( pTCGroups->pTCG );
2781
+
2782
+ return ret;
2783
+ }
2784
+ /******************************************************************************************************/
2785
+ int OneInChI2Atom( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, const char *szCurHdr, long num_inp,
2786
+ StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[])
2787
+ {
2788
+ int ret;
2789
+ INPUT_PARMS *ip, ip_loc;
2790
+
2791
+ ip_loc = *ip_inp;
2792
+ ip = &ip_loc;
2793
+
2794
+ sd->pStrErrStruct[0] = '\0';
2795
+ ret = RestoreAtomConnectionsSetStereo( pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]);
2796
+ if ( ret < 0 ) {
2797
+ goto exit_function;
2798
+ }
2799
+ ret = SetStereoBondTypesFrom0DStereo( pStruct, pInChI[0]);
2800
+ if ( ret < 0 ) {
2801
+ goto exit_function;
2802
+ }
2803
+ ret = ReconcileAllCmlBondParities( pStruct->at, pStruct->num_atoms, 0 );
2804
+ if ( ret < 0 ) {
2805
+ goto exit_function;
2806
+ }
2807
+ /* main InChI restore function */
2808
+ ret = RestoreAtomMakeBNS( ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH );
2809
+
2810
+ #ifndef INCHI_ANSI_ONLY
2811
+ if ( (pStruct->num_inp_actual>0? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number &&
2812
+ ( (/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings ) && pStruct->pXYZ ) ) {
2813
+ inchiTime ulTStart;
2814
+ InchiTimeGet( &ulTStart );
2815
+ DisplayRestoredComponent( pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr );
2816
+ sd->ulStructTime -= InchiTimeElapsed( &ulTStart ); /* subtract display time */
2817
+ }
2818
+ #endif
2819
+ if ( ret < 0 ) {
2820
+ goto exit_function;
2821
+ }
2822
+ if ( (pStruct->num_inp_actual? pStruct->num_inp_actual: num_inp) >= ip->first_struct_number && ret >= 0 ) {
2823
+ /* remove t-group markings and increment zero-order bonds,
2824
+ otherwise MakeInChIOutOfStrFromINChI2() woild fail */
2825
+ /* --- moved to MakeInChIOutOfStrFromINChI2 ---
2826
+ IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1);
2827
+ CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );
2828
+ */
2829
+ /* include all restored structure features in pStruct->at2 */
2830
+ /* make full InChI out of pStruct->at2, pStruct->num_atoms */
2831
+ /***************************************************************************************/
2832
+ /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */
2833
+ /***************************************************************************************/
2834
+ if ( bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES &&
2835
+ !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) {
2836
+ /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
2837
+ ip->nMode |= REQ_MODE_BASIC;
2838
+ }
2839
+ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct, iComponent, iAtNoOffset, num_inp );
2840
+ if ( ret >= 0 ) {
2841
+ ;
2842
+ }
2843
+ #if( bRELEASE_VERSION == 0 )
2844
+ #ifndef INCHI_LIBRARY
2845
+ else {
2846
+ fprintf( stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp,
2847
+ szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret);
2848
+ }
2849
+ #endif
2850
+ #endif
2851
+ }
2852
+
2853
+
2854
+ exit_function:
2855
+ return ret;
2856
+ }
2857
+ /********************************************************************************************/
2858
+ int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot )
2859
+ {
2860
+ inp_ATOM *at = NULL;
2861
+ int i;
2862
+
2863
+ if ( num_prot <= 0 ) {
2864
+ return 0;
2865
+ }
2866
+ /* allocate */
2867
+ pStruct->at = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at[0]) );
2868
+ pStruct->at2 = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at2[0]) );
2869
+ if ( !pStruct->at || !pStruct->at2 ) {
2870
+ return 0;
2871
+ }
2872
+ /* create protons */
2873
+ at = pStruct->at;
2874
+ /* fill out proton atom info */
2875
+ for ( i = 0; i < num_prot; i ++ ) {
2876
+ strcpy( at[i].elname, "H" );
2877
+ at[i].el_number = EL_NUMBER_H;
2878
+ at[i].orig_at_number = i+1;
2879
+ /*
2880
+ at[i].orig_compt_at_numb = i + 1;
2881
+ at[i].component = i + 1;
2882
+ */
2883
+ at[i].charge = 1;
2884
+ }
2885
+ memcpy( pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]) );
2886
+ pStruct->bDeleted = 0;
2887
+ pStruct->num_atoms = num_prot;
2888
+ pStruct->bMobileH = TAUT_YES;
2889
+ pStruct->iMobileH = TAUT_YES;
2890
+ return num_prot;
2891
+ }
2892
+ /********************************************************************************************/
2893
+ int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp,
2894
+ int bHasSomeFixedH,
2895
+ StrFromINChI *pStruct, int num_components,
2896
+ StrFromINChI *pStructR, int num_componentsR,
2897
+ NUM_H *nProtonsToBeRemovedByNormFromRevrs, int *recmet_change_balance )
2898
+ {
2899
+ int iComp, q, ret = 0;
2900
+ int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed, num_deleted_components;
2901
+ inp_ATOM *at;
2902
+ INPUT_PARMS *ip, ip_loc;
2903
+ int num_prot = *nProtonsToBeRemovedByNormFromRevrs;
2904
+ int delta_recmet_prot, num_prot_prev, bAccumulateChanges=0, nNumProtAddedByRevrs;
2905
+ INChI_Aux *pINChI_Aux;
2906
+ INCHI_MODE bNormalizationFlags;
2907
+ int nChargeRevrs, nChargeInChI;
2908
+
2909
+ if ( !num_prot ) {
2910
+ return 0;
2911
+ }
2912
+ delta_recmet_prot = 0;
2913
+ num_changed = 0;
2914
+ num_deleted_components = 0;
2915
+ ip_loc = *ip_inp;
2916
+ ip = &ip_loc;
2917
+ /*----------------------------------------------------------------------------------
2918
+ nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is
2919
+ same as already processed reconnected one
2920
+ Do no preicess it
2921
+
2922
+ nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component;
2923
+ (should not happen) It it is a result of (nLink-1)th Reconeected
2924
+ component disconnection (NOT IMPLEMENTED YET)
2925
+
2926
+ nLink = 0 => Process this component. It is either a reconnected
2927
+ component, or a result of a disconnection (for now)
2928
+
2929
+ nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as
2930
+ a disconnected one that will not be processed.
2931
+ Process and save charge delta.
2932
+ -----------------------------------------------------------------------------------*/
2933
+
2934
+ for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) {
2935
+ bAccumulateChanges = 0;
2936
+ if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) {
2937
+ /* check */
2938
+ q = -(pStruct[iComp].nLink+1);
2939
+ if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) {
2940
+ ret = RI_ERR_PROGR;
2941
+ goto exit_function;
2942
+ }
2943
+ continue; /* Disconnected structure component has already been processed as a Reconnected one */
2944
+ }
2945
+
2946
+ at = pStruct[iComp].at2;
2947
+ num_atoms = pStruct[iComp].num_atoms;
2948
+ tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H);
2949
+ bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR );
2950
+ nChargeRevrs = pStruct[iComp].nChargeRevrs;
2951
+ nChargeInChI = pStruct[iComp].nChargeInChI;
2952
+ num_deleted_components += (0 != pStruct[iComp].bDeleted);
2953
+ if ( !at || !num_atoms ) {
2954
+ continue;
2955
+ }
2956
+ /* find whether it is a reconnected structure */
2957
+ q = bRevInchiComponentExists( pStruct+iComp, INCHI_REC, TAUT_YES, 0 )? INCHI_REC : INCHI_BAS;
2958
+ /*
2959
+ q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] &&
2960
+ pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] &&
2961
+ pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS;
2962
+ */
2963
+ pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */
2964
+ /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/
2965
+ nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs;
2966
+ bNormalizationFlags = pINChI_Aux->bNormalizationFlags;
2967
+ num_tg = pINChI_Aux->nNumberOfTGroups;
2968
+
2969
+
2970
+ if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) {
2971
+ goto exit_function;
2972
+ }
2973
+ num_prot_prev = num_prot;
2974
+ ret = AddRemoveProtonsRestr( at, num_atoms, &num_prot, nNumProtAddedByRevrs,
2975
+ bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI );
2976
+
2977
+ pStruct[iComp].bPostProcessed = ret;
2978
+ num_changed += (ret > 0);
2979
+ if ( ret < 0 ) {
2980
+ goto exit_function;
2981
+ }
2982
+ if ( ret > 0 ) {
2983
+ /* recalculate InChI; it will reconnect at */
2984
+ StrFromINChI *pStruct1 = pStruct + iComp;
2985
+ INCHI_MODE nMode = ip->nMode;
2986
+ FreeAllINChIArrays( pStruct1->RevInChI.pINChI,
2987
+ pStruct1->RevInChI.pINChI_Aux,
2988
+ pStruct1->RevInChI.num_components );
2989
+
2990
+ if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
2991
+ !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) {
2992
+ /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
2993
+ ip->nMode |= REQ_MODE_BASIC;
2994
+ }
2995
+ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp );
2996
+ ip->nMode = nMode;
2997
+ if ( ret < 0 ) {
2998
+ goto exit_function;
2999
+ }
3000
+ } else {
3001
+ if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) {
3002
+ goto exit_function;
3003
+ }
3004
+ }
3005
+ if ( bAccumulateChanges && recmet_change_balance ) {
3006
+ /* processed Reconnected layer component that is also present in Disconnected layer */
3007
+ delta_recmet_prot += num_prot - num_prot_prev;
3008
+ }
3009
+ }
3010
+
3011
+ iComp = num_components-1;
3012
+ if ( !bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted ) {
3013
+ /* add bare protons to the deleted Mobile-H component; undelete the component */
3014
+ num_prot_prev = num_prot;
3015
+ if ( !MakeProtonComponent( pStruct+iComp, iComp, num_prot ) ) {
3016
+ goto exit_function;
3017
+ } else {
3018
+ /* recalculate InChI; it will reconnect at */
3019
+ StrFromINChI *pStruct1 = pStruct + iComp;
3020
+ INCHI_MODE nMode = ip->nMode;
3021
+ num_changed ++;
3022
+ num_prot = 0;
3023
+ FreeAllINChIArrays( pStruct1->RevInChI.pINChI,
3024
+ pStruct1->RevInChI.pINChI_Aux,
3025
+ pStruct1->RevInChI.num_components );
3026
+
3027
+ if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
3028
+ !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) {
3029
+ /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3030
+ ip->nMode |= REQ_MODE_BASIC;
3031
+ }
3032
+ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp );
3033
+ ip->nMode = nMode;
3034
+ if ( ret < 0 ) {
3035
+ goto exit_function;
3036
+ }
3037
+ if ( bAccumulateChanges && recmet_change_balance ) {
3038
+ /* processed Reconnected layer component that is also present in Disconnected layer */
3039
+ delta_recmet_prot += num_prot - num_prot_prev;
3040
+ }
3041
+ }
3042
+ }
3043
+ *nProtonsToBeRemovedByNormFromRevrs = num_prot;
3044
+ if ( recmet_change_balance ) {
3045
+ *recmet_change_balance = delta_recmet_prot;
3046
+ }
3047
+
3048
+ exit_function:
3049
+ return ret < 0? ret : num_changed;
3050
+ }
3051
+ /**********************************************************************************/
3052
+ int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH,
3053
+ StrFromINChI *pStruct, int num_components,
3054
+ StrFromINChI *pStructR, int num_componentsR,
3055
+ NUM_H pProtonBalance[], NUM_H recmet_change_balance[] )
3056
+ {
3057
+ int iComp, q, k, ret = 0, bNotEmpty;
3058
+ int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed;
3059
+ inp_ATOM *at;
3060
+ NUM_H num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES];
3061
+ int bAccumulateChanges;
3062
+ INChI_Aux *pINChI_Aux;
3063
+ INChI *pINChI;
3064
+ INCHI_MODE bNormalizationFlags;
3065
+ INPUT_PARMS *ip, ip_loc;
3066
+
3067
+ ip_loc = *ip_inp;
3068
+ ip = &ip_loc;
3069
+
3070
+ memcpy( num_prot, pProtonBalance, sizeof(num_prot) );
3071
+ for ( bNotEmpty=0, k = 0; k < NUM_H_ISOTOPES; k ++ ) {
3072
+ bNotEmpty |= num_prot[k];
3073
+ }
3074
+ if ( !bNotEmpty ) {
3075
+ return 0;
3076
+ }
3077
+ memset ( delta_recmet_prot, 0, sizeof(delta_recmet_prot));
3078
+ num_changed = 0;
3079
+ /*----------------------------------------------------------------------------------
3080
+ nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is
3081
+ same as already processed reconnected one
3082
+ Do no preicess it
3083
+
3084
+ nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component;
3085
+ (should not happen) It it is a result of (nLink-1)th Reconeected
3086
+ component disconnection (NOT IMPLEMENTED YET)
3087
+
3088
+ nLink = 0 => Process this component. It is either a reconnected
3089
+ component, or a result of a disconnection (for now)
3090
+
3091
+ nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as
3092
+ a disconnected one that will not be processed.
3093
+ Process and save charge delta.
3094
+ -----------------------------------------------------------------------------------*/
3095
+
3096
+ for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) {
3097
+ bAccumulateChanges = 0;
3098
+ if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) {
3099
+ /* check */
3100
+ q = -(pStruct[iComp].nLink+1);
3101
+ if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) {
3102
+ ret = RI_ERR_PROGR;
3103
+ goto exit_function;
3104
+ }
3105
+ continue; /* Disconnected structure component has already been processed as a Reconnected one */
3106
+ }
3107
+
3108
+ at = pStruct[iComp].at2;
3109
+ num_atoms = pStruct[iComp].num_atoms;
3110
+ tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H);
3111
+ bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR );
3112
+
3113
+ if ( !at || !num_atoms ) {
3114
+ continue;
3115
+ }
3116
+ /* find whether it is a reconnected structure */
3117
+ q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] &&
3118
+ pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] &&
3119
+ pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS;
3120
+
3121
+ pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */
3122
+ pINChI = pStruct[iComp].RevInChI.pINChI[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */
3123
+ bNormalizationFlags = pINChI_Aux->bNormalizationFlags;
3124
+ num_tg = pINChI_Aux->nNumberOfTGroups;
3125
+ memcpy( num_prot_prev, num_prot, sizeof(num_prot_prev) );
3126
+
3127
+ /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */
3128
+ ret = AddRemoveIsoProtonsRestr( at, num_atoms, num_prot, num_tg );
3129
+
3130
+ pStruct[iComp].bPostProcessed |= ret;
3131
+ num_changed += (ret > 0);
3132
+ if ( ret < 0 ) {
3133
+ goto exit_function;
3134
+ }
3135
+ if ( ret > 0 ) {
3136
+ StrFromINChI *pStruct1 = pStruct+iComp;
3137
+ INCHI_MODE nMode = ip->nMode;
3138
+ /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */
3139
+ if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) {
3140
+ goto exit_function;
3141
+ }
3142
+ FreeAllINChIArrays( pStruct1->RevInChI.pINChI,
3143
+ pStruct1->RevInChI.pINChI_Aux,
3144
+ pStruct1->RevInChI.num_components );
3145
+ if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES &&
3146
+ !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) {
3147
+ /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */
3148
+ ip->nMode |= REQ_MODE_BASIC;
3149
+ }
3150
+ /* input: disconnected explicit H, output: connected explicit H */
3151
+ ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp );
3152
+ ip->nMode = nMode;
3153
+ if ( ret < 0 ) {
3154
+ goto exit_function;
3155
+ }
3156
+ } else {
3157
+ if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) {
3158
+ goto exit_function;
3159
+ }
3160
+ }
3161
+ if ( bAccumulateChanges ) {
3162
+ /* processed Reconnected layer component that is also present in Disconnected layer */
3163
+ for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) {
3164
+ delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k];
3165
+ }
3166
+ }
3167
+ }
3168
+
3169
+ memcpy ( pProtonBalance, num_prot, sizeof(num_prot) );
3170
+ if ( recmet_change_balance ) {
3171
+ memcpy ( recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot) );
3172
+ }
3173
+ exit_function:
3174
+ return ret < 0? ret : num_changed;
3175
+ }
3176
+
3177
+ #endif