1 /**********************************************************************
2 Copyright (C) 2007 by Mike N. Burnett, burnettmn@ornl.gov
3 Copyright (C) 2007 by Sergei V. Trepalin sergey_trepalin@chemical-block.com
4 Copyright (C) 2007 by Andrei Gakh andrei.gakh@nnsa.doe.gov
5 
6 Code translation from Java
7 
8 This file is part of the Open Babel project.
9 For more information, see <http://openbabel.org/>
10 
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation version 2 of the License.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 ***********************************************************************/
20 #include <openbabel/babelconfig.h>
21 #include <openbabel/obmolecformat.h>
22 #include <openbabel/mol.h>
23 #include <openbabel/atom.h>
24 #include <openbabel/elements.h>
25 #include <openbabel/bond.h>
26 
27 #include <openbabel/mcdlutil.h>
28 #include <cstdlib>
29 
30 using namespace std;
31 namespace OpenBabel
32 {
33 
34 class MCDLFormat : public OBMoleculeFormat
35 {
36 public:
MCDLFormat()37   MCDLFormat()
38   {
39     OBConversion::RegisterFormat("mcdl",this);
40     init();
41   }
42 
Description()43   virtual const char* Description() //required
44   {
45     return
46     "MCDL format\n"
47     "Modular Chemical Descriptor Language\n\n"
48     "As described in [gb2001]_.\n\n"
49 
50 ".. [gb2001] A.A. Gakh and M.N. Burnett. **Modular Chemical Descriptor\n"
51 "            Language (MCDL): Composition, Connectivity and\n"
52 "            Supplementary Modules.**\n"
53 "            *J. Chem. Inf. Comput. Sci.*, **2004**, *41*, 1491-1499.\n"
54 "            [`Link <https://doi.org/10.1021/ci000108y>`_]\n\n"
55 
56 "Here's an example conversion from SMILES to MCDL::\n\n"
57 "  obabel -:\"CC(=O)Cl\" -omcdl\n"
58 "  CHHH;COCl[2]\n";
59   }
60 
SpecificationURL()61   virtual const char* SpecificationURL(){return
62      "http://pubs.acs.org/cgi-bin/abstract.cgi/jcisd8/2001/41/i06/abs/ci000108y.html";}
63 
GetMIMEType()64   virtual const char* GetMIMEType()
65   { return "chemical/x-MCDL"; }
66 
67   /* Flags() can return be any of the following combined by |
68      or be omitted if none apply
69      NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY  DEFAULTFORMAT
70      READBINARY  WRITEBINARY  READXML  ZEROATOMSOK*/
Flags()71   virtual unsigned int Flags()
72   {
73       return 0;
74   }
75 
SkipObjects(int n,OBConversion * pConv)76   virtual int SkipObjects(int n, OBConversion* pConv)
77   {
78       if(n==0) n++;
79       string temp;
80       istream& ifs = *pConv->GetInStream();
81       do {
82           if(ifs.good())
83             getline(ifs, temp);
84         }while(ifs.good() && --n);
85       return ifs.good() ? 1 : -1;
86   }
87 
88   ////////////////////////////////////////////////////
89   /// Declarations for the "API" interface functions. Definitions are below
90   virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
91   virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
92 
93 private:
94 
95   string fsastart;
96   string fsbstart;
97   string fchstart;
98   string fradstart;
99     //coordinates string
100   string fnastart;
101   string fnbstart;
102   string fzcoorstart;
103   string fablockstart;
104   string fbblockstart;
105   string fchargeblockstart;
106   string fstereobondstart;
107   string ftitlestart;
108 
109 //  int fragNo;
110   int maxdepth;
111   int kflag;
112 
113   int ntatoms;
114   int nbonds;
115   string  finalstr;
116 
117   int qx [MAXFRAGS];
118   int qa [MAXBONDS][4];
119 //  int HVal[NELEMMAX];
120 
121 private:
122   void init();
123   void initGlobals();
124   void solve(int ntypes, int z[MAXBONDS][4], int depth);
125   string constring(int conntab [MAXBONDS][4], char * tstr);
126   string intToStr(int k);
127   string getMCDL(OBMol* pmol, bool includeCoordinates);
128   void restoreFullMCDL(string value, OBMol* pmol);
129   void setMCDL(const string lineToParse, OBMol* pmol, string & sout);
130   void assignCharges(const std::vector <int> aPosition, const std::vector <int> iA1,
131     const std::vector <int> iA2, std::vector <int>& aCharges, std::vector <int>& charges,
132     std::vector <int>& bondOrder, int aPos, int nPrev, int nt, int acount, int bcount);
133   int indexOf(const string instring, const string substring, int fromPos=0);
134   int lastIndexOf(const string instring, const string substring);
135   bool parseFormula(const string formulaString, std::vector <int>& enumber);
136   string getMolTitle(string & line);
137 
138 };
139 
140 
141 
init()142   void MCDLFormat::init(){
143     fsastart="{SA:";
144     fsbstart="{SB:";
145     fchstart="{CZ:";
146     fradstart="{RA:";
147     //coordinates string
148     fnastart="{NA:";
149     fnbstart="{NB:";
150     fzcoorstart="{ZV:";
151     fablockstart="{CC:";
152     fbblockstart="{BB:";
153     fchargeblockstart="{MM:CHG,";
154     fstereobondstart="{BS:";
155     ftitlestart="{CN:}";
156 
157    };
158 
initGlobals()159   void MCDLFormat::initGlobals(){
160     int i,j;
161 
162     maxdepth=0;
163     kflag=0;
164 
165     ntatoms=0;
166     nbonds=0;
167     finalstr="";
168 
169   for (i=0; i<MAXFRAGS; i++) {
170     qx[i]=0;
171     for (j=0; j<4; j++) qa[i][j]=0;
172   };
173 
174   };
175 
176 
177 
178 /* Create descriptor connectivity string from connectivity table of fragments */
constring(int conntab[MAXBONDS][4],char * tstr)179   string MCDLFormat::constring(int conntab [MAXBONDS][4], char * tstr)
180 {
181     int  i,j,k,n,nn,icons[6],comma;
182     char line[82],semis[100];
183     string result="";
184 
185     result="[";
186     semis[0] = '\0';
187 
188     for (i=0; i<ntatoms; i++)
189     {
190         if (i > 0)
191             strcat(semis,";");
192 
193         n = 0;
194         for (j=0; j<nbonds; j++)
195             if (conntab[j][2] == (i+1))
196                 icons[n++] = conntab[j][3];
197         if (n > 1)
198         {
199             for (j=0; j<n-1; j++)
200                 for (k=j+1; k<n; k++)
201                     if (icons[k] < icons[j])
202                     {
203                         nn = icons[j];
204                         icons[j] = icons[k];
205                         icons[k] = nn;
206                     }
207 
208         }
209         comma = 0;
210         for (j=0; j<n; j++)
211         {
212             if (icons[j] > (i+1) && comma == 0)
213             {
214                 sprintf(line,"%s%d",semis,icons[j]);
215                 result=result+line;//strcat(sp,line);
216                 comma = 1;
217                 semis[0] = '\0';
218             }
219             else if (icons[j] > (i+1) && comma == 1)
220             {
221                 sprintf(line,",%d",icons[j]);
222                 result=result+line;//strcat(sp,line);
223             }
224 
225         }
226     }
227 
228     result=result+"]";
229     return result;
230 }
231 
232 
solve(int ntypes,int z[MAXBONDS][4],int depth)233   void MCDLFormat::solve(int ntypes, int z[MAXBONDS][4], int depth)
234 {
235     int  i,dupnum,j,k,ktype,nn,nt;
236     bool iflag,newone;
237     int *  nsum[MAXBONDS];
238     bool lflag[MAXFRAGS];
239     char strg[MAXFRAGS+1];
240 	char * strngs[MAXFRAGS+1];
241     char tstr[MAXFRAGS+1];
242     int  numdups, dupfrag, jump;
243     bool jflag;
244     int  ix[MAXFRAGS],conntab[MAXBONDS][4],cx[MAXFRAGS];
245     int  mx[MAXFRAGS];
246 
247 	//stack overflow message-move data from stack to heap
248 	for (i=0; i<=MAXFRAGS; i++) strngs[i]=(char *)malloc(MAXFRAGS);
249     for (i=0; i<MAXBONDS; i++)  nsum[i]=(int *) malloc(MAXFRAGS);
250 
251     // depth = recursion level
252     if (depth > 10)
253     {
254         printf("Ten recursion levels exceeded.\n");
255         return;
256         //exitprog(freeflag);
257     }
258     if (depth > maxdepth)
259         maxdepth = depth;
260 
261     for (i=0; i<nbonds; i++)
262     {
263         for (j=0; j<4; j++)
264             conntab[i][j] = z[i][j];
265         mx[conntab[i][0]-1] = conntab[i][2];
266     }
267 
268     jflag = true;
269     if (depth == 1)
270     {
271         jflag = false;
272         for (i=0; i<ntatoms; i++)
273         {
274             ix[i] = mx[i];
275             if (ix[i] != mx[0])
276                 jflag = true;
277         }
278     }
279 
280     iflag = true;
281     while (iflag)
282     {
283         if (jflag)
284             for (i=0; i<ntatoms; i++)
285                 ix[i] = 0;
286 
287             nt = 1;
288             ktype = 1;
289             iflag = false;
290 
291            while (nt <= ntypes && jflag)
292            {
293             for (j=0; j<ntatoms; j++)
294             {
295                    lflag[j] = false;
296                 for (i=0; i<nbonds; i++)
297                        nsum[i][j] = 0;
298             }
299 
300             for (i=0; i<nbonds; i++)
301             if (conntab[i][2] == nt)
302             {
303                 j = conntab[i][0]-1;
304                 k = conntab[i][3]-1;
305                 nsum[j][k]++;
306                 lflag[j] = true;
307             }
308 
309                 nn = 0;
310 
311             for (i=0; i<ntatoms; i++)
312             if (lflag[i])
313             {
314                 for (j=0; j<ntypes; j++)
315                     strg[j] = nsum[i][j]+48;
316                 strg[ntypes] = '\0';
317                 newone = true;
318                 for (j=0; j<nn; j++)
319                     if (strcmp(strngs[j],strg) == 0)
320                     {    newone = false;
321                         break;
322                     }
323                 if (newone)
324                     strcpy(strngs[nn++],strg);
325             }
326 
327             if (nn > 1)
328             {
329             iflag = true;
330 
331             // sort strings
332             for (i=0; i<nn-1; i++)
333                 for (j=i+1; j<nn; j++)
334                     if (strcmp(strngs[j],strngs[i]) > 0)
335                     {
336                     strcpy(tstr,strngs[i]);
337                     strcpy(strngs[i],strngs[j]);
338                     strcpy(strngs[j],tstr);
339                     }
340                 }
341 
342             for (i=0; i<ntatoms; i++)
343             if (lflag[i])
344             {
345                 for (j=0; j<ntypes; j++)
346                     strg[j] = nsum[i][j]+48;
347                 strg[ntypes] = '\0';
348                 for (j=0; j<nn; j++)
349                     if (strcmp(strg,strngs[j]) == 0)
350                         ix[i] = ktype+j;
351             }
352 
353                 nt++;
354                 ktype += nn;
355            }
356 
357         ntypes = 0;
358         for (i=0; i<nbonds; i++)
359         {
360             conntab[i][2] = ix[conntab[i][0]-1];
361             conntab[i][3] = ix[conntab[i][1]-1];
362             if (conntab[i][2] > ntypes)
363                 ntypes = conntab[i][2];
364         }
365 
366             if (iflag)
367             continue;
368 
369         if (ntypes < ntatoms)
370         {
371             for (j=0; j<ntypes; j++)
372             {
373             numdups = 0;
374             for (i=0; i<ntatoms; i++)
375                 if (ix[i] == j+1)
376                 {
377                     numdups++;
378                     jump = ix[i] - mx[i];
379                 }
380             if (numdups > 1)
381             {
382                 dupfrag = j+1;
383                 break;
384             }
385             }
386 
387             for (i=0; i<ntatoms; i++)
388                 cx[i] = ix[i];
389 
390             for (j=0; j<numdups; j++)
391             {
392                 for (i=0; i<ntatoms; i++)
393                     ix[i] = cx[i];
394 
395             dupnum = 0;
396                 for (i=0; i<ntatoms; i++)
397             {
398                 if (ix[i] > dupfrag)
399                     ix[i] = mx[i] + jump + 1;
400                 else if (ix[i] == dupfrag && j != dupnum)
401                 {
402                     dupnum++;
403                     ix[i] = mx[i] + jump + 1;
404                 }
405                 else if (ix[i] == dupfrag && j == dupnum)
406                     dupnum++;
407             }
408 
409             ntypes = 0;
410                 for (i=0; i<nbonds; i++)
411                 {
412                 conntab[i][2] = ix[conntab[i][0]-1];
413                 conntab[i][3] = ix[conntab[i][1]-1];
414                 if (conntab[i][2] > ntypes)
415                     ntypes = conntab[i][2];
416                 }
417                 solve(ntypes,conntab,depth+1);
418             }
419         }
420     }
421 
422     if (ntypes == ntatoms)
423     {
424             //if (true) fprintf(o_verbose,"%s\n",constring(conntab,tstr));
425 
426         if (!kflag)
427         {
428             kflag++;
429       finalstr=constring(conntab,tstr);
430             for (i=0; i<ntatoms; i++)
431                 qx[i] = ix[i];
432             for (i=0; i<nbonds; i++)
433                 for (j=0; j<4; j++)
434                     qa[i][j] = conntab[i][j];
435         }
436         else if (finalstr != constring(conntab,tstr))
437         {
438             for (i=0; i<ntatoms; i++)
439             {
440                 tstr[0] = '\0';
441                 strg[0] = '\0';
442                 for (j=0; j<ntatoms; j++)
443                 {
444                     strcat(tstr,"0");
445                     strcat(strg,"0");
446                 }
447                 for (j=0; j<nbonds; j++)
448                 {
449                     if (conntab[j][2] == (i+1))
450                            tstr[conntab[j][3]-1] = '1';
451                     if (qa[j][2] == (i+1))
452                            strg[qa[j][3]-1] = '1';
453                 }
454                 if (strcmp(tstr,strg) < 0)
455                        break;
456                 if (strcmp(tstr,strg) > 0)
457                 {
458                     finalstr=constring(conntab,tstr);
459                     for (j=0; j<ntatoms; j++)
460                         qx[j] = ix[j];
461                     for (j=0; j<nbonds; j++)
462                         for (k=0; k<4; k++)
463                             qa[j][k] = conntab[j][k];
464                     break;
465                 }
466             }
467         }
468     }
469 	//freeing resources
470 	for (i=0; i<=MAXFRAGS; i++) free(strngs[i]);
471 	for (i=0; i< MAXBONDS; i++) free(nsum[i]);
472 }
473 
474 
intToStr(int k)475   string MCDLFormat::intToStr(int k) {
476     char temp[16];
477     sprintf(temp,"%d",k);
478   string line=temp;
479   return line;
480   };
481 
482 
483 
getMCDL(OBMol * pmol,bool includeCoordinates)484   string MCDLFormat::getMCDL(OBMol* pmol, bool includeCoordinates) {
485     int  i=0;
486     int  j=0;
487     int  k=0;
488     int  n=0;
489     int  nt=0;
490     int  nn=0;
491     int  ntypes=0;
492     int  charge=0;
493     int  netcharge=0;
494     int  netradical=0;
495     int  chgarray[60][2];
496     std::vector<int> ifragnum(MAXFRAGS);
497     int  ltypes=0;
498     int  ia0=0;
499     int  ia1=0;
500     int  chgflag=0;
501     int  radicalflag=0;
502     int  icons[96];
503     int  newone=0;
504     std::vector<int> nchg(MAXFRAGS);
505     std::vector<int> nrad(MAXFRAGS);
506     int naStore,nbStore;
507     string constr="";
508     string frag="";
509     string fragment [MAXFRAGS];
510     string line="";
511     string semis="";
512     int  comma;
513     std::vector<int> ix(MAXFRAGS);
514     int ia[MAXBONDS][4];
515     string data="";
516     int z[MAXBONDS][4];
517     //FRAGCON
518     int nHydr[MAXFRAGS];
519     string aSymb [MAXFRAGS];
520     int aCharge[MAXFRAGS];
521     int aRadical[MAXFRAGS];
522     std::vector<int> aNumber(MAXFRAGS);
523     int bonds[MAXBONDS][4];
524     int nConn[MAXFRAGS];
525     //string s;
526     int l;//,m;
527     string astereo="";
528     string bstereo="";
529     string s1,s2;
530     std::vector <int> eqList;
531     std::vector <int>  bondStereoList;
532     std::vector <int>  atomStereoList;
533     std::vector <int>  anumStereo;
534     string as1,as2,as3,as4;
535     string linestereo;
536     OBAtom *atom;
537     OBBond *bond;
538 
539 /* read in fragments and find different types */
540 /* ntypes: number of fragment types */
541 /* ntatoms: total number of fragments */
542 /* fragment[i]: atomic symbol string of fragment type i */
543 /* ifragnum[i]: number of fragments of type i */
544 
545   initGlobals();
546     ntypes = 0;
547     ntatoms = 0;
548     for (i=0; i<MAXFRAGS; i++)
549         ifragnum[i] = 0;
550 
551     chgflag = 0;
552     netcharge = 0;
553     radicalflag=0;
554     netradical=0;
555 
556   netcharge=pmol->GetTotalCharge();
557   netradical=pmol->GetTotalSpinMultiplicity()-1;
558 
559 //Here FRAGCON conversion...
560   pmol->DeleteHydrogens();
561   createStereoLists(pmol,bondStereoList,atomStereoList,eqList);
562 //    return "started"; //passed
563   naStore=pmol->NumAtoms();
564   nbStore=pmol->NumBonds();
565   for (i=1; i<=naStore; i++) {
566     atom=pmol->GetAtom(i);
567     nHydr[i-1]=atom->GetImplicitHCount()+atom->ExplicitHydrogenCount();
568     aCharge[i-1]=atom->GetFormalCharge();
569     aRadical[i-1]=atom->GetSpinMultiplicity();
570     aSymb[i-1]=OBElements::GetSymbol(atom->GetAtomicNum());
571     nConn[i-1]=atom->GetHvyDegree();
572     aNumber[i-1]=i-1;
573   };
574   for (i=1; i<=nbStore; i++) {
575     bond=pmol->GetBond(i-1);
576     bonds[i-1][0]=bond->GetBeginAtomIdx();
577     bonds[i-1][1]=bond->GetEndAtomIdx();
578       bonds[i-1][2]=bond->GetBondOrder();  //type of bond store
579       bonds[i-1][3]=i;                     //bond number in sm store
580     };
581     //FRAGCON
582     i=0;
583     while (i < naStore) {
584       if ((nConn[i] == 1) && (nHydr[i] == 0)) {
585         k=-1;
586         for (j=0; j<nbStore; j++) if ((bonds[j][0] == (i+1)) || (bonds[j][1] == (i+1))) {
587           k=j;
588           break;
589         }
590         if (k>=0) {
591           if (bonds[k][0] == (i+1)) nn=bonds[k][1]-1; else nn=bonds[k][0]-1;
592           aCharge[nn]=aCharge[nn]+aCharge[i];
593           aRadical[nn]=aRadical[nn]+aRadical[i];
594           aSymb[nn]=aSymb[nn]+aSymb[i];
595           for (j=i; j<(naStore-1); j++) {
596             nHydr[j]=nHydr[j+1];
597             aCharge[j]=aCharge[j+1];
598             aRadical[j]=aRadical[j+1];
599             aSymb[j]=aSymb[j+1];
600             nConn[j]=nConn[j+1];
601             aNumber[j]=aNumber[j+1];
602           };
603           for (j=0; j<nbStore; j++) {
604             if (bonds[j][0] > (i+1)) bonds[j][0]--;
605             if (bonds[j][1] > (i+1)) bonds[j][1]--;
606           };
607           for (j=k; j<(nbStore-1); j++) {
608             bonds[j][0]=bonds[j+1][0];
609             bonds[j][1]=bonds[j+1][1];
610             bonds[j][2]=bonds[j+1][2];
611             bonds[j][3]=bonds[j+1][3];
612           };
613           i--;
614           naStore--;
615           nbStore--;
616         };
617       };
618       i++;
619     }
620 //Ald without fragcon
621   for (i=1; i<=naStore; i++) {
622       charge=aCharge[i-1];
623       if (charge != 0) chgflag = 1;
624       if (aRadical[i-1] != 0) radicalflag=1;
625       ntatoms++;
626       newone = 1;
627       frag=aSymb[i-1];
628       j=nHydr[i-1];
629       if (j>0) for (l=0; l<j; l++) {
630         frag=frag+"H";
631       };
632       for (j=0; j<ntypes; j++) if (fragment[j] == frag) {
633         ifragnum[j]++;
634         newone = 0;
635         break;
636       }
637       if (newone != 0) {
638         fragment[ntypes]=frag;
639         ifragnum[ntypes]++;
640         ntypes++;
641       }
642     }
643 
644     ltypes=ntypes;
645 // order fragment types in ASCII order
646     for (i=0; i<ntypes-1; i++) for (j=i+1; j<ntypes; j++) if (fragment[j] < fragment[i]) {
647       frag=fragment[i];
648       fragment[i]=fragment[j];
649       fragment[j]=frag;
650       k = ifragnum[i];
651       ifragnum[i] = ifragnum[j];
652       ifragnum[j] = k;
653     }
654 // reread the fragments
655 // ix[i]: fragment type of fragment i
656     for (i=1; i<=ntatoms; i++) {
657       frag=aSymb[i-1];
658       j=nHydr[i-1];
659       if (j>0) for (l=0; l<j; l++) {
660         frag=frag+"H";
661       };
662       for (j=0; j<ntypes; j++) if (frag == fragment[j]) ix[i-1] = j+1;
663     }
664 
665 //FRAGCON format atoms conversion
666 // read in the connections
667 // ia[i][0]: from fragment; ia[i][1]: to fragment of connection i
668     nbonds = 0;
669     for (i=1; i<=nbStore; i++) {
670       // Possible, DRAGON format can contains multiply bonds between 2 atoms. Bond order?
671       ia0=bonds[i-1][0];
672       ia1=bonds[i-1][1];
673 
674       newone = 1;
675       for (j=0; j<nbonds; j++)
676           if ((ia[j][0] == ia0 && ia[j][1] == ia1) ||
677               (ia[j][0] == ia1 && ia[j][1] == ia0))
678           {
679               newone = 0;
680               break;
681           }
682       if (newone == 1) {
683         ia[nbonds][0] = ia0;
684         ia[nbonds][1] = ia1;
685         nbonds=nbonds+1;
686         ia[nbonds][0] = ia1;
687         ia[nbonds][1] = ia0;
688         nbonds=nbonds+1;
689       }
690     };
691     for (i=0; i<nbonds; i++) {
692         ia[i][2] = ix[ia[i][0]-1];
693         ia[i][3] = ix[ia[i][1]-1];
694     };
695     for (i=0; i<nbonds; i++) for (j=0; j<4; j++) {
696       z[i][j] = ia[i][j];
697     };
698     maxdepth = 1;
699 
700         // data ready for solution
701     solve(ntypes,z,1);
702 
703     for (i=0; i<ntatoms; i++) ix[i] = qx[i];
704     for (i=0; i<nbonds; i++) for (j=0; j<4; j++) ia[i][j] = qa[i][j];
705 
706 //atoms stereo getting
707     astereo=getAtomMCDL(pmol,ntatoms,ix,aNumber,atomStereoList,eqList);
708 //bond stereo getting
709     bstereo=getBondMCDL(pmol,nbStore,ntatoms,ix,aNumber,bonds,bondStereoList,eqList);
710 
711     // original and final fragment numbers
712     if (chgflag != 0) for (i=0; i<ntatoms; i++) nchg[ix[i]] = aCharge[i];
713     if (radicalflag != 0) for (i=0; i<ntatoms; i++) nrad[ix[i]] = aRadical[i];
714 
715     // net charge
716     data="";
717     if (netcharge != 0)
718     {
719         if (netcharge > 0)
720             data=data+intToStr(netcharge)+"+;";
721         else data=data+intToStr(abs(netcharge))+"-;";
722     }
723     if (netradical != 0) data=data+intToStr(netradical)+"*;";
724     // fragment portion of linear descriptor
725     for (i=0; i<ltypes; i++) {
726       if (i > 0) {
727         data=data+';';
728       }
729       if (ifragnum[i] > 1) {
730         data=data+intToStr(ifragnum[i]);
731       }
732       data=data+fragment[i];
733     }
734     constr="[";
735     // connectivity portion of linear descriptor
736     semis="";
737     nt = 1;
738     for (i=0; i<ntatoms; i++) {
739       if (i > 0) {
740         semis=semis+";";
741       }
742       n = 0;
743       for (j=0; j<nbonds; j++) if (ia[j][2] == (i+1)) icons[n++] = ia[j][3];
744       if (n > 1) {
745         for (j=0; j<n-1; j++) for (k=j+1; k<n; k++) if (icons[k] < icons[j]) {
746           nn = icons[j];
747           icons[j] = icons[k];
748           icons[k] = nn;
749         }
750       }
751       comma = 0;
752       for (j=0; j<n; j++) {
753         if ((icons[j] > (i+1)) && (comma == 0)) {
754           constr=constr+semis;
755           constr=constr+intToStr(icons[j]);
756           comma = 1;
757           semis="";
758         }  else if ((icons[j] > (i+1)) && (comma == 1)) {
759           constr=constr+","+intToStr(icons[j]);
760         }
761       }
762       nt++;
763     };
764     line=line+"]";
765 
766     constr=constr+line;
767     data=data+constr;
768     // fragment charges
769     if (chgflag != 0) {
770       nt = 0;
771       for (n=-3; n<4; n++) for (i=0; i<ntatoms; i++) if ((nchg[ix[i]] == n) && (n != 0)) {
772         chgarray[nt][0] = n;
773         chgarray[nt++][1] = ix[i];
774       }
775       for (i=0; i<nt-1; i++) for (j=i; j<nt; j++) if (chgarray[i][0] > chgarray[j][0]) {
776         k = chgarray[i][0];
777         n = chgarray[i][1];
778         chgarray[i][0] = chgarray[j][0];
779         chgarray[i][1] = chgarray[j][1];
780         chgarray[j][0] = k;
781         chgarray[j][1] = n;
782       }
783       for (i=0; i<nt-1; i++) for (j=i; j<nt; j++) if ((chgarray[i][0] == chgarray[j][0]) && (chgarray[i][1] > chgarray[j][1])) {
784         n = chgarray[i][1];
785         chgarray[i][1] = chgarray[j][1];
786         chgarray[j][1] = n;
787       }
788       line="";
789       for (i=-1; i>-4; i--) {
790         n = 0;
791         for (j=0; j<nt; j++) if (chgarray[j][0] == i) n++;
792         if (n > 0) {
793           for (j=0; j<nt; j++) if (chgarray[j][0] == i) {
794             if (chgarray[j][1] == 0)
795               frag=""+intToStr(chgarray[j][1])+","+intToStr(abs(i))+"-";//Math.abs(i)+"-1";
796             else
797               frag=""+intToStr(chgarray[j][1])+","+intToStr(abs(i))+"-";//Math.abs(i)+"-"+chgarray[j][1];
798             if (line.length() > 0) line=line+";";
799             line=line+frag;
800           }
801         }
802       }
803       for (i=1; i<4; i++){
804         n = 0;
805         for (j=0; j<nt; j++) if (chgarray[j][0] == i) n++;
806         if (n > 0) {
807           for (j=0; j<nt; j++) if (chgarray[j][0] == i) {
808             if (chgarray[j][1] == 0)
809               frag=""+intToStr(chgarray[j][1])+","+intToStr(i)+"+";//i+"+1";
810             else
811               frag=""+intToStr(chgarray[j][1])+","+intToStr(i)+"+";
812             if (line.length() > 0) line=line+";";
813             line=line+frag;
814           }
815         }
816       }
817       if (ntypes>1) {
818         constr=fchstart;//"{CZ:";
819         constr=constr+line;
820         constr=constr+"}";
821         data=data+constr;
822       };
823 
824     };
825     //radical processing
826     if (radicalflag != 0) {
827       for (i=0; i<ntatoms; i++) {
828         chgarray[i][0]=0;
829         chgarray[i][1]=0;
830       };
831       nt = 0;
832       for (n=1; n<3; n++) for (i=0; i<ntatoms; i++) if (nrad[ix[i]] == n) {
833         chgarray[nt][0] = n;
834         chgarray[nt++][1] = ix[i];
835       }
836       for (i=0; i<nt-1; i++) for (j=i; j<nt; j++) if (chgarray[i][0] > chgarray[j][0]) {
837         k = chgarray[i][0];
838         n = chgarray[i][1];
839         chgarray[i][0] = chgarray[j][0];
840         chgarray[i][1] = chgarray[j][1];
841         chgarray[j][0] = k;
842         chgarray[j][1] = n;
843       }
844       for (i=0; i<nt-1; i++) for (j=i; j<nt; j++) if ((chgarray[i][0] == chgarray[j][0]) && (chgarray[i][1] > chgarray[j][1])) {
845         n = chgarray[i][1];
846         chgarray[i][1] = chgarray[j][1];
847         chgarray[j][1] = n;
848       }
849       line="";
850       for (i=1; i<3; i++){
851         n = 0;
852         for (j=0; j<nt; j++) if (chgarray[j][0] == i) n++;
853         if (n > 0) {
854           for (j=0; j<nt; j++) if (chgarray[j][0] == i) {
855             if (chgarray[j][1] == 0)
856               frag=""+intToStr(chgarray[j][1])+","+intToStr(i)+"*";
857             else
858               frag=""+intToStr(chgarray[j][1])+","+intToStr(i)+"*";
859             if (line.length() > 0) line=line+";";
860             line=line+frag;
861           }
862         }
863       }
864       if (ntypes>1) {
865         constr=fradstart;//"{CZ:";
866         constr=constr+line;
867         constr=constr+"}";
868         data=data+constr;
869       };
870 
871     };
872 
873 //*****************************************************************************
874     data=data+astereo;
875     data=data+bstereo;
876 //*****************************************************************************
877   /*   Block should be saved for future using
878 
879     if (includeCoordinates && (sm.fAtom.maxIndex() > 0))  {
880       //Atom and bond blocks include
881       data=data+fnastart+sm.fAtom.maxIndex()+"}";//};
882       data=data+fnbstart+sm.fBond.maxIndex()+"}";//};
883       data=data+fzcoorstart+"N}";
884       //analizing minimal and maximal values of atomic coordinates
885       xMin=sm.fAtom.getRX(1); xMax=sm.fAtom.getRX(1); yMin=sm.fAtom.getRY(1); yMax=sm.fAtom.getRY(1);
886       for (i=1; i<=sm.fAtom.maxIndex(); i++) {
887         if (sm.fAtom.getRX(i) < xMin) xMin=sm.fAtom.getRX(i);
888         if (sm.fAtom.getRX(i) > xMax) xMax=sm.fAtom.getRX(i);
889         if (sm.fAtom.getRY(i) < yMin) yMin=sm.fAtom.getRY(i);
890         if (sm.fAtom.getRY(i) > yMax) yMax=sm.fAtom.getRY(i);
891       };
892       scale=0;
893       if (xMax != xMin) scale=1/(xMax-xMin);
894       if (yMax != yMin) if ((1/(yMax-yMin)) < scale) scale=1/(yMax-yMin);
895       if (scale == 0) scale=1;
896       line=fablockstart;
897       nCharge=0;
898       for (i=1; i<=sm.fAtom.maxIndex(); i++) {
899         r=sm.fAtom.getRX(i);
900         r=9.99*(r-xMin)*scale;
901         s=CommonRout.str(r,0,2);
902         line=line+s+",";
903         r=sm.fAtom.getRY(i);
904         r=9.99*(r-yMin)*scale;
905         s=CommonRout.str(r,0,2);
906         line=line+s+Atom.symbolofAtom(sm.fAtom.getNA(i));
907         if (i < sm.fAtom.maxIndex()) line=line+";"; else line=line+"}";//};
908         if (sm.fAtom.getNC(i) != 0) nCharge++;
909       };
910       data=data+line;
911       line=fbblockstart;
912       //two lines - adition for stereo handling
913       test=false;
914       linestereo=fstereobondstart;
915       for (i=1; i<=sm.fBond.maxIndex(); i++) {
916         n=sm.fBond.getTB(i);
917         s="s";
918         if (n > 3) n=1;
919         if (n == 2) s="d"; else if (n == 3) s="t";
920         s=""+sm.fBond.getAT(i,1)+s+sm.fBond.getAT(i,2);
921         line=line+s;
922         if (i < sm.fBond.maxIndex()) line=line+";"; else line=line+"}"; //};
923         //stereoanalizing
924         n=sm.fBond.getTB(i);
925         if ((n >= 9) && (n <= 11)) {
926           if (test) linestereo=linestereo+";";
927           test=true;
928           s="p";
929           if (n == 10) s="o"; else if (n == 11) s="e";
930           s=""+sm.fBond.getAT(i,1)+s+sm.fBond.getAT(i,2);
931           linestereo=linestereo+s;
932         };
933       };
934       data=data+line;
935       if (test) {
936         linestereo=linestereo+"}"; //};
937         data=data+linestereo;
938       };
939       if (nCharge != 0) {
940         line=fchargeblockstart+nCharge;
941         for (i=1; i<= sm.fAtom.maxIndex(); i++) if (sm.fAtom.getNC(i) != 0) line=line+","+i+","+sm.fAtom.getNC(i);
942         line=line+"}";
943         data=data+line;
944       };
945     };
946   */
947     return data;
948   };
949 
restoreFullMCDL(string value,OBMol * pmol)950   void MCDLFormat::restoreFullMCDL(string value, OBMol* pmol) {
951 
952   };
953 
setMCDL(const string lineToParse,OBMol * pmol,string & sout)954   void MCDLFormat::setMCDL(const string lineToParse, OBMol* pmol, string & sout) {
955 
956   std::vector <int> nH(MAXFRAGS);
957   std::vector <int> nF(MAXFRAGS);
958 //  int nb;//,na;
959   unsigned int i, j, nt, n1, n2;//,n3, nfrag;
960   bool test;
961   OBAtom sa;
962   string mf="";
963   string s="";
964   string temp="";
965   string ss="";
966   string sstore="";
967   int m,n,k;
968   std::vector <int> nHydr(MAXFRAGS);
969   string astereo="";
970   string bstereo="";
971   string chargestring="";
972   string radicalstring="";
973   int nPrev;
974   string sa1="";
975   string sa2="";
976   string sF="";
977   std::vector <int> charges(MAXFRAGS);
978   std::vector <int> radicals(MAXFRAGS);
979   std::vector <int> fragIndex(MAXFRAGS);
980   std::vector <int> sbSpecial(MAXBONDS);
981   std::vector <int> enumber(NELEMMAX);  //number of elements...
982   std::vector <int> bondOrders(MAXBONDS);
983   std::vector <double> rx(MAXFRAGS);
984   std::vector <double> ry(MAXFRAGS);
985   std::vector <string> names(MAXFRAGS);
986   int netcharge=0;
987   int netradical=0;
988   unsigned int nelements=0;
989   int kk;
990   string value="";
991   unsigned int acount, bcount, flags;
992 
993   std::vector <int> iA1(MAXBONDS);
994   std::vector <int> iA2(MAXBONDS);
995   std::vector <int> aPosition(MAXFRAGS);
996   std::vector <int> aCharge(MAXFRAGS);
997   std::vector <int> aRad(MAXFRAGS);
998   std::vector <int> stereoBonds(MAXBONDS);
999 
1000   value=lineToParse;
1001   sout="";
1002 
1003   if ((indexOf(value,fablockstart) >= 0) && (indexOf(value,fbblockstart) > 0)) {
1004     restoreFullMCDL(value,pmol);
1005     return;
1006   };
1007   for (i=0; i<MAXBONDS; i++) {
1008     stereoBonds[i]=0;
1009     sbSpecial[i]=0;
1010     iA1[i]=0;
1011     iA2[i]=0;
1012   };
1013 
1014   for (i=0; i<MAXFRAGS; i++) {
1015     charges[i]=0;
1016     radicals[i]=0;
1017     fragIndex[i]=0;
1018     //special[i]=0;
1019     nHydr[i]=0;
1020     names[i]="";
1021     aPosition[i]=6;
1022     aCharge[i]=0;
1023     aRad[i]=0;
1024   };
1025   //removing net charges and radiacal
1026   test=false;
1027   n=1;
1028   while ((value.length() > 0) && (value.at(0) >= '0') && (value.at(0) <= '9') && (! test) && (n>0)) {
1029     n=indexOf(value,";");
1030     if (n>0) {
1031       s=value.substr(0,n);
1032       n1=indexOf(s,"+");
1033       if (n1 < 0) n1=indexOf(s,"-");
1034       if (n1 < 0) n1=indexOf(s,"*");
1035       if (n1 < 0) {
1036         n=0;
1037         test=true;
1038       };
1039     };
1040     if (n > 0) {
1041       s=value.substr(0,n);
1042       if ((s.at(s.length()-1) == '+') || (s.at(s.length()-1) == '-')) {
1043         //total charge processing
1044         if (s.at(s.length()-1) == '+') netcharge=1; else netcharge=-1;
1045         s=s.substr(0,s.length()-1);
1046         n1=atoi(s.c_str());
1047         netcharge=netcharge*n1;
1048       } else if (s.at(s.length()-1) == '*') {
1049         //radical processing
1050         s=s.substr(0,s.length()-1);
1051         n1=atoi(s.c_str());
1052         netradical=n1;
1053       };
1054       value=value.substr(n+1,value.length());
1055       if (value.length() > 0) if (value.at(0) == ';'){
1056         value=value.substr(1);
1057       };
1058     };
1059   };
1060 
1061   //stereo string extraction
1062 
1063   n1=indexOf(value,fsastart);
1064   if (n1>=0) {
1065     n2=indexOf(value,"}",n1);
1066     if (n2>0) {
1067       astereo=""+value.substr(n1+fsastart.length(),n2);
1068       value=value.substr(0,n1)+value.substr(n2+1);
1069     }
1070   };
1071   n1=indexOf(value,fsbstart);
1072   if (n1>=0) {
1073     n2=indexOf(value,"}",n1);
1074     if (n2>0) {
1075       bstereo=""+value.substr(n1+fsbstart.length(),n2);
1076       value=value.substr(0,n1)+value.substr(n2+1);
1077     }
1078   };
1079   //charges processing
1080   n1=indexOf(value,fchstart);
1081   if (n1>=0) {
1082     n2=indexOf(value,"}",n1);
1083     if (n2>0) {
1084       chargestring=""+value.substr(n1+fchstart.length(),n2);
1085       value=value.substr(0,n1)+value.substr(n2+1);
1086     };
1087   };
1088   if ((netcharge != 0) && (chargestring == "")) {
1089     chargestring = "1," +intToStr(abs(netcharge));
1090     if (netcharge < 0) chargestring=chargestring+"-"; else chargestring=chargestring+"+";
1091   };
1092   //radical processing
1093   n1=indexOf(value,fradstart);
1094   if (n1>=0) {
1095     n2=indexOf(value,"}",n1);
1096     if (n2>0) {
1097       radicalstring=""+value.substr(n1+fradstart.length(),n2);
1098       value=value.substr(0,n1)+value.substr(n2+1);
1099     };
1100   };
1101   if ((netradical != 0) && (radicalstring == "")) {
1102     radicalstring= "1," + intToStr(abs(netcharge)) + "*";
1103   };
1104 
1105   n1=indexOf(value,"]");
1106   if (n1 > 0) {
1107     value=value.substr(0,n1);  //by unknown reason it was n1-1
1108   };
1109 
1110   //Atom array analizing
1111   test=true;
1112   nt=0;
1113   nelements=0;
1114 
1115 
1116   while (test) {
1117     n1=indexOf(value,";");
1118     n2=indexOf(value,"[");
1119 
1120     if (n1<0) {
1121       n1=n2;
1122     } else {
1123       if ((n2<n1) && (n2>=0)) n1=n2;
1124     };
1125     mf=value.substr(0,n1);
1126     value=value.substr(n1,value.length());
1127     if (value.at(0)==';') value=value.substr(1,value.length());
1128     n1=1;
1129     if ((mf.at(0)>='0') && (mf.at(0)<='9')) {
1130       test=true;
1131       temp="";
1132       while (test) {
1133         temp=temp+mf.at(0);
1134         mf=mf.substr(1,mf.length());
1135         test=(mf.at(0)>='0') && (mf.at(0)<='9');
1136       };
1137       n1=atoi(temp.c_str());
1138     };
1139     //do not change n1==nFrag!!!
1140     nt=nt+n1;
1141   //Conversion of XxHHHH to XxHn
1142     n2=lastIndexOf(mf,"HH");
1143     while (n2>0) {
1144       if (n2<(mf.length()-2)) {
1145         ss=mf.substr(mf.length()-1,mf.length());
1146         k=atoi(ss.c_str())+1;//Integer.parseInt(ss)+1;
1147       } else k=2;
1148       mf=mf.substr(0,n2+1)+intToStr(k);
1149       n2=lastIndexOf(mf,"HH");
1150     };
1151     //passed to this point...
1152     //End convsrsion
1153     n2=indexOf(mf,"H");
1154     temp="0";
1155     if (n2>0)  {
1156       if (n2<(mf.length()-1)) {
1157         temp=mf.substr(n2+1,mf.length());
1158       } else temp="1";
1159       mf=mf.substr(0,n2);
1160     };
1161     n2=atoi(temp.c_str());//Integer.parseInt(temp);
1162     //do not change n2 - number of hydrogens...
1163 
1164     nH[nelements]=n2;
1165     nF[nelements]=n1;
1166     names[nelements]=mf;
1167     nelements++;
1168 
1169     test=value.at(0) != '[';
1170   };
1171 
1172   sa.Clear();
1173 
1174   acount=nt;
1175 
1176   //parsing and analizing...
1177   for (i=0; i<MAXFRAGS; i++) charges[i]=0;
1178   if (chargestring != "") while (chargestring.length() > 0) {
1179     n1=indexOf(chargestring,";");
1180     if (n1>0) {
1181       s=chargestring.substr(0,n1);
1182       chargestring=chargestring.substr(n1+1);
1183     } else {
1184       s=chargestring;
1185       chargestring="";
1186     };
1187     n1=indexOf(s,",");
1188     if (n1 > 0) {
1189       sa1=s.substr(0,n1);
1190       s=s.substr(n1+1);
1191       n2=1;
1192       if (indexOf(s,"-") > 0) n2=-1;
1193       s=s.substr(0,s.length()-1);
1194       n1=atoi(s.c_str());
1195       n1=n1*n2;
1196       n2=atoi(sa1.c_str());
1197       charges[n2-1]=n1;
1198     };
1199   };
1200   for (i=0; i<MAXFRAGS; i++) radicals[i]=0;
1201   if (radicalstring != "") while (radicalstring.length() > 0) {
1202     n1=indexOf(radicalstring,";");
1203     if (n1>0) {
1204       s=radicalstring.substr(0,n1);
1205       radicalstring=radicalstring.substr(n1+1);
1206     } else {
1207       s=radicalstring;
1208       radicalstring="";
1209     };
1210     n1=indexOf(s,",");
1211     if (n1 > 0) {
1212       sa1=s.substr(0,n1);
1213       s=s.substr(n1+1);
1214       n2=1;
1215       if (indexOf(s,"-") > 0) n2=-1;
1216       s=s.substr(0,s.length()-1);
1217       n2=atoi(sa1.c_str());
1218       radicals[n2-1]=1;
1219     };
1220   };
1221 //passed
1222 
1223 
1224 
1225   nt=0;
1226   ss="";
1227   bcount=0;
1228 
1229   for (i=0; i<nelements; i++) {
1230     s=names[i];
1231     n1=1;
1232     if (s.length()>1) if ((s.at(1)>='a') && (s.at(1)<='z')) n1=2;
1233     temp=s.substr(0,n1);
1234     if (n1<s.length()) sstore=s.substr(n1,s.length()); else sstore="";
1235     n1=nF[i];
1236     n2=OBElements::GetAtomicNum(temp.c_str());//Atom.positionofAtom(temp);
1237     nPrev=acount;
1238 
1239     for (j=1; j<=n1; j++) {
1240       fragIndex[nt]=nt+1;//sa.fragIndex=fragNo;
1241       nHydr[nt]=nH[i];      //sa.special=(byte)nH.getValue(i+1);
1242       nt++;
1243 
1244       aPosition[nt-1]=n2;
1245 
1246       if (sstore.length()>0) if (parseFormula(sstore,enumber)) { //bf=Formula.parseString(sstore);
1247         for (k=1; k<NELEMMAX; k++) if (enumber[k] > 0) for (m=1; m<=enumber[k]; m++) {
1248           fragIndex[acount]=nt+1;//sa.fragIndex=fragNo;  //Do not increment!
1249           nHydr[acount]=0;//special[acount]=0;
1250           aPosition[acount]=k;
1251           acount++;
1252 
1253           kk=hydrogenValency(k);//HVal[k];
1254           if (kk == 0) kk=1;
1255           bcount++;
1256           sbSpecial[bcount-1]=1;
1257           bondOrders[bcount-1]=kk;
1258           iA1[bcount-1]=nt-1;
1259           iA2[bcount-1]=acount-1;
1260         };
1261         //checking for nitrogen...
1262         //length analizing-extracting and addition of other atoms...
1263       };
1264       //charges analizing and restore
1265 
1266     if (charges[nt-1] != 0) {      //search for appropriate atom to put charge..
1267     if (nPrev == acount) {
1268       aCharge[nt-1]=charges[nt-1];
1269     } else  {
1270       if (charges[nt-1] < 0) {
1271         assignCharges(aPosition,iA1,iA2,aCharge,charges,bondOrders,8,nPrev,nt,acount,bcount);
1272         assignCharges(aPosition,iA1,iA2,aCharge,charges,bondOrders,7,nPrev,nt,acount,bcount);
1273         assignCharges(aPosition,iA1,iA2,aCharge,charges,bondOrders,16,nPrev,nt,acount,bcount);
1274         if (charges[nt-1] != 0) {
1275           aCharge[nt-1]=charges[nt-1];
1276         };
1277       } else {  //positive charge-at central atom
1278         aCharge[nt-1]=charges[nt-1];
1279       };
1280     };
1281   };
1282   if (radicals[nt-1] != 0) {      //search for appropriate atom to put charge..
1283     aRad[nt-1]=radicals[nt-1];
1284   };
1285   nPrev=acount;
1286 };
1287 
1288   };
1289 
1290   //Bond analizing....
1291   value=value.substr(1,value.length());
1292 
1293   nt=0;
1294   ss="";
1295   test=true;
1296   while ((value.length()>0) && test) {
1297     n1=indexOf(value,";");
1298     n2=indexOf(value,"]");
1299     if (n1<0) n1=n2; else if ((n2<n1) && (n2>=0)) n1=n2;
1300     mf="";
1301     if ((n1>=0) && (value.length() > n1)) {
1302       mf=value.substr(0,n1);
1303       value=value.substr(n1+1,value.length());
1304     } else {
1305       test=false;
1306       mf=value;
1307       value="";
1308     };
1309     nt++;
1310     //parsing each fragment
1311     while (mf.length()>0) {
1312       n1=indexOf(mf,",");
1313       if (n1>=0) {
1314         s=mf.substr(0,n1);
1315         mf=mf.substr(n1+1,mf.length());
1316       } else {
1317         s=mf;
1318         mf="";
1319       };
1320       n1=atoi(s.c_str());
1321       bcount++;
1322       bondOrders[bcount-1]=0;
1323       iA1[bcount-1]=nt-1;
1324       iA2[bcount-1]=n1-1;
1325     };
1326 
1327     if (value.length()>0) if (value.at(0)==']') value=""; //end parsing
1328   };
1329 
1330   //Alternation
1331   alternate(aPosition,aCharge,aRad,nHydr,iA1,iA2,bondOrders,acount,bcount);
1332   generateDiagram(iA1,iA2,rx,ry,acount,bcount);
1333   for (i=0; i<bcount; i++) if (bondOrders[i] == 1) stereoBonds[i]=-1; //flags for bonds, which might be stereo
1334   implementAtomStereo(iA1,iA2,stereoBonds,rx,ry,acount,bcount,astereo);
1335   implementBondStereo(iA1,iA2,rx,ry,acount,bcount,bstereo);
1336 
1337 
1338   if (pmol != nullptr) {
1339     for (i=0; i<acount; i++) {
1340       sa.Clear();
1341       sa.SetAtomicNum(aPosition[i]);
1342       if (aCharge[i] != 0) sa.SetFormalCharge(aCharge[i]);
1343       if (aRad[i] != 0) sa.SetSpinMultiplicity(1);
1344       sa.SetVector(rx[i],ry[i],0.0);
1345       pmol->AddAtom(sa);
1346     };
1347     for (i=0; i<bcount; i++) {
1348       flags=0;
1349       if (stereoBonds[i] == 1) flags=OB_WEDGE_BOND; else
1350       if (stereoBonds[i] == 2) flags=OB_HASH_BOND;
1351       pmol->AddBond(iA1[i]+1,iA2[i]+1,bondOrders[i],flags);
1352     };
1353   };
1354 };
1355 
assignCharges(const std::vector<int> aPosition,const std::vector<int> iA1,const std::vector<int> iA2,std::vector<int> & aCharges,std::vector<int> & charges,std::vector<int> & bondOrder,int aPos,int nPrev,int nt,int acount,int bcount)1356   void MCDLFormat::assignCharges(const std::vector <int> aPosition, const std::vector <int> iA1,
1357   const std::vector <int> iA2, std::vector <int>& aCharges, std::vector <int>& charges,
1358   std::vector <int>& bondOrder, int aPos, int nPrev, int nt, int acount, int bcount){
1359   //set negative charges
1360     int k,j,n;
1361 
1362   //return;
1363 
1364   for (k=nPrev; k<acount; k++) {
1365     if (aPosition[k] == aPos) {
1366         aCharges[k]=-1;
1367         charges[nt-1]=charges[nt-1]+1;
1368     for (j=0; j<bcount; j++) {
1369       if ((((iA1[j]+1) == nt) && (iA2[j] == k)) || ((iA1[j] == k) && ((iA2[j]+1) == nt))) {
1370       n=bondOrder[j];
1371             if (n > 1) {
1372               n=n-1;
1373         bondOrder[j]=n;
1374           };
1375           };
1376         };
1377     };
1378       if (charges[nt-1] == 0) break;
1379     };
1380   };
1381 
1382 
indexOf(const string instring,const string substring,int fromPos)1383   int MCDLFormat::indexOf(const string instring, const string substring, int fromPos) {
1384     int result=instring.find(substring,fromPos);
1385     if (result == string::npos) result=-1;
1386     if (result >= instring.length()) result=-1;
1387       return result;
1388   };
1389 
lastIndexOf(const string instring,const string substring)1390   int MCDLFormat::lastIndexOf(const string instring, const string substring) {
1391     int result,n;
1392   bool test;
1393 
1394   result=-1;
1395   test=true;
1396   n=-1;
1397   while (test) {
1398     n=instring.find(substring,n+1);
1399     if (n == string::npos)
1400       test=false;
1401     else {
1402         result=n;
1403     };
1404   };
1405     //int result=instring.find_last_of(substring);
1406     //if  (result == string::npos) result=-1;
1407     //if  (result > (instring.length()-substring.length())) result=-1;
1408     return result;
1409   };
1410 
parseFormula(const string formulaString,std::vector<int> & enumber)1411 bool MCDLFormat::parseFormula(const string formulaString, std::vector <int>& enumber) {
1412   //vector<string> items;
1413   unsigned int i, n, k, n1, n2;//,j,nStart;
1414   string s;
1415   bool test;
1416   string asym;
1417   string value=formulaString;
1418 
1419   for (i = 0; i<NELEMMCDL; i++) enumber[i] = 0;
1420 
1421   for (i = 1; i<NELEMMCDL; i++) if (strlen(OBElements::GetSymbol(i)) == 2) {
1422       test=true;
1423     asym=OBElements::GetSymbol(i);
1424       while (test) {
1425         test=false;
1426         n=indexOf(value,asym);
1427         if (n>=0) {
1428           test=true;
1429           value=value.substr(0,n)+value.substr(n+asym.length(),value.length());
1430           k=1;
1431           if (n<value.length()) if ((value.at(n)>='0') && (value.at(n)<='9')) {
1432             n1=n;
1433             n2=n;
1434             while ((n2<(value.length()-1)) && (value.at(n2)>='0') && (value.at(n2)<='9')) n2++;
1435             if (! ((value.at(n2)>='0') && (value.at(n2)<='9'))) n2--;
1436             s=value.substr(n1,n2+1);
1437             k=atoi(s.c_str());
1438             value=value.substr(0,n1)+value.substr(n2+1,value.length());
1439           };
1440           enumber[i]=enumber[i]+k;
1441         };
1442       };
1443     };
1444   for (i = 1; i<NELEMMCDL; i++) if (strlen(OBElements::GetSymbol(i)) == 1) {
1445       test=true;
1446     asym=OBElements::GetSymbol(i);
1447       while (test) {
1448         test=false;
1449         n=indexOf(value,asym);
1450         if (n>=0) {
1451           test=true;
1452           value=value.substr(0,n)+value.substr(n+asym.length(),value.length());
1453           k=1;
1454           if (n<value.length()) if ((value.at(n)>='0') && (value.at(n)<='9')) {
1455             n1=n;
1456             n2=n;
1457             while ((n2<(value.length()-1)) && (value.at(n2)>='0') && (value.at(n2)<='9')) n2++;
1458             if (! ((value.at(n2)>='0') && (value.at(n2)<='9'))) n2--;
1459             s=value.substr(n1,n2+1);
1460       k=atoi(s.c_str());
1461             value=value.substr(0,n1)+value.substr(n2+1,value.length());
1462           };
1463           enumber[i]=enumber[i]+k;
1464         };
1465       };
1466     };
1467   return (value.length() == 0);
1468 };
1469 
getMolTitle(string & line)1470   string MCDLFormat::getMolTitle(string & line) {
1471     string::size_type n,k;
1472   string result;
1473   n=line.find(ftitlestart);
1474   if (n != string::npos) {
1475     k=line.find("}",n+ftitlestart.size());
1476     if (k != string::npos) {
1477     result=line.substr(n+ftitlestart.length(),k-n-ftitlestart.length());
1478     line=line.substr(0,n+1)+line.substr(k+1);
1479     }
1480   }
1481   return result;
1482   }
1483 
1484 
1485   ////////////////////////////////////////////////////
1486 
1487 //Make an instance of the format class
1488 MCDLFormat theMCDLFormat;
1489 
1490 /////////////////////////////////////////////////////////////////
1491 
ReadMolecule(OBBase * pOb,OBConversion * pConv)1492 bool MCDLFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
1493 {
1494   OBMol* pmol = pOb->CastAndClear<OBMol>();
1495   if (pmol == nullptr)
1496       return false;
1497 
1498   istream& ifs = *pConv->GetInStream();
1499 
1500   pmol->BeginModify();
1501 
1502   int dim=0;
1503   pmol->SetDimension(dim);
1504 
1505   string line="";
1506     if(ifs.good()) getline(ifs, line);
1507 
1508   string molTitle=getMolTitle(line);
1509 
1510   if (molTitle.length() > 0) pmol->SetTitle(molTitle.c_str());
1511   if (line.length() > 0) setMCDL(line,pmol,molTitle);
1512 
1513   pmol->EndModify();
1514 
1515   return true;
1516 }
1517 
1518 ////////////////////////////////////////////////////////////////
1519 
WriteMolecule(OBBase * pOb,OBConversion * pConv)1520 bool MCDLFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
1521 {
1522   OBMol* pmol = dynamic_cast<OBMol*>(pOb);
1523   if (pmol == nullptr) return false;
1524 
1525   std::ostream & ofs = *pConv->GetOutStream();
1526 
1527   //To use an output option
1528   string title=pmol->GetTitle();
1529   if (title.length() > 0) title=ftitlestart+title+"}";
1530   ofs << getMCDL(pmol,false) << title << endl;
1531   // prepareTest(pmol,ofs);
1532   //generateDiagram(pmol,ofs);
1533 
1534   return true; //or false to stop converting
1535 }
1536 
1537 } //namespace OpenBabel
1538 
1539