rino 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }