rino 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/Rakefile +1 -1
  2. data/ext/extconf.rb +1 -24
  3. data/ext/libinchi.so +0 -0
  4. data/ext/src/aux2atom.h +120 -39
  5. data/ext/src/comdef.h +3 -3
  6. data/ext/src/dispstru.c +2547 -0
  7. data/ext/src/dispstru.h +73 -0
  8. data/ext/src/extr_ct.h +5 -2
  9. data/ext/src/ichi.h +27 -11
  10. data/ext/src/ichi_bns.c +1800 -254
  11. data/ext/src/ichi_bns.h +205 -4
  12. data/ext/src/ichican2.c +197 -86
  13. data/ext/src/ichicano.c +8 -13
  14. data/ext/src/ichicano.h +2 -2
  15. data/ext/src/ichicans.c +11 -6
  16. data/ext/src/ichicant.h +2 -2
  17. data/ext/src/ichicomn.h +2 -2
  18. data/ext/src/ichicomp.h +19 -4
  19. data/ext/src/ichidrp.h +9 -5
  20. data/ext/src/ichierr.h +5 -3
  21. data/ext/src/ichiisot.c +2 -2
  22. data/ext/src/ichimain.c +461 -0
  23. data/ext/src/ichimain.h +23 -15
  24. data/ext/src/ichimak2.c +6 -6
  25. data/ext/src/ichimake.c +843 -42
  26. data/ext/src/ichimake.h +4 -2
  27. data/ext/src/ichimap1.c +5 -5
  28. data/ext/src/ichimap2.c +2 -2
  29. data/ext/src/ichimap4.c +34 -21
  30. data/ext/src/ichinorm.c +11 -5
  31. data/ext/src/ichinorm.h +3 -2
  32. data/ext/src/ichiparm.c +2 -2
  33. data/ext/src/ichiparm.h +232 -30
  34. data/ext/src/ichiprt1.c +35 -11
  35. data/ext/src/ichiprt2.c +78 -7
  36. data/ext/src/ichiprt3.c +300 -120
  37. data/ext/src/ichiqueu.c +17 -2
  38. data/ext/src/ichiread.c +6932 -0
  39. data/ext/src/ichiring.c +3 -2
  40. data/ext/src/ichiring.h +2 -2
  41. data/ext/src/ichirvr1.c +4891 -0
  42. data/ext/src/ichirvr2.c +6344 -0
  43. data/ext/src/ichirvr3.c +5499 -0
  44. data/ext/src/ichirvr4.c +3177 -0
  45. data/ext/src/ichirvr5.c +1166 -0
  46. data/ext/src/ichirvr6.c +1287 -0
  47. data/ext/src/ichirvr7.c +2319 -0
  48. data/ext/src/ichirvrs.h +882 -0
  49. data/ext/src/ichisize.h +2 -2
  50. data/ext/src/ichisort.c +5 -5
  51. data/ext/src/ichister.c +281 -86
  52. data/ext/src/ichister.h +9 -3
  53. data/ext/src/ichitaut.c +208 -9
  54. data/ext/src/ichitaut.h +13 -11
  55. data/ext/src/ichitime.h +16 -2
  56. data/ext/src/inchicmp.h +107 -0
  57. data/ext/src/inpdef.h +6 -3
  58. data/ext/src/libinchi_wrap.c +912 -0
  59. data/ext/src/lreadmol.h +34 -31
  60. data/ext/src/mode.h +244 -7
  61. data/ext/src/mol2atom.c +1060 -0
  62. data/ext/src/mol2atom.h +31 -0
  63. data/ext/src/readinch.c +239 -0
  64. data/ext/src/readmol.c +28 -0
  65. data/ext/src/{e_readmol.h → readmol.h} +7 -9
  66. data/ext/src/runichi.c +251 -177
  67. data/ext/src/strutil.c +444 -238
  68. data/ext/src/strutil.h +150 -11
  69. data/ext/src/util.c +176 -118
  70. data/ext/src/util.h +15 -3
  71. data/lib/rino.rb +71 -3
  72. data/test/test.rb +33 -4
  73. metadata +22 -34
  74. data/ext/ruby_inchi_main.so +0 -0
  75. data/ext/src/e_0dstereo.c +0 -3014
  76. data/ext/src/e_0dstereo.h +0 -31
  77. data/ext/src/e_comdef.h +0 -57
  78. data/ext/src/e_ctl_data.h +0 -147
  79. data/ext/src/e_ichi_io.c +0 -498
  80. data/ext/src/e_ichi_io.h +0 -40
  81. data/ext/src/e_ichi_parms.c +0 -37
  82. data/ext/src/e_ichi_parms.h +0 -41
  83. data/ext/src/e_ichicomp.h +0 -50
  84. data/ext/src/e_ichierr.h +0 -40
  85. data/ext/src/e_ichimain.c +0 -593
  86. data/ext/src/e_ichisize.h +0 -43
  87. data/ext/src/e_inchi_atom.c +0 -75
  88. data/ext/src/e_inchi_atom.h +0 -33
  89. data/ext/src/e_inpdef.h +0 -41
  90. data/ext/src/e_mode.h +0 -706
  91. data/ext/src/e_mol2atom.c +0 -649
  92. data/ext/src/e_readinch.c +0 -58
  93. data/ext/src/e_readmol.c +0 -54
  94. data/ext/src/e_readstru.c +0 -251
  95. data/ext/src/e_readstru.h +0 -33
  96. data/ext/src/e_util.c +0 -284
  97. data/ext/src/e_util.h +0 -61
  98. data/ext/src/ichilnct.c +0 -286
  99. data/ext/src/inchi_api.h +0 -670
  100. data/ext/src/inchi_dll.c +0 -1480
  101. data/ext/src/inchi_dll.h +0 -34
  102. data/ext/src/inchi_dll_main.c +0 -23
  103. data/ext/src/inchi_dll_main.h +0 -31
  104. data/ext/src/ruby_inchi_main.c +0 -558
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -38,10 +38,10 @@ void swap ( char *a, char *b, size_t width )
38
38
  /* Sort by insertions */
