rino 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. data/README +44 -0
  2. data/Rakefile +123 -0
  3. data/ext/extconf.rb +26 -0
  4. data/ext/ruby_inchi_main.so +0 -0
  5. data/ext/src/aux2atom.h +2786 -0
  6. data/ext/src/comdef.h +148 -0
  7. data/ext/src/e_0dstereo.c +3014 -0
  8. data/ext/src/e_0dstereo.h +31 -0
  9. data/ext/src/e_comdef.h +57 -0
  10. data/ext/src/e_ctl_data.h +147 -0
  11. data/ext/src/e_ichi_io.c +498 -0
  12. data/ext/src/e_ichi_io.h +40 -0
  13. data/ext/src/e_ichi_parms.c +37 -0
  14. data/ext/src/e_ichi_parms.h +41 -0
  15. data/ext/src/e_ichicomp.h +50 -0
  16. data/ext/src/e_ichierr.h +40 -0
  17. data/ext/src/e_ichimain.c +593 -0
  18. data/ext/src/e_ichisize.h +43 -0
  19. data/ext/src/e_inchi_atom.c +75 -0
  20. data/ext/src/e_inchi_atom.h +33 -0
  21. data/ext/src/e_inpdef.h +41 -0
  22. data/ext/src/e_mode.h +706 -0
  23. data/ext/src/e_mol2atom.c +649 -0
  24. data/ext/src/e_readinch.c +58 -0
  25. data/ext/src/e_readmol.c +54 -0
  26. data/ext/src/e_readmol.h +180 -0
  27. data/ext/src/e_readstru.c +251 -0
  28. data/ext/src/e_readstru.h +33 -0
  29. data/ext/src/e_util.c +284 -0
  30. data/ext/src/e_util.h +61 -0
  31. data/ext/src/extr_ct.h +251 -0
  32. data/ext/src/ichi.h +206 -0
  33. data/ext/src/ichi_bns.c +7999 -0
  34. data/ext/src/ichi_bns.h +231 -0
  35. data/ext/src/ichican2.c +5000 -0
  36. data/ext/src/ichicano.c +2195 -0
  37. data/ext/src/ichicano.h +49 -0
  38. data/ext/src/ichicans.c +1625 -0
  39. data/ext/src/ichicant.h +379 -0
  40. data/ext/src/ichicomn.h +260 -0
  41. data/ext/src/ichicomp.h +50 -0
  42. data/ext/src/ichidrp.h +119 -0
  43. data/ext/src/ichierr.h +124 -0
  44. data/ext/src/ichiisot.c +101 -0
  45. data/ext/src/ichilnct.c +286 -0
  46. data/ext/src/ichimain.h +132 -0
  47. data/ext/src/ichimak2.c +1189 -0
  48. data/ext/src/ichimake.c +3812 -0
  49. data/ext/src/ichimake.h +205 -0
  50. data/ext/src/ichimap1.c +851 -0
  51. data/ext/src/ichimap2.c +2856 -0
  52. data/ext/src/ichimap4.c +1609 -0
  53. data/ext/src/ichinorm.c +741 -0
  54. data/ext/src/ichinorm.h +67 -0
  55. data/ext/src/ichiparm.c +45 -0
  56. data/ext/src/ichiparm.h +1441 -0
  57. data/ext/src/ichiprt1.c +3612 -0
  58. data/ext/src/ichiprt2.c +1511 -0
  59. data/ext/src/ichiprt3.c +3011 -0
  60. data/ext/src/ichiqueu.c +1003 -0
  61. data/ext/src/ichiring.c +326 -0
  62. data/ext/src/ichiring.h +49 -0
  63. data/ext/src/ichisize.h +35 -0
  64. data/ext/src/ichisort.c +539 -0
  65. data/ext/src/ichister.c +3538 -0
  66. data/ext/src/ichister.h +35 -0
  67. data/ext/src/ichitaut.c +3843 -0
  68. data/ext/src/ichitaut.h +387 -0
  69. data/ext/src/ichitime.h +74 -0
  70. data/ext/src/inchi_api.h +670 -0
  71. data/ext/src/inchi_dll.c +1480 -0
  72. data/ext/src/inchi_dll.h +34 -0
  73. data/ext/src/inchi_dll_main.c +23 -0
  74. data/ext/src/inchi_dll_main.h +31 -0
  75. data/ext/src/inpdef.h +328 -0
  76. data/ext/src/lreadmol.h +1246 -0
  77. data/ext/src/mode.h +706 -0
  78. data/ext/src/ruby_inchi_main.c +558 -0
  79. data/ext/src/runichi.c +4179 -0
  80. data/ext/src/strutil.c +3861 -0
  81. data/ext/src/strutil.h +182 -0
  82. data/ext/src/util.c +1130 -0
  83. data/ext/src/util.h +85 -0
  84. data/lib/clean_tempfile.rb +220 -0
  85. data/lib/rino.rb +111 -0
  86. data/test/test.rb +386 -0
  87. metadata +130 -0
@@ -0,0 +1,1189 @@
1
+ /*
2
+ * International Union of Pure and Applied Chemistry (IUPAC)
3
+ * International Chemical Identifier (InChI)
4
+ * Version 1
5
+ * Software version 1.00
6
+ * April 13, 2005
7
+ * Developed at NIST
8
+ */
9
+
10
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+ #include <ctype.h>
14
+ #include <math.h>
15
+
16
+ #include "mode.h"
17
+
18
+ #include "inpdef.h"
19
+ #include "ichi.h"
20
+ #include "strutil.h"
21
+ #include "util.h"
22
+ #include "extr_ct.h"
23
+ #include "ichitaut.h"
24
+ #include "ichinorm.h"
25
+ #include "ichicant.h"
26
+ #include "ichicano.h"
27
+ #include "ichicomn.h"
28
+
29
+ #include "ichicomp.h"
30
+ #include "ichimain.h"
31
+ #include "ichimake.h"
32
+
33
+ int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms,
34
+ AT_NUMB *nTautomer, int lenTautomer,
35
+ int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms );
36
+ int MakeHillFormula( U_CHAR *nAtom, int num_atoms,
37
+ char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow );
38
+
39
+ char *AllocateAndFillHillFormula( INChI *pINChI );
40
+
41
+ int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow );
42
+
43
+ int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds,
44
+ AT_STEREO_DBLE *LinearCTStereoDble,
45
+ AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic );
46
+
47
+ int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo,
48
+ AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb,
49
+ AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble
50
+ , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic
51
+ , AT_STEREO_CARB *LinearCTStereoCarbInv
52
+ , AT_STEREO_DBLE *LinearCTStereoDbleInv
53
+ , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv );
54
+ int GetHillFormulaIndexLength( int count );
55
+
56
+ int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd,
57
+ AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb,
58
+ AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble );
59
+
60
+ INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode );
61
+
62
+ int CleanCoord( MOL_COORD szCoord, int delim );
63
+
64
+ /**********************************************************************************************/
65
+ int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow)
66
+ {
67
+ int nLen;
68
+ if ( szHillFormula && !*bOverflow ) {
69
+ if ( nLen_szLinearCT > ( nLen = strlen(szHillFormula) ) ) {
70
+ memcpy( szLinearCT, szHillFormula, nLen+1 );
71
+ return nLen;
72
+ }
73
+ *bOverflow |= 1;
74
+ return nLen_szLinearCT+1;
75
+ }
76
+ return 0;
77
+ }
78
+ /**********************************************************************************************
79
+ * MS Windows dependent: sprintf() is supposed to return the length of the output string
80
+ * Carbon atoms are always first
81
+ * Bridging hydrogen atoms are always last
82
+ **********************************************************************************************/
83
+ int GetHillFormulaIndexLength( int count )
84
+ {
85
+ char szCount[16];
86
+ if ( count > 1 ) {
87
+ return sprintf( szCount, "%d", count );
88
+ }
89
+ return 0;
90
+ }
91
+ /**********************************************************************************************/
92
+ int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms,
93
+ AT_NUMB *nTautomer, int lenTautomer,
94
+ int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms )
95
+ {
96
+ char szElement[4];
97
+ U_CHAR nPrevAtom = (U_CHAR)-1;
98
+ int bCarbon, bHydrogen, nElemLen, nFormLen, nNumNonHAtoms;
99
+ int mult, i, num_H, num_C;
100
+
101
+ num_H = 0;
102
+ num_C = 0;
103
+ bCarbon = 0;
104
+ bHydrogen = 0;
105
+ nElemLen = 0;
106
+ nFormLen = 0;
107
+ mult = 0;
108
+ nNumNonHAtoms = num_atoms;
109
+ for ( i = 0; i < num_atoms; i ++ ) {
110
+ if ( nPrevAtom != nAtom[i] ) {
111
+ if ( mult ) {
112
+ if ( bHydrogen ) {
113
+ num_H += mult;
114
+ }else
115
+ if ( bCarbon ) {
116
+ num_C += mult;
117
+ } else {
118
+ nFormLen += nElemLen;
119
+ nFormLen += GetHillFormulaIndexLength( mult );
120
+ }
121
+ }
122
+
123
+ if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) {
124
+ return -1; /* wrong element */
125
+ }
126
+ mult = 1;
127
+
128
+ nElemLen = strlen(szElement);
129
+ nPrevAtom = nAtom[i];
130
+ bCarbon = !strcmp( szElement, "C" );
131
+ bHydrogen = !strcmp( szElement, "H" );
132
+ if ( bHydrogen ) {
133
+ nNumNonHAtoms = i;
134
+ }
135
+ } else {
136
+ mult ++;
137
+ }
138
+
139
+ num_H += nNum_H[i];
140
+ }
141
+ /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...) */
142
+ if ( nTautomer && lenTautomer > 0 ) {
143
+ int num_groups = nTautomer[0];
144
+ for ( i = 1; i < lenTautomer && num_groups > 0; i += nTautomer[i]+1, num_groups -- ) {
145
+ num_H += nTautomer[i+1];
146
+ }
147
+ }
148
+
149
+ if ( mult ) {
150
+ if ( bHydrogen ) {
151
+ num_H += mult;
152
+ } else
153
+ if ( bCarbon ) {
154
+ num_C += mult;
155
+ } else {
156
+ nFormLen += nElemLen;
157
+ nFormLen += GetHillFormulaIndexLength( mult );
158
+ }
159
+ }
160
+
161
+ if ( num_C ) {
162
+ nFormLen += strlen( "C" );
163
+ nFormLen += GetHillFormulaIndexLength( num_C );
164
+ }
165
+
166
+ if ( num_H ) {
167
+ nFormLen += strlen( "H" );
168
+ nFormLen += GetHillFormulaIndexLength( num_H );
169
+ }
170
+ *pnum_C = num_C;
171
+ *pnum_H = num_H;
172
+ *pnLen = nFormLen;
173
+ *pnNumNonHAtoms = nNumNonHAtoms;
174
+
175
+ return 0;
176
+ }
177
+ /**********************************************************************************************/
178
+ int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow )
179
+ {
180
+ char szMult[16];
181
+ int len1, len2;
182
+ if ( mult > 0 && !*bOverflow && 0 < (len1 = strlen( szElement )) ) {
183
+ if ( mult > 1 ) {
184
+ len2 = sprintf( szMult, "%d", mult );
185
+ } else {
186
+ len2 = 0;
187
+ szMult[0] = '\0';
188
+ }
189
+ if ( len1 + len2 < nLenLinearCT ) {
190
+ memcpy( szLinearCT, szElement, len1 );
191
+ memcpy( szLinearCT+len1, szMult, len2+1 ); /* adding zero termination */
192
+ return len1+len2;
193
+ } else {
194
+ (*bOverflow) ++;
195
+ }
196
+ }
197
+ return 0;
198
+ }
199
+ /**********************************************************************************************/
200
+ /* if num_C > 0 then nAtom does not contain C or H */
201
+ /* otherwise all elements are in alphabetic order */
202
+ int MakeHillFormula( U_CHAR *nAtom, int num_atoms,
203
+ char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow )
204
+ {
205
+ char szElement[4];
206
+ int mult, compare2H;
207
+ int i, nLen, bOvfl;
208
+ U_CHAR nPrevAtom;
209
+
210
+ nLen = 0;
211
+ mult = 0;
212
+ bOvfl = 0;
213
+ nPrevAtom = (U_CHAR)-1; /* non-existent number */
214
+
215
+
216
+ if ( num_C ) {
217
+ nLen += AddElementAndCount( "C", num_C, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
218
+ if ( num_H ) {
219
+ nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
220
+ num_H = 0;
221
+ }
222
+ }
223
+
224
+ for ( i = 0; i < num_atoms; i ++ ) {
225
+
226
+ if ( nPrevAtom != nAtom[i] ) {
227
+ if ( mult ) {
228
+ nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
229
+ }
230
+ mult = 1;
231
+ if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) {
232
+ return -1; /* wrong element */
233
+ }
234
+ nPrevAtom = nAtom[i];
235
+ if ( !strcmp( "C", szElement ) ) {
236
+ return -1;
237
+ }
238
+ compare2H = strcmp( "H", szElement );
239
+ if ( !compare2H ) {
240
+ return -1;
241
+ }
242
+ if ( compare2H < 0 && num_H ) {
243
+ /* H-atom should be located in front of szElement */
244
+ nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
245
+ num_H = 0;
246
+ }
247
+ } else {
248
+ mult ++;
249
+ }
250
+ }
251
+ if ( mult ) {
252
+ /* the last element if any */
253
+ nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
254
+ }
255
+ if ( num_H ) {
256
+ /* if H has not been output... */
257
+ nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl );
258
+ }
259
+ *bOverflow |= (0 != bOvfl);
260
+ return bOvfl? nLen_szLinearCT+1: nLen;
261
+ }
262
+ /**********************************************************************************************/
263
+ char *AllocateAndFillHillFormula( INChI *pINChI )
264
+ {
265
+ int num_C, num_H, nLen, nNumNonHAtoms, ret, bOverflow;
266
+ char *pHillFormula = NULL;
267
+ bOverflow = 0;
268
+ if ( !GetHillFormulaCounts( pINChI->nAtom, pINChI->nNum_H, pINChI->nNumberOfAtoms,
269
+ pINChI->nTautomer, pINChI->lenTautomer,
270
+ &num_C, &num_H, &nLen, &nNumNonHAtoms ) ) {
271
+ if ( pHillFormula = (char*)inchi_malloc( nLen+1 ) ) {
272
+ ret = MakeHillFormula( pINChI->nAtom+num_C, nNumNonHAtoms-num_C,
273
+ pHillFormula, nLen+1, num_C, num_H, &bOverflow );
274
+ if ( ret != nLen || bOverflow ) {
275
+ inchi_free( pHillFormula );
276
+ pHillFormula = NULL;
277
+ }
278
+ }
279
+ }
280
+ return pHillFormula;
281
+ }
282
+
283
+ /************************************************************************************/
284
+ /* return value: 0 => copied to stereo bonds; 1=> Allene copied to stereocenters */
285
+ /* on input nNumberOfStereoBonds==NULL means second call, use Stereo->...Inv */
286
+ /************************************************************************************/
287
+ int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds,
288
+ AT_STEREO_DBLE *LinearCTStereoDble,
289
+ AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic )
290
+ {
291
+ int cumulene_len, j, next_j /* ordering number of the central allene atom */, next_neigh;
292
+ AT_RANK at_num;
293
+ int parity;
294
+ if ( pCanonOrd && pCanonRank ) {
295
+ j = pCanonOrd[(int)LinearCTStereoDble->at_num1-1];
296
+ /* if allene then find the central atom, at[next_j] */
297
+ if ( bIsotopic ) {
298
+ cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity2[0]);
299
+ if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor2[1]) ) {
300
+ next_j = at[j].neighbor[(int)at[j].stereo_bond_ord2[0]];
301
+ for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) {
302
+ next_neigh = (j == at[next_j].neighbor[0]);
303
+ j = next_j;
304
+ next_j = at[next_j].neighbor[next_neigh];
305
+ }
306
+ /* next_j is the central atom */
307
+ } else {
308
+ cumulene_len = -1; /* not an allene */
309
+ }
310
+
311
+ } else {
312
+ cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity[0]);
313
+ if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor[1]) ) {
314
+ next_j = at[j].neighbor[(int)at[j].stereo_bond_ord[0]];
315
+ for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) {
316
+ next_neigh = (j == at[next_j].neighbor[0]);
317
+ j = next_j;
318
+ next_j = at[next_j].neighbor[next_neigh];
319
+ }
320
+ } else {
321
+ cumulene_len = -1; /* not an allene */
322
+ }
323
+ }
324
+ if ( !cumulene_len ) {
325
+ /* allene has been found; insert new stereocenter and parity */
326
+ AT_NUMB *nNumber;
327
+ S_CHAR *t_parity;
328
+ nNumber = nNumberOfStereoBonds? Stereo->nNumber : Stereo->nNumberInv;
329
+ t_parity = nNumberOfStereoBonds? Stereo->t_parity : Stereo->t_parityInv;
330
+ at_num = pCanonRank[next_j];
331
+ parity = LinearCTStereoDble->parity;
332
+ /* free room for the new stereocenter */
333
+ for ( j = 0; j < *nNumberOfStereoCenters && Stereo->nNumber[j] < at_num; j ++ )
334
+ ;
335
+ if ( j < *nNumberOfStereoCenters ) {
336
+ memmove( nNumber + j + 1, nNumber + j, (*nNumberOfStereoCenters-j)*sizeof(nNumber[0]) );
337
+ memmove( t_parity + j + 1, t_parity + j, (*nNumberOfStereoCenters-j)*sizeof(t_parity[0]) );
338
+ }
339
+ /* fill the new stereo center info */
340
+
341
+ nNumber[j] = at_num;
342
+ t_parity[j] = parity;
343
+ (*nNumberOfStereoCenters) ++;
344
+ return 1;
345
+ }
346
+ }
347
+ /* save the stereo bond info */
348
+ if ( nNumberOfStereoBonds ) {
349
+ j = *nNumberOfStereoBonds;
350
+ Stereo->b_parity[j] = LinearCTStereoDble->parity;
351
+ Stereo->nBondAtom1[j] = LinearCTStereoDble->at_num1;
352
+ Stereo->nBondAtom2[j] = LinearCTStereoDble->at_num2;
353
+ (*nNumberOfStereoBonds) ++;
354
+ }
355
+ return 0;
356
+ }
357
+ /***************************************************************************/
358
+ int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo,
359
+ AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb,
360
+ AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble
361
+ , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic
362
+ , AT_STEREO_CARB *LinearCTStereoCarbInv
363
+ , AT_STEREO_DBLE *LinearCTStereoDbleInv
364
+ , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv )
365
+ {
366
+ int n, i, nErrorCode = 0, len;
367
+ int bAllene;
368
+ int diff;
369
+ int lenInv, bAlleneInv;
370
+ /* stereo centers */
371
+ n = Stereo->nNumberOfStereoCenters = nLenLinearCTStereoCarb;
372
+ for ( i = 0; i < n; i ++ ) {
373
+ Stereo->nNumber[i] = LinearCTStereoCarb[i].at_num;
374
+ Stereo->t_parity[i] = LinearCTStereoCarb[i].parity;
375
+ Stereo->nNumberInv[i] = LinearCTStereoCarbInv[i].at_num;
376
+ Stereo->t_parityInv[i] = LinearCTStereoCarbInv[i].parity;
377
+ }
378
+ /* stereo bonds */
379
+ n = nLenLinearCTStereoDble;
380
+ lenInv = Stereo->nNumberOfStereoCenters;
381
+ for ( i = len = 0; i < n; i ++ ) {
382
+ bAllene =
383
+ Copy2StereoBondOrAllene( Stereo, &Stereo->nNumberOfStereoCenters,
384
+ &len, LinearCTStereoDble+i, pCanonOrd, pCanonRank, at, bIsotopic );
385
+ bAlleneInv =
386
+ Copy2StereoBondOrAllene( Stereo, &lenInv,
387
+ NULL, LinearCTStereoDbleInv+i, pCanonOrdInv, pCanonRankInv, at, bIsotopic );
388
+ /* make sure double bond stereo is identical in original and inverted geometry */
389
+ /* Note: all allenes are AFTER double bonds in LinearCTStereoDble... */
390
+ if ( bAllene != bAlleneInv || !bAllene &&
391
+ CompareLinCtStereoDble ( LinearCTStereoDble+i, 1,
392
+ LinearCTStereoDbleInv+i, 1 ) ) {
393
+ nErrorCode = -4; /* double bond stereo Inv is NOT identical to Abs */
394
+ goto exit_function;
395
+ }
396
+ }
397
+ Stereo->nNumberOfStereoBonds = len;
398
+
399
+ if ( lenInv != Stereo->nNumberOfStereoCenters ) {
400
+ nErrorCode = -5; /* different number of stereo centers in Abs and Inv */
401
+ goto exit_function;
402
+ }
403
+ /* compare inverted stereocenters to absolute */
404
+ n = Stereo->nNumberOfStereoCenters;
405
+ diff = 0;
406
+ for ( i = 0, diff = 0; i < n; i ++ ) {
407
+ if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) {
408
+ diff = (Stereo->nNumberInv[i] > Stereo->nNumber[i])? 2 : -2;
409
+ break; /* Abs != Inv */
410
+ }
411
+ if ( Stereo->t_parityInv[i] != Stereo->t_parity[i] ) {
412
+ diff = (Stereo->t_parityInv[i] > Stereo->t_parity[i])? 1 : -1;
413
+ break; /* Abs != Inv */
414
+ }
415
+ }
416
+ Stereo->nCompInv2Abs = (diff > 0)? 1 : (diff < 0)? -1 : 0;
417
+ if ( diff == -1 || diff == 1 ) {
418
+ /* the first found difference was in parities */
419
+ for ( i = 0, diff = 0; i < n; i ++ ) {
420
+ if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) {
421
+ diff = 2; /* difference in stereo center numbering */
422
+ break;
423
+ }
424
+ /* parities can be only 1, 2, 3, 4. Therefore only mutually inverted pairs
425
+ * (t_parityInv, t_parity) = (1,2) or (2,1) statisfy conditions
426
+ * (t_parityInv != t_parity) && (t_parityInv + t_parity == 3)
427
+ */
428
+ if ( Stereo->t_parityInv[i] == Stereo->t_parity[i] ||
429
+ Stereo->t_parityInv[i] + Stereo->t_parity[i] != 3 ) {
430
+ diff = 1; /* parities are same or different and cannot be obtained by simple inversion */
431
+ break;
432
+ }
433
+ }
434
+ Stereo->bTrivialInv = !diff;
435
+ } else {
436
+ Stereo->bTrivialInv = 0;
437
+ }
438
+ exit_function:
439
+
440
+ return nErrorCode;
441
+ }
442
+ /***************************************************************************/
443
+ int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd,
444
+ AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb,
445
+ AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble )
446
+ {
447
+ int n, i, j1, j2, num, mark_atom, mark_bond;
448
+
449
+ if ( !pCanonOrd )
450
+ return -1;
451
+ num = 0;
452
+ n = nLenLinearCTStereoCarb;
453
+ mark_atom = bIsotopic? AMBIGUOUS_STEREO_ATOM_ISO : AMBIGUOUS_STEREO_ATOM;
454
+ for ( i = 0; i < n; i ++ ) {
455
+ /* mark ambiguous stereo centers (for displaying and "Ambiguous stereo" message) */
456
+ if ( ATOM_PARITY_NOT_UNKN(LinearCTStereoCarb[i].parity) &&
457
+ at[j1=pCanonOrd[(int)LinearCTStereoCarb[i].at_num-1]].bAmbiguousStereo ) {
458
+ at[j1].bAmbiguousStereo |= mark_atom;
459
+ norm_at[j1].bAmbiguousStereo |= mark_atom;
460
+ num ++;
461
+ }
462
+ }
463
+
464
+ n = nLenLinearCTStereoDble;
465
+ mark_bond = bIsotopic? AMBIGUOUS_STEREO_BOND_ISO : AMBIGUOUS_STEREO_BOND;
466
+ for ( i = 0; i < n; i ++ ) {
467
+ /* mark ambiguous stereo bonds or allenes (for displaying and "Ambiguous stereo" message) */
468
+ if ( ATOM_PARITY_WELL_DEF(LinearCTStereoDble[i].parity) ) {
469
+ j1=pCanonOrd[(int)LinearCTStereoDble[i].at_num1-1];
470
+ j2=pCanonOrd[(int)LinearCTStereoDble[i].at_num2-1];
471
+ if ( at[j1].bAmbiguousStereo || at[j2].bAmbiguousStereo ) {
472
+ /* if it is an allene then mark the central atom only
473
+ because the bonds should not be marked to avoid misleading
474
+ message "Ambiguous stereo: bond(s)": Allene makes a stereocenter
475
+ */
476
+ int j1_parity = bIsotopic? at[j1].stereo_bond_parity2[0] :
477
+ at[j1].stereo_bond_parity[0];
478
+ int cumulene_len = BOND_CHAIN_LEN(j1_parity); /* 0 => double bond, 1 => allene, 2 => cumulene,..*/
479
+ if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS ||
480
+ !(bIsotopic? at[j1].stereo_bond_neighbor2[1] :
481
+ at[j1].stereo_bond_neighbor[1] )) ) {
482
+ /* found an allene; locate its central atom */
483
+ int next_j, next_neigh;
484
+ int j = j1;
485
+ next_j = at[j].neighbor[bIsotopic? at[j].stereo_bond_ord2[0] :
486
+ at[j].stereo_bond_ord[0] ];
487
+ for ( cumulene_len = (cumulene_len-1)/2;
488
+ cumulene_len && 2==at[next_j].valence;
489
+ cumulene_len -- ) {
490
+ next_neigh = (j == at[next_j].neighbor[0]);
491
+ j = next_j;
492
+ next_j = at[next_j].neighbor[next_neigh];
493
+ }
494
+ /* next_j is the central atom */
495
+ if ( 2==at[next_j].valence ) {
496
+ at[next_j].bAmbiguousStereo |= mark_atom;
497
+ norm_at[next_j].bAmbiguousStereo |= mark_atom;
498
+ num ++;
499
+ continue; /* do not mark the cumulene "bond" endpoints */
500
+ }
501
+ }
502
+ /* not an allene, mark double bond or cumulene end atoms */
503
+ if ( at[j1].bAmbiguousStereo ) {
504
+ at[j1].bAmbiguousStereo |= mark_bond; /* ??? */
505
+ norm_at[j1].bAmbiguousStereo |= mark_bond;
506
+ num ++;
507
+ }
508
+ if ( at[j2].bAmbiguousStereo ) {
509
+ at[j2].bAmbiguousStereo |= mark_bond; /* ??? */
510
+ norm_at[j2].bAmbiguousStereo |= mark_bond;
511
+ num ++;
512
+ }
513
+ }
514
+ }
515
+ }
516
+ return num;
517
+
518
+ }
519
+ /**********************************************************************************************/
520
+ INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode )
521
+ {
522
+ INCHI_MODE nRet = 0;
523
+ int i, n;
524
+ if ( !Stereo || Stereo && !Stereo->nNumberOfStereoCenters && !Stereo->nNumberOfStereoBonds) {
525
+ return nRet;
526
+ }
527
+
528
+ /* stereocenters */
529
+ if ( !Stereo->nCompInv2Abs &&
530
+ (n=Stereo->nNumberOfStereoCenters) > 0 && (nUserMode & REQ_MODE_SC_IGN_ALL_UU) ) {
531
+
532
+ for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]); i ++ )
533
+ ;
534
+ if ( i == n ) {
535
+ Stereo->nNumberOfStereoCenters = 0;
536
+ for ( i = 0; i < n; i ++ ) {
537
+ Stereo->t_parity[i] = 0;
538
+ Stereo->nNumber[i] = 0;
539
+ Stereo->t_parityInv[i] = 0;
540
+ Stereo->nNumberInv[i] = 0;
541
+ }
542
+ nRet |= REQ_MODE_SC_IGN_ALL_UU;
543
+ }
544
+ }
545
+ /* stereobonds */
546
+ if ( (n=Stereo->nNumberOfStereoBonds) > 0 && (nUserMode & REQ_MODE_SB_IGN_ALL_UU) ) {
547
+ for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]); i ++ )
548
+ ;
549
+ if ( i == n ) {
550
+ Stereo->nNumberOfStereoBonds = 0;
551
+ for ( i = 0; i < n; i ++ ) {
552
+ Stereo->b_parity[i] = 0;
553
+ Stereo->nBondAtom1[i] = 0;
554
+ Stereo->nBondAtom2[i] = 0;
555
+ }
556
+ nRet |= REQ_MODE_SB_IGN_ALL_UU;
557
+ }
558
+ }
559
+
560
+ return nRet;
561
+ }
562
+ #if( defined(INCHI_LIBRARY) || ADD_CMLPP==1 )
563
+ /**********************************************************************************************/
564
+ void WriteCoord( char *str, double x )
565
+ {
566
+ if ( x < -9999999.9 ) {
567
+ sprintf( str, "%10.2e", x );
568
+ } else
569
+ if ( x < -999999.99 ) {
570
+ sprintf( str, "%10.2f", x );
571
+ } else
572
+ if ( x < -99999.999 ) {
573
+ sprintf( str, "%10.3f", x );
574
+ } else
575
+ if ( x < 99999.9999 ) {
576
+ sprintf( str, "%10.4f", x );
577
+ } else
578
+ if ( x < 999999.999 ) {
579
+ sprintf( str, "%10.3f", x );
580
+ } else
581
+ if ( x < 9999999.99 ) {
582
+ sprintf( str, "%10.2f", x );
583
+ } else
584
+ if ( x < 99999999.9 ) {
585
+ sprintf( str, "%10.1f", x );
586
+ } else {
587
+ sprintf( str, "%10.3e", x );
588
+ }
589
+ }
590
+ #endif
591
+ /* used CANON_STAT members
592
+
593
+ pCS->LinearCT
594
+ pCS->LinearCTIsotopic
595
+ pCS->LinearCTIsotopicStereoCarb
596
+ pCS->LinearCTIsotopicStereoCarbInv
597
+ pCS->LinearCTIsotopicStereoDble
598
+ pCS->LinearCTIsotopicStereoDbleInv
599
+ pCS->LinearCTIsotopicTautomer
600
+ pCS->LinearCTStereoCarb
601
+ pCS->LinearCTStereoCarbInv
602
+ pCS->LinearCTStereoDble
603
+ pCS->LinearCTStereoDbleInv
604
+ pCS->nCanonOrd
605
+ pCS->nCanonOrdIsotopic
606
+ pCS->nCanonOrdIsotopicStereo
607
+ pCS->nCanonOrdIsotopicStereoInv
608
+ pCS->nCanonOrdIsotopicStereoTaut
609
+ pCS->nCanonOrdIsotopicTaut
610
+ pCS->nCanonOrdStereo
611
+ pCS->nCanonOrdStereoInv
612
+ pCS->nCanonOrdStereoTaut
613
+ pCS->nCanonOrdTaut
614
+ pCS->nLenCanonOrd
615
+ pCS->nLenCanonOrdIsotopic
616
+ pCS->nLenCanonOrdIsotopicStereo
617
+ pCS->nLenCanonOrdIsotopicStereoTaut
618
+ pCS->nLenCanonOrdIsotopicTaut
619
+ pCS->nLenCanonOrdStereo
620
+ pCS->nLenCanonOrdStereoTaut
621
+ pCS->nLenCanonOrdTaut
622
+ pCS->nLenLinearCTAtOnly
623
+ pCS->nLenLinearCTIsotopic
624
+ pCS->nLenLinearCTIsotopicStereoCarb
625
+ pCS->nLenLinearCTIsotopicStereoDble
626
+ pCS->nLenLinearCTIsotopicTautomer
627
+ pCS->nLenLinearCTStereoCarb
628
+ pCS->nLenLinearCTStereoDble
629
+ pCS->nNum_H
630
+ pCS->nNum_H_fixed
631
+ pCS->nSymmRank
632
+ pCS->nSymmRankIsotopic
633
+ pCS->nSymmRankIsotopicTaut
634
+ pCS->nSymmRankTaut
635
+ pCS->t_group_info
636
+ pCS->t_group_info->num_t_groups
637
+
638
+ */
639
+ /**********************************************************************************************/
640
+ int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux,
641
+ int num_atoms, int num_at_tg, int num_removed_H,
642
+ sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric,
643
+ INCHI_MODE nUserMode, char *pStrErrStruct )
644
+ {
645
+ int i, j, m, n, g, len, ii, ret=0;
646
+
647
+ AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut;
648
+ T_GROUP_INFO *t_group_info = pCS->t_group_info;
649
+ T_GROUP *t_group;
650
+ int nErrorCode = 0;
651
+ AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */
652
+ AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL;
653
+ AT_RANK nMinOrd;
654
+ INChI_Stereo *Stereo;
655
+ int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0;
656
+ INCHI_MODE nStereoUnmarkMode;
657
+
658
+ /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/
659
+ /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/
660
+
661
+ /* Check for warnings */
662
+ if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 ||
663
+ pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) {
664
+ nErrorCode |= WARN_FAILED_STEREO;
665
+ }
666
+ if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 ||
667
+ pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) {
668
+ nErrorCode |= WARN_FAILED_ISOTOPIC;
669
+ }
670
+ if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 ||
671
+ pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) {
672
+ nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO;
673
+ }
674
+ pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) );
675
+ pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */
676
+
677
+ if ( !pCanonRankAtoms || !pSortOrd ) {
678
+ nErrorCode = 0;
679
+ ret = CT_OUT_OF_RAM; /* <BRKPT> */
680
+ pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM;
681
+ goto exit_function;
682
+ }
683
+
684
+ /* total charge */
685
+ for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) {
686
+ n += at[i].charge;
687
+ }
688
+ pINChI->nTotalCharge = n;
689
+
690
+ /* number of atoms */
691
+ pINChI->nNumberOfAtoms = num_atoms;
692
+ pINChI_Aux->nNumberOfAtoms = num_atoms;
693
+
694
+ /* removed protons and detachable isotopic H */
695
+ if ( bTautomeric && t_group_info ) {
696
+ pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons;
697
+ for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) {
698
+ pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i]
699
+ + t_group_info->tni.nNumRemovedProtonsIsotopic[i];
700
+ }
701
+ if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) {
702
+ pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON;
703
+ }
704
+ if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) {
705
+ AddMOLfileError(pStrErrStruct, "Proton(s) added/removed");
706
+ }
707
+ if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) {
708
+ AddMOLfileError(pStrErrStruct, "Charges neutralized");
709
+ }
710
+ }
711
+
712
+ /* abs or rel stereo may establish one of two canonical numberings */
713
+ if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) &&
714
+ pCS->nLenCanonOrdStereo > 0 &&
715
+ (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv ||
716
+ pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) &&
717
+ pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv
718
+ ) {
719
+
720
+ pCanonRank = pCanonRankAtoms;
721
+ pCanonOrd = pCS->nCanonOrdStereo;
722
+ pCanonRankInv = pSortOrd;
723
+ pCanonOrdInv = pCS->nCanonOrdStereoInv;
724
+ Stereo = pINChI->Stereo;
725
+ for ( i = 0; i < num_at_tg; i ++ ) {
726
+ pCanonRankInv[pCanonOrdInv[i]] =
727
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
728
+ }
729
+ /********************************************************************/
730
+ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */
731
+ /********************************************************************/
732
+ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo,
733
+ pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb,
734
+ pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble
735
+ , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */
736
+ , pCS->LinearCTStereoCarbInv
737
+ , pCS->LinearCTStereoDbleInv
738
+ , pCanonOrdInv, pCanonRankInv );
739
+
740
+ if ( Stereo->t_parityInv && Stereo->nNumberInv ) {
741
+ if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) {
742
+ pINChI->nFlags |= INCHI_FLAG_REL_STEREO;
743
+ }
744
+ if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) {
745
+ pINChI->nFlags |= INCHI_FLAG_RAC_STEREO;
746
+ }
747
+ if ( Stereo->nCompInv2Abs ) {
748
+ if ( Stereo->nCompInv2Abs == -1 ) {
749
+ /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */
750
+ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */
751
+ AT_NUMB *nNumberInv = Stereo->nNumberInv;
752
+ S_CHAR *t_parityInv = Stereo->t_parityInv;
753
+ Stereo->nNumberInv = Stereo->nNumber;
754
+ Stereo->t_parityInv = Stereo->t_parity;
755
+ Stereo->nNumber = nNumberInv;
756
+ Stereo->t_parity = t_parityInv;
757
+ /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd
758
+ and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */
759
+ switch_ptrs( &pCanonRank, &pCanonRankInv );
760
+ switch_ptrs( &pCanonOrd, &pCanonOrdInv );
761
+ bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */
762
+ }
763
+ }
764
+ }
765
+
766
+ for ( i = 0; i < num_atoms; i ++ ) {
767
+ pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number;
768
+ pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number;
769
+ }
770
+ if ( bUseNumberingInv ) {
771
+ /* switch ptrs back to avoid confusion */
772
+ switch_ptrs( &pCanonRank, &pCanonRankInv );
773
+ switch_ptrs( &pCanonOrd, &pCanonOrdInv );
774
+ /* save inverted stereo ranks & order because it represents the smallest (relative) */
775
+ memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) );
776
+ /* change pCS->nCanonOrdStereo[] to inverted: */
777
+ memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) );
778
+ }
779
+ pCanonRankInv = NULL;
780
+ pCanonOrdInv = NULL;
781
+ pOrigNosInCanonOrd = NULL;
782
+
783
+ } else { /*------------------------------ no stereo */
784
+
785
+ pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo :
786
+ pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL;
787
+ pCanonRank = pCanonRankAtoms;
788
+ pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd;
789
+ if ( pCanonOrd && pCanonRank ) {
790
+ for ( i = 0; i < num_atoms; i ++ ) {
791
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
792
+ pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number;
793
+ }
794
+ for ( ; i < num_at_tg; i ++ ) {
795
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
796
+ }
797
+ }
798
+ }
799
+ /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */
800
+
801
+
802
+ if ( pINChI_Aux->OrigInfo ) {
803
+ /* charges, radicals, valences */
804
+ for ( i = 0; i < num_atoms; i ++ ) {
805
+ ii = pCanonOrd[i];
806
+ if ( norm_at[ii].valence || norm_at[ii].num_H ) {
807
+ pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge;
808
+ pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 :
809
+ (norm_at[ii].radical==RADICAL_DOUBLET)? 1 :
810
+ (norm_at[ii].radical==RADICAL_TRIPLET)? 2 :
811
+ norm_at[ii].radical? 3 : 0 ;
812
+ pINChI_Aux->OrigInfo[i].cUnusualValence =
813
+ get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical,
814
+ norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence );
815
+ } else {
816
+ /* charge of a single atom component is in the INChI; valence = 0 is standard */
817
+ pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 :
818
+ (norm_at[ii].radical==RADICAL_DOUBLET)? 1 :
819
+ (norm_at[ii].radical==RADICAL_TRIPLET)? 2 :
820
+ norm_at[ii].radical? 3 : 0 ;
821
+ }
822
+
823
+ }
824
+ }
825
+
826
+ /* non-isotopic canonical numbers and equivalence of atoms (Aux) */
827
+ pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */
828
+ pSymmRank = pCS->nSymmRank;
829
+ if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) {
830
+ for ( i = 0; i < num_atoms; i ++ ) {
831
+ pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */
832
+ pSortOrd[i] = i;
833
+ }
834
+ for ( ; i < num_at_tg; i ++ ) {
835
+ pSortOrd[i] = MAX_ATOMS; /* for debugging only */
836
+ }
837
+ pn_RankForSort = pConstitEquNumb;
838
+ qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd );
839
+ for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) {
840
+ if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) {
841
+ nMinOrd ++;
842
+ if ( j - i > 1 ) {
843
+ /* found a sequence of equivalent atoms: i..j-1 */
844
+ while ( i < j ) {
845
+ pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */
846
+ }
847
+ /* at this point j == i */
848
+ } else {
849
+ pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */
850
+ }
851
+ nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */
852
+ }
853
+ }
854
+ } else {
855
+ nErrorCode |= ERR_NO_CANON_RESULTS;
856
+ ret = -1; /* program error; no breakpoint here */
857
+ goto exit_function;
858
+ }
859
+ /* atomic numbers from the Periodic Table */
860
+ for ( i = 0; i < num_atoms; i ++ ) {
861
+ pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number;
862
+ }
863
+ /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */
864
+ if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) {
865
+ nErrorCode |= ERR_NO_CANON_RESULTS;
866
+ ret = -2;
867
+ goto exit_function;
868
+ }
869
+ memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly);
870
+ pINChI->lenConnTable = pCS->nLenLinearCTAtOnly;
871
+
872
+ /* tautomeric group(s) canonical representation */
873
+ len = 0;
874
+ if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) {
875
+ /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */
876
+ pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0;
877
+ /* number of tautomeric groups */
878
+ pINChI->nTautomer[len ++] = (AT_NUMB)n;
879
+ /* store each tautomeric group, one by one */
880
+ for ( i = 0; i < n; i ++ ) {
881
+ g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */
882
+ t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */
883
+ /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */
884
+ pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE;
885
+ /* Num(H), Num(-) */
886
+ for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ )
887
+ pINChI->nTautomer[len ++] = t_group->num[j];
888
+ for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ )
889
+ pINChI->nTautomer[len ++] = 0; /* should not happen */
890
+ /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */
891
+ for ( j = (int)t_group->nFirstEndpointAtNoPos,
892
+ m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) {
893
+ pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */
894
+ }
895
+ }
896
+ pINChI->lenTautomer = len;
897
+ pINChI_Aux->nNumberOfTGroups = n;
898
+ } else {
899
+ pINChI->lenTautomer = 0;
900
+ pINChI_Aux->nNumberOfTGroups = 0;
901
+ if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) ||
902
+ t_group_info->nNumIsotopicEndpoints>1 &&
903
+ (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)))
904
+ ) {
905
+ /* only protons (re)moved or added */
906
+ pINChI->lenTautomer = 1;
907
+ pINChI->nTautomer[0] = 0;
908
+ }
909
+ }
910
+
911
+ /* number of H (excluding tautomeric) */
912
+ if ( pCS->nNum_H ) {
913
+ for ( i = 0; i < num_atoms; i ++ ) {
914
+ pINChI->nNum_H[i] = pCS->nNum_H[i];
915
+ }
916
+ }
917
+ /* number of fixed H (tautomeric H in non-tautomeric representation) */
918
+ if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) {
919
+ for ( i = 0; i < num_atoms; i ++ ) {
920
+ pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i];
921
+ pINChI->nNum_H[i] += pCS->nNum_H_fixed[i];
922
+ }
923
+ }
924
+
925
+ /***********************************************************
926
+ * tautomeric group(s) numbering and symmetry;
927
+ * should not depend on switching to rel. stereo numbering
928
+ */
929
+ if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) {
930
+ pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut :
931
+ pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL;
932
+ pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers;
933
+ pSymmRank = pCS->nSymmRankTaut;
934
+ if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) {
935
+ for ( i = 0; i < n; i ++ ) {
936
+ pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]];
937
+ pSortOrd[i] = i;
938
+ }
939
+ pn_RankForSort = pConstitEquNumb;
940
+ qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd );
941
+ for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) {
942
+ if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) {
943
+ nMinOrd ++; /* make is start from 1, not from zero */
944
+ if ( j - i > 1 ) {
945
+ /* found a sequence of more than one equivalent t-groups: i..j-1 */
946
+ while ( i < j ) {
947
+ pConstitEquNumb[pSortOrd[i++]] = nMinOrd;
948
+ }
949
+ } else {
950
+ pConstitEquNumb[pSortOrd[i++]] = 0;
951
+ }
952
+ nMinOrd = pSortOrd[j]; /* at the end j == n */
953
+ }
954
+ }
955
+ }
956
+ }
957
+
958
+ /* Allocate and fill Hill formula */
959
+ if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) {
960
+ nErrorCode = 0;
961
+ ret = CT_OUT_OF_RAM; /* <BRKPT> */
962
+ pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM;
963
+ goto exit_function;
964
+ }
965
+
966
+ if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) {
967
+ pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0;
968
+ pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0;
969
+ if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) ||
970
+ (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) {
971
+ AddMOLfileError(pStrErrStruct, "Omitted undefined stereo");
972
+ }
973
+ }
974
+
975
+ /*************************/
976
+ /* mark ambiguous stereo */
977
+ /*************************/
978
+ MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd,
979
+ pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb,
980
+ pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble );
981
+
982
+
983
+ /************************************************************************
984
+ *
985
+ * isotopic part
986
+ */
987
+ /* abs or rel stereo may establish one of two canonical numberings */
988
+ if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) &&
989
+ pCS->nLenCanonOrdIsotopicStereo > 0 &&
990
+ (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv ||
991
+ pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) &&
992
+ pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv
993
+ ) {
994
+ /* found isotopic stereo */
995
+ pCanonRank = pCanonRankAtoms;
996
+ pCanonOrd = pCS->nCanonOrdIsotopicStereo;
997
+ pCanonRankInv = pSortOrd;
998
+ pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv;
999
+ Stereo = pINChI->StereoIsotopic;
1000
+ for ( i = 0; i < num_at_tg; i ++ ) {
1001
+ pCanonRankInv[pCanonOrdInv[i]] =
1002
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
1003
+ }
1004
+ /********************************************************************/
1005
+ /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */
1006
+ /********************************************************************/
1007
+ nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo,
1008
+ pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb,
1009
+ pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble
1010
+ , pCanonOrd, pCanonRank, at, 1 /* isotopic */
1011
+ , pCS->LinearCTIsotopicStereoCarbInv
1012
+ , pCS->LinearCTIsotopicStereoDbleInv
1013
+ , pCanonOrdInv, pCanonRankInv );
1014
+
1015
+ if ( Stereo->t_parityInv && Stereo->nNumberInv ) {
1016
+ if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) {
1017
+ pINChI->nFlags |= INCHI_FLAG_REL_STEREO;
1018
+ }
1019
+ if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) {
1020
+ pINChI->nFlags |= INCHI_FLAG_RAC_STEREO;
1021
+ }
1022
+ if ( Stereo->nCompInv2Abs ) {
1023
+ if ( Stereo->nCompInv2Abs == -1 ) {
1024
+ /* switch pointers so that the stereo becomes the smallest (relative) */
1025
+ /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */
1026
+ AT_NUMB *nNumberInv = Stereo->nNumberInv;
1027
+ S_CHAR *t_parityInv = Stereo->t_parityInv;
1028
+ Stereo->nNumberInv = Stereo->nNumber;
1029
+ Stereo->t_parityInv = Stereo->t_parity;
1030
+ Stereo->nNumber = nNumberInv;
1031
+ Stereo->t_parity = t_parityInv;
1032
+ switch_ptrs( &pCanonRank, &pCanonRankInv );
1033
+ switch_ptrs( &pCanonOrd, &pCanonOrdInv );
1034
+ bUseIsotopicNumberingInv = 1;
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ for ( i = 0; i < num_atoms; i ++ ) {
1040
+ pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number;
1041
+ pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number;
1042
+ }
1043
+ if ( bUseIsotopicNumberingInv ) {
1044
+ switch_ptrs( &pCanonRank, &pCanonRankInv );
1045
+ switch_ptrs( &pCanonOrd, &pCanonOrdInv );
1046
+ memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) );
1047
+ memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) );
1048
+ }
1049
+ pCanonRankInv = NULL;
1050
+ pCanonOrdInv = NULL;
1051
+ pOrigNosInCanonOrd = NULL;
1052
+
1053
+ } else {
1054
+ /* no isotopic stereo */
1055
+ pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo :
1056
+ pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL;
1057
+ pCanonRank = pCanonRankAtoms;
1058
+ pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd;
1059
+ if ( pCanonOrd && pCanonRank ) {
1060
+ for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */
1061
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
1062
+ pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number;
1063
+ }
1064
+ for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */
1065
+ pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1);
1066
+ }
1067
+ }
1068
+ }
1069
+ /*pCanonOrdIso = pCanonOrd;*/
1070
+
1071
+ pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers;
1072
+ pSymmRank = pCS->nSymmRankIsotopic;
1073
+ if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) {
1074
+ for ( i = 0; i < num_atoms; i ++ ) {
1075
+ pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]];
1076
+ pSortOrd[i] = i;
1077
+ }
1078
+ for ( ; i < num_at_tg; i ++ ) {
1079
+ pSortOrd[i] = i;
1080
+ }
1081
+ pn_RankForSort = pConstitEquNumb;
1082
+ qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd );
1083
+ for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) {
1084
+ if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) {
1085
+ nMinOrd ++;
1086
+ if ( j - i > 1 ) {
1087
+ /* found a sequence of equivalent atoms: i..j-1 */
1088
+ while ( i < j ) {
1089
+ pConstitEquNumb[pSortOrd[i++]] = nMinOrd;
1090
+ }
1091
+ } else {
1092
+ pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */
1093
+ }
1094
+ nMinOrd = pSortOrd[j];
1095
+ }
1096
+ }
1097
+ } else {
1098
+ goto exit_function; /* no isotopic info available */
1099
+ }
1100
+ /* isotopic atoms */
1101
+ n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic;
1102
+ for ( i = 0; i < n; i ++ ) {
1103
+ pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num;
1104
+ pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff;
1105
+ pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H;
1106
+ pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D;
1107
+ pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T;
1108
+ }
1109
+ /* isotopic tautomeric groups */
1110
+ n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer;
1111
+ for ( i = 0; i < n; i ++ ) {
1112
+ pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num;
1113
+ pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2];
1114
+ pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1];
1115
+ pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0];
1116
+ }
1117
+ /* atoms that may exchange isotopic H-atoms */
1118
+ if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) {
1119
+ for ( i = 0, j = 1; i < num_atoms; i ++ ) {
1120
+ if ( pCS->nExchgIsoH[i] ) {
1121
+ pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */
1122
+ }
1123
+ }
1124
+ pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */
1125
+ }
1126
+
1127
+ if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) {
1128
+ pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0;
1129
+ pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0;
1130
+ if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) ||
1131
+ (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) {
1132
+ AddMOLfileError(pStrErrStruct, "Omitted undefined stereo");
1133
+ }
1134
+ }
1135
+ /* mark ambiguous stereo */
1136
+ MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd,
1137
+ pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb,
1138
+ pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble );
1139
+
1140
+ /***********************************************************
1141
+ * isotopic tautomeric group(s) numbering and symmetry;
1142
+ * should not depend on switching to rel. stereo numbering
1143
+ */
1144
+ if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut &&
1145
+ (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) &&
1146
+ t_group_info && t_group_info->num_t_groups > 0 ) {
1147
+ n = t_group_info->num_t_groups;
1148
+ pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0?
1149
+ (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) :
1150
+ pCS->nLenCanonOrdIsotopicTaut > 0?
1151
+ (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL);
1152
+ pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers;
1153
+ pSymmRank = pCS->nSymmRankIsotopicTaut;
1154
+ if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) {
1155
+ for ( i = 0; i < n; i ++ ) {
1156
+ pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]];
1157
+ pSortOrd[i] = i;
1158
+ }
1159
+ pn_RankForSort = pConstitEquNumb;
1160
+ qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd );
1161
+ for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) {
1162
+ if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) {
1163
+ nMinOrd ++;
1164
+ if ( j - i > 1 ) {
1165
+ /* found a sequence of equivalent t-groups: i..j-1 */
1166
+ while ( i < j ) {
1167
+ pConstitEquNumb[pSortOrd[i++]] = nMinOrd;
1168
+ }
1169
+ } else {
1170
+ pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */
1171
+ }
1172
+ nMinOrd = pSortOrd[j]; /* at the end j = n */
1173
+ }
1174
+ }
1175
+ }
1176
+ }
1177
+
1178
+
1179
+ exit_function:
1180
+ if ( pCanonRankAtoms )
1181
+ inchi_free( pCanonRankAtoms );
1182
+ if ( pSortOrd )
1183
+ inchi_free( pSortOrd );
1184
+
1185
+ pINChI->nErrorCode |= nErrorCode;
1186
+ pINChI_Aux->nErrorCode |= nErrorCode;
1187
+
1188
+ return ret;
1189
+ }