rino 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +44 -0
- data/Rakefile +123 -0
- data/ext/extconf.rb +26 -0
- data/ext/ruby_inchi_main.so +0 -0
- data/ext/src/aux2atom.h +2786 -0
- data/ext/src/comdef.h +148 -0
- data/ext/src/e_0dstereo.c +3014 -0
- data/ext/src/e_0dstereo.h +31 -0
- data/ext/src/e_comdef.h +57 -0
- data/ext/src/e_ctl_data.h +147 -0
- data/ext/src/e_ichi_io.c +498 -0
- data/ext/src/e_ichi_io.h +40 -0
- data/ext/src/e_ichi_parms.c +37 -0
- data/ext/src/e_ichi_parms.h +41 -0
- data/ext/src/e_ichicomp.h +50 -0
- data/ext/src/e_ichierr.h +40 -0
- data/ext/src/e_ichimain.c +593 -0
- data/ext/src/e_ichisize.h +43 -0
- data/ext/src/e_inchi_atom.c +75 -0
- data/ext/src/e_inchi_atom.h +33 -0
- data/ext/src/e_inpdef.h +41 -0
- data/ext/src/e_mode.h +706 -0
- data/ext/src/e_mol2atom.c +649 -0
- data/ext/src/e_readinch.c +58 -0
- data/ext/src/e_readmol.c +54 -0
- data/ext/src/e_readmol.h +180 -0
- data/ext/src/e_readstru.c +251 -0
- data/ext/src/e_readstru.h +33 -0
- data/ext/src/e_util.c +284 -0
- data/ext/src/e_util.h +61 -0
- data/ext/src/extr_ct.h +251 -0
- data/ext/src/ichi.h +206 -0
- data/ext/src/ichi_bns.c +7999 -0
- data/ext/src/ichi_bns.h +231 -0
- data/ext/src/ichican2.c +5000 -0
- data/ext/src/ichicano.c +2195 -0
- data/ext/src/ichicano.h +49 -0
- data/ext/src/ichicans.c +1625 -0
- data/ext/src/ichicant.h +379 -0
- data/ext/src/ichicomn.h +260 -0
- data/ext/src/ichicomp.h +50 -0
- data/ext/src/ichidrp.h +119 -0
- data/ext/src/ichierr.h +124 -0
- data/ext/src/ichiisot.c +101 -0
- data/ext/src/ichilnct.c +286 -0
- data/ext/src/ichimain.h +132 -0
- data/ext/src/ichimak2.c +1189 -0
- data/ext/src/ichimake.c +3812 -0
- data/ext/src/ichimake.h +205 -0
- data/ext/src/ichimap1.c +851 -0
- data/ext/src/ichimap2.c +2856 -0
- data/ext/src/ichimap4.c +1609 -0
- data/ext/src/ichinorm.c +741 -0
- data/ext/src/ichinorm.h +67 -0
- data/ext/src/ichiparm.c +45 -0
- data/ext/src/ichiparm.h +1441 -0
- data/ext/src/ichiprt1.c +3612 -0
- data/ext/src/ichiprt2.c +1511 -0
- data/ext/src/ichiprt3.c +3011 -0
- data/ext/src/ichiqueu.c +1003 -0
- data/ext/src/ichiring.c +326 -0
- data/ext/src/ichiring.h +49 -0
- data/ext/src/ichisize.h +35 -0
- data/ext/src/ichisort.c +539 -0
- data/ext/src/ichister.c +3538 -0
- data/ext/src/ichister.h +35 -0
- data/ext/src/ichitaut.c +3843 -0
- data/ext/src/ichitaut.h +387 -0
- data/ext/src/ichitime.h +74 -0
- data/ext/src/inchi_api.h +670 -0
- data/ext/src/inchi_dll.c +1480 -0
- data/ext/src/inchi_dll.h +34 -0
- data/ext/src/inchi_dll_main.c +23 -0
- data/ext/src/inchi_dll_main.h +31 -0
- data/ext/src/inpdef.h +328 -0
- data/ext/src/lreadmol.h +1246 -0
- data/ext/src/mode.h +706 -0
- data/ext/src/ruby_inchi_main.c +558 -0
- data/ext/src/runichi.c +4179 -0
- data/ext/src/strutil.c +3861 -0
- data/ext/src/strutil.h +182 -0
- data/ext/src/util.c +1130 -0
- data/ext/src/util.h +85 -0
- data/lib/clean_tempfile.rb +220 -0
- data/lib/rino.rb +111 -0
- data/test/test.rb +386 -0
- metadata +130 -0
data/ext/src/ichinorm.c
ADDED
@@ -0,0 +1,741 @@
|
|
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
|
+
|
14
|
+
#include "mode.h"
|
15
|
+
|
16
|
+
#include "inpdef.h"
|
17
|
+
#include "extr_ct.h"
|
18
|
+
#include "ichitaut.h"
|
19
|
+
#include "ichinorm.h"
|
20
|
+
#include "ichierr.h"
|
21
|
+
#include "util.h"
|
22
|
+
|
23
|
+
#include "ichicomp.h"
|
24
|
+
|
25
|
+
#ifndef INCHI_ALL_CPP
|
26
|
+
#ifdef __cplusplus
|
27
|
+
extern "C" {
|
28
|
+
#endif
|
29
|
+
#endif
|
30
|
+
|
31
|
+
/* defined in ichisort.c, prototype in ichicomn.h */
|
32
|
+
int insertions_sort_AT_RANK( AT_RANK *base, int num );
|
33
|
+
|
34
|
+
#ifndef INCHI_ALL_CPP
|
35
|
+
#ifdef __cplusplus
|
36
|
+
}
|
37
|
+
#endif
|
38
|
+
#endif
|
39
|
+
|
40
|
+
|
41
|
+
/* local prototypes */
|
42
|
+
int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 );
|
43
|
+
|
44
|
+
/********************************************************************************/
|
45
|
+
int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 )
|
46
|
+
{
|
47
|
+
int ret = (int)((const inp_ATOM*)a1)->iso_atw_diff - (int)((const inp_ATOM*)a2)->iso_atw_diff;
|
48
|
+
if ( !ret ) /* make the sort stable */
|
49
|
+
ret = (int)((const inp_ATOM*)a1)->component - (int)((const inp_ATOM*)a2)->component;
|
50
|
+
return ret;
|
51
|
+
}
|
52
|
+
typedef struct tagTreeAtom {
|
53
|
+
AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */
|
54
|
+
S_CHAR valence; /* number of bonds = number of neighbors */
|
55
|
+
AT_NUMB nRingSystem;
|
56
|
+
AT_NUMB nBlockSystem;
|
57
|
+
S_CHAR bCutVertex;
|
58
|
+
} tre_ATOM;
|
59
|
+
|
60
|
+
#if( FIND_RING_SYSTEMS == 1 ) /* { */
|
61
|
+
/********************************************************************************/
|
62
|
+
int MarkRingSystemsInp( inp_ATOM *at, int num_atoms )
|
63
|
+
{
|
64
|
+
AT_NUMB *nStackAtom = NULL;
|
65
|
+
int nTopStackAtom=-1;
|
66
|
+
AT_NUMB *nRingStack = NULL;
|
67
|
+
int nTopRingStack=-1; /* was AT_NUMB */
|
68
|
+
AT_NUMB *nDfsNumber = NULL;
|
69
|
+
AT_NUMB *nLowNumber = NULL;
|
70
|
+
S_CHAR *cNeighNumb = NULL;
|
71
|
+
AT_NUMB nDfs;
|
72
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
73
|
+
AT_NUMB nRs, *nRsConnect = NULL;
|
74
|
+
int k;
|
75
|
+
AT_NUMB *tree = NULL;
|
76
|
+
int nNumConnect, nMaxNumConnect, nLenConnect;
|
77
|
+
#endif
|
78
|
+
AT_NUMB nNumAtInRingSystem;
|
79
|
+
int i, j, u, start, nNumRingSystems, nNumStartChildren;
|
80
|
+
|
81
|
+
/* allocate arrays */
|
82
|
+
nStackAtom = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nStackAtom[0]));
|
83
|
+
nRingStack = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nRingStack[0]));
|
84
|
+
nDfsNumber = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nDfsNumber[0]));
|
85
|
+
nLowNumber = (AT_NUMB *)inchi_malloc(num_atoms*sizeof(nLowNumber[0]));
|
86
|
+
cNeighNumb = (S_CHAR *)inchi_malloc(num_atoms*sizeof(cNeighNumb[0]));
|
87
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
88
|
+
nRsConnect = (AT_NUMB *)inchi_calloc(3*num_atoms+3,sizeof(nRsConnect[0]));
|
89
|
+
#endif
|
90
|
+
/* check allocation */
|
91
|
+
if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !cNeighNumb
|
92
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
93
|
+
|| !nRsConnect
|
94
|
+
#endif
|
95
|
+
) {
|
96
|
+
nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* <BRKPT> */
|
97
|
+
goto exit_function;
|
98
|
+
}
|
99
|
+
|
100
|
+
/********************************************
|
101
|
+
*
|
102
|
+
* Find Cut-vertices & Blocks
|
103
|
+
*
|
104
|
+
********************************************/
|
105
|
+
|
106
|
+
/* initiation */
|
107
|
+
start = 0;
|
108
|
+
nNumRingSystems = 0;
|
109
|
+
u = start; /* start atom */
|
110
|
+
nDfs = 0;
|
111
|
+
nTopStackAtom =-1;
|
112
|
+
nTopRingStack =-1;
|
113
|
+
memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0]));
|
114
|
+
memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0]));
|
115
|
+
/* push the start atom on the stack */
|
116
|
+
nLowNumber[u] = nDfsNumber[u] = ++nDfs;
|
117
|
+
nStackAtom[++nTopStackAtom] = (AT_NUMB)u;
|
118
|
+
nRingStack[++nTopRingStack] = (AT_NUMB)u;
|
119
|
+
|
120
|
+
nNumStartChildren = 0;
|
121
|
+
|
122
|
+
do {
|
123
|
+
/* advance */
|
124
|
+
advance_block:
|
125
|
+
if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) ) {
|
126
|
+
cNeighNumb[i] ++;
|
127
|
+
u = (int)at[i].neighbor[j];
|
128
|
+
if ( !nDfsNumber[u] ) {
|
129
|
+
/* tree edge, 1st visit -- advance */
|
130
|
+
nStackAtom[++nTopStackAtom] = (AT_NUMB)u;
|
131
|
+
nRingStack[++nTopRingStack] = (AT_NUMB)u;
|
132
|
+
nLowNumber[u] = nDfsNumber[u] = ++nDfs;
|
133
|
+
nNumStartChildren += (i == start);
|
134
|
+
} else
|
135
|
+
if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */
|
136
|
+
/* back edge: u is not a predecessor of i */
|
137
|
+
if ( nDfsNumber[u] < nDfsNumber[i] ) {
|
138
|
+
/* Back edge, 1st visit: u is an ancestor of i. Compare */
|
139
|
+
if ( nLowNumber[i] > nDfsNumber[u] ) {
|
140
|
+
nLowNumber[i] = nDfsNumber[u];
|
141
|
+
}
|
142
|
+
}
|
143
|
+
} /* may comment out ? */
|
144
|
+
goto advance_block;
|
145
|
+
} else {
|
146
|
+
cNeighNumb[i] = 0;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* back up */
|
150
|
+
if ( i != start ) {
|
151
|
+
u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */
|
152
|
+
if ( nLowNumber[i] >= nDfsNumber[u] ) {
|
153
|
+
/* output the block */
|
154
|
+
nNumRingSystems ++;
|
155
|
+
at[u].nBlockSystem = nNumRingSystems;
|
156
|
+
if ( u != start || nNumStartChildren > 1 ) {
|
157
|
+
at[u].bCutVertex += 1;
|
158
|
+
}
|
159
|
+
while ( nTopRingStack >= 0 ) {
|
160
|
+
j = nRingStack[nTopRingStack--];
|
161
|
+
at[j].nBlockSystem = nNumRingSystems; /* mark the atom */
|
162
|
+
if ( i == j ) {
|
163
|
+
break;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
} else
|
167
|
+
if ( nLowNumber[u] > nLowNumber[i] ) {
|
168
|
+
/* inherit */
|
169
|
+
nLowNumber[u] = nLowNumber[i];
|
170
|
+
}
|
171
|
+
}
|
172
|
+
} while ( --nTopStackAtom >= 0 );
|
173
|
+
|
174
|
+
|
175
|
+
/********************************************
|
176
|
+
*
|
177
|
+
* Find Ring Systems
|
178
|
+
* Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges.
|
179
|
+
*
|
180
|
+
********************************************/
|
181
|
+
|
182
|
+
/* initiation */
|
183
|
+
start = 0;
|
184
|
+
nNumRingSystems = 0;
|
185
|
+
u = start; /* start atom */
|
186
|
+
nDfs = 0;
|
187
|
+
nTopStackAtom =-1;
|
188
|
+
nTopRingStack =-1;
|
189
|
+
memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0]));
|
190
|
+
memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0]));
|
191
|
+
/* push the start atom on the stack */
|
192
|
+
nLowNumber[u] = nDfsNumber[u] = ++nDfs;
|
193
|
+
nStackAtom[++nTopStackAtom] = (AT_NUMB)u;
|
194
|
+
nRingStack[++nTopRingStack] = (AT_NUMB)u;
|
195
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
196
|
+
nNumConnect = nLenConnect = nMaxNumConnect = 0;
|
197
|
+
#endif
|
198
|
+
|
199
|
+
do {
|
200
|
+
/* advance */
|
201
|
+
advance_ring:
|
202
|
+
if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) ) {
|
203
|
+
cNeighNumb[i] ++;
|
204
|
+
u = (int)at[i].neighbor[j];
|
205
|
+
if ( !nDfsNumber[u] ) {
|
206
|
+
/* tree edge, 1st visit -- advance */
|
207
|
+
nStackAtom[++nTopStackAtom] = (AT_NUMB)u;
|
208
|
+
nRingStack[++nTopRingStack] = (AT_NUMB)u;
|
209
|
+
nLowNumber[u] = nDfsNumber[u] = ++nDfs;
|
210
|
+
} else
|
211
|
+
if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) {
|
212
|
+
/* back edge: u is not a predecessor of i */
|
213
|
+
if ( nDfsNumber[u] < nDfsNumber[i] ) {
|
214
|
+
/* Back edge, 1st visit: u is ancestor of i. Compare */
|
215
|
+
if ( nLowNumber[i] > nDfsNumber[u] ) {
|
216
|
+
nLowNumber[i] = nDfsNumber[u];
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
goto advance_ring;
|
221
|
+
} else {
|
222
|
+
cNeighNumb[i] = 0;
|
223
|
+
}
|
224
|
+
|
225
|
+
/* back up */
|
226
|
+
if ( nDfsNumber[i] == nLowNumber[i] ) {
|
227
|
+
/* found a ring system */
|
228
|
+
nNumRingSystems ++;
|
229
|
+
/* unwind nRingStack[] down to i */
|
230
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
231
|
+
nNumConnect = 2;
|
232
|
+
/* data structure: for each ring system nRsConnect[] contains:
|
233
|
+
* 1) nNumConnect+1 = (number of already discovered neighboring "ring systems" + 1)+1
|
234
|
+
* 2) nNumAtInRingSystem
|
235
|
+
* 3) (nNumConnect-1) numbers (IDs) of neighboring ring systems.
|
236
|
+
* BFS guarantees that each neighboring ring system is encountered only one time
|
237
|
+
* Number of all neighboring ring systems = (nNumConnect-1)+1 = nNumConnect
|
238
|
+
* (One additional ring system is where the BFS retracts from the vertex #i,
|
239
|
+
* except when i=DFS root node. In the latter case there is/are only (nNumConnect-1)
|
240
|
+
* neighboring ring system(s).
|
241
|
+
*/
|
242
|
+
#endif
|
243
|
+
/* count atoms in a ring system */
|
244
|
+
for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) {
|
245
|
+
nNumAtInRingSystem ++;
|
246
|
+
if ( i == (int)nRingStack[j] ) {
|
247
|
+
break;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
while ( nTopRingStack >= 0 ) {
|
251
|
+
j = (int)nRingStack[nTopRingStack--];
|
252
|
+
at[j].nRingSystem = (AT_NUMB)nNumRingSystems; /* ring system id */
|
253
|
+
at[j].nNumAtInRingSystem = nNumAtInRingSystem;
|
254
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
255
|
+
for ( k = 0; k < at[j].valence; k ++ ) {
|
256
|
+
if ( (nRs = at[at[j].neighbor[k]].nRingSystem) && (int)nRs != nNumRingSystems ) {
|
257
|
+
nRsConnect[nLenConnect + (nNumConnect++)] = nRs; /* adjacent ring system id */
|
258
|
+
}
|
259
|
+
}
|
260
|
+
#endif
|
261
|
+
if ( i == j ) {
|
262
|
+
/* reached atom on the top of nStackAtom[] stack */
|
263
|
+
break;
|
264
|
+
}
|
265
|
+
}
|
266
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
267
|
+
nRsConnect[nLenConnect] = nNumConnect;
|
268
|
+
nRsConnect[nLenConnect+1] = nNumAtInRingSystem;
|
269
|
+
nLenConnect += nNumConnect;
|
270
|
+
if ( nMaxNumConnect < nNumConnect ) {
|
271
|
+
/* max number of neighboring ring systems */
|
272
|
+
nMaxNumConnect = nNumConnect;
|
273
|
+
}
|
274
|
+
#endif
|
275
|
+
} else
|
276
|
+
if ( nTopStackAtom > 0 ) {
|
277
|
+
j = (int)nStackAtom[nTopStackAtom-1];
|
278
|
+
/* inherit nLowNumber */
|
279
|
+
if ( nLowNumber[j] > nLowNumber[i] ) {
|
280
|
+
nLowNumber[j] = nLowNumber[i];
|
281
|
+
}
|
282
|
+
}
|
283
|
+
} while ( --nTopStackAtom >= 0 );
|
284
|
+
|
285
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 ) /* normally disabled */
|
286
|
+
nMaxNumConnect ++;
|
287
|
+
if ( nNumRingSystems > 1 ) {
|
288
|
+
int nCol = nMaxNumConnect+1;
|
289
|
+
int nNumInSyst= nMaxNumConnect;
|
290
|
+
int nMaxNeigh = nMaxNumConnect-1;
|
291
|
+
#define T(a,b) tree[(a)*nCol+b]
|
292
|
+
if ( tree = (AT_NUMB *)inchi_calloc( nCol * (nNumRingSystems+1), sizeof(tree[0])) ) {
|
293
|
+
int len, neigh;
|
294
|
+
/* reuse previous allocations */
|
295
|
+
AT_NUMB *nNumVisitedNeighbors = nStackAtom;
|
296
|
+
AT_NUMB *nDistanceFromTerminal = nRingStack;
|
297
|
+
AT_NUMB *nCurrActiveRingSystem = nDfsNumber;
|
298
|
+
AT_NUMB *nNextActiveRingSystem = nLowNumber;
|
299
|
+
int nNumCurrActiveRingSystems, nNumNextActiveRingSystems, pass;
|
300
|
+
/* build a "condensation graph (actually, a tree)" in which
|
301
|
+
* each vertex corresponds to a ring system T(row, col) = T(ring syst, neighbors)
|
302
|
+
* Number of rows = column length = max. number of ring system neighbors + 2
|
303
|
+
* Number of cols = row length = number of ring systems + 1
|
304
|
+
* Neighboring ring systems are contiguously stored in a row
|
305
|
+
* T(i,0) = number of neighbors, 1 <= i <= nNumRingSystems;
|
306
|
+
* T(i,k) = number of a neighboring ring system, 1 <= k <= T(i,0)
|
307
|
+
* T(i,nCol-1) = number of atoms in the system #i
|
308
|
+
*/
|
309
|
+
for ( i = 1, j = 0; len=nRsConnect[j]; i ++ ) {
|
310
|
+
T(i, nNumInSyst) = nRsConnect[j+1];
|
311
|
+
for ( k = 2; k < len; k ++ ) {
|
312
|
+
neigh = nRsConnect[j+k];
|
313
|
+
if ( T(i,0) < nMaxNeigh && T(neigh,0) < nMaxNeigh ) {
|
314
|
+
T(i,0) ++;
|
315
|
+
T(neigh,0) ++;
|
316
|
+
T(i,T(i,0)) = neigh;
|
317
|
+
T(neigh,T(neigh,0)) = i;
|
318
|
+
} else {
|
319
|
+
nNumRingSystems = CT_OVERFLOW; /* program error */ /* <BRKPT> */
|
320
|
+
goto exit_function;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
j += len;
|
324
|
+
}
|
325
|
+
/* clear memory */
|
326
|
+
memset( nNumVisitedNeighbors, 0, nNumRingSystems*sizeof(nNumVisitedNeighbors[0]) );
|
327
|
+
memset( nDistanceFromTerminal, 0, nNumRingSystems*sizeof(nDistanceFromTerminal[0]) );
|
328
|
+
memset( nCurrActiveRingSystem, 0, nNumRingSystems*sizeof(nCurrActiveRingSystem[0]) );
|
329
|
+
memset( nNextActiveRingSystem, 0, nNumRingSystems*sizeof(nNextActiveRingSystem[0]) );
|
330
|
+
nNumNextActiveRingSystems = 0;
|
331
|
+
for ( i = 0; i < nNumRingSystems; i ++ ) {
|
332
|
+
if ( 1 == T(i+1,0) ) {
|
333
|
+
nNextActiveRingSystem[i] = 1; /* number of traversed neighbors + 1 */
|
334
|
+
nDistanceFromTerminal[i] = 1;
|
335
|
+
nNumNextActiveRingSystems ++;
|
336
|
+
} else {
|
337
|
+
nNextActiveRingSystem[i] = 0;
|
338
|
+
nDistanceFromTerminal[i] = 0;
|
339
|
+
}
|
340
|
+
nNumVisitedNeighbors[i] = 0;
|
341
|
+
}
|
342
|
+
|
343
|
+
/* nCurrActiveRingSystem[i] = a sum of:
|
344
|
+
* 1) +1 if it is or was active
|
345
|
+
* 2) +(number of neighbors from which it was reached)
|
346
|
+
* 3) +1 if it was left and not active anymore
|
347
|
+
*/
|
348
|
+
pass = 0;
|
349
|
+
do {
|
350
|
+
nNumCurrActiveRingSystems = nNumNextActiveRingSystems;
|
351
|
+
nNumNextActiveRingSystems = 0;
|
352
|
+
memcpy( nCurrActiveRingSystem, nNextActiveRingSystem,
|
353
|
+
nNumRingSystems*sizeof(nNextActiveRingSystem[0]));
|
354
|
+
for ( i = 0; i < nNumRingSystems; i ++ ) {
|
355
|
+
if ( T(i+1,0) == nCurrActiveRingSystem[i] ) {
|
356
|
+
/* on the previous pass currently active ring system i+1 bas been reached
|
357
|
+
* from all neighbors except one;
|
358
|
+
* the neighbors from which it was reached have
|
359
|
+
* T(neigh,0)+1 == nCurrActiveRingSystem[i]
|
360
|
+
* this ring system has not been left yet
|
361
|
+
*/
|
362
|
+
for ( k = 1, len=T(i+1,0); k <= len; k ++ ) {
|
363
|
+
neigh = (int)T(i+1,k);
|
364
|
+
if ( T(neigh,0) >= nCurrActiveRingSystem[neigh-1] ) {
|
365
|
+
if ( 0 == pass ) {
|
366
|
+
nDistanceFromTerminal[i] = 1;
|
367
|
+
}
|
368
|
+
break;
|
369
|
+
}
|
370
|
+
}
|
371
|
+
if ( k <= len ) {
|
372
|
+
/* neigh was not reached from at least 2 neighbors
|
373
|
+
* walk along -R- chain (T(neigh,0)==2) up to
|
374
|
+
* 1) a terminal system, not including it or
|
375
|
+
* 2) a branching point.
|
376
|
+
*
|
377
|
+
* pass = 0: started from terminal systems:
|
378
|
+
* reach the branching point.
|
379
|
+
* If chain system next to a terminal system has already been reached
|
380
|
+
* then walk along it according to Note below
|
381
|
+
*
|
382
|
+
* pass > 0: started from branching points
|
383
|
+
* 2a) If the branching point has not been reached from 2 or more neighbors,
|
384
|
+
* then include it
|
385
|
+
* 2b) If the branching point has not been reached from 1 neighbor only,
|
386
|
+
* then do not include it: it will be a starting point later
|
387
|
+
* Note: if a chain atom already has nDistanceFromTerminal[i] > 0, then
|
388
|
+
* the last atom should be the one such that
|
389
|
+
* its nDistanceFromTerminal[]+1>= nDistanceFromTerminal[] of the
|
390
|
+
* next in the chain
|
391
|
+
*/
|
392
|
+
int bOk = 0;
|
393
|
+
k = i+1; /* starting point */
|
394
|
+
if ( 0 == pass && T(k,nNumInSyst) > 1 ) {
|
395
|
+
nNumNextActiveRingSystems ++; /* request next pass */
|
396
|
+
continue; /* stop a the terminal ring system */
|
397
|
+
}
|
398
|
+
while( 2 == T(neigh,0) ) {
|
399
|
+
/* walk along a chain */
|
400
|
+
if ( !nNextActiveRingSystem[neigh-1] ) {
|
401
|
+
nNextActiveRingSystem[neigh-1] = 1; /* make neighbor active */
|
402
|
+
} else
|
403
|
+
if ( nDistanceFromTerminal[k-1]+1 <= nDistanceFromTerminal[neigh-1] ) {
|
404
|
+
/* walking along the chain; already have had a walk */
|
405
|
+
/* in the opposite direction at this pass */
|
406
|
+
} else {
|
407
|
+
/* k is the last; neigh (it is a bridge -X-) has not been reached */
|
408
|
+
bOk = 1;
|
409
|
+
break;
|
410
|
+
}
|
411
|
+
nNextActiveRingSystem[k-1] ++; /* leave system k */
|
412
|
+
if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) {
|
413
|
+
nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */
|
414
|
+
}
|
415
|
+
nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1;
|
416
|
+
j = (T(neigh,1)==k)? 2:1;
|
417
|
+
k = neigh;
|
418
|
+
neigh = T(k,j); /* next in the chain */
|
419
|
+
nNumNextActiveRingSystems ++;
|
420
|
+
if ( T(k,nNumInSyst) > 1 ) {
|
421
|
+
bOk = 1;
|
422
|
+
break; /* stop on a ring system */
|
423
|
+
}
|
424
|
+
}
|
425
|
+
/* neigh is a terminal or a bridge or a branching point */
|
426
|
+
if ( 2 > T(neigh,0) ) {
|
427
|
+
/* neighbor is a terminal atom */
|
428
|
+
if ( 1 < pass ) {
|
429
|
+
nNumRingSystems = CT_UNKNOWN_ERR; /* error (debug only) */ /* <BRKPT> */
|
430
|
+
goto exit_function;
|
431
|
+
}
|
432
|
+
continue;
|
433
|
+
}
|
434
|
+
if ( 2 == T(neigh,0) ) {
|
435
|
+
/* neighbor is a bridge */
|
436
|
+
continue;
|
437
|
+
}
|
438
|
+
/* neighbor is a branching point */
|
439
|
+
if ( T(neigh,0) > nCurrActiveRingSystem[neigh-1] ) {
|
440
|
+
/* move to the neigh (make neigh active): on previous pass it */
|
441
|
+
/* has not been reached from 2 or more neighbors */
|
442
|
+
if ( !nNextActiveRingSystem[neigh-1] ) {
|
443
|
+
nNextActiveRingSystem[neigh-1] = 1;
|
444
|
+
}
|
445
|
+
if ( nDistanceFromTerminal[neigh-1] < nDistanceFromTerminal[k-1]+1 ) {
|
446
|
+
nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1;
|
447
|
+
}
|
448
|
+
nNextActiveRingSystem[k-1] ++; /* leave system k */
|
449
|
+
if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) {
|
450
|
+
nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */
|
451
|
+
}
|
452
|
+
nNumNextActiveRingSystems ++;
|
453
|
+
}
|
454
|
+
}
|
455
|
+
}
|
456
|
+
}
|
457
|
+
pass ++;
|
458
|
+
} while ( nNumNextActiveRingSystems );
|
459
|
+
|
460
|
+
for ( i = 0; i < num_atoms; i ++ ) {
|
461
|
+
at[i].nDistanceFromTerminal = nDistanceFromTerminal[(int)at[i].nRingSystem-1];
|
462
|
+
}
|
463
|
+
|
464
|
+
inchi_free( tree );
|
465
|
+
tree = NULL;
|
466
|
+
#undef T
|
467
|
+
} else {
|
468
|
+
nNumRingSystems = CT_OUT_OF_RAM; /* error */ /* <BRKPT> */
|
469
|
+
goto exit_function;
|
470
|
+
}
|
471
|
+
}
|
472
|
+
#endif
|
473
|
+
|
474
|
+
|
475
|
+
exit_function:
|
476
|
+
if ( nStackAtom )
|
477
|
+
inchi_free( nStackAtom );
|
478
|
+
if ( nRingStack )
|
479
|
+
inchi_free( nRingStack );
|
480
|
+
if ( nDfsNumber )
|
481
|
+
inchi_free( nDfsNumber );
|
482
|
+
if ( nLowNumber )
|
483
|
+
inchi_free( nLowNumber );
|
484
|
+
if ( cNeighNumb )
|
485
|
+
inchi_free( cNeighNumb );
|
486
|
+
#if( FIND_RINS_SYSTEMS_DISTANCES == 1 )
|
487
|
+
if ( nRsConnect )
|
488
|
+
inchi_free( nRsConnect );
|
489
|
+
if ( tree )
|
490
|
+
inchi_free( tree );
|
491
|
+
#endif
|
492
|
+
return nNumRingSystems;
|
493
|
+
}
|
494
|
+
|
495
|
+
|
496
|
+
#endif /* } FIND_RING_SYSTEMS */
|
497
|
+
|
498
|
+
/********************************************************************************/
|
499
|
+
/* Return value: new number of atoms > 0 or -1=out of RAM */
|
500
|
+
int remove_terminal_HDT( int num_atoms, inp_ATOM *at )
|
501
|
+
{
|
502
|
+
AT_NUMB *new_ord;
|
503
|
+
inp_ATOM *new_at;
|
504
|
+
char *p;
|
505
|
+
const static char szHDT[]="HDT";
|
506
|
+
const static int kMax = sizeof(szHDT); /* = 4 */
|
507
|
+
int ret = -1;
|
508
|
+
int num_hydrogens=0, num_H = 0; /* number of terminal H, D, T */
|
509
|
+
int i, j, k, n, m;
|
510
|
+
int val;
|
511
|
+
AT_RANK new_HydrogenAt_order[NUM_H_ISOTOPES+1];
|
512
|
+
AT_RANK new_OtherNeigh_order[MAXVAL];
|
513
|
+
S_CHAR old_trans[MAX_NUM_STEREO_BONDS];
|
514
|
+
|
515
|
+
int num_OtherNeigh, num_HydrogenAt;
|
516
|
+
|
517
|
+
new_ord=(AT_NUMB *)inchi_calloc(num_atoms, sizeof(new_ord[0])); /* changed malloc to calloc 9-11-2003 */
|
518
|
+
new_at =(inp_ATOM *)inchi_malloc(sizeof(new_at[0]) *num_atoms);
|
519
|
+
if (!new_ord || !new_at)
|
520
|
+
goto exit_function;
|
521
|
+
/* move H. D, T to the end of the list of atoms */
|
522
|
+
for ( i = 0; i < num_atoms; i ++ ) {
|
523
|
+
at[i].component = i; /* temporarily save original numbering */
|
524
|
+
/* get k = temp. hydrogen isotope/non-hydrogen atom type: */
|
525
|
+
/* k=0:H, k=2:D, k=3:T, k=4=kMax: not a hydrogen */
|
526
|
+
k = at[i].elname[1]? kMax : (p=strchr(szHDT, at[i].elname[0]))? p-szHDT : kMax;
|
527
|
+
/* set hydrogen isotope atw differences */
|
528
|
+
/* Notes: k-value of isotopic H is incremented to correct iso_atw_diff value later. */
|
529
|
+
/* 1H isotope cannot be detected here. */
|
530
|
+
if ( k == ATW_H || k == ATW_H+1 ) { /* D or T, k = 1 or 2 */
|
531
|
+
at[i].elname[0] = 'H'; /* hydrogen isotope */
|
532
|
+
at[i].iso_atw_diff = ++k; /* increment k to make k = iso_atw_diff ( 2 for D, 3 for T ) */
|
533
|
+
}
|
534
|
+
num_H += (k != kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && !NUMH(at,i) );
|
535
|
+
}
|
536
|
+
|
537
|
+
/* special case: HD, HT, DT, HH: the only non-isotopic H or
|
538
|
+
* the lightest isotopic H out of two is removed
|
539
|
+
* to become implicit (make the heavier H the "central atom").
|
540
|
+
* Note: This must be consistent with mol_to_atom()
|
541
|
+
* treatment of isotopic Hn aliases.
|
542
|
+
*/
|
543
|
+
if ( 2 == num_H && 2 == num_atoms && !NUMH(at,0) && !NUMH(at,1) ) {
|
544
|
+
|
545
|
+
if ( at[0].iso_atw_diff >= at[1].iso_atw_diff ) {
|
546
|
+
new_ord[0] = 0;
|
547
|
+
new_ord[1] = 1;
|
548
|
+
} else {
|
549
|
+
new_ord[0] = 1;
|
550
|
+
new_ord[1] = 0;
|
551
|
+
}
|
552
|
+
if ( at[new_ord[1]].charge ) {
|
553
|
+
at[new_ord[0]].charge += at[new_ord[1]].charge;
|
554
|
+
at[new_ord[1]].charge = 0;
|
555
|
+
}
|
556
|
+
new_at[new_ord[0]] = at[0];
|
557
|
+
new_at[new_ord[1]] = at[1];
|
558
|
+
num_hydrogens = 1;
|
559
|
+
|
560
|
+
} else {
|
561
|
+
/* general case except H-H */
|
562
|
+
for ( i = 0; i < num_atoms; i ++ ) {
|
563
|
+
k = (at[i].elname[1] || NUMH(at,i))? kMax : (at[i].elname[0]=='H')? at[i].iso_atw_diff : kMax;
|
564
|
+
if ( k < kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 &&
|
565
|
+
/* the order of comparison is important */
|
566
|
+
((n=(int)at[i].neighbor[0]) > i /* at[n] has not been encountered yet*/ ||
|
567
|
+
(int)new_ord[n] < num_atoms - num_hydrogens) /* at[n] might have been encountered; it has not been moved */ ) {
|
568
|
+
/* found an explicit terminal hydrogen */
|
569
|
+
num_hydrogens ++;
|
570
|
+
if ( k==0 && ATW_H <= at[i].iso_atw_diff && at[i].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) {
|
571
|
+
k = at[i].iso_atw_diff; /* H isotope has already been marked above or elsewhere */
|
572
|
+
}
|
573
|
+
if ( at[i].charge ) { /* transfer charge from the hydrogen */
|
574
|
+
at[n].charge += at[i].charge;
|
575
|
+
at[i].charge = 0;
|
576
|
+
}
|
577
|
+
new_ord[i] = num_atoms - num_hydrogens; /* move hydrogens to the end of the list */
|
578
|
+
} else {
|
579
|
+
new_ord[i] = i - num_hydrogens; /* adjust non-hydrogens positions */
|
580
|
+
}
|
581
|
+
new_at[new_ord[i]] = at[i]; /* copy atoms to their new positions */
|
582
|
+
}
|
583
|
+
}
|
584
|
+
|
585
|
+
if ( num_hydrogens ) {
|
586
|
+
int num_others = num_atoms-num_hydrogens; /* atoms which are not terminal H, D, T */
|
587
|
+
if ( num_hydrogens > 1 ) {
|
588
|
+
/* sort hydrogen isotopes in ascending order, */
|
589
|
+
/* orig, numbers being the secondary sorting key */
|
590
|
+
qsort( new_at+num_others, num_hydrogens, sizeof(new_at[0]), cmp_iso_atw_diff_component_no );
|
591
|
+
}
|
592
|
+
/* save new numbering of hydrogen atoms using temporarily saved orig numbering */
|
593
|
+
for ( i = num_others; i < num_atoms; i ++ ) {
|
594
|
+
new_ord[(int)new_at[i].component] = i;
|
595
|
+
}
|
596
|
+
|
597
|
+
/* renumber neighbors according to new_ord[] and detach terminal hydrogens */
|
598
|
+
for ( i = 0; i < num_others; i++ ) {
|
599
|
+
memset( new_HydrogenAt_order, 0, sizeof(new_HydrogenAt_order) );
|
600
|
+
memset( new_OtherNeigh_order, 0, sizeof(new_OtherNeigh_order) );
|
601
|
+
num_OtherNeigh = 0;
|
602
|
+
num_HydrogenAt = 0;
|
603
|
+
num_H = 0;
|
604
|
+
|
605
|
+
for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) {
|
606
|
+
old_trans[m] = 2 - (new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2;
|
607
|
+
}
|
608
|
+
|
609
|
+
for ( k = j = val = 0; k < new_at[i].valence; k++ ) {
|
610
|
+
if ( num_others <= ( n = new_ord[new_at[i].neighbor[k]] ) ) {
|
611
|
+
/* discovered neighbor = disconnected explicit hydrogen
|
612
|
+
* i = new atom new_at[i] ordering number
|
613
|
+
* n = new number of the explicit H
|
614
|
+
* k = ordering number of the explicit H in new_at[i] adjacency list
|
615
|
+
*/
|
616
|
+
if ( 0 < new_at[n].iso_atw_diff && new_at[n].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) {
|
617
|
+
/* make explicit isotopic H implicit */
|
618
|
+
new_at[i].num_iso_H[new_at[n].iso_atw_diff-1] ++; /* isotopic H */
|
619
|
+
num_HydrogenAt += !new_HydrogenAt_order[new_at[n].iso_atw_diff];
|
620
|
+
new_HydrogenAt_order[new_at[n].iso_atw_diff] = k+1;
|
621
|
+
} else {
|
622
|
+
/* make explicit non-isotopic H implicit */
|
623
|
+
new_at[i].num_H ++; /* non-isotopic H */
|
624
|
+
num_HydrogenAt += !num_H;
|
625
|
+
num_H ++;
|
626
|
+
new_HydrogenAt_order[0] = k+1;
|
627
|
+
}
|
628
|
+
/* decrement chem. bonds valence because one bond is removed */
|
629
|
+
new_at[i].chem_bonds_valence = inchi_max( 0, new_at[i].chem_bonds_valence-1 );
|
630
|
+
new_at[n].neighbor[0] = i; /* update removed hydrogen neighbor number */
|
631
|
+
if ( new_at[i].sb_parity[0] ) {
|
632
|
+
/* if the removed H is an SB neighbor then mark it as removed */
|
633
|
+
for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) {
|
634
|
+
if ( k == (int)new_at[i].sn_ord[m] ) {
|
635
|
+
new_at[i].sn_ord[m] = -(new_at[n].iso_atw_diff+1);
|
636
|
+
/* means the SB neighbor has been removed; (-4)=H, (-3)=1H, (-2)=D, (-1)=T */
|
637
|
+
}
|
638
|
+
}
|
639
|
+
}
|
640
|
+
} else {
|
641
|
+
/* discovered a regular (not an explicit H) neighbor */
|
642
|
+
if ( new_at[i].sb_parity[0] ) {
|
643
|
+
if ( num_OtherNeigh < MAX_NUM_STEREO_BONDS ) {
|
644
|
+
new_OtherNeigh_order[num_OtherNeigh] = k+1;
|
645
|
+
}
|
646
|
+
num_OtherNeigh ++; /* increment outside of if() to detect overflow */
|
647
|
+
if ( val != k ) {
|
648
|
+
/* store new stereobond and sb-neighbor ordering numbers */
|
649
|
+
for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) {
|
650
|
+
if ( k == (int)new_at[i].sb_ord[m] )
|
651
|
+
new_at[i].sb_ord[m] = val;
|
652
|
+
else
|
653
|
+
if ( k == (int)new_at[i].sn_ord[m] )
|
654
|
+
new_at[i].sn_ord[m] = val;
|
655
|
+
}
|
656
|
+
}
|
657
|
+
}
|
658
|
+
new_at[i].neighbor[val] = new_ord[new_at[i].neighbor[k]];
|
659
|
+
new_at[i].bond_type[val] = new_at[i].bond_type[k];
|
660
|
+
new_at[i].bond_stereo[val] = new_at[i].bond_stereo[k];
|
661
|
+
val ++;
|
662
|
+
}
|
663
|
+
}
|
664
|
+
if ( new_at[i].valence > val && new_at[i].sb_parity[0] ) {
|
665
|
+
if ( num_HydrogenAt == new_at[i].valence - val && num_HydrogenAt + num_OtherNeigh <= MAXVAL ) {
|
666
|
+
/* recalculate parity so that it would describe neighbor sequence H,1H,D,T,neigh[0],neigh[1]... */
|
667
|
+
memmove( new_OtherNeigh_order + num_HydrogenAt, new_OtherNeigh_order, num_OtherNeigh*sizeof(new_OtherNeigh_order[0]));
|
668
|
+
for ( k = 0, j = 1; k <= NUM_H_ISOTOPES; k ++ ) {
|
669
|
+
if ( new_HydrogenAt_order[k] ) {
|
670
|
+
new_OtherNeigh_order[num_HydrogenAt - j] = new_HydrogenAt_order[k];
|
671
|
+
for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) {
|
672
|
+
if ( (int)new_at[i].sn_ord[m] == -(k+1) ) {
|
673
|
+
new_at[i].sn_ord[m] = -j;
|
674
|
+
/* negative means explicit H isotope ord are
|
675
|
+
(contiguously) in front of the adjacency list */
|
676
|
+
}
|
677
|
+
}
|
678
|
+
j ++;
|
679
|
+
}
|
680
|
+
}
|
681
|
+
/* at this point new_OtherNeigh_order[] contains
|
682
|
+
incremented old ordering numbers in new order */
|
683
|
+
k = insertions_sort_AT_RANK( new_OtherNeigh_order, num_HydrogenAt + num_OtherNeigh );
|
684
|
+
k = k%2; /* seems to be of no use */
|
685
|
+
/*if ( k ) {*/
|
686
|
+
/*
|
687
|
+
for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) {
|
688
|
+
if ( PARITY_WELL_DEF(new_at[i].sb_parity[m]) ) {
|
689
|
+
if ( old_trans[m] != 2 - (4 + new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2 ) {
|
690
|
+
new_at[i].sb_parity[m] = 3 - new_at[i].sb_parity[m];
|
691
|
+
}
|
692
|
+
}
|
693
|
+
}
|
694
|
+
*/
|
695
|
+
/*}*/
|
696
|
+
}
|
697
|
+
#ifdef _DEBUG
|
698
|
+
else {
|
699
|
+
/* error */
|
700
|
+
int stop = 1;
|
701
|
+
}
|
702
|
+
#endif
|
703
|
+
}
|
704
|
+
new_at[i].valence = val;
|
705
|
+
}
|
706
|
+
memcpy( at, new_at, sizeof(at[0])*num_atoms );
|
707
|
+
ret = num_others;
|
708
|
+
} else {
|
709
|
+
ret = num_atoms;
|
710
|
+
}
|
711
|
+
exit_function:
|
712
|
+
if ( new_ord )
|
713
|
+
inchi_free ( new_ord );
|
714
|
+
if ( new_at )
|
715
|
+
inchi_free ( new_at );
|
716
|
+
return ret;
|
717
|
+
}
|
718
|
+
/************************************************************************/
|
719
|
+
int add_DT_to_num_H( int num_atoms, inp_ATOM *at )
|
720
|
+
/* assume num_1H, num_D and num_T are not included in num_H */
|
721
|
+
{
|
722
|
+
int i, j;
|
723
|
+
for ( i = 0; i < num_atoms; i ++ ) {
|
724
|
+
for ( j = 0; j < NUM_H_ISOTOPES; j ++ )
|
725
|
+
at[i].num_H += at[i].num_iso_H[j];
|
726
|
+
}
|
727
|
+
return 0;
|
728
|
+
}
|
729
|
+
/***************************************************************/
|
730
|
+
/* not used ---
|
731
|
+
int FixAromaticOxygenAndSulfur( inp_ATOM *atom )
|
732
|
+
{
|
733
|
+
if ( !atom->elname[1] && (atom->elname[0]=='O' || atom->elname[0]=='S') &&
|
734
|
+
atom->valence==2 && !atom->charge && !atom->radical &&
|
735
|
+
atom->bond_type[0] + atom->bond_type[1] == 3 ) {
|
736
|
+
atom->charge = 1;
|
737
|
+
return 1; // fixed
|
738
|
+
}
|
739
|
+
return 0;
|
740
|
+
}
|
741
|
+
*/
|