39
39
  int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) )
40
40
  {
41
- char *i, *j, *pk;
41
+ char *i, *j, *pk = (char*)base;
42
42
  int num_trans = 0;
43
43
  size_t k;
44
- for( k=1, pk = (char*)base; k < num; k++, pk += width ) {
44
+ for( k=1; k < num; k++, pk += width ) {
45
45
  for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width ) {
46
46
  swap( i, j, width );
47
47
  num_trans ++;
@@ -203,7 +203,7 @@ int CompAtomInvariants2( const void* a1, const void* a2 )
203
203
  return ret;
204
204
  }
205
205
  /**********************************************************************************/
206
- /* Compare two elements lexic�graphically */
206
+ /* Compare two elements lexicographically */
207
207
  int CompChemElemLex( const void *a1, const void *a2 )
208
208
  {
209
209
  return memcmp( a1, a2, 2);
@@ -2,8 +2,8 @@
2
2
  * International Union of Pure and Applied Chemistry (IUPAC)
3
3
  * International Chemical Identifier (InChI)
4
4
  * Version 1
5
- * Software version 1.00
6
- * April 13, 2005
5
+ * Software version 1.01
6
+ * July 21, 2006
7
7
  * Developed at NIST
8
8
  */
9
9
 
@@ -51,43 +51,41 @@
51
51
  #define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */
52
52
  #endif
53
53
  /* local prototypes */
54
- int save_a_stereo_bond( int z_prod, int result_action,
54
+ static int save_a_stereo_bond( int z_prod, int result_action,
55
55
  int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1,
56
56
  int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 );
57
- double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo );
58
- double len3( const double c[] );
59
- double len2( const double c[] );
60
- double* diff3( const double a[], const double b[], double result[] );
61
- double* add3( const double a[], const double b[], double result[] );
62
- double* mult3( const double a[], double b, double result[] );
63
- double* copy3( const double a[], double result[] );
64
- double* change_sign3( const double a[], double result[] );
65
- double dot_prod3( const double a[], const double b[] );
66
- int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] );
67
- double* cross_prod3( const double a[], const double b[], double result[] );
68
- double triple_prod( double a[], double b[], double c[], double *sine_value );
69
- double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine);
70
- double sp3_triple_prod_and_min_abs_sine(double at_coord[][3], double central_at_coord[], double *min_sine, int *bAmbiguous);
71
- int are_3_vect_in_one_plane( double at_coord[][3], double min_sine);
72
- int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1,
73
- int at_2, int i_next_at_2, S_CHAR *z_dir2 );
74
-
75
- int CompDble( const void *a1, const void *a2 );
76
- int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor );
77
- double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous);
78
- int are_4at_in_one_plane( double at_coord[][3], double min_sine);
79
- int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds );
80
- int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical );
81
- int bIsSuitableHeteroInpAtom( inp_ATOM *at );
82
- int bIsOxide( inp_ATOM *at, int cur_at );
83
- int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, int bPointedEdgeStereo );
84
- int get_allowed_stereo_bond_type( int bond_type );
85
- int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode );
86
- int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic );
87
- int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H,
88
- INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK min_sb_ring_size, int bPointedEdgeStereo );
89
- int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at );
90
- int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, int bPointedEdgeStereo );
57
+ static double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo );
58
+ static double len3( const double c[] );
59
+ static double len2( const double c[] );
60
+ static double* diff3( const double a[], const double b[], double result[] );
61
+ static double* add3( const double a[], const double b[], double result[] );
62
+ static double* mult3( const double a[], double b, double result[] );
63
+ static double* copy3( const double a[], double result[] );
64
+ static double* change_sign3( const double a[], double result[] );
65
+ static double dot_prod3( const double a[], const double b[] );
66
+ static int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] );
67
+ static double* cross_prod3( const double a[], const double b[], double result[] );
68
+ static double triple_prod( double a[], double b[], double c[], double *sine_value );
69
+ static double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine);
70
+ static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine);
71
+ static int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1,
72
+ int at_2, int i_next_at_2, S_CHAR *z_dir2 );
73
+
74
+ static int CompDble( const void *a1, const void *a2 );
75
+ static int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase );
76
+ static double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous);
77
+ static int are_4at_in_one_plane( double at_coord[][3], double min_sine);
78
+ static int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds );
79
+ static int bIsSuitableHeteroInpAtom( inp_ATOM *at );
80
+ static int bIsOxide( inp_ATOM *at, int cur_at );
81
+ static int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, int bPointedEdgeStereo );
82
+ static int get_allowed_stereo_bond_type( int bond_type );
83
+ static int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode );
84
+ static int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic );
85
+ static int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H,
86
+ INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK min_sb_ring_size, int bPointedEdgeStereo );
87
+ static int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo );
88
+ static int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, int bPointedEdgeStereo );
91
89
  /*
92
90
  int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H,
93
91
  int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo );
@@ -293,7 +291,7 @@ int CompDble( const void *a1, const void *a2 )
293
291
  #define T2D_OKAY 1
294
292
  #define T2D_WARN 2
295
293
  #define T2D_UNDF 4
296
- int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor )
294
+ int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase )
297
295
  {
298
296
  const double one_pi = 2.0*atan2(1.0 /* y */, 0.0 /* x */);
299
297
  const double two_pi = 2.0*one_pi;
@@ -367,6 +365,83 @@ int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor )
367
365
  }
368
366
  }
369
367
  }
368
+ #if( FIX_2D_STEREO_BORDER_CASE == 1 )
369
+ /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */
370
+ /* have identical angles. In this case switch their order to enlarge the Up sequence */
371
+ #define ZERO_ANGLE 0.000001
372
+ if ( nNumNeigh - len_Up >= 2 ) {
373
+ int next1, next2;
374
+ for ( i = 1; i < nNumNeigh - len_Up; i ++ ) {
375
+ next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */
376
+ if ( nBondType[nBondOrder[next2]] > 0 ) {
377
+ next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */
378
+ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]];
379
+ if ( fabs(dAngle) < ZERO_ANGLE ) {
380
+ swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) );
381
+ len_Up ++;
382
+ break;
383
+ }
384
+ }
385
+ }
386
+ }
387
+ /* check whether the not-Up bond (located before the found first-Up) has */
388
+ /* same angle as the Up bond that precedes this not-Up bond */
389
+ if ( nNumNeigh - len_Up >= 2 ) {
390
+ int next1, next2;
391
+ for ( i = 1; i < nNumNeigh - len_Up; i ++ ) {
392
+ next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */
393
+ if ( nBondType[nBondOrder[next2]] > 0 ) {
394
+ next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */
395
+ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]];
396
+ if ( fabs(dAngle) < ZERO_ANGLE ) {
397
+ swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) );
398
+ first_Up = next1;
399
+ len_Up ++;
400
+ break;
401
+ }
402
+ }
403
+ }
404
+ }
405
+ #else
406
+ if ( bFix2DstereoBorderCase ) {
407
+ /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */
408
+ /* have identical angles. In this case switch their order to enlarge the Up sequence */
409
+ #define ZERO_ANGLE 0.000001
410
+ if ( nNumNeigh - len_Up >= 2 ) {
411
+ int next1, next2;
412
+ for ( i = 1; i < nNumNeigh - len_Up; i ++ ) {
413
+ next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */
414
+ if ( nBondType[nBondOrder[next2]] > 0 ) {
415
+ next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */
416
+ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]];
417
+ if ( fabs(dAngle) < ZERO_ANGLE ) {
418
+ swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) );
419
+ len_Up ++;
420
+ break;
421
+ }
422
+ }
423
+ }
424
+ }
425
+ /* check whether the not-Up bond (located before the found first-Up) has */
426
+ /* same angle as the Up bond that precedes this not-Up bond */
427
+ if ( nNumNeigh - len_Up >= 2 ) {
428
+ int next1, next2;
429
+ for ( i = 1; i < nNumNeigh - len_Up; i ++ ) {
430
+ next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */
431
+ if ( nBondType[nBondOrder[next2]] > 0 ) {
432
+ next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */
433
+ dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]];
434
+ if ( fabs(dAngle) < ZERO_ANGLE ) {
435
+ swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) );
436
+ first_Up = next1;
437
+ len_Up ++;
438
+ break;
439
+ }
440
+ }
441
+ }
442
+ }
443
+ }
444
+ #endif
370
445
  /* Turn all the bonds around the center so that */
371
446
  /* the 1st Up bond has zero radian direction */
372
447
  dAlpha = dBondDirection[nBondOrder[first_Up]];
@@ -517,11 +592,51 @@ int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor )
517
592
  break;
518
593
  /* -------------------------- 2 Up ------------ */
519
594
  case 2:
520
- if ( cur_len_Up == 1 ) {
595
+ #if( FIX_2D_STEREO_BORDER_CASE == 1 )
596
+ if ( len_Up == 1 ) {
521
597
  ret = T2D_OKAY;
522
598
  } else {
523
- ret = (T2D_UNDF | T2D_WARN);
599
+ dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] -
600
+ dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]];
601
+ dAngle = fabs(two_pi - dAngle);
602
+ dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] -
603
+ dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]];
604
+ dAlpha = fabs(dAlpha);
605
+ if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE ||
606
+ dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) {
607
+ ret = (T2D_OKAY | T2D_WARN);
608
+ } else {
609
+ ret = (T2D_UNDF | T2D_WARN);
610
+ }
524
611
  }
612
+ #else
613
+ if ( bFix2DstereoBorderCase ) {
614
+ /* bug fix */
615
+ if ( len_Up == 1 ) {
616
+ ret = T2D_OKAY;
617
+ } else {
618
+ dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] -
619
+ dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]];
620
+ dAngle = fabs(two_pi - dAngle);
621
+ dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] -
622
+ dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]];
623
+ dAlpha = fabs(dAlpha);
624
+ if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE ||
625
+ dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) {
626
+ ret = (T2D_OKAY | T2D_WARN);
627
+ } else {
628
+ ret = (T2D_UNDF | T2D_WARN);
629
+ }
630
+ }
631
+ } else {
632
+ /* original InChI v. 1 bug */
633
+ if ( cur_len_Up == 1 ) {
634
+ ret = T2D_OKAY;
635
+ } else {
636
+ ret = (T2D_UNDF | T2D_WARN);
637
+ }
638
+ }
639
+ #endif
525
640
  break;
526
641
  /* -------------------------- 3 Up ------------ */
527
642
  case 3:
@@ -945,7 +1060,7 @@ int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, in
945
1060
  return 1;
946
1061
  }
947
1062
  /********************************************************************************************/
948
- int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
1063
+ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo )
949
1064
  {
950
1065
 
951
1066
  /*************************************************************************************
@@ -955,6 +1070,7 @@ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
955
1070
  * to make it easier to read
956
1071
  *
957
1072
  * --------- 4 single bonds stereocenters -------
1073
+ * 0 1 2 3 4 5
958
1074
  *
959
1075
  * | | | | | |
960
1076
  * -C- -Si- -Ge- -Sn- >As[+] >B[-]
@@ -968,6 +1084,7 @@ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
968
1084
  #define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0,
969
1085
  /*
970
1086
  * --------------- S, Se stereocenters ----------
1087
+ * 6 7 8 9 10 11 12 13
971
1088
  *
972
1089
  * | | || | | ||
973
1090
  * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+]
@@ -980,21 +1097,24 @@ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
980
1097
  #define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0,
981
1098
  #define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3,
982
1099
  /*
983
- * ------------------ N, P stereocenters --------
984
- *
985
- * X---Y
986
- * | | \ / | |
987
- * =N- >N[+] N >P[+] =P-
988
- * | | | | |
989
- */
990
- #define SZELEM3 "N\000","N\000","N\000","P\000","P\000",
991
- #define CCHARGE3 0, 1, 0, 1, 0,
992
- #define CNUMBONDSANDH3 4, 4, 3, 4, 4,
993
- #define CCHEMVALENCEH3 5, 4, 3, 4, 5,
994
- #define CHAS3MEMBRING3 0, 0, 1, 0, 0,
995
- #define CREQUIRDNEIGH3 3, 3, 1, 3, 3,
996
-
997
-
1100
+ * ------------------ N, P stereocenters -----------------
1101
+ * 14 15 16 17 18 19 20
1102
+ *
1103
+ * Phosphine Arsine
1104
+ * X---Y
1105
+ * | | \ / | | \ / \ /
1106
+ * =N- >N[+] N >P[+] =P- P As
1107
+ * | | | | | | |
1108
+ */
1109
+ #define SZELEM3 "N\000","N\000","N\000","P\000","P\000","P\000", "As",
1110
+ #define CCHARGE3 0, 1, 0, 1, 0, 0, 0,
1111
+ #define CNUMBONDSANDH3 4, 4, 3, 4, 4, 3, 3,
1112
+ #define CCHEMVALENCEH3 5, 4, 3, 4, 5, 3, 3,
1113
+ #define CHAS3MEMBRING3 0, 0, 1, 0, 0, 0, 0,
1114
+ #define CREQUIRDNEIGH3 3, 3, 1, 3, 3, 2, 2,
1115
+
1116
+ #define PHOSPHINE_STEREO 19 /* the number must match Phosphine number in the comments, see above */
1117
+ #define ARSINE_STEREO 20 /* the number must match Arsine number in the comments, see above */
998
1118
 
999
1119
  static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 };
1000
1120
  static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 };
@@ -1027,6 +1147,20 @@ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
1027
1147
  break;
1028
1148
  }
1029
1149
  }
1150
+ #if ( ADD_PHOSPHINE_STEREO == 1 )
1151
+ if ( i == PHOSPHINE_STEREO && !(bPointedEdgeStereo & PES_BIT_PHOSPHINE_STEREO) )
1152
+ ret = 0;
1153
+ #else
1154
+ if ( i == PHOSPHINE_STEREO )
1155
+ ret = 0;
1156
+ #endif
1157
+ #if ( ADD_ARSINE_STEREO == 1 )
1158
+ if ( i == ARSINE_STEREO && !(bPointedEdgeStereo & PES_BIT_ARSINE_STEREO) )
1159
+ ret = 0;
1160
+ #else
1161
+ if ( i == ARSINE_STEREO )
1162
+ ret = 0;
1163
+ #endif
1030
1164
  return ret;
1031
1165
  }
1032
1166
 
@@ -1035,8 +1169,8 @@ int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at )
1035
1169
  /********************************************************************************************/
1036
1170
  int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical )
1037
1171
  {
1038
- static char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" };
1039
- static S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 };
1172
+ static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" };
1173
+ static const S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 };
1040
1174
  int i, ret = 0;
1041
1175
  for ( i = 0; i < sizeof(szElem)/sizeof(szElem[0]); i++ ) {
1042
1176
  if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) {
@@ -1052,8 +1186,8 @@ int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical )
1052
1186
  /* used for atoms adjacent to stereogenic bonds only */
1053
1187
  int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical )
1054
1188
  {
1055
- static char szElem[][3] = { "N\000" };
1056
- static S_CHAR cCharge[] = { 0, };
1189
+ static const char szElem[][3] = { "N\000" };
1190
+ static const S_CHAR cCharge[] = { 0, };
1057
1191
  int i, ret = 0;
1058
1192
  for ( i = 0; i < (int)(sizeof(szElem)/sizeof(szElem[0])); i++ ) {
1059
1193
  if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) {
@@ -1068,9 +1202,9 @@ int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical )
1068
1202
  /* used for atoms adjacent to stereogenic bonds only */
1069
1203
  int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical )
1070
1204
  {
1071
- static char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" };
1072
- static S_CHAR cCharge[] = { 0, 0, 0, 0, 1, };
1073
- static int n = sizeof(szElem)/sizeof(szElem[0]);
1205
+ static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" };
1206
+ static const S_CHAR cCharge[] = { 0, 0, 0, 0, 1, };
1207
+ static const int n = sizeof(szElem)/sizeof(szElem[0]);
1074
1208
  int i, ret = 0;
1075
1209
  for ( i = 0; i < n; i++ ) {
1076
1210
  if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) {
@@ -1084,9 +1218,9 @@ int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical )
1084
1218
  /* used for atoms adjacent to stereogenic bonds only */
1085
1219
  int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical )
1086
1220
  {
1087
- static char szElem[][3] = { "C\000", "Si", "Ge", };
1088
- static S_CHAR cCharge[] = { 0, 0, 0, };
1089
- static int n = sizeof(szElem)/sizeof(szElem[0]);
1221
+ static const char szElem[][3] = { "C\000", "Si", "Ge", };
1222
+ static const S_CHAR cCharge[] = { 0, 0, 0, };
1223
+ static const int n = sizeof(szElem)/sizeof(szElem[0]);
1090
1224
  int i, ret = 0;
1091
1225
  for ( i = 0; i < n; i++ ) {
1092
1226
  if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) {
@@ -1149,9 +1283,9 @@ int bIsOxide( inp_ATOM *at, int cur_at )
1149
1283
  /* used for atoms adjacent to stereogenic bonds only */
1150
1284
  int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical )
1151
1285
  {
1152
- static char szElem[][3] = { "C\000", "Si", "Ge", };
1153
- static S_CHAR cCharge[] = { 0, 0, 0, };
1154
- static int n = sizeof(szElem)/sizeof(szElem[0]);
1286
+ static const char szElem[][3] = { "C\000", "Si", "Ge", };
1287
+ static const S_CHAR cCharge[] = { 0, 0, 0, };
1288
+ static const int n = sizeof(szElem)/sizeof(szElem[0]);
1155
1289
  int i, ret = 0;
1156
1290
  for ( i = 0; i < n; i++ ) {
1157
1291
  if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) {
@@ -1372,7 +1506,47 @@ set_default:
1372
1506
  }
1373
1507
  return 0;
1374
1508
  }
1509
+ /**********************************************************/
1510
+ /* without this InChI fails on reconstructed CID=450438 */
1511
+ /* (isotopic, Unkown SB adjacent to SB with known parity) */
1512
+ /**********************************************************/
1513
+ int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at)
1514
+ {
1515
+ int i, m, num=0;
1375
1516
 
1517
+ /* add usual Unknown stereobond descriptors to each Unknown bond */
1518
+ for( i = 0; i < num_at; i ++ ) {
1519
+ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) {
1520
+ if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) {
1521
+ at[i].bond_stereo[ (int)at[i].sb_ord[m] ] = STEREO_DBLE_EITHER;
1522
+ num ++;
1523
+ }
1524
+ }
1525
+ }
1526
+ #ifdef NEVER
1527
+ if ( num ) {
1528
+ int j;
1529
+ /* how to remove Unknown stereo bond parities */
1530
+ for( i = 0; i < num_at; i ++ ) {
1531
+ for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) {
1532
+ if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) {
1533
+ for ( j = m+1; j < MAX_NUM_STEREO_BONDS; j ++ ) {
1534
+ at[i].sb_parity[j-1] = at[i].sb_parity[j];
1535
+ at[i].sb_ord[j-1] = at[i].sb_ord[j];
1536
+ at[i].sn_ord[j-1] = at[i].sn_ord[j];
1537
+ at[i].sn_orig_at_num[j-1] = at[i].sn_orig_at_num[j];
1538
+ }
1539
+ at[i].sb_parity[j-1] = 0;
1540
+ at[i].sb_ord[j-1] = 0;
1541
+ at[i].sn_ord[j-1] = 0;
1542
+ at[i].sn_orig_at_num[j-1] = 0;
1543
+ }
1544
+ }
1545
+ }
1546
+ }
1547
+ #endif
1548
+ return num;
1549
+ }
1376
1550
  /*======================================================================================================
1377
1551
 
1378
1552
  half_stereo_bond_parity() General Description:
@@ -1545,7 +1719,7 @@ int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, i
1545
1719
  at_coord[nNumExplictAttachments][1] = at_removed_H[next].y - at[cur_at].y;
1546
1720
  nSbNeighOrigAtNumb[nNumExplictAttachments] = at_removed_H[next].orig_at_number;
1547
1721
  /* use the fact that (at_removed_H - at) = (number of atoms except removed explicit H) */
1548
- z = -get_z_coord( at, (at_removed_H-at)+next, 0 /*neighbor #*/, &nType, -bPointedEdgeStereo );
1722
+ z = -get_z_coord( at, (at_removed_H-at)+next, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) );
1549
1723
  switch ( nType ) {
1550
1724
  case ZTYPE_EITHER:
1551
1725
  num_either_single ++; /* bond in "Either" direction. */
@@ -1574,7 +1748,7 @@ int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, i
1574
1748
  at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y;
1575
1749
  nSbNeighOrigAtNumb[nNumExplictAttachments] = at[next].orig_at_number;
1576
1750
 
1577
- z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, bPointedEdgeStereo );
1751
+ z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) );
1578
1752
  switch ( nType ) {
1579
1753
  case ZTYPE_EITHER:
1580
1754
  num_either_single ++; /* bond in "Either" direction. */
@@ -2150,9 +2324,10 @@ int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *
2150
2324
  for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].valence; i_next_at_1 ++ ) {
2151
2325
  nUnknown = (at[at_1].bond_stereo[i_next_at_1] == STEREO_DBLE_EITHER);
2152
2326
  bond_type = get_allowed_stereo_bond_type( (int)at[at_1].bond_type[i_next_at_1] );
2327
+ at_2 = -1; /* not found */
2153
2328
  if ( bond_type == BOND_ALTERN ||
2154
2329
  bond_type == BOND_DOUBLE ) {
2155
- at_2 = next_at_1 = at[at_1].neighbor[i_next_at_1];
2330
+ next_at_1 = at_2 = at[at_1].neighbor[i_next_at_1];
2156
2331
  next_at_2 = at_1;
2157
2332
  }
2158
2333
  switch ( bond_type ) {
@@ -2230,7 +2405,7 @@ int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *
2230
2405
 
2231
2406
  /* check atom at the opposite end of possibly stereogenic bond */
2232
2407
 
2233
- bFound = ( at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */
2408
+ bFound = (at_2 >= 0 && at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */
2234
2409
 
2235
2410
  if ( bFound ) {
2236
2411
  /* check "at_2" atom on the opposite side of the bond or cumulene chain */
@@ -2630,8 +2805,8 @@ int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *
2630
2805
  memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) );
2631
2806
  }
2632
2807
  }
2633
- if ( !out_at[at_2].parity2 ||
2634
- next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity)) ) {
2808
+ if ( !out_at[at_2].parity2 || /* next line changed from abs(out_at[at_2].parity) 2006-03-05 */
2809
+ next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity2)) ) {
2635
2810
  out_at[at_2].parity2 = next_parity /*| chain_len_bits*/;
2636
2811
  if ( !out_at[at_2].parity ) {
2637
2812
  memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) );
@@ -2659,10 +2834,10 @@ int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *
2659
2834
  /* if isotopic H, D, T added, can the atom be a stereo center? */
2660
2835
  #if( NEW_STEREOCENTER_CHECK == 1 )
2661
2836
  /* int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at ) */
2662
- int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at )
2837
+ int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo )
2663
2838
  {
2664
2839
  int nNumNeigh;
2665
- if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at )) &&
2840
+ if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo )) &&
2666
2841
  at[cur_at].valence + at[cur_at].num_H == nNumNeigh &&
2667
2842
  at[cur_at].num_H <= NUM_H_ISOTOPES
2668
2843
  ) {
@@ -2750,7 +2925,7 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
2750
2925
  num_explicit_H = 0;
2751
2926
 
2752
2927
  #if( NEW_STEREOCENTER_CHECK == 1 )
2753
- if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at ) ) ||
2928
+ if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo ) ) ||
2754
2929
  at[cur_at].num_H > NUM_H_ISOTOPES
2755
2930
  ) {
2756
2931
  goto exit_function;
@@ -2822,7 +2997,7 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
2822
2997
  /* use bond description located at removed_H atom */
2823
2998
  /* minus sign at get_z_coord: at_removed_H[] contains bonds TO at[cur_at], not FROM it. */
2824
2999
  /* Note: &at[(at_removed_H-at)+ next_at] == &at_removed_H[next_at] */
2825
- z = -get_z_coord( at, (at_removed_H-at)+ next_at, 0 /*neighbor #*/, &nType, -bPointedEdgeStereo );
3000
+ z = -get_z_coord( at, (at_removed_H-at)+ next_at, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) );
2826
3001
  switch ( nType ) {
2827
3002
  case ZTYPE_EITHER:
2828
3003
  parity = AB_PARITY_UNKN; /* no parity: bond in "Either" direction. */
@@ -2850,7 +3025,7 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
2850
3025
  /* add all coordinates of other neighboring atoms */
2851
3026
  for ( j = 0; j < at[cur_at].valence; j ++, j1 ++ ) {
2852
3027
  next_at = at[cur_at].neighbor[j];
2853
- z = get_z_coord( at, cur_at, j, &nType, bPointedEdgeStereo );
3028
+ z = get_z_coord( at, cur_at, j, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) );
2854
3029
  switch ( nType ) {
2855
3030
  case ZTYPE_EITHER:
2856
3031
  parity = AB_PARITY_UNKN; /* unknown parity: bond in "Either" direction. */
@@ -2991,8 +3166,30 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
2991
3166
  copy3( sum_xyz, at_coord[j] );
2992
3167
  change_sign3( at_coord[j], at_coord[j] );
2993
3168
  z = len3( at_coord[j] );
2994
- rmax = inchi_max( rmax, z );
2995
- rmin = inchi_min( rmin, z );
3169
+ #if ( FIX_STEREO_SCALING_BUG == 1 )
3170
+ if ( z > 1.0 ) {
3171
+ rmax *= z;
3172
+ } else {
3173
+ rmin *= z;
3174
+ }
3175
+ #else
3176
+ /* Comparing the original bond lengths to lenghts derived from normalized to 1 */
3177
+ /* This bug leads to pronouncing legitimate stereogenic atoms */
3178
+ /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */
3179
+ /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */
3180
+ if ( bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) {
3181
+ /* coordinate scaling bug fixed here */
3182
+ if ( z > 1.0 ) {
3183
+ rmax *= z;
3184
+ } else {
3185
+ rmin *= z;
3186
+ }
3187
+ } else {
3188
+ /* original InChI v.1 bug */
3189
+ rmax = inchi_max( rmax, z );
3190
+ rmin = inchi_min( rmin, z );
3191
+ }
3192
+ #endif
2996
3193
  if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) {
2997
3194
  /* the new 4th bond is too short: try to get 0D parities */
2998
3195
  if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) {
@@ -3038,7 +3235,7 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
3038
3235
  * check for tetrahedral ambiguity in 2D case
3039
3236
  */
3040
3237
  if ( b2D ) {
3041
- if ( 0 < (n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor )) ) {
3238
+ if ( 0 < (n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor, (bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) ) ) ) {
3042
3239
  if ( T2D_WARN & n2DTetrahedralAmbiguity ) {
3043
3240
  bAmbiguous |= AMBIGUOUS_STEREO;
3044
3241
  }
@@ -3081,9 +3278,7 @@ int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM
3081
3278
  ********************************************************/
3082
3279
  triple_product = triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous);
3083
3280
  /*
3084
- *triple_product = triple_prod_and_min_abs_sine( &at_coord[1], &min_sine);
3085
3281
  * check for tetrahedral ambiguity -- leave it out for now
3086
- *triple_product = sp3_triple_prod_and_min_abs_sine( &at_coord[1], at_coord_center, &min_sine, &bAmbiguous);
3087
3282
  */
3088
3283
  if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) {
3089
3284
  /* Even => sorted in correct order, Odd=>transposed */
@@ -3274,7 +3469,7 @@ int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_rem
3274
3469
  if ( nMaxNumStereoAtoms || nMaxNumStereoBonds ) {
3275
3470
  for( i = 0, num_stereo = 0; i < num_at; i ++ ) {
3276
3471
  int num;
3277
- num = can_be_a_stereo_atom_with_isotopic_H( at, i );
3472
+ num = can_be_a_stereo_atom_with_isotopic_H( at, i, bPointedEdgeStereo );
3278
3473
  if ( num ) {
3279
3474
  max_stereo_atoms += num;
3280
3475
  } else