OSDN Git Service

Support load/save of NBO info
[molby/Molby.git] / MolLib / Molecule.c
1 /*
2  *  Molecule.c
3  *
4  *  Created by Toshi Nagata on 06/03/11.
5  *  Copyright 2006 Toshi Nagata. All rights reserved.
6  *
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation version 2 of the License.
10  
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15  */
16
17 #include "MolLib.h"
18 #include <string.h>
19 #include <stddef.h>
20 #include <ctype.h>
21 #include <math.h>
22 #include <float.h>
23
24 #include "Missing.h"
25 #include "Dcd.h"
26 #include "MD/MDCore.h"
27 #include "MD/MDPressure.h"
28
29 static Molecule *sMoleculeRoot = NULL;
30 static int sMoleculeUntitledCount = 0;
31
32 Int gSizeOfAtomRecord = sizeof(Atom);
33
34 /*  These are the pasteboard data type. Since the internal representation of the
35         pasteboard data includes binary data that may be dependent on the software version,
36     the revision number is appended to these strings on startup (See MyApp::OnInit())  */
37 char *gMoleculePasteboardType = "Molecule";
38 char *gParameterPasteboardType = "Parameter";
39
40 #pragma mark ====== Utility function ======
41
42 int
43 strlen_limit(const char *s, int limit)
44 {
45         int len;
46         for (len = 0; *s != 0 && (limit < 0 || len < limit); s++, len++);
47         return len;
48 }
49
50 #pragma mark ======  Atom handling  ======
51
52 static Atom *
53 s_AtomDuplicate(Atom *dst, const Atom *src, Int copy_frame)
54 {
55         if (dst == NULL) {
56                 dst = (Atom *)malloc(gSizeOfAtomRecord);
57                 if (dst == NULL)
58                         return NULL;
59         }
60         memmove(dst, src, gSizeOfAtomRecord);
61         if (src->aniso != NULL) {
62                 dst->aniso = (Aniso *)malloc(sizeof(Aniso));
63                 if (dst->aniso != NULL)
64                         memmove(dst->aniso, src->aniso, sizeof(Aniso));
65         }
66         if (src->frames != NULL && copy_frame) {
67                 dst->frames = (Vector *)malloc(sizeof(Vector) * src->nframes);
68                 if (dst->frames != NULL) {
69                         memmove(dst->frames, src->frames, sizeof(Vector) * src->nframes);
70                         dst->nframes = src->nframes;
71                 } else {
72                         dst->nframes = 0;
73                 }
74         }
75         if (src->connect.count > ATOM_CONNECT_LIMIT) {
76                 dst->connect.u.ptr = NULL;
77                 dst->connect.count = 0;
78                 NewArray(&(dst->connect.u.ptr), &(dst->connect.count), sizeof(Int), src->connect.count);
79                 memmove(dst->connect.u.ptr, src->connect.u.ptr, sizeof(Int) * src->connect.count);
80         }
81         if (src->anchor != NULL) {
82                 dst->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
83                 if (dst->anchor != NULL)
84                         memmove(dst->anchor, src->anchor, sizeof(PiAnchor));
85                 if (dst->anchor->connect.count > ATOM_CONNECT_LIMIT) {
86                         dst->anchor->connect.u.ptr = NULL;
87                         dst->anchor->connect.count = 0;
88                         NewArray(&(dst->anchor->connect.u.ptr), &(dst->anchor->connect.count), sizeof(Int), src->anchor->connect.count);
89                         memmove(dst->anchor->connect.u.ptr, src->anchor->connect.u.ptr, sizeof(Int) * src->anchor->connect.count);
90                 }
91                 if (dst->anchor->ncoeffs > 0) {
92                         NewArray(&(dst->anchor->coeffs), &(dst->anchor->ncoeffs), sizeof(Double), src->anchor->ncoeffs);
93                         memmove(dst->anchor->coeffs, src->anchor->coeffs, sizeof(Double) * src->anchor->ncoeffs);
94                 }
95         }
96         return dst;
97 }
98
99 Atom *
100 AtomDuplicate(Atom *dst, const Atom *src)
101 {
102         return s_AtomDuplicate(dst, src, 1);
103 }
104
105 Atom *
106 AtomDuplicateNoFrame(Atom *dst, const Atom *src)
107 {
108         return s_AtomDuplicate(dst, src, 0);
109 }
110
111 void
112 AtomClean(Atom *ap)
113 {
114         if (ap->aniso != NULL) {
115                 free(ap->aniso);
116                 ap->aniso = NULL;
117         }
118         if (ap->frames != NULL) {
119                 free(ap->frames);
120                 ap->frames = NULL;
121                 ap->nframes = 0;
122         }
123         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
124                 ap->connect.count = 0;
125                 free(ap->connect.u.ptr);
126                 ap->connect.u.ptr = NULL;
127         }
128 }
129
130 void
131 CubeRelease(Cube *cp)
132 {
133         if (cp != NULL) {
134                 if (cp->dp != NULL)
135                         free(cp->dp);
136                 free(cp);
137         }
138 }
139
140 void
141 BasisSetRelease(BasisSet *bset)
142 {
143         int i;
144         if (bset == NULL)
145                 return;
146         if (bset->shells != NULL)
147                 free(bset->shells);
148         if (bset->priminfos != NULL)
149                 free(bset->priminfos);
150         if (bset->mo != NULL)
151                 free(bset->mo);
152         if (bset->cns != NULL)
153                 free(bset->cns);
154         if (bset->moenergies != NULL)
155                 free(bset->moenergies);
156         if (bset->scfdensities != NULL)
157                 free(bset->scfdensities);
158 /*      if (bset->pos != NULL)
159                 free(bset->pos); */
160         if (bset->nuccharges != NULL)
161                 free(bset->nuccharges);
162         if (bset->cubes != NULL) {
163                 for (i = 0; i < bset->ncubes; i++) {
164                         CubeRelease(bset->cubes[i]);
165                 }
166                 free(bset->cubes);
167         }
168         free(bset);
169 }
170
171 Int *
172 AtomConnectData(AtomConnect *ac)
173 {
174         if (ac == NULL)
175                 return NULL;
176         return ATOM_CONNECT_PTR(ac);
177 }
178
179 void
180 AtomConnectResize(AtomConnect *ac, Int nconnects)
181 {
182         Int *p;
183         if (ac == NULL)
184                 return;
185         if (nconnects <= ATOM_CONNECT_LIMIT) {
186                 if (ac->count > ATOM_CONNECT_LIMIT) {
187                         p = ac->u.ptr;
188                         memmove(ac->u.data, p, sizeof(Int) * nconnects);
189                         free(p);
190                 }
191         } else {
192                 if (ac->count <= ATOM_CONNECT_LIMIT) {
193                         p = NULL;
194                         ac->count = 0;
195                         NewArray(&p, &(ac->count), sizeof(Int), nconnects);
196                         memmove(p, ac->u.data, sizeof(Int) * ac->count);
197                         ac->u.ptr = p;
198                 } else if (ac->count < nconnects) {
199                         /*  Reallocate  */
200                         AssignArray(&(ac->u.ptr), &(ac->count), sizeof(Int), nconnects - 1, NULL);
201                 }
202         }
203         ac->count = nconnects;
204 }
205
206 void
207 AtomConnectInsertEntry(AtomConnect *ac, Int idx, Int connect)
208 {
209         Int n, *p;
210         if (ac == NULL)
211                 return;
212         if (idx > ac->count)
213                 idx = ac->count;
214         else if (idx < 0) {
215                 /*  Insert after the last component that is smaller than connect
216                     (i.e. keep them sorted)  */
217                 p = ATOM_CONNECT_PTR(ac);
218                 for (idx = 0; idx < ac->count; idx++) {
219                         if (p[idx] >= connect)
220                                 break;
221                 }
222         }
223         AtomConnectResize(ac, ac->count + 1);
224         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the end  */
225         p = ATOM_CONNECT_PTR(ac);
226         if (n > 0) {
227                 memmove(p + idx + 1, p + idx, sizeof(Int) * n);
228         }
229         p[idx] = connect;
230 }
231
232 void
233 AtomConnectDeleteEntry(AtomConnect *ac, Int idx)
234 {
235         Int n, *p;
236         if (ac == NULL)
237                 return;
238         if (idx < 0 || idx >= ac->count)
239                 return;
240         n = ac->count - idx - 1;  /*  Number of entries to be moved towards the top  */
241         p = ATOM_CONNECT_PTR(ac);
242         if (n > 0) {
243                 memmove(p + idx, p + idx + 1, sizeof(Int) * n);
244         }
245         AtomConnectResize(ac, ac->count - 1);
246 }
247
248 int
249 AtomConnectHasEntry(AtomConnect *ac, Int ent)
250 {
251         Int n, *p;
252         if (ac == NULL)
253                 return 0;
254         p = ATOM_CONNECT_PTR(ac);
255         for (n = 0; n < ac->count; n++) {
256                 if (ent == p[n])
257                         return 1;
258         }
259         return 0;
260 }
261
262 #pragma mark ====== Accessor types ======
263
264 MolEnumerable *
265 MolEnumerableNew(Molecule *mol, int kind)
266 {
267         MolEnumerable *mseq = (MolEnumerable *)calloc(sizeof(MolEnumerable), 1);
268         if (mseq != NULL) {
269                 mseq->mol = MoleculeRetain(mol);
270                 mseq->kind = kind;
271         }
272         return mseq;
273 }
274
275 void
276 MolEnumerableRelease(MolEnumerable *mseq)
277 {
278         if (mseq != NULL) {
279                 MoleculeRelease(mseq->mol);
280                 free(mseq);
281         }
282 }
283
284 AtomRef *
285 AtomRefNew(Molecule *mol, int idx)
286 {
287         AtomRef *aref = (AtomRef *)calloc(sizeof(AtomRef), 1);
288         if (aref != NULL) {
289                 aref->mol = MoleculeRetain(mol);
290                 aref->idx = idx;
291         }
292         return aref;
293 }
294
295 void
296 AtomRefRelease(AtomRef *aref)
297 {
298         if (aref != NULL) {
299                 MoleculeRelease(aref->mol);
300                 free(aref);
301         }
302 }
303
304 #pragma mark ====== Creation of molecules ======
305
306 Molecule *
307 MoleculeNew(void)
308 {
309         char name[40];
310         Molecule *mp = (Molecule *)calloc(sizeof(Molecule), 1);
311         if (mp == NULL)
312                 Panic("Cannot allocate new molecule record");
313         snprintf(name, sizeof name, "Untitled %d", sMoleculeUntitledCount++);
314         ObjectInit((Object *)mp, (Object **)&sMoleculeRoot, name);
315         mp->mview = MainView_new();
316         mp->mview->mol = mp;
317         return mp;
318 }
319
320 Molecule *
321 MoleculeNewWithName(const char *name)
322 {
323         Molecule *mp = MoleculeNew();
324         MoleculeSetName(mp, name);
325         return mp;
326 }
327
328 Molecule *
329 MoleculeInitWithAtoms(Molecule *mp, const Atom *atoms, int natoms)
330 {
331         int i;
332         if (mp == NULL)
333                 mp = MoleculeNew();
334         if (natoms == 0)
335                 return mp;
336         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
337                 Panic("Cannot allocate memory for atoms");
338         for (i = 0; i < natoms; i++)
339                 AtomDuplicate(mp->atoms + i, atoms + i);
340         mp->nframes = -1;  /*  Should be recalculated later  */
341         return mp;
342 }
343
344 Molecule *
345 MoleculeInitWithMolecule(Molecule *mp2, Molecule *mp)
346 {
347         int i, n;
348         MoleculeFlushFrames(mp);
349         MoleculeInitWithAtoms(mp2, mp->atoms, mp->natoms);
350         if (mp->nbonds > 0) {
351                 if (NewArray(&mp2->bonds, &mp2->nbonds, sizeof(Int)*2, mp->nbonds) == NULL)
352                         goto error;
353                 memmove(mp2->bonds, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
354         }
355         if (mp->nangles > 0) {
356                 if (NewArray(&mp2->angles, &mp2->nangles, sizeof(Int)*3, mp->nangles) == NULL)
357                         goto error;
358                 memmove(mp2->angles, mp->angles, sizeof(Int) * 3 * mp->nangles);
359         }
360         if (mp->ndihedrals > 0) {
361                 if (NewArray(&mp2->dihedrals, &mp2->ndihedrals, sizeof(Int)*4, mp->ndihedrals) == NULL)
362                         goto error;
363                 memmove(mp2->dihedrals, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
364         }
365         if (mp->nimpropers > 0) {
366                 if (NewArray(&mp2->impropers, &mp2->nimpropers, sizeof(Int)*4, mp->nimpropers) == NULL)
367                         goto error;
368                 memmove(mp2->impropers, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
369         }
370         if (mp->nresidues > 0) {
371                 if (NewArray(&mp2->residues, &mp2->nresidues, sizeof(mp->residues[0]), mp->nresidues) == NULL)
372                         goto error;
373                 memmove(mp2->residues, mp->residues, sizeof(mp->residues[0]) * mp->nresidues);
374         }
375         if (mp->cell != NULL) {
376                 mp2->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
377                 memmove(mp2->cell, mp->cell, sizeof(XtalCell));
378         }
379         if (mp->nsyms > 0) {
380                 NewArray(&(mp2->syms), &(mp2->nsyms), sizeof(Transform), mp->nsyms);
381                 memmove(mp2->syms, mp->syms, sizeof(Transform) * mp2->nsyms);
382         }
383
384         /*      mp2->useFlexibleCell = mp->useFlexibleCell; */
385         if (mp->nframe_cells > 0) {
386                 if (NewArray(&mp2->frame_cells, &mp2->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells) == NULL)
387                         goto error;
388                 memmove(mp2->frame_cells, mp->frame_cells, sizeof(Vector) * 4 * mp->nframe_cells);
389         }
390         
391         if (mp->nmolprops > 0) {
392                 if (NewArray(&mp2->molprops, &mp2->nmolprops, sizeof(MolProp), mp->nmolprops) == NULL)
393                         goto error;
394                 n = MoleculeGetNumberOfFrames(mp);
395                 for (i = 0; i < mp2->nmolprops; i++) {
396                         mp2->molprops[i].propname = strdup(mp->molprops[i].propname);
397                         mp2->molprops[i].propvals = (Double *)malloc(sizeof(Double) * n);
398                         memcpy(mp2->molprops[i].propvals, mp->molprops[i].propvals, sizeof(Double) * n);
399                 }
400         }
401         
402         /* FIXME: should bset (basis set info) and elpot be duplicated or not?  */
403
404         if (mp->par != NULL)
405                 mp2->par = ParameterDuplicate(mp->par);
406         if (mp->arena != NULL) {
407                 md_arena_new(mp2);
408                 md_arena_init_from_arena(mp2->arena, mp->arena);
409         }
410         
411         return mp2;
412   error:
413         Panic("Cannot allocate memory for duplicate molecule");
414         return NULL;  /*  Not reached  */
415 }
416
417 /*  Assign a unique name to this parameter record  */
418 void
419 MoleculeSetName(Molecule *mp, const char *name)
420 {
421         ObjectSetName((Object *)mp, name, (Object *)sMoleculeRoot);
422 }
423
424 const char *
425 MoleculeGetName(Molecule *mp)
426 {
427         return ObjectGetName((Object *)mp);
428 }
429
430 Molecule *
431 MoleculeWithName(const char *name)
432 {
433         return (Molecule *)ObjectWithName(name, (Object *)sMoleculeRoot);
434 }
435
436 void
437 MoleculeSetPath(Molecule *mol, const char *fname)
438 {
439         char *buf, *cwd;
440         if (mol == NULL || fname == NULL)
441                 return;
442         if (fname[0] == '/' || (isalpha(fname[0]) && fname[1] == ':')) {
443                 /*  Full path  */
444                 buf = strdup(fname);
445         } else {
446                 cwd = getcwd(NULL, 0);
447                 asprintf(&buf, "%s/%s", cwd, fname);
448                 free(cwd);
449         }
450         if (mol->path != NULL) {
451                 if (strcmp(mol->path, buf) == 0) {
452                         /*  No change  */
453                         free(buf);
454                         return;
455                 }
456                 free((void *)(mol->path));
457         }
458         mol->path = buf;
459         if (mol->arena != NULL) {
460                 md_close_output_files(mol->arena);
461         }
462 }
463
464 const char *
465 MoleculeGetPath(Molecule *mol)
466 {
467         if (mol == NULL)
468                 return NULL;
469         return mol->path;
470 }
471
472 Molecule *
473 MoleculeRetain(Molecule *mp)
474 {
475         ObjectIncrRefCount((Object *)mp);
476         MoleculeRetainExternalObj(mp);
477         return mp;
478 }
479
480 void
481 MoleculeClear(Molecule *mp)
482 {
483         int i;
484         if (mp == NULL)
485                 return;
486         if (mp->arena != NULL) {
487                 md_arena_set_molecule(mp->arena, NULL);
488                 mp->arena = NULL;
489         }
490         if (mp->par != NULL) {
491                 ParameterRelease(mp->par);
492                 mp->par = NULL;
493         }
494         if (mp->atoms != NULL) {
495                 for (i = 0; i < mp->natoms; i++)
496                         AtomClean(mp->atoms + i);
497                 free(mp->atoms);
498                 mp->atoms = NULL;
499                 mp->natoms = 0;
500         }
501         if (mp->bonds != NULL) {
502                 free(mp->bonds);
503                 mp->bonds = NULL;
504                 mp->nbonds = 0;
505         }
506         if (mp->angles != NULL) {
507                 free(mp->angles);
508                 mp->angles = NULL;
509                 mp->nangles = 0;
510         }
511         if (mp->dihedrals != NULL) {
512                 free(mp->dihedrals);
513                 mp->dihedrals = NULL;
514                 mp->ndihedrals = 0;
515         }
516         if (mp->impropers != NULL) {
517                 free(mp->impropers);
518                 mp->impropers = NULL;
519                 mp->nimpropers = 0;
520         }
521         if (mp->residues != NULL) {
522                 free(mp->residues);
523                 mp->residues = NULL;
524                 mp->nresidues = 0;
525         }
526         if (mp->cell != NULL) {
527                 free(mp->cell);
528                 mp->cell = NULL;
529         }
530         if (mp->syms != NULL) {
531                 free(mp->syms);
532                 mp->syms = NULL;
533                 mp->nsyms = 0;
534         }
535         if (mp->selection != NULL) {
536                 IntGroupRelease(mp->selection);
537                 mp->selection = NULL;
538         }
539         if (mp->frame_cells != NULL) {
540                 free(mp->frame_cells);
541                 mp->frame_cells = NULL;
542                 mp->nframe_cells = 0;
543         }
544         if (mp->bset != NULL) {
545                 BasisSetRelease(mp->bset);
546                 mp->bset = NULL;
547         }
548         if (mp->mcube != NULL) {
549                 MoleculeDeallocateMCube(mp->mcube);
550                 mp->mcube = NULL;
551         }
552         if (mp->molprops != NULL) {
553                 for (i = 0; i < mp->nmolprops; i++) {
554                         free(mp->molprops[i].propname);
555                         free(mp->molprops[i].propvals);
556                 }
557                 free(mp->molprops);
558                 mp->molprops = NULL;
559                 mp->nmolprops = 0;
560         }
561         if (mp->par != NULL) {
562                 ParameterRelease(mp->par);
563                 mp->par = NULL;
564         }
565         if (mp->elpots != NULL) {
566                 free(mp->elpots);
567                 mp->elpots = NULL;
568                 mp->nelpots = 0;
569         }
570         if (mp->path != NULL) {
571                 free((void *)mp->path);
572                 mp->path = NULL;
573         }
574 }
575
576 void
577 MoleculeRelease(Molecule *mp)
578 {
579         if (mp == NULL)
580                 return;
581         MoleculeReleaseExternalObj(mp);
582         if (ObjectDecrRefCount((Object *)mp) == 0) {
583                 MoleculeClear(mp);
584                 mp->mview->mol = NULL;
585                 MainView_release(mp->mview);
586                 ObjectDealloc((Object *)mp, (Object **)&sMoleculeRoot);
587         }
588 }
589
590 void
591 MoleculeExchange(Molecule *mp1, Molecule *mp2)
592 {
593         Molecule mp_temp;
594         struct MainView *mview1, *mview2;
595         struct MDArena *arena1, *arena2;
596         /*  mview and arena must be kept as they are  */
597         mview1 = mp1->mview;
598         mview2 = mp2->mview;
599         arena1 = mp1->arena;
600         arena2 = mp2->arena;
601         /*  'natoms' is the first member to be copied  */
602         int ofs = offsetof(Molecule, natoms);
603         memmove((char *)(&mp_temp) + ofs, (char *)mp1 + ofs, sizeof(Molecule) - ofs);
604         memmove((char *)mp1 + ofs, (char *)mp2 + ofs, sizeof(Molecule) - ofs);
605         memmove((char *)mp2 + ofs, (char *)(&mp_temp) + ofs, sizeof(Molecule) - ofs);
606         mp1->arena = arena1;
607         mp2->arena = arena2;
608         mp1->mview = mview1;
609         mp2->mview = mview2;
610 /*      if (mp1->arena != NULL && mp1->arena->mol == mp2)
611                 mp1->arena->mol = mp1;
612         if (mp1->arena != NULL && mp1->arena->xmol == mp2)
613                 mp1->arena->xmol = mp1;
614         if (mp2->arena != NULL && mp2->arena->mol == mp1)
615                 mp2->arena->mol = mp2;
616         if (mp2->arena != NULL && mp2->arena->xmol == mp1)
617                 mp2->arena->xmol = mp2; */
618 }
619
620 #pragma mark ====== Mutex ======
621
622 void
623 MoleculeLock(Molecule *mol)
624 {
625         if (mol == NULL || mol->mutex == NULL)
626                 return;
627         MoleculeCallback_lockMutex(mol->mutex);
628 }
629
630 void
631 MoleculeUnlock(Molecule *mol)
632 {
633         if (mol == NULL || mol->mutex == NULL)
634                 return;
635         MoleculeCallback_unlockMutex(mol->mutex);
636 }
637
638 #pragma mark ====== Modify count ======
639
640 void
641 MoleculeIncrementModifyCount(Molecule *mp)
642 {
643         if (mp != NULL) {
644                 if (++(mp->modifyCount) == 1)
645                         MoleculeCallback_notifyModification(mp, 0);
646         /*      fprintf(stderr, "MoleculeIncrementModifyCount: %d\n", mp->modifyCount); */
647         }
648 }
649
650 void
651 MoleculeClearModifyCount(Molecule *mp)
652 {
653         if (mp != NULL) {
654                 mp->modifyCount = 0;
655         /*      fprintf(stderr, "MoleculeClearModifyCount: %d\n", mp->modifyCount); */
656         }
657 }
658
659 #pragma mark ====== File handling functions ======
660
661 static const char *
662 guessMoleculeType(const char *fname)
663 {
664         char buf[1024], *p;
665         FILE *fp;
666         const char *retval = NULL;
667         fp = fopen(fname, "rb");
668         if (fp != NULL) {
669                 memset(buf, 0, sizeof buf);
670                 if (fread(buf, 1, sizeof buf - 1, fp) > 0) {
671                         if (strncmp(buf, "PSF", 3) == 0)
672                                 retval = "psf";
673                         else if (((p = strstr(buf, "ATOM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r'))
674                         || ((p = strstr(buf, "HETATM")) != NULL && (p == buf || p[-1] == '\n' || p[-1] == '\r')))
675                                 retval = "pdb";
676                         else
677                                 retval = "???";  /*  unknown  */
678                 }
679                 fclose(fp);
680         }
681         return retval;
682 }
683
684 static int
685 guessElement(Atom *ap)
686 {
687         int atomicNumber = -1;
688         if (ap->atomicNumber > 0)
689                 atomicNumber = ap->atomicNumber;
690         else {
691                 atomicNumber = GuessAtomicNumber(ap->element, ap->weight);
692                 if (atomicNumber <= 0 && ap->aname[0] != 0)
693                         atomicNumber = GuessAtomicNumber(ap->aname, ap->weight);
694         }
695         if (atomicNumber >= 0) {
696                 ap->atomicNumber = atomicNumber;
697                 if (ap->weight <= 0)
698                         ap->weight = WeightForAtomicNumber(atomicNumber);
699                 if (ap->element[0] == 0)
700                         ElementToString(atomicNumber, ap->element);
701         }
702         return atomicNumber;
703 }
704
705 static int
706 sReadLineWithInterrupt(char *buf, int size, FILE *stream, int *lineNumber)
707 {
708         static int lastLineNumber = 0;
709         if (lineNumber != NULL) {
710                 if (*lineNumber == 0)
711                         lastLineNumber = 0;
712                 else if (*lineNumber >= lastLineNumber + 1000) {
713                         if (MyAppCallback_checkInterrupt() != 0)
714                                 return -1;  /*  User interrupt  */
715                         lastLineNumber = *lineNumber;
716                 }
717         }
718         return ReadLine(buf, size, stream, lineNumber);
719 }
720
721 static int
722 s_append_asprintf(char **buf, const char *fmt, ...)
723 {
724         int len;
725         char *s;
726         va_list va;
727         va_start(va, fmt);
728         vasprintf(&s, fmt, va);
729         len = (*buf == NULL ? 0 : strlen(*buf));
730         if (s == NULL)
731                 return len;
732         len += strlen(s);
733         if (*buf == NULL) {
734                 *buf = malloc(len + 1);
735                 **buf = 0;
736         } else {
737                 *buf = realloc(*buf, len + 1);
738         }
739         strcat(*buf, s);
740         free(s);
741         return len;
742 }
743
744 int
745 MoleculeLoadFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
746 {
747         int retval;
748         if (ftype == NULL || *ftype == 0) {
749                 const char *cp;
750                 cp = strrchr(fname, '.');
751                 if (cp != NULL)
752                         ftype = cp + 1;
753                 else {
754                         cp = guessMoleculeType(fname);
755                         if (strcmp(cp, "???") != 0)
756                                 ftype = cp;
757                 }
758         }
759         if (strcasecmp(ftype, "psf") == 0) {
760                 retval = MoleculeLoadPsfFile(mp, fname, errbuf);
761         } else if (strcasecmp(ftype, "pdb") == 0) {
762                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
763         } else if (strcasecmp(ftype, "tep") == 0) {
764                 retval = MoleculeLoadTepFile(mp, fname, errbuf);
765         } else if (strcasecmp(ftype, "res") == 0 || strcasecmp(ftype, "ins") == 0) {
766                 retval = MoleculeLoadShelxFile(mp, fname, errbuf);
767         } else if (strcasecmp(ftype, "fchk") == 0 || strcasecmp(ftype, "fch") == 0) {
768                 retval = MoleculeLoadGaussianFchkFile(mp, fname, errbuf);
769         } else {
770                 s_append_asprintf(errbuf, "Unknown format %s", ftype);
771                 return 1;
772         }
773 /*      if (retval != 0) {
774                 retval = MoleculeLoadPsfFile(mp, fname, errbuf, errbufsize);
775         } */
776         if (retval == 0)
777                 MoleculeSetPath(mp, fname);
778         return retval;
779 }
780
781 int
782 MoleculeLoadMbsfFile(Molecule *mp, const char *fname, char **errbuf)
783 {
784         FILE *fp;
785         char buf[1024];
786         int i, j, k, err, fn, nframes, nwarnings;
787         int lineNumber;
788         int ibuf[12];
789         Int iibuf[4];
790         double dbuf[12];
791         int mview_ibuf[18];
792         double mview_dbuf[10];
793         char cbuf[12][8];
794         const char **pp;
795         char *bufp, *valp, *comp;
796         Int *ip;
797         Double *dp;
798         Vector v;
799         Atom *ap;
800         const int kUndefined = -10000000;
801         err = 0;
802         *errbuf = NULL;
803         nwarnings = 0;
804         if (mp->natoms != 0 || mp->par != NULL || mp->arena != NULL) {
805                 s_append_asprintf(errbuf, "The molecule must be empty");
806                 return 1;
807         }
808         fp = fopen(fname, "rb");
809         if (fp == NULL) {
810                 s_append_asprintf(errbuf, "Cannot open file");
811                 return 1;
812         }
813         for (i = 0; i < 10; i++)
814                 mview_dbuf[i] = kUndefined;
815         for (i = 0; i < 18; i++)
816                 mview_ibuf[i] = kUndefined;
817         /*      flockfile(fp); */
818         lineNumber = 0;
819         fn = 0;
820         nframes = 0;
821         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
822                 if (strncmp(buf, "!:", 2) != 0)
823                         continue;   /*  Skip until section header is found  */
824                 bufp = buf;
825                 strsep(&bufp, " \t\n");
826                 if (strcmp(buf, "!:atoms") == 0) {
827                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
828                                 if (buf[0] == '!')
829                                         continue;
830                                 if (buf[0] == '\n')
831                                         break;
832                                 /* idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge */
833                                 if (sscanf(buf, "%d %6s %d %6s %6s %6s %lf %lf %6s %d %lf %lf %d", &ibuf[0], cbuf[0], &ibuf[1], cbuf[1], cbuf[2], cbuf[3], &dbuf[0], &dbuf[1], cbuf[4], &ibuf[2], &dbuf[2], &dbuf[3], &ibuf[3]) < 13) {
834                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, mp->natoms + 1);
835                                         goto err_exit;
836                                 }
837                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
838                                 strncpy(ap->segName, cbuf[0], 4);
839                                 ap->resSeq = ibuf[1];
840                                 strncpy(ap->resName, cbuf[1], 4);
841                                 strncpy(ap->aname, cbuf[2], 4);
842                                 ap->type = AtomTypeEncodeToUInt(cbuf[3]);
843                                 ap->charge = dbuf[0];
844                                 ap->weight = dbuf[1];
845                                 strncpy(ap->element, cbuf[4], 2);
846                                 ap->atomicNumber = ibuf[2];
847                                 ap->occupancy = dbuf[2];
848                                 ap->tempFactor = dbuf[3];
849                                 ap->intCharge = ibuf[3];
850                         }
851                         continue;
852                 } else if (strcmp(buf, "!:atoms_symop") == 0) {
853                         i = 0;
854                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
855                                 if (buf[0] == '!')
856                                         continue;
857                                 if (buf[0] == '\n')
858                                         break;
859                                 /* idx symop symbase */
860                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
861                                         s_append_asprintf(errbuf, "line %d: symmetry operations cannot be read for atom %d", lineNumber, i + 1);
862                                         goto skip_section;
863                                 }
864                                 if (i >= mp->natoms) {
865                                         s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
866                                         goto skip_section;
867                                 }
868                                 ap = ATOM_AT_INDEX(mp->atoms, i);
869                                 ap->symop.sym = ibuf[1] / 1000000;
870                                 ap->symop.dx = (ibuf[1] % 1000000) / 10000;
871                                 ap->symop.dy = (ibuf[1] % 10000) / 100;
872                                 ap->symop.dz = ibuf[1] % 100;
873                                 ap->symbase = ibuf[2];
874                                 i++;
875                         }
876                         continue;
877                 } else if (strcmp(buf, "!:atoms_fix") == 0) {
878                         i = 0;
879                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
880                                 if (buf[0] == '!')
881                                         continue;
882                                 if (buf[0] == '\n')
883                                         break;
884                                 /* idx fix_force fix_pos */
885                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 5) {
886                                         s_append_asprintf(errbuf, "line %d: fix atom info cannot be read for atom %d", lineNumber, i + 1);
887                                         goto skip_section;
888                                 }
889                                 if (i >= mp->natoms) {
890                                         s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
891                                         goto skip_section;
892                                 }
893                                 ap = ATOM_AT_INDEX(mp->atoms, i);
894                                 ap->fix_force = dbuf[0];
895                                 ap->fix_pos.x = dbuf[1];
896                                 ap->fix_pos.y = dbuf[2];
897                                 ap->fix_pos.z = dbuf[3];
898                                 i++;
899                         }
900                         continue;
901                 } else if (strcmp(buf, "!:uff_types") == 0) {
902                         i = 0;
903                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
904                                 if (buf[0] == '!')
905                                         continue;
906                                 if (buf[0] == '\n')
907                                         break;
908                                 /* idx uff_type */
909                                 if (sscanf(buf, "%d %6s", &ibuf[0], cbuf[0]) < 2) {
910                                         s_append_asprintf(errbuf, "line %d: uff type info cannot be read for atom %d", lineNumber, i + 1);
911                                         goto skip_section;
912                                 }
913                                 if (i >= mp->natoms) {
914                                         s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
915                                         goto skip_section;
916                                 }
917                                 ap = ATOM_AT_INDEX(mp->atoms, i);
918                                 strncpy(ap->uff_type, cbuf[0], 5);
919                                 ap->uff_type[5] = 0;
920                                 i++;
921                         }
922                 } else if (strcmp(buf, "!:mm_exclude") == 0) {
923                         i = 0;
924                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
925                                 if (buf[0] == '!')
926                                         continue;
927                                 if (buf[0] == '\n')
928                                         break;
929                                 /* idx mm_exclude periodic_exclude */
930                                 if (sscanf(buf, "%d %d %d", &ibuf[0], &ibuf[1], &ibuf[2]) < 3) {
931                                         s_append_asprintf(errbuf, "line %d: mm_exclude flags cannot be read for atom %d", lineNumber, i + 1);
932                                         goto skip_section;
933                                 }
934                                 if (i >= mp->natoms) {
935                                         s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
936                                         goto skip_section;
937                                 }
938                                 ap = ATOM_AT_INDEX(mp->atoms, i);
939                                 ap->mm_exclude = (ibuf[1] != 0);
940                                 ap->periodic_exclude = (ibuf[2] != 0);
941                                 i++;
942                         }
943                         continue;
944                 } else if (strcmp(buf, "!:pi_anchor") == 0) {
945                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
946                                 if (buf[0] == '!')
947                                         continue;
948                                 if (buf[0] == '\n')
949                                         break;
950                                 /* idx count */
951                                 if ((j = sscanf(buf, "%d %d", &ibuf[0], &ibuf[1])) < 2) {
952                                         s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
953                                         goto skip_section;
954                                 }
955                                 i = ibuf[0];
956                                 ap = ATOM_AT_INDEX(mp->atoms, i);
957                                 if (ap->anchor != NULL) {
958                                         s_append_asprintf(errbuf, "line %d: warning: duplicate pi_anchor entry", lineNumber);
959                                         AtomConnectResize(&ap->anchor->connect, 0);
960                                         free(ap->anchor->coeffs);
961                                         free(ap->anchor);
962                                 }
963                                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
964                                 if (ibuf[1] < 2 || ibuf[1] >= mp->natoms) {
965                                         s_append_asprintf(errbuf, "line %d: bad number of components for pi_anchor", lineNumber);
966                                         goto skip_section;
967                                 }
968                                 AtomConnectResize(&ap->anchor->connect, ibuf[1]);
969                                 ip = AtomConnectData(&ap->anchor->connect);
970                                 NewArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), ibuf[1]);
971                                 j = ibuf[1];
972                                 for (i = 0; i < j; i++) {
973                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
974                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file while reading pi_anchors", lineNumber);
975                                                 goto err_exit;
976                                         }
977                                         if (sscanf(buf, "%d %lf", &ibuf[0], &dbuf[0]) < 2) {
978                                                 s_append_asprintf(errbuf, "line %d: bad format for pi_anchor", lineNumber);
979                                                 goto skip_section;
980                                         }
981                                         if (ibuf[0] < 0 || ibuf[0] >= mp->natoms) {
982                                                 s_append_asprintf(errbuf, "line %d: atom index out of range", lineNumber);
983                                                 goto skip_section;
984                                         }
985                                         if (dbuf[0] <= 0.0) {
986                                                 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
987                                                 goto skip_section;
988                                         }
989                                         ip[i] = ibuf[0];
990                                         ap->anchor->coeffs[i] = dbuf[0];
991                                 }
992                         }
993                         continue;
994                 } else if (strcmp(buf, "!:positions") == 0) {
995                         i = 0;
996                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
997                                 if (buf[0] == '!')
998                                         continue;
999                                 if (buf[0] == '\n')
1000                                         break;
1001                                 /* idx x y z */
1002                                 if ((j = sscanf(buf, "%d %lf %lf %lf %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5])) < 4) {
1003                                         s_append_asprintf(errbuf, "line %d: atom position cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1004                                         goto err_exit;
1005                                 }
1006                                 if (j > 4 && nframes != 0) {
1007                                         s_append_asprintf(errbuf, "line %d: atom position sigma can only be given for frame 0", lineNumber);
1008                                         goto skip_section;
1009                                 }
1010                                 if (j > 4 && j != 7) {
1011                                         s_append_asprintf(errbuf, "line %d: atom position sigma cannot be read for atom %d frame %d", lineNumber, i + 1, nframes);
1012                                         goto skip_section;
1013                                 }
1014                                 if (i >= mp->natoms) {
1015                                         s_append_asprintf(errbuf, "line %d: too many atom position records\n", lineNumber);
1016                                         goto err_exit;
1017                                 }
1018                                 v.x = dbuf[0];
1019                                 v.y = dbuf[1];
1020                                 v.z = dbuf[2];
1021                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1022                                 if (nframes > 0) {
1023                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes, &v);
1024                                         if (nframes == 1)
1025                                                 ap->frames[0] = ap->r;
1026                                 }
1027                                 ap->r = v;
1028                                 if (j == 7) {
1029                                         ap->sigma.x = dbuf[3];
1030                                         ap->sigma.y = dbuf[4];
1031                                         ap->sigma.z = dbuf[5];
1032                                 }
1033                                 i++;
1034                         }
1035                         nframes++;
1036                         if (nframes >= 2) {
1037                                 mp->nframes = nframes;
1038                                 mp->cframe = nframes - 1;
1039                         } else {
1040                                 mp->nframes = mp->cframe = 0;
1041                         }
1042                         continue;
1043                 } else if (strcmp(buf, "!:bonds") == 0) {
1044                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1045                                 if (buf[0] == '!')
1046                                         continue;
1047                                 if (buf[0] == '\n')
1048                                         break;
1049                                 /* from1 to1 from2 to2 from3 to3 from4 to4 */ 
1050                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
1051                                 if (i < 2 || i % 2 != 0) {
1052                                         s_append_asprintf(errbuf, "line %d: bad bond format", lineNumber);
1053                                         goto skip_section;
1054                                 }
1055                                 for (j = 0; j < i; j += 2) {
1056                                         iibuf[0] = ibuf[j];
1057                                         iibuf[1] = ibuf[j + 1];
1058                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[0] == iibuf[1]) {
1059                                                 s_append_asprintf(errbuf, "line %d: warning: bad bond specification (%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1060                                                 nwarnings++;
1061                                         } else if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), iibuf[1])) {
1062                                                 s_append_asprintf(errbuf, "line %d: warning: bond %d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1]);
1063                                                 nwarnings++;
1064                                         } else {
1065                                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, iibuf);
1066                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[0])->connect), -1, iibuf[1]);
1067                                                 AtomConnectInsertEntry(&(ATOM_AT_INDEX(mp->atoms, iibuf[1])->connect), -1, iibuf[0]);
1068                                         }
1069                                 }
1070                         }
1071                         continue;
1072                 } else if (strcmp(buf, "!:bond_orders") == 0) {
1073                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1074                                 if (buf[0] == '!')
1075                                         continue;
1076                                 if (buf[0] == '\n')
1077                                         break;
1078                                 /* b1 b2 b3 b4 */
1079                                 i = sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]);
1080                                 if (i == 0) {
1081                                         s_append_asprintf(errbuf, "line %d: bad bond order format", lineNumber);
1082                                         goto skip_section;
1083                                 }
1084                                 for (j = 0; j < i; j++) {
1085                                         AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbondOrders, &dbuf[j]);
1086                                 }
1087                         }
1088                         if (mp->nbondOrders > mp->nbonds) {
1089                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) exceeds number of bonds (%d) - ignoring excess info\n", lineNumber, mp->nbondOrders, mp->nbonds);
1090                                 nwarnings++;
1091                                 mp->nbondOrders = mp->nbonds;
1092                         } else if (mp->nbondOrders < mp->nbonds) {
1093                                 s_append_asprintf(errbuf, "line %d: warning: the number of bond order info (%d) is less than number of bonds (%d)\n", lineNumber, mp->nbondOrders, mp->nbonds);
1094                                 nwarnings++;
1095                                 j = mp->nbondOrders;
1096                                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
1097                                 for (i = j; i < mp->nbonds; i++)
1098                                         mp->bondOrders[i] = 0.0;
1099                         }
1100                         continue;
1101                         
1102                 } else if (strcmp(buf, "!:angles") == 0) {
1103                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1104                                 if (buf[0] == '!')
1105                                         continue;
1106                                 if (buf[0] == '\n')
1107                                         break;
1108                                 /* a1 b1 c1 a2 b2 c2 a3 b3 c3 */ 
1109                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7], &ibuf[8]);
1110                                 if (i == 0 || i % 3 != 0) {
1111                                         s_append_asprintf(errbuf, "line %d: bad angle format", lineNumber);
1112                                         goto skip_section;
1113                                 }
1114                                 for (j = 0; j < i; j += 3) {
1115                                         iibuf[0] = ibuf[j];
1116                                         iibuf[1] = ibuf[j + 1];
1117                                         iibuf[2] = ibuf[j + 2];
1118                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2]) {
1119                                                 s_append_asprintf(errbuf, "line %d: warning: bad angle specification (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1120                                                 nwarnings++;
1121                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0) {
1122                                                 s_append_asprintf(errbuf, "line %d: warning: angle with non-bonded atoms (%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1123                                                 nwarnings++;                                            
1124                                         } else if (MoleculeLookupAngle(mp, iibuf[0], iibuf[1], iibuf[2]) >= 0) {
1125                                                 s_append_asprintf(errbuf, "line %d: warning: angle %d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2]);
1126                                                 nwarnings++;
1127                                         } else {
1128                                                 AssignArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, mp->nangles, iibuf);
1129                                         }
1130                                 }
1131                         }
1132                         continue;
1133                 } else if (strcmp(buf, "!:dihedrals") == 0) {
1134                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1135                                 if (buf[0] == '!')
1136                                         continue;
1137                                 if (buf[0] == '\n')
1138                                         break;
1139                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1140                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
1141                                 if (i == 0 || i % 4 != 0) {
1142                                         s_append_asprintf(errbuf, "line %d: bad dihedral format", lineNumber);
1143                                         goto skip_section;
1144                                 }
1145                                 for (j = 0; j < i; j += 4) {
1146                                         iibuf[0] = ibuf[j];
1147                                         iibuf[1] = ibuf[j + 1];
1148                                         iibuf[2] = ibuf[j + 2];
1149                                         iibuf[3] = ibuf[j + 3];
1150                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
1151                                                 s_append_asprintf(errbuf, "line %d: warning: bad dihedral specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1152                                                 nwarnings++;
1153                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[1], iibuf[2]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1154                                                 s_append_asprintf(errbuf, "line %d: warning: dihedral with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1155                                                 nwarnings++;                                            
1156                                         } else if (MoleculeLookupDihedral(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1157                                                 s_append_asprintf(errbuf, "line %d: warning: dihedral %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1158                                                 nwarnings++;
1159                                         } else {
1160                                                 AssignArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, mp->ndihedrals, iibuf);
1161                                         }
1162                                 }
1163                         }
1164                         continue;
1165                 } else if (strcmp(buf, "!:impropers") == 0) {
1166                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1167                                 if (buf[0] == '!')
1168                                         continue;
1169                                 if (buf[0] == '\n')
1170                                         break;
1171                                 /* a1 b1 c1 d1 a2 b2 c2 d2 */ 
1172                                 i = sscanf(buf, "%d %d %d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5], &ibuf[6], &ibuf[7]);
1173                                 if (i == 0 || i % 4 != 0) {
1174                                         s_append_asprintf(errbuf, "line %d: bad improper format", lineNumber);
1175                                         goto skip_section;
1176                                 }
1177                                 for (j = 0; j < i; j += 4) {
1178                                         iibuf[0] = ibuf[j];
1179                                         iibuf[1] = ibuf[j + 1];
1180                                         iibuf[2] = ibuf[j + 2];
1181                                         iibuf[3] = ibuf[j + 3];
1182                                         if (iibuf[0] < 0 || iibuf[0] >= mp->natoms || iibuf[1] < 0 || iibuf[1] >= mp->natoms || iibuf[2] < 0 || iibuf[2] >= mp->natoms || iibuf[3] < 0 || iibuf[3] >= mp->natoms || iibuf[0] == iibuf[1] || iibuf[1] == iibuf[2] || iibuf[2] == iibuf[3] || iibuf[0] == iibuf[2] || iibuf[1] == iibuf[3] || iibuf[0] == iibuf[3]) {
1183                                                 s_append_asprintf(errbuf, "line %d: warning: bad improper specification (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1184                                                 nwarnings++;
1185                                         } else if (MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[0]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[1]) == 0 || MoleculeAreAtomsConnected(mp, iibuf[2], iibuf[3]) == 0) {
1186                                                 s_append_asprintf(errbuf, "line %d: warning: improper with non-bonded atoms (%d-%d-%d-%d) - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1187                                                 nwarnings++;                                            
1188                                         } else if (MoleculeLookupImproper(mp, iibuf[0], iibuf[1], iibuf[2], iibuf[3]) >= 0) {
1189                                                 s_append_asprintf(errbuf, "line %d: warning: improper %d-%d-%d-%d is already present - skipped\n", lineNumber, iibuf[0], iibuf[1], iibuf[2], iibuf[3]);
1190                                                 nwarnings++;
1191                                         } else {
1192                                                 AssignArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, mp->nimpropers, iibuf);
1193                                         }
1194                                 }
1195                         }
1196                         continue;
1197                 } else if (strcmp(buf, "!:xtalcell") == 0 && mp->cell == NULL) {
1198                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1199                                 if (buf[0] == '!')
1200                                         continue;
1201                                 if (buf[0] == '\n')
1202                                         break;
1203                                 /* a b c alpha beta gamma [sigmaflag] */ 
1204                                 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1205                                         s_append_asprintf(errbuf, "line %d: bad xtalcell format", lineNumber);
1206                                         goto skip_section;
1207                                 }
1208                                 MoleculeSetCell(mp, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], 0);
1209                                 if (j == 7 && ibuf[0] != 0) {
1210                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1211                                                 s_append_asprintf(errbuf, "line %d: sigma for xtalcell are missing", lineNumber);
1212                                                 goto skip_section;
1213                                         }
1214                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1215                                                 s_append_asprintf(errbuf,"line %d: bad xtalcell sigma format", lineNumber);
1216                                                 goto skip_section;
1217                                         }
1218                                         if (mp->cell != NULL) {
1219                                                 mp->cell->has_sigma = 1;
1220                                                 for (i = 0; i < 6; i++) {
1221                                                         mp->cell->cellsigma[i] = dbuf[i];
1222                                                 }
1223                                         } else {
1224                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1225                                         }
1226                                 }
1227                         }
1228                         continue;
1229                 } else if (strcmp(buf, "!:symmetry_operations") == 0) {
1230                         i = 0;
1231                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1232                                 Transform tr;
1233                                 if (buf[0] == '!')
1234                                         continue;
1235                                 if (buf[0] == '\n')
1236                                         break;
1237                                 /* a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3 */
1238                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1239                                         s_append_asprintf(errbuf, "line %d: bad symmetry_operation format", lineNumber);
1240                                         goto skip_section;
1241                                 }
1242                                 if (i < 3) {
1243                                         tr[i] = dbuf[0];
1244                                         tr[i + 3] = dbuf[1];
1245                                         tr[i + 6] = dbuf[2];
1246                                 } else {
1247                                         tr[9] = dbuf[0];
1248                                         tr[10] = dbuf[1];
1249                                         tr[11] = dbuf[2];
1250                                 }
1251                                 i++;
1252                                 if (i == 4) {
1253                                         AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
1254                                         i = 0;
1255                                 }
1256                         }
1257                         continue;
1258                 } else if (strcmp(buf, "!:anisotropic_thermal_parameters") == 0) {
1259                         i = 0;
1260                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1261                                 if (buf[0] == '!')
1262                                         continue;
1263                                 if (buf[0] == '\n')
1264                                         break;
1265                                 /* b11 b22 b33 b12 b13 b23 [has_sigma] */
1266                                 if ((j = sscanf(buf, "%lf %lf %lf %lf %lf %lf %d", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &ibuf[0])) < 6) {
1267                                         s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters cannot be read for atom %d", lineNumber, i + 1);
1268                                         goto skip_section;
1269                                 }
1270                                 if (i >= mp->natoms) {
1271                                         s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1272                                         goto skip_section;
1273                                 }
1274                                 if (dbuf[0] == 0.0 && dbuf[1] == 0.0 && dbuf[2] == 0.0 && dbuf[3] == 0.0 && dbuf[4] == 0.0 && dbuf[5] == 0.0) {
1275                                         /*  Skip it  */
1276                                 } else {
1277                                         MoleculeSetAniso(mp, i, 0, dbuf[0], dbuf[1], dbuf[2], dbuf[3], dbuf[4], dbuf[5], NULL);
1278                                 }
1279                                 if (j == 7 && ibuf[0] != 0) {
1280                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1281                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma missing", lineNumber);
1282                                                 goto skip_section;
1283                                         }
1284                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1285                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma cannot be read for atom %d", lineNumber, i + 1);
1286                                                 goto skip_section;
1287                                         }
1288                                         ap = ATOM_AT_INDEX(mp->atoms, i);
1289                                         if (ap->aniso == NULL) {
1290                                                 s_append_asprintf(errbuf, "line %d: anisotropic thermal parameters sigma are given while the parameters are not given", lineNumber);
1291                                                 goto skip_section;
1292                                         }
1293                                         ap->aniso->has_bsig = 1;
1294                                         for (j = 0; j < 6; j++)
1295                                                 ap->aniso->bsig[j] = dbuf[j];
1296                                 }
1297                                 i++;
1298                         }
1299                         continue;
1300                 } else if (strcmp(buf, "!:periodic_box") == 0) {
1301                         Vector vs[5];
1302                         Byte has_sigma = 0;
1303                         i = 0;
1304                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1305                                 if (buf[0] == '!')
1306                                         continue;
1307                                 if (buf[0] == '\n')
1308                                         break;
1309                                 /* ax ay az; bx by bz; cx cy cz; ox oy oz; fx fy fz [sigma; sa sb sc s_alpha s_beta s_gamma] */
1310                                 if (i < 4) {
1311                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1312                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1313                                                 goto err_exit;
1314                                         }
1315                                         vs[i].x = dbuf[0];
1316                                         vs[i].y = dbuf[1];
1317                                         vs[i].z = dbuf[2];
1318                                         i++;
1319                                         continue;
1320                                 }
1321                                 if ((j = sscanf(buf, "%d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3])) < 3) {
1322                                         s_append_asprintf(errbuf, "line %d: bad periodic_box format", lineNumber);
1323                                         goto skip_section;
1324                                 }
1325                                 if (j == 4 && ibuf[3] != 0)
1326                                         has_sigma = 1;
1327                                 cbuf[0][0] = ibuf[0];
1328                                 cbuf[0][1] = ibuf[1];
1329                                 cbuf[0][2] = ibuf[2];
1330                                 MoleculeSetPeriodicBox(mp, vs, vs + 1, vs + 2, vs + 3, cbuf[0], 0);
1331                                 if (has_sigma) {
1332                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
1333                                                 s_append_asprintf(errbuf, "line %d: sigma for cell parameters are missing", lineNumber);
1334                                                 goto skip_section;
1335                                         }
1336                                         if (sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]) < 6) {
1337                                                 s_append_asprintf(errbuf, "line %d: bad periodic_box sigma format", lineNumber);
1338                                                 goto skip_section;
1339                                         }
1340                                         if (mp->cell != NULL) {
1341                                                 mp->cell->has_sigma = 1;
1342                                                 for (i = 0; i < 6; i++) {
1343                                                         mp->cell->cellsigma[i] = dbuf[i];
1344                                                 }
1345                                         } else {
1346                                                 s_append_asprintf(errbuf, "line %d: cell sigma are given while cell is not given", lineNumber);
1347                                         }
1348                                 }
1349                                 break;
1350                         }
1351                         continue;
1352                 } else if (strcmp(buf, "!:frame_periodic_boxes") == 0) {
1353                         Vector vs[5];
1354                         i = 0;
1355                 /*      mp->useFlexibleCell = 1;  *//*  The presence of this block causes asserting this flag  */
1356                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1357                                 if (buf[0] == '!')
1358                                         continue;
1359                                 if (buf[0] == '\n')
1360                                         break;
1361                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1362                                         s_append_asprintf(errbuf, "line %d: bad frame_periodic_box format", lineNumber);
1363                                         goto skip_section;
1364                                 }
1365                                 vs[i].x = dbuf[0];
1366                                 vs[i].y = dbuf[1];
1367                                 vs[i].z = dbuf[2];
1368                                 i++;
1369                                 if (i == 4) {
1370                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, mp->nframe_cells, vs);
1371                                         i = 0;
1372                                 }
1373                         }
1374                         if (mp->cframe < mp->nframe_cells) {
1375                                 /*  mp->cframe should already have been set when positions are read  */
1376                                 Vector *vp = &mp->frame_cells[mp->cframe * 4];
1377                                 static char defaultFlags[] = {1, 1, 1};
1378                                 char *flags = (mp->cell != NULL ? mp->cell->flags : defaultFlags);
1379                                 MoleculeSetPeriodicBox(mp, vp, vp + 1, vp + 2, vp + 3, flags, 0);
1380                         }
1381                         continue;
1382                 } else if (strcmp(buf, "!:md_parameters") == 0) {
1383                         MDArena *arena;
1384                         if (mp->arena == NULL)
1385                                 mp->arena = md_arena_new(NULL);
1386                         arena = mp->arena;
1387                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1388                                 if (buf[0] == '!')
1389                                         continue;
1390                                 if (buf[0] == '\n')
1391                                         break;
1392                                 bufp = buf;
1393                                 comp = strsep(&bufp, " \t");
1394                                 if (bufp != NULL) {
1395                                         while (*bufp == ' ' || *bufp == '\t')
1396                                                 bufp++;
1397                                         valp = strsep(&bufp, "\n");
1398                                 } else valp = NULL;
1399                                 if (strcmp(comp, "alchem_flags") == 0) {
1400                                         j = (valp == NULL ? 0 : atoi(valp));
1401                                         if (j > 0) {
1402                                                 valp = (char *)malloc(j);
1403                                                 i = 0;
1404                                                 while ((k = fgetc(fp)) >= 0) {
1405                                                         ungetc(k, fp);
1406                                                         if (k < '0' || k > '9') {
1407                                                                 s_append_asprintf(errbuf, "line %d: too few flags in alchem_flags block", lineNumber + 1);
1408                                                                 free(valp);
1409                                                                 goto skip_section;
1410                                                         }
1411                                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
1412                                                         bufp = buf;
1413                                                         while (*bufp != 0) {
1414                                                                 if (*bufp >= '0' && *bufp <= '2') {
1415                                                                         if (i >= j) {
1416                                                                                 s_append_asprintf(errbuf, "line %d: too many flags in alchem_flags block", lineNumber);
1417                                                                                 free(valp);
1418                                                                                 goto skip_section;
1419                                                                         }
1420                                                                         valp[i++] = *bufp - '0';
1421                                                                 } else if (*bufp != ' ' && *bufp != '\t' && *bufp != '\n') {
1422                                                                         s_append_asprintf(errbuf, "line %d: strange character (0x%02x) in alchem_flags block", lineNumber, (int)*bufp);
1423                                                                         free(valp);
1424                                                                         goto skip_section;
1425                                                                 }
1426                                                                 bufp++;
1427                                                         }
1428                                                         if (i == j)
1429                                                                 break;
1430                                                 }
1431                                                 md_set_alchemical_flags(arena, j, valp);
1432                                                 free(valp);
1433                                         }
1434                                         continue;
1435                                 }
1436                                 /*  In the following, the redundant "!= NULL" is to suppress suprious warning  */
1437                                 if ((strcmp(comp, "log_file") == 0 && (pp = &arena->log_result_name) != NULL)
1438                                         || (strcmp(comp, "coord_file") == 0 && (pp = &arena->coord_result_name) != NULL)
1439                                         || (strcmp(comp, "vel_file") == 0 && (pp = &arena->vel_result_name) != NULL)
1440                                         || (strcmp(comp, "force_file") == 0 && (pp = &arena->force_result_name) != NULL)
1441                                         || (strcmp(comp, "debug_file") == 0 && (pp = &arena->debug_result_name) != NULL)) {
1442                                         if (*valp == 0 || strstr(valp, "(null)") == valp)
1443                                                 *pp = NULL;
1444                                         else {
1445                                                 valp = strdup(valp);
1446                                                 if (valp != NULL) {
1447                                                         char *valp1 = strchr(valp, '\n');
1448                                                         if (valp1 != NULL)
1449                                                                 *valp1 = 0;
1450                                                 }
1451                                                 *pp = valp;
1452                                         }
1453                                 } else if ((strcmp(comp, "debug_output_level") == 0 && (ip = &arena->debug_output_level) != NULL)
1454                                                    || (strcmp(comp, "coord_output_freq") == 0 && (ip = &arena->coord_output_freq) != NULL)
1455                                                    || (strcmp(comp, "energy_output_freq") == 0 && (ip = &arena->energy_output_freq) != NULL)
1456                                                    || (strcmp(comp, "coord_frame") == 0 && (ip = &arena->coord_result_frame) != NULL)
1457                                                    || (strcmp(comp, "andersen_freq") == 0 && (ip = &arena->andersen_thermo_freq) != NULL)
1458                                                    || (strcmp(comp, "random_seed") == 0 && (ip = &arena->random_seed) != NULL)
1459                                                    || (strcmp(comp, "use_xplor_shift") == 0 && (ip = &arena->use_xplor_shift) != NULL)
1460                                                    || (strcmp(comp, "relocate_center") == 0 && (ip = &arena->relocate_center) != NULL)
1461                                                    || (strcmp(comp, "surface_potential_freq") == 0 && (ip = &arena->surface_potential_freq) != NULL)
1462                                                    || (strcmp(comp, "use_graphite") == 0 && (ip = &arena->use_graphite) != NULL)) {
1463                                         *ip = (valp == NULL ? 0 : atoi(valp));
1464                                 } else if ((strcmp(comp, "timestep") == 0 && (dp = &arena->timestep) != NULL)
1465                                                    || (strcmp(comp, "cutoff") == 0 && (dp = &arena->cutoff) != NULL)
1466                                                    || (strcmp(comp, "electro_cutoff") == 0 && (dp = &arena->electro_cutoff) != NULL)
1467                                                    || (strcmp(comp, "pairlist_distance") == 0 && (dp = &arena->pairlist_distance) != NULL)
1468                                                    || (strcmp(comp, "switch_distance") == 0 && (dp = &arena->switch_distance) != NULL)
1469                                                    || (strcmp(comp, "temperature") == 0 && (dp = &arena->temperature) != NULL)
1470                                                    || (strcmp(comp, "andersen_coupling") == 0 && (dp = &arena->andersen_thermo_coupling) != NULL)
1471                                                    || (strcmp(comp, "dielectric") == 0 && (dp = &arena->dielectric) != NULL)
1472                                                    || (strcmp(comp, "gradient_convergence") == 0 && (dp = &arena->gradient_convergence) != NULL)
1473                                                    || (strcmp(comp, "coordinate_convergence") == 0 && (dp = &arena->coordinate_convergence) != NULL)
1474                                                    || (strcmp(comp, "scale14_vdw") == 0 && (dp = &arena->scale14_vdw) != NULL)
1475                                                    || (strcmp(comp, "scale14_elect") == 0 && (dp = &arena->scale14_elect) != NULL)
1476                                                    || (strcmp(comp, "surface_probe_radius") == 0 && (dp = &arena->probe_radius) != NULL)
1477                                                    || (strcmp(comp, "surface_tension") == 0 && (dp = &arena->surface_tension) != NULL)
1478                                                    || (strcmp(comp, "alchemical_lambda") == 0 && (dp = &arena->alchem_lambda) != NULL)
1479                                                    || (strcmp(comp, "alchemical_delta_lambda") == 0 && (dp = &arena->alchem_dlambda) != NULL)) {
1480                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1481                                 }
1482                         }
1483                         continue;
1484                 } else if (strcmp(buf, "!:pressure_control_parameters") == 0) {
1485                         MDPressureArena *pressure;
1486                         if (mp->arena == NULL)
1487                                 mp->arena = md_arena_new(mp);
1488                         if (mp->arena->pressure == NULL)
1489                                 mp->arena->pressure = pressure_new();
1490                         pressure = mp->arena->pressure;
1491                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1492                                 if (buf[0] == '!')
1493                                         continue;
1494                                 if (buf[0] == '\n')
1495                                         break;
1496                                 bufp = buf;
1497                                 comp = strsep(&bufp, " \t");
1498                                 if (bufp != NULL) {
1499                                         while (*bufp == ' ' || *bufp == '\t')
1500                                                 bufp++;
1501                                         valp = strsep(&bufp, "\n");
1502                                 } else valp = NULL;
1503                                 if (strcmp(comp, "pressure") == 0) {
1504                                         if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7], &dbuf[8]) < 9) {
1505                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1506                                                 goto skip_section;
1507                                         }
1508                                         for (i = 0; i < 9; i++)
1509                                                 pressure->apply[i] = dbuf[i];
1510                                 } else if (strcmp(comp, "pressure_cell_flexibility") == 0) {
1511                                         if (sscanf(valp, "%lf %lf %lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 8) {
1512                                                 s_append_asprintf(errbuf, "line %d: bad format", lineNumber);
1513                                                 goto skip_section;
1514                                         }
1515                                         for (i = 0; i < 8; i++)
1516                                                 pressure->cell_flexibility[i] = dbuf[i];
1517                                 } else if ((strcmp(comp, "pressure_freq") == 0 && (ip = &pressure->freq) != NULL)) {
1518                                         *ip = (valp == NULL ? 0 : atoi(valp));
1519                                 } else if ((strcmp(comp, "pressure_coupling") == 0 && (dp = &pressure->coupling) != NULL)
1520                                                    || (strcmp(comp, "pressure_fluctuate_cell_origin") == 0 && (dp = &pressure->fluctuate_cell_origin) != NULL)
1521                                                    || (strcmp(comp, "pressure_fluctuate_cell_orientation") == 0 && (dp = &pressure->fluctuate_cell_orientation) != NULL)) {
1522                                         *dp = (valp == NULL ? 0.0 : strtod(valp, NULL));
1523                                 }
1524                         }
1525                         continue;
1526                 } else if (strcmp(buf, "!:velocity") == 0) {
1527                         i = 0;
1528                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1529                                 if (buf[0] == '!')
1530                                         continue;
1531                                 if (buf[0] == '\n')
1532                                         break;
1533                                 /* idx vx vy vz */
1534                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1535                                         s_append_asprintf(errbuf, "line %d: atom velocity cannot be read for atom %d", lineNumber, i + 1);
1536                                         goto skip_section;
1537                                 }
1538                                 if (i >= mp->natoms) {
1539                                         s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1540                                         goto skip_section;
1541                                 }
1542                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1543                                 ap->v.x = dbuf[0];
1544                                 ap->v.y = dbuf[1];
1545                                 ap->v.z = dbuf[2];
1546                                 i++;
1547                         }
1548                         continue;
1549                 } else if (strcmp(buf, "!:force") == 0) {
1550                         i = 0;
1551                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1552                                 if (buf[0] == '!')
1553                                         continue;
1554                                 if (buf[0] == '\n')
1555                                         break;
1556                                 /* idx fx fy fz */
1557                                 if (sscanf(buf, "%d %lf %lf %lf", &ibuf[0], &dbuf[0], &dbuf[1], &dbuf[2]) < 4) {
1558                                         s_append_asprintf(errbuf, "line %d: atom force cannot be read for atom %d", lineNumber, i + 1);
1559                                         goto skip_section;
1560                                 }
1561                                 if (i >= mp->natoms) {
1562                                         s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1563                                         goto skip_section;
1564                                 }
1565                                 ap = ATOM_AT_INDEX(mp->atoms, i);
1566                                 ap->f.x = dbuf[0];
1567                                 ap->f.y = dbuf[1];
1568                                 ap->f.z = dbuf[2];
1569                                 i++;
1570                         }
1571                         continue;
1572                 } else if (strcmp(buf, "!:parameter") == 0 || strcmp(buf, "!:parameters") == 0) {
1573                         Parameter *par = mp->par;
1574                         if (par == NULL) {
1575                                 mp->par = ParameterNew();
1576                                 par = mp->par;
1577                         }
1578                         bufp = NULL;
1579                         i = 0;
1580                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1581                                 if (buf[0] == '!')
1582                                         continue;
1583                                 if (buf[0] == '\n')
1584                                         break;
1585                                 j = ParameterReadFromString(par, buf, &bufp, fname, lineNumber, 0);
1586                                 if (j < 0) {
1587                                         s_append_asprintf(errbuf, "%s", bufp);
1588                                         free(bufp);
1589                                         goto skip_section;
1590                                 }
1591                                 i += j;
1592                         }
1593                         if (bufp != NULL) {
1594                                 s_append_asprintf(errbuf, "%s", bufp);
1595                                 free(bufp);
1596                         }
1597                         continue;
1598                 } else if (strcmp(buf, "!:trackball") == 0) {
1599                         i = 0;
1600                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1601                                 if (buf[0] == '!')
1602                                         continue;
1603                                 if (buf[0] == '\n')
1604                                         break;
1605                                 if (mp->mview == NULL || mp->mview->track == NULL)
1606                                         continue;  /*  Skip (this should not happen though)  */
1607                                 /* scale; trx try trz; theta_deg x y z */
1608                                 if ((i == 0 && sscanf(buf, "%lf", &dbuf[0]) < 1)
1609                                         || (i == 1 && sscanf(buf, "%lf %lf %lf",
1610                                                                                  &dbuf[1], &dbuf[2], &dbuf[3]) < 3)
1611                                         || (i == 2 && sscanf(buf, "%lf %lf %lf %lf",
1612                                                                                  &dbuf[4], &dbuf[5], &dbuf[6], &dbuf[7]) < 4)) {
1613                                         s_append_asprintf(errbuf, "line %d: bad trackball format", lineNumber);
1614                                         goto skip_section;
1615                                 }
1616                                 if (i == 0)
1617                                         TrackballSetScale(mp->mview->track, dbuf[0]);
1618                                 else if (i == 1)
1619                                         TrackballSetTranslate(mp->mview->track, dbuf + 1);
1620                                 else if (i == 2)
1621                                         TrackballSetRotate(mp->mview->track, dbuf + 4);
1622                                 i++;
1623                         }
1624                         continue;
1625                 } else if (strcmp(buf, "!:view") == 0) {
1626                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1627                                 if (buf[0] == '!')
1628                                         continue;
1629                                 if (buf[0] == '\n')
1630                                         break;
1631                                 if (mp->mview == NULL)
1632                                         continue;  /*  Skip (this should not happen, though)  */
1633                                 bufp = buf;
1634                                 comp = strsep(&bufp, " \t");
1635                                 if (bufp != NULL) {
1636                                         while (*bufp == ' ' || *bufp == '\t')
1637                                                 bufp++;
1638                                         valp = strsep(&bufp, "\n");
1639                                 } else valp = NULL;
1640                                 if (strcmp(comp, "show_unit_cell") == 0)
1641                                         mp->mview->showUnitCell = atoi(valp);
1642                                 else if (strcmp(comp, "show_periodic_box") == 0)
1643                                         mp->mview->showPeriodicBox = atoi(valp);
1644                                 else if (strcmp(comp, "show_expanded_atoms") == 0)
1645                                         mp->mview->showExpandedAtoms = atoi(valp);
1646                                 else if (strcmp(comp, "show_ellipsoids") == 0)
1647                                         mp->mview->showEllipsoids = atoi(valp);
1648                                 else if (strcmp(comp, "show_hydrogens") == 0)
1649                                         mp->mview->showHydrogens = atoi(valp);
1650                                 else if (strcmp(comp, "show_dummy_atoms") == 0)
1651                                         mp->mview->showDummyAtoms = atoi(valp);
1652                                 else if (strcmp(comp, "show_rotation_center") == 0)
1653                                         mp->mview->showRotationCenter = atoi(valp);
1654                                 else if (strcmp(comp, "show_graphite_flag") == 0)
1655                                         mp->mview->showGraphiteFlag = atoi(valp);
1656                                 else if (strcmp(comp, "show_periodic_image_flag") == 0)
1657                                         mp->mview->showPeriodicImageFlag = atoi(valp);
1658                                 else if (strcmp(comp, "show_graphite") == 0)
1659                                         mp->mview->showGraphite = atoi(valp);
1660                                 else if (strcmp(comp, "show_expanded_atoms") == 0)
1661                                         mp->mview->showExpandedAtoms = atoi(valp);
1662                                 else if (strcmp(comp, "atom_resolution") == 0 && (i = atoi(valp)) >= 6)
1663                                         mp->mview->atomResolution = i;
1664                                 else if (strcmp(comp, "bond_resolution") == 0 && (i = atoi(valp)) >= 4)
1665                                         mp->mview->bondResolution = i;
1666                                 else if (strcmp(comp, "atom_radius") == 0)
1667                                         mp->mview->atomRadius = strtod(valp, NULL);
1668                                 else if (strcmp(comp, "bond_radius") == 0)
1669                                         mp->mview->bondRadius = strtod(valp, NULL);
1670                                 else if (strcmp(comp, "show_periodic_image") == 0) {
1671                                         sscanf(valp, "%d %d %d %d %d %d", &ibuf[0], &ibuf[1], &ibuf[2], &ibuf[3], &ibuf[4], &ibuf[5]);
1672                                         for (i = 0; i < 6; i++)
1673                                                 mp->mview->showPeriodicImage[i] = ibuf[i];
1674                                 }
1675                         }
1676                         continue;
1677                 } else if (strcmp(buf, "!:property") == 0) {
1678                         char dec[1024];
1679                         i = 0;
1680                         bufp = buf + 13;
1681                         while (*bufp != 0 && *bufp != '\n' && bufp < (buf + sizeof buf - 3)) {
1682                                 if (*bufp == '%') {
1683                                         dec[i] = bufp[1];
1684                                         dec[i + 1] = bufp[2];
1685                                         dec[i + 2] = 0;
1686                                         dec[i++] = strtol(dec, NULL, 16);
1687                                         bufp += 3;
1688                                 } else {
1689                                         dec[i++] = *bufp++;
1690                                 }
1691                                 if (i >= 1000)
1692                                         break;
1693                         }
1694                         if (i == 0)
1695                                 continue;
1696                         dec[i] = 0;
1697                         i = MoleculeCreateProperty(mp, dec);
1698                         if (i < 0) {
1699                                 s_append_asprintf(errbuf, "line %d: warning: duplicate molecular property %s - ignored\n", lineNumber, dec);
1700                                 nwarnings++;
1701                                 continue;
1702                         }
1703                         j = 0;
1704                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1705                                 if (buf[0] == '!')
1706                                         continue;
1707                                 if (buf[0] == '\n')
1708                                         break;
1709                                 if (j >= nframes) {
1710                                         s_append_asprintf(errbuf, "line %d: warning: too many molecular property %s - ignored\n", lineNumber, dec);
1711                                         nwarnings++;
1712                                         break;
1713                                 }
1714                                 dbuf[0] = strtod(buf, NULL);
1715                                 mp->molprops[i].propvals[j] = dbuf[0];
1716                                 j++;
1717                         }
1718                         continue;
1719                 } else if (strcmp(buf, "!:gaussian_primitives") == 0) {
1720                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1721                                 if (buf[0] == '!')
1722                                         continue;
1723                                 if (buf[0] == '\n')
1724                                         break;
1725                                 /* sym nprims a_idx [add_exp] */
1726                 i = sscanf(buf, "%6s %d %d %d", cbuf[0], &ibuf[0], &ibuf[1], &ibuf[3]);
1727                 if (i < 3) {
1728                                         s_append_asprintf(errbuf, "line %d: the gaussian primitive info cannot be read", lineNumber);
1729                                         goto skip_section;
1730                                 }
1731                 if (i == 3)
1732                     ibuf[3] = 0;  /*  Additional exponent (JANPA extension)  */
1733                                 if (strcasecmp(cbuf[0], "S") == 0) {
1734                                         ibuf[2] = 0;
1735                                 } else if (strcasecmp(cbuf[0], "P") == 0) {
1736                                         ibuf[2] = 1;
1737                                 } else if (strcasecmp(cbuf[0], "SP") == 0) {
1738                                         ibuf[2] = -1;
1739                                 } else if (strcasecmp(cbuf[0], "D") == 0) {
1740                                         ibuf[2] = 2;
1741                                 } else if (strcasecmp(cbuf[0], "D5") == 0) {
1742                                         ibuf[2] = -2;
1743                                 } else if (strcasecmp(cbuf[0], "F") == 0) {
1744                                         ibuf[2] = 3;
1745                                 } else if (strcasecmp(cbuf[0], "F7") == 0) {
1746                                         ibuf[2] = -3;
1747                                 } else if (strcasecmp(cbuf[0], "G") == 0) {
1748                                         ibuf[2] = 4;
1749                                 } else if (strcasecmp(cbuf[0], "G9") == 0) {
1750                                         ibuf[2] = -4;
1751                                 } else {
1752                                         s_append_asprintf(errbuf, "line %d: the gaussian primitive type %s is unknown", lineNumber, cbuf[0]);
1753                                         goto skip_section;
1754                                 }
1755                                 if (ibuf[0] <= 0) {
1756                                         s_append_asprintf(errbuf, "line %d: the number of primitive (%d) must be positive", lineNumber, ibuf[0]);
1757                                         goto skip_section;
1758                                 }
1759                                 if (ibuf[1] < 0 || ibuf[1] >= mp->natoms) {
1760                                         s_append_asprintf(errbuf, "line %d: the atom index (%d) is out of range", lineNumber, ibuf[1]);
1761                                         goto skip_section;
1762                                 }
1763                                 MoleculeAddGaussianOrbitalShell(mp, ibuf[1], ibuf[2], ibuf[0], ibuf[3]);
1764                                 i = ibuf[0];
1765                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1766                                         if (buf[0] == '!')
1767                                                 continue;
1768                                         if (buf[0] == '\n')
1769                                                 break;
1770                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1771                                                 s_append_asprintf(errbuf, "line %d: cannot read gaussian primitive coefficients", lineNumber);
1772                                                 goto skip_section;
1773                                         }
1774                                         MoleculeAddGaussianPrimitiveCoefficients(mp, dbuf[0], dbuf[1], dbuf[2]);
1775                                         if (--i == 0)
1776                                                 break;
1777                                 }
1778                                 if (buf[0] == '\n')
1779                                         break;
1780                         }
1781                         continue;
1782                 } else if (strcmp(buf, "!:mo_info") == 0) {
1783                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1784                                 if (buf[0] == '!')
1785                                         continue;
1786                                 if (buf[0] == '\n')
1787                                         break;
1788                                 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1789                                         s_append_asprintf(errbuf, "line %d: the MO info cannot be correctly read", lineNumber);
1790                                         goto skip_section;
1791                                 }
1792                                 if (strcasecmp(cbuf[0], "RHF") == 0) {
1793                                         ibuf[2] = 1;
1794                                 } else if (strcasecmp(cbuf[0], "ROHF") == 0) {
1795                                         ibuf[2] = 2;
1796                                 } else if (strcasecmp(cbuf[0], "UHF") == 0) {
1797                                         ibuf[2] = 0;
1798                                 } else {
1799                                         s_append_asprintf(errbuf, "line %d: unknown HF type: %s", lineNumber, cbuf[0]);
1800                                         goto skip_section;
1801                                 }
1802                                 if (ibuf[0] < 0 || ibuf[1] < 0) {
1803                                         s_append_asprintf(errbuf, "line %d: incorrect number of electrons", lineNumber);
1804                                         goto skip_section;
1805                                 }
1806                                 MoleculeSetMOInfo(mp, ibuf[2], ibuf[0], ibuf[1]);
1807                         }
1808                         continue;
1809                 } else if (strcmp(buf, "!:mo_coefficients") == 0) {
1810                         if (mp->bset == NULL || mp->bset->nshells == 0) {
1811                                 s_append_asprintf(errbuf, "line %d: the :gaussian_primitive section must come before :mo_coefficients", lineNumber);
1812                                 goto skip_section;
1813                         }
1814                         /*  Count the number of components  */
1815                         dp = (Double *)malloc(sizeof(Double) * mp->bset->ncomps);
1816                         i = 1;
1817                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1818                                 if (buf[0] == '!')
1819                                         continue;
1820                                 if (buf[0] == '\n')
1821                                         break;
1822                                 if (sscanf(buf, "MO %d %lf", &ibuf[0], &dbuf[6]) < 2) {
1823                                         s_append_asprintf(errbuf, "line %d: cannot read the MO index or energy", lineNumber);
1824                                         goto skip_section;
1825                                 }
1826                                 if (ibuf[0] != i) {
1827                                         s_append_asprintf(errbuf, "line %d: the MO index (%d) must be in ascending order", lineNumber, ibuf[0]);
1828                                         goto skip_section;
1829                                 }
1830                                 i = 0;
1831                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1832                                         j = sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]);
1833                                         if (j == 0) {
1834                                                 s_append_asprintf(errbuf, "line %d: cannot read the MO coefficients", lineNumber);
1835                                                 goto err_exit;
1836                                         }
1837                                         for (k = 0; k < j; k++, i++) {
1838                                                 if (i >= mp->bset->ncomps) {
1839                                                         s_append_asprintf(errbuf, "line %d: too many MO coefficients", lineNumber);
1840                                                         goto err_exit;
1841                                                 }
1842                                                 dp[i] = dbuf[k];
1843                                         }
1844                                         if (i >= mp->bset->ncomps)
1845                                                 break;
1846                                 }
1847                                 i = MoleculeSetMOCoefficients(mp, ibuf[0], dbuf[6], mp->bset->ncomps, dp);
1848                                 if (i != 0) {
1849                                         s_append_asprintf(errbuf, "line %d: cannot set MO coefficients", lineNumber);
1850                                         goto skip_section;
1851                                 }
1852                                 i = ibuf[0] + 1;  /*  For next entry  */
1853                         }
1854                         continue;
1855                 } else if (strcmp(buf, "!:graphics") == 0) {
1856                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1857                                 MainViewGraphic *gp = NULL;
1858                                 if (buf[0] == '!')
1859                                         continue;
1860                                 if (buf[0] == '\n')
1861                                         break;
1862                                 if (mp->mview == NULL)
1863                                         continue;  /*  Skip  */
1864                         redo:
1865                                 if (strcmp(buf, "line\n") == 0) {
1866                                         ibuf[0] = kMainViewGraphicLine;
1867                                 } else if (strcmp(buf, "poly\n") == 0) {
1868                                         ibuf[0] = kMainViewGraphicPoly;
1869                                 } else if (strcmp(buf, "cylinder\n") == 0) {
1870                                         ibuf[0] = kMainViewGraphicCylinder;
1871                                 } else if (strcmp(buf, "cone\n") == 0) {
1872                                         ibuf[0] = kMainViewGraphicCone;
1873                                 } else if (strcmp(buf, "ellipsoid\n") == 0) {
1874                                         ibuf[0] = kMainViewGraphicEllipsoid;
1875                                 } else {
1876                                         continue;  /*  Skip  */
1877                                 }
1878                                 gp = (MainViewGraphic *)calloc(sizeof(MainViewGraphic), 1);
1879                                 gp->kind = ibuf[0];
1880                                 i = 0;
1881                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1882                                         if (buf[0] == '!')
1883                                                 continue;
1884                                         if (buf[0] == '\n')
1885                                                 break;
1886                                         if (i == 0) {
1887                                                 if (sscanf(buf, "%d %d", &ibuf[0], &ibuf[1]) < 2) {
1888                                                         s_append_asprintf(errbuf, "line %d: the closed/visible flags cannot be read for graphic object", lineNumber);
1889                                                         goto skip_section;
1890                                                 }
1891                                                 gp->closed = ibuf[0];
1892                                                 gp->visible = ibuf[1];
1893                                         } else if (i == 1) {
1894                                                 if (sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 4) {
1895                                                         s_append_asprintf(errbuf, "line %d: the color cannot be read for graphic object", lineNumber);
1896                                                         goto skip_section;
1897                                                 }
1898                                                 for (j = 0; j < 4; j++)
1899                                                         gp->rgba[j] = dbuf[j];
1900                                         } else if (i == 2) {
1901                                                 j = atoi(buf);
1902                                                 if (j < 0) {
1903                                                         s_append_asprintf(errbuf, "line %d: the number of control points must be non-negative", lineNumber);
1904                                                         goto skip_section;
1905                                                 }
1906                                                 if (j > 0)
1907                                                         NewArray(&gp->points, &gp->npoints, sizeof(GLfloat) * 3, j);
1908                                         } else if (i >= 3 && i < gp->npoints + 3) {
1909                                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1910                                                         s_append_asprintf(errbuf, "line %d: the control point cannot be read for graphic object", lineNumber);
1911                                                         goto skip_section;
1912                                                 }
1913                                                 j = (i - 3) * 3;
1914                                                 gp->points[j++] = dbuf[0];
1915                                                 gp->points[j++] = dbuf[1];
1916                                                 gp->points[j] = dbuf[2];
1917                                         } else if (i == gp->npoints + 3) {
1918                                                 j = atoi(buf);
1919                                                 if (j < 0) {
1920                                                         s_append_asprintf(errbuf, "line %d: the number of normals must be non-negative", lineNumber);
1921                                                         goto skip_section;
1922                                                 }
1923                                                 if (j > 0)
1924                                                         NewArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, j);
1925                                         } else if (i >= gp->npoints + 4 && i < gp->npoints + gp->nnormals + 4) {
1926                                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1927                                                         s_append_asprintf(errbuf, "line %d: the normal vector cannot be read for graphic object", lineNumber);
1928                                                         goto skip_section;
1929                                                 }
1930                                                 j = (i - gp->npoints - 4) * 3;
1931                                                 gp->normals[j++] = dbuf[0];
1932                                                 gp->normals[j++] = dbuf[1];
1933                                                 gp->normals[j] = dbuf[2];
1934                                         } else break;
1935                                         i++;
1936                                 }
1937                                 MainView_insertGraphic(mp->mview, -1, gp);
1938                                 free(gp);
1939                                 if (buf[0] == '\n' || buf[0] == 0)
1940                                         break;
1941                                 goto redo;
1942                         }
1943                         continue;
1944         } else if (strcmp(buf, "!:nbo") == 0) {
1945             Int stringLen;
1946             char *stringBuf, *returnString;
1947             i = strlen(buf) + 1;  /*  Including \n  */
1948             NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1949             strcpy(stringBuf, buf);
1950             strcat(stringBuf, "\n");
1951             k = lineNumber;
1952             while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1953                 /*  The comment lines are _not_ skipped  */
1954                 if (buf[0] == '\n')
1955                     break;
1956                 j = strlen(buf);
1957                 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1958                 strncpy(stringBuf + i, buf, j);
1959                 i += j;
1960             }
1961             if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1962                                           "proc { |s,i| mbsfstring2nbo(s,i) }",
1963                                           stringBuf, k, &returnString) != 0) {
1964                 s_append_asprintf(errbuf, "line %d: cannot call mbsfstring2nbo at line ", lineNumber);
1965                 goto skip_section;
1966             } else if (returnString[0] != 0) {
1967                 s_append_asprintf(errbuf, "%s", returnString);
1968                 goto skip_section;
1969             }
1970             free(stringBuf);
1971             continue;
1972                 } else if (strncmp(buf, "!:@", 3) == 0) {
1973                         /*  Plug-in implemented in the ruby world  */
1974                         Int stringLen;
1975                         char *stringBuf, *returnString;
1976                         i = strlen(buf);
1977                         NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1978                         strcpy(stringBuf, buf);
1979                         k = lineNumber;
1980                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1981                                 /*  The comment lines are _not_ skipped  */
1982                                 if (buf[0] == '\n')
1983                                         break;
1984                                 j = strlen(buf);
1985                                 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1986                                 strncpy(stringBuf + i, buf, j);
1987                                 i += j;
1988                         }
1989                         if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1990                                                                                   "proc { |i| loadmbsf_plugin(i) rescue \"line #{i}: #{$i.to_s}\" }",
1991                                                                                   stringBuf, k, &returnString) != 0) {
1992                                 s_append_asprintf(errbuf, "line %d: cannot invoke Ruby plugin", lineNumber);
1993                                 goto skip_section;
1994                         } else if (returnString[0] != 0) {
1995                                 s_append_asprintf(errbuf, "%s", returnString);
1996                                 goto skip_section;
1997                         }
1998                         free(stringBuf);
1999                         continue;
2000                 }
2001                 /*  Unknown sections are silently ignored  */
2002     skip_section:;
2003         }
2004
2005         MoleculeCleanUpResidueTable(mp);
2006         if (mp->arena != NULL)
2007                 md_arena_set_molecule(mp->arena, mp);
2008
2009         fclose(fp);
2010
2011 /*      if (mp->mview != NULL) {
2012                 if (mview_ibuf[0] != kUndefined)
2013                         mp->mview->showUnitCell = mview_ibuf[0];
2014                 if (mview_ibuf[1] != kUndefined)
2015                         mp->mview->showPeriodicBox = mview_ibuf[1];
2016                 if (mview_ibuf[2] != kUndefined)
2017                         mp->mview->showExpandedAtoms = mview_ibuf[2];
2018                 if (mview_ibuf[3] != kUndefined)
2019                         mp->mview->showEllipsoids = mview_ibuf[3];
2020                 if (mview_ibuf[4] != kUndefined)
2021                         mp->mview->showHydrogens = mview_ibuf[4];
2022                 if (mview_ibuf[5] != kUndefined)
2023                         mp->mview->showDummyAtoms = mview_ibuf[5];
2024                 if (mview_ibuf[6] != kUndefined)
2025                         mp->mview->showRotationCenter = mview_ibuf[6];
2026                 if (mview_ibuf[7] != kUndefined)
2027                         mp->mview->showGraphiteFlag = mview_ibuf[7];
2028                 if (mview_ibuf[8] != kUndefined)
2029                         mp->mview->showPeriodicImageFlag = mview_ibuf[8];
2030                 if (mview_ibuf[9] != kUndefined)
2031                         mp->mview->showGraphite = mview_ibuf[9];
2032                 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
2033                         mp->mview->atomResolution = mview_ibuf[10];
2034                 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
2035                         mp->mview->bondResolution = mview_ibuf[11];
2036                 for (i = 0; i < 6; i++) {
2037                         if (mview_ibuf[12 + i] != kUndefined)
2038                                 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
2039                 }
2040                 if (mview_dbuf[8] != kUndefined)
2041                         mp->mview->atomRadius = mview_dbuf[8];
2042                 if (mview_dbuf[9] != kUndefined)
2043                         mp->mview->bondRadius = mview_dbuf[9];          
2044                 if (mp->mview->track != NULL) {
2045                         if (mview_dbuf[0] != kUndefined)
2046                                 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
2047                         if (mview_dbuf[1] != kUndefined)
2048                                 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
2049                         if (mview_dbuf[4] != kUndefined)
2050                                 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
2051                 }
2052         }
2053 */
2054
2055         return 0;
2056
2057 err_exit:
2058         fclose(fp);
2059         /*  The content of mp may be broken, so make it empty  */
2060         MoleculeClear(mp);
2061         return -1;      
2062 }
2063
2064 int
2065 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
2066 {
2067         FILE *fp;
2068         char buf[1024];
2069         char *p;
2070         int section = -1;
2071         int i, j, err, fn;
2072         int lineNumber;
2073         Int ibuf[12];
2074         Vector *frames = NULL;
2075         Atom *ap;
2076         err = 0;
2077         *errbuf = NULL;
2078         if (mp == NULL)
2079                 mp = MoleculeNew();
2080         else MoleculeClear(mp);
2081         fp = fopen(fname, "rb");
2082         if (fp == NULL) {
2083                 s_append_asprintf(errbuf, "Cannot open file");
2084                 return 1;
2085         }
2086 /*      flockfile(fp); */
2087         lineNumber = 0;
2088         fn = 0;
2089         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2090                 if (strncmp(buf, "PSF", 3) == 0) {
2091                         section = 0;
2092                         continue;
2093                 } else {
2094                         for (p = buf; *p != 0 && isspace(*p); p++) {}
2095                         if (*p == 0) {
2096                                 section++;
2097                                 continue;
2098                         }
2099                 }
2100                 if (strstr(buf, "!COORD") != NULL) {
2101                         /*  Extended psf file with coordinates  */
2102                         if (fn > 0) {
2103                                 /*  Allocate a temporary storage for frames  */
2104                                 size_t size = sizeof(Vector) * mp->natoms * fn;
2105                                 if (frames == NULL)
2106                                         frames = (Vector *)malloc(size);
2107                                 else
2108                                         frames = (Vector *)realloc(frames, size);
2109                                 if (frames == NULL)
2110                                         goto panic;
2111                         }
2112                         /*  Read coordinates  */
2113                         for (i = 0; i < mp->natoms; i++) {
2114                                 double dval[3];
2115                                 Vector r;
2116                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2117                                         err = 1;
2118                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
2119                                         goto exit;
2120                                 }
2121                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
2122                                         err = 1;
2123                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
2124                                         goto exit;
2125                                 }
2126                                 r.x = dval[0];
2127                                 r.y = dval[1];
2128                                 r.z = dval[2];
2129                                 if (fn == 0)
2130                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
2131                                 else
2132                                         frames[mp->natoms * (fn - 1) + i] = r;
2133                         }
2134                         fn++;
2135                         continue;
2136                 }
2137                 
2138                 if (section == 2) {
2139                         /*  Atoms  */
2140                         Int natoms;
2141                         ReadFormat(buf, "I8", &natoms);
2142                         if (natoms == 0)
2143                                 continue;
2144                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
2145                                 goto panic;
2146                         mp->nresidues = 0;
2147                         for (i = 0; i < natoms; i++) {
2148                                 struct {
2149                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
2150                                         Int serial;
2151                                 } w;
2152                                 memset(&w, 0, sizeof(w));
2153                                 ap = ATOM_AT_INDEX(mp->atoms, i);
2154                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2155                                         err = 1;
2156                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
2157                                         goto exit;
2158                                 }
2159                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
2160                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
2161                                         w.atomType, &ap->charge, &ap->weight);
2162                                 strncpy(ap->segName, w.segName, 4);
2163                                 strncpy(ap->resName, w.resName, 3);
2164                                 strncpy(ap->aname, w.atomName, 4);
2165                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
2166                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
2167                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
2168                                 ElementToString(ap->atomicNumber, w.element);
2169                                 strncpy(ap->element, w.element, 2);
2170                         /*      w.element[0] = 0;
2171                                 for (p = w.atomName; *p != 0; p++) {
2172                                         if (isalpha(*p) && *p != '_') {
2173                                                 w.element[0] = toupper(*p);
2174                                                 if (isalpha(p[1]) && p[1] != '_') {
2175                                                         w.element[1] = toupper(p[1]);
2176                                                         w.element[2] = 0;
2177                                                 } else {
2178                                                         w.element[1] = 0;
2179                                                 }
2180                                                 break;
2181                                         }
2182                                 }
2183                                 strncpy(ap->element, w.element, 2);
2184                                 ap->atomicNumber = ElementToInt(w.element); */
2185                                 if (w.resName[0] == 0)
2186                                         strncpy(ap->resName, "XXX", 3);
2187                                 if (ap->resSeq > mp->nresidues)
2188                                         mp->nresidues = ap->resSeq;
2189                         }
2190                         if (mp->residues != NULL)
2191                                 free(mp->residues);
2192                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
2193                                 goto panic;
2194                         for (i = 0; i < mp->natoms; i++) {
2195                                 j = mp->atoms[i].resSeq;
2196                                 if (mp->residues[j][0] == 0)
2197                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
2198                         }
2199                         continue;
2200                 } else if (section == 3) {
2201                         /*  Bonds  */
2202                         Int nbonds;
2203                         Int *bp;
2204                         ReadFormat(buf, "I8", &nbonds);
2205                         if (nbonds == 0)
2206                                 continue;
2207                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
2208                                 goto panic;
2209                         bp = mp->bonds;
2210                         for (i = 0; i < nbonds; i += 4) {
2211                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2212                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
2213                                         err = 1;
2214                                         goto exit;
2215                                 }
2216                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2217                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2218                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
2219                                         Int b1, b2;
2220                                         Atom *ap;
2221                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
2222                                         b2 = ibuf[j * 2 + 1] - 1;
2223                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
2224                                                 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
2225                                                 err = 1;
2226                                                 goto exit;
2227                                         }
2228                                         *bp++ = b1;
2229                                         *bp++ = b2;
2230                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
2231                                         AtomConnectInsertEntry(&ap->connect, -1, b2);
2232                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
2233                                         AtomConnectInsertEntry(&ap->connect, -1, b1);
2234                                 }
2235                         }
2236                         continue;
2237                 } else if (section == 4) {
2238                         /*  Angles  */
2239                         Int nangles;
2240                         Int *gp;
2241                         ReadFormat(buf, "I8", &nangles);
2242                         if (nangles == 0)
2243                                 continue;
2244                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
2245                                 goto panic;
2246                         gp = mp->angles;
2247                         for (i = 0; i < nangles; i += 3) {
2248                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2249                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
2250                                         err = 1;
2251                                         goto exit;
2252                                 }
2253                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2254                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
2255                                 for (j = 0; j < 3 && i + j < nangles; j++) {
2256                                         Int a1, a2, a3;
2257                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
2258                                         a2 = ibuf[j * 3 + 1] - 1;
2259                                         a3 = ibuf[j * 3 + 2] - 1;
2260                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
2261                                                 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
2262                                                 err = 1;
2263                                                 goto exit;
2264                                         }
2265                                         *gp++ = a1;
2266                                         *gp++ = a2;
2267                                         *gp++ = a3;
2268                                 }
2269                         }
2270                         continue;
2271                 } else if (section == 5 || section == 6) {
2272                         /*  Dihedrals and Impropers  */
2273                         Int ndihedrals;
2274                         Int *dp;
2275                         ReadFormat(buf, "I8", &ndihedrals);
2276                         if (ndihedrals == 0)
2277                                 continue;
2278                         if (section == 5) {
2279                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
2280                                         goto panic;
2281                                 dp = mp->dihedrals;
2282                         } else {
2283                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
2284                                         goto panic;
2285                                 dp = mp->impropers;
2286                         }
2287                         for (i = 0; i < ndihedrals; i += 2) {
2288                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2289                                         fclose(fp);
2290                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
2291                                         err = 1;
2292                                         goto exit;
2293                                 }
2294                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2295                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
2296                                         Int d1, d2, d3, d4;
2297                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
2298                                         d2 = ibuf[j * 4 + 1] - 1;
2299                                         d3 = ibuf[j * 4 + 2] - 1;
2300                                         d4 = ibuf[j * 4 + 3] - 1;
2301                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
2302                                                 s_append_asprintf(errbuf, "line %d: The %s %d-%d-%d-%d angle includes non-existent atom", lineNumber, (section == 5 ? "dihedral" : "improper"), d1+1, d2+1, d3+1, d4+1);
2303                                                 err = 1;
2304                                                 goto exit;
2305                                         }
2306                                         *dp++ = d1;
2307                                         *dp++ = d2;
2308                                         *dp++ = d3;
2309                                         *dp++ = d4;
2310                                 }
2311                         }
2312                         continue;
2313                 }
2314         }
2315         
2316         /*  Create frames for each atom if necessary  */
2317         if (fn > 1) {
2318                 for (i = 0; i < mp->natoms; i++) {
2319                         ap = ATOM_AT_INDEX(mp->atoms, i);
2320                         NewArray(&ap->frames, &ap->nframes, sizeof(Vector), fn);
2321                         if (ap->frames == NULL)
2322                                 goto panic;
2323                         for (j = 0; j < fn; j++)
2324                                 ap->frames[j] = frames[mp->natoms * j + i];
2325                 }
2326                 free(frames);
2327                 frames = NULL;
2328         }
2329
2330   exit:
2331 /*      funlockfile(fp); */
2332         fclose(fp);
2333         mp->nframes = -1;  /*  Should be recalculated later  */
2334         if (err)
2335                 return 1;
2336         else if (section == -1)
2337                 return -1;
2338         return 0;
2339   panic:
2340         Panic("low memory while reading structure file %s", fname);
2341         return 1; /* not reached */
2342 }
2343
2344 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
2345 static int
2346 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
2347 {
2348         int i;
2349         char *symop;
2350         memset(tr, 0, sizeof(Transform));
2351         for (i = 0; i < 3; i++) {
2352                 symop = symops[i];
2353                 if (symop == NULL)
2354                         return 1;
2355                 while (*symop != 0) {
2356                         int sn = 1;
2357                         while (isspace(*symop))
2358                                 symop++;
2359                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
2360                                 break;
2361                         if (*symop == '-') {
2362                                 sn = -1;
2363                                 symop++;
2364                         } else if (*symop == '+') {
2365                                 sn = 1;
2366                                 symop++;
2367                         }
2368                         while (isspace(*symop))
2369                                 symop++;
2370                         if (*symop == '.' || isdigit(*symop)) {
2371                                 /*  Numerical offset  */
2372                                 double d = strtod(symop, &symop);
2373                                 if (*symop == '/') {
2374                                         double dd = strtod(symop + 1, &symop);
2375                                         if (dd > 0)
2376                                                 d /= dd;
2377                                         else
2378                                                 return 1;  /*  Bad format  */
2379                                 }
2380                                 tr[9 + i] = d * sn;
2381                         } else if (*symop == 'x' || *symop == 'X') {
2382                                 tr[i] = sn;
2383                                 symop++;
2384                         } else if (*symop == 'y' || *symop == 'Y') {
2385                                 tr[i + 3] = sn;
2386                                 symop++;
2387                         } else if (*symop == 'z' || *symop == 'Z') {
2388                                 tr[i + 6] = sn;
2389                                 symop++;
2390                         } else return 1;  /*  Bad format  */
2391                 } /* end while (*symop != 0) */
2392         }
2393         return 0;
2394 }
2395
2396 static void
2397 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2398 {
2399         int i, j;
2400         Transform tr;
2401         if (num <= 0)
2402                 num = mp->nsyms;
2403         for (i = 0; i < num; i++) {
2404                 memmove(tr, mp->syms[i], sizeof(Transform));
2405                 TransformMul(tr, gtr, tr);
2406                 for (j = 9; j < 12; j++) {
2407                         if (tr[j] >= 1.0)
2408                                 tr[j] -= 1.0;
2409                         else if (tr[j] <= 0.0)
2410                                 tr[j] += 1.0;
2411                 }
2412                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2413         }
2414 }
2415
2416 static char *
2417 sChomp(char *buf)
2418 {
2419         char *p = buf + strlen(buf) - 1;
2420         if (p >= buf && (*p == '\n' || *p == '\r')) {
2421                 *p = 0;
2422                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2423                         *p = 0;
2424                 }
2425         }
2426         return buf;
2427 }
2428
2429 int
2430 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2431 {
2432         FILE *fp;
2433         char buf[1024];
2434         int section = -1;
2435         int lineNumber;
2436         int cellType;
2437         Int ibuf[12];
2438         Double fbuf[12];
2439         Int *bonds, nbonds;
2440         *errbuf = NULL;
2441         if (mp == NULL)
2442                 mp = MoleculeNew();
2443         fp = fopen(fname, "rb");
2444         if (fp == NULL) {
2445                 s_append_asprintf(errbuf, "Cannot open file");
2446                 return 1;
2447         }
2448         lineNumber = 0;
2449         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2450                 if (section == -1) {
2451                         /*  Title  */
2452                         section = 0;
2453                         continue;
2454                 }
2455                 if (section == 0) {
2456                         /*  XtalCell  */
2457                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2458                         cellType = ibuf[0];
2459                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2460                         section = 1;
2461                         continue;
2462                 }
2463                 if (section == 1) {
2464                         /*  Symmetry  */
2465                         Transform tr;
2466                         if (cellType == 0) {
2467                                 ReadFormat(buf, "I1F14F3F3F3F15F3F3F3F15F3F3F3", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6, fbuf+7, fbuf+8, fbuf+9, fbuf+10, fbuf+11);
2468                                 tr[0] = fbuf[1];
2469                                 tr[3] = fbuf[2];
2470                                 tr[6] = fbuf[3];
2471                                 tr[1] = fbuf[5];
2472                                 tr[4] = fbuf[6];
2473                                 tr[7] = fbuf[7];
2474                                 tr[2] = fbuf[9];
2475                                 tr[5] = fbuf[10];
2476                                 tr[8] = fbuf[11];
2477                                 tr[9] = fbuf[0];
2478                                 tr[10] = fbuf[4];
2479                                 tr[11] = fbuf[8];
2480                         } else {
2481                                 char *symops[3], *brks;
2482                                 sChomp(buf);
2483                                 memset(tr, 0, sizeof(Transform));
2484                                 ReadFormat(buf, "I1", ibuf);
2485                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
2486                                 symops[1] = strtok_r(NULL, ", ", &brks);
2487                                 symops[2] = strtok_r(NULL, ", ", &brks);
2488                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2489                                         s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2490                                         return 1;
2491                                 }
2492                         }
2493                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2494                                 goto panic;
2495                         if (ibuf[0] != 0)
2496                                 section = 2;
2497                         continue;
2498                 }
2499                 if (section == 2) {      /*  Atoms  */
2500                         char name[8];
2501                         Atom *ap;
2502                         int atomType;
2503                         int atomIndex = mp->natoms;
2504                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2505                         memset(ap, 0, gSizeOfAtomRecord);
2506                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2507                         strncpy(ap->aname, name, 4);
2508                         ap->r.x = fbuf[0];
2509                         ap->r.y = fbuf[1];
2510                         ap->r.z = fbuf[2];
2511                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2512                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2513                         ElementToString(ap->atomicNumber, ap->element); */
2514                 /*      sAtomSetElement(ap, -1, ap->name); */
2515                         guessElement(ap);
2516                         atomType = fbuf[3];
2517                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2518                                 s_append_asprintf(errbuf, "unexpected end of file");
2519                                 return 1;
2520                         }
2521                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2522                         atomType = fbuf[6];
2523                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
2524                                 /*  Anisotropic thermal parameters  */
2525                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2526                         }
2527                         if (ibuf[0] != 0)
2528                                 section = 3;
2529                         continue;
2530                 }
2531         }
2532         fclose(fp);
2533         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2534         if (nbonds > 0) {
2535                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2536                 free(bonds);
2537         }
2538         mp->nframes = -1;  /*  Should be recalculated later  */
2539         return 0;
2540   panic:
2541         Panic("low memory while reading structure file %s", fname);
2542         return -1; /* not reached */
2543 }
2544
2545 int
2546 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2547 {
2548         FILE *fp;
2549         char buf[1024];
2550         char *p1, *p2;
2551         int n;
2552         int lineNumber;
2553         int latticeType;
2554         int currentResSeq = 0;
2555         char currentResName[6];
2556         Transform tr;
2557         int ibuf[12];
2558         float fbuf[12];
2559         Double dbuf[12];
2560         Int nsfacs = 0;
2561         Int nbonds, *bonds;
2562         char (*sfacs)[4] = NULL;
2563
2564         *errbuf = NULL;
2565         if (mp == NULL)
2566                 mp = MoleculeNew();
2567         currentResName[0] = 0;
2568         fp = fopen(fname, "rb");
2569         if (fp == NULL) {
2570                 s_append_asprintf(errbuf, "Cannot open file");
2571                 return 1;
2572         }
2573         lineNumber = 0;
2574         tr[0] = tr[4] = tr[8] = 1;
2575         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2576         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2577                 goto panic;
2578         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2579                 if (strncmp(buf, "CELL", 4) == 0) {
2580                         /*  XtalCell  */
2581                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2582                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2583                         continue;
2584                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2585                         sChomp(buf);
2586                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2587                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2588                                 if (pp == NULL)
2589                                         goto panic;
2590                                 strncpy(pp, p1, 3);
2591                                 pp[3] = 0;
2592                         }
2593                         continue;
2594                 } else if (strncmp(buf, "LATT", 4) == 0) {
2595                         sscanf(buf + 4, " %d", &latticeType);
2596                         continue;
2597                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2598                         char *symops[3], *brks;
2599                         memset(tr, 0, sizeof(Transform));
2600                 //      ReadFormat(buf + 4, "I1", ibuf);
2601                         sChomp(buf);
2602                         symops[0] = strtok_r(buf + 4, ",", &brks);
2603                         symops[1] = strtok_r(NULL, ",", &brks);
2604                         symops[2] = strtok_r(NULL, ",", &brks);
2605                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2606                                 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2607                                 return 1;
2608                         }
2609                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2610                                 goto panic;
2611                         continue;
2612                 } else if (strncmp(buf, "RESI", 4) == 0) {
2613                         for (p1 = buf + 4; isspace(*p1); p1++);
2614                         if (isalpha(*p1)) {
2615                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2616                                 *p2 = 0;
2617                                 strncpy(currentResName, p1, 4);
2618                                 currentResName[4] = 0;
2619                                 p1 = p2 + 1;
2620                         } else currentResName[0] = 0;
2621                         sscanf(buf + 4, " %d", &currentResSeq);
2622                         continue;
2623                 } else {
2624                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2625                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2626                         if (p1 > buf) {
2627                                 while (isdigit(*p1))
2628                                         p1++;
2629                         }
2630                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2631                                 /*  Atom  */
2632                                 Atom *ap;
2633                                 char cont[4];
2634                                 int atomIndex = mp->natoms;
2635                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2636                                 memset(ap, 0, gSizeOfAtomRecord);
2637                                 strncpy(ap->aname, buf, 4);
2638                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2639                                 if (n == 8 && strcmp(cont, "=") == 0) {
2640                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2641                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2642                                                 return 1;
2643                                         }
2644                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2645                                         n = 10;   /*  Aniso  */
2646                                 } else n = 5; /*  Iso  */
2647                                 ap->r.x = fbuf[0];
2648                                 ap->r.y = fbuf[1];
2649                                 ap->r.z = fbuf[2];
2650                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2651                                 ap->occupancy = fbuf[3];
2652                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2653                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2654                                         ap->element[2] = 0;
2655                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2656                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2657                                         ap->atomicNumber = ElementToInt(ap->element); */
2658                         /*      } else {
2659                                         sAtomSetElement(ap, -1, ap->name); */
2660                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2661                                         ElementToString(ap->atomicNumber, ap->element); */
2662                                 }
2663                                 guessElement(ap);
2664                                 if (n == 10 || fbuf[4] >= 0.0) {
2665                                         int i, c, j;
2666                                         /*  Read in the standard deviations  */
2667                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2668                                         for (i = 0; i < 9; i++) {
2669                                                 j = 3 + i * 8;
2670                                                 c = buf[j + 8];
2671                                                 buf[j + 8] = 0;
2672                                                 dbuf[i] = strtod(buf + j, NULL);
2673                                                 buf[j + 8] = c;
2674                                         }
2675                                         ap->sigma.x = dbuf[0];
2676                                         ap->sigma.y = dbuf[1];
2677                                         ap->sigma.z = dbuf[2];
2678                                 }
2679                                 if (n == 5)
2680                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2681                                 else
2682                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2683                                 ap->resSeq = currentResSeq;
2684                                 strncpy(ap->resName, currentResName, 4);
2685                         }
2686                         continue;
2687                 }
2688         }
2689         fclose(fp);
2690
2691         /*  Add symmetry operations according to the lattice type  */
2692         switch (latticeType < 0 ? -latticeType : latticeType) {
2693                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2694                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2695                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2696                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2697                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2698                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2699                 case 1:  /* P */
2700                         break;
2701                 case 2:  /* I */
2702                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2703                         break;
2704                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2705                         n = mp->nsyms;
2706                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2707                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2708                         break;
2709                 case 4:  /* F */
2710                         n = mp->nsyms;
2711                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2712                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2713                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2714                         break;
2715                 case 5:  /* A */
2716                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2717                         break;
2718                 case 6:  /* B */
2719                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2720                         break;
2721                 case 7:  /* C */
2722                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2723                         break;
2724         }
2725                 
2726         if (latticeType > 0) {
2727                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2728                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2729         }
2730         
2731         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2732         if (nbonds > 0) {
2733                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2734                 free(bonds);
2735         }
2736         mp->nframes = -1;  /*  Should be recalculated later  */
2737         return 0;
2738   panic:
2739         Panic("low memory while reading structure file %s", fname);
2740         return -1; /* not reached */
2741 }
2742
2743 /*  Add one gaussian orbital shell information (not undoable)  */
2744 int
2745 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int a_idx, Int sym, Int nprims, Int add_exp)
2746 {
2747         BasisSet *bset;
2748         ShellInfo *shellp;
2749         if (mol == NULL)
2750                 return -1;  /*  Molecule is empty  */
2751         bset = mol->bset;
2752         if (bset == NULL) {
2753                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2754                 if (bset == NULL)
2755                         return -2;  /*  Low memory  */
2756         }
2757         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2758         if (shellp == NULL)
2759                 return -2;  /*  Low memory  */
2760         switch (sym) {
2761                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2762                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2763                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2764                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2765                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2766                 case 3:  shellp->sym = kGTOType_F;  shellp->ncomp = 10; break;
2767                 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2768                 case 4:  shellp->sym = kGTOType_G;  shellp->ncomp = 15; break;
2769                 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2770                 default:
2771                         return -3;  /* Unsupported shell type  */
2772         }
2773         shellp->nprim = nprims;
2774         shellp->a_idx = a_idx;
2775         if (bset->shells < shellp) {
2776                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2777                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2778         } else {
2779                 shellp->m_idx = 0;
2780                 shellp->p_idx = 0;
2781         }
2782     shellp->add_exp = add_exp;
2783         /*  Update the number of components (if not yet determined)  */
2784         if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2785                 bset->ncomps = shellp->m_idx + shellp->ncomp;
2786         return 0;
2787 }
2788
2789 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2790 int
2791 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2792 {
2793         BasisSet *bset;
2794         PrimInfo *primp;
2795         if (mol == NULL)
2796                 return -1;  /*  Molecule is empty  */
2797         bset = mol->bset;
2798         if (bset == NULL) {
2799                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2800                 if (bset == NULL)
2801                         return -2;  /*  Low memory  */
2802         }
2803         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2804         if (primp == NULL)
2805                 return -2;  /*  Low memory  */
2806         primp->A = exponent;
2807         primp->C = contraction;
2808         primp->Csp = contraction_sp;
2809         return 0;
2810 }
2811
2812 /*  Get the shell information from the component index  */
2813 /*  The outLabel must have space for at least 31 non-Null characters  */
2814 /*  See also: sub load_janpa_log in loadsave.rb  */
2815 int
2816 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char outLabel[32], Int *outShellIdx)
2817 {
2818         BasisSet *bset;
2819         ShellInfo *shellp;
2820         int si;
2821         if (mol == NULL || (bset = mol->bset) == NULL)
2822                 return -1;  /*  No basis set info  */
2823         if (comp_idx < 0 || comp_idx >= bset->ncomps)
2824                 return -2;  /*  Component index out of range  */
2825         for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2826                 if (comp_idx >= shellp->ncomp) {
2827                         comp_idx -= shellp->ncomp;
2828                         continue;
2829                 } else {
2830                         static const char *type_p = "xyz";
2831                         static const char *type_d = "xxyyzzxyxzyz";
2832                         static const char *type_d5[] = {"zz-rr", "xy", "yz", "xx-yy", "xy"};
2833                         static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2834                         static const char *type_f7[] = {"z3-zr2", "xz2-xr2", "yz2-yr2", "x2z-y2z", "xyz", "x3-xy2", "x2y-y3"};
2835                         static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "xy2z", "xyz2"};
2836                         static const char *type_g9[] = {"z4-z2r2+r4", "xz3-xzr2", "yz3-yzr2", "x2z2-y2z2", "xyz2-xyr2", "x3z-xy2z", "x2yz-y3z", "x4-x2y2+y4", "x3y-xy3"};
2837             *outAtomIdx = shellp->a_idx;
2838                         *outShellIdx = si;
2839                         switch (shellp->sym) {
2840                                 case kGTOType_S:
2841                                         strcpy(outLabel, "S");
2842                                         break;
2843                                 case kGTOType_P:
2844                                         outLabel[0] = 'P';
2845                                         outLabel[1] = type_p[comp_idx];
2846                                         outLabel[2] = 0;
2847                                         break;
2848                                 case kGTOType_SP:
2849                                         if (comp_idx == 0)
2850                                                 strcpy(outLabel, "S");
2851                                         else {
2852                                                 outLabel[0] = 'P';
2853                                                 outLabel[1] = type_p[comp_idx - 1];
2854                                                 outLabel[2] = 0;
2855                                         }
2856                                         break;
2857                                 case kGTOType_D:
2858                                         outLabel[0] = 'D';
2859                                         strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2860                                         outLabel[3] = 0;
2861                                         break;
2862                                 case kGTOType_D5:
2863                                         outLabel[0] = 'D';
2864                                         strncpy(outLabel + 1, type_d5[comp_idx], 30);
2865                     outLabel[31] = 0;
2866                                         break;
2867                                 case kGTOType_F:
2868                                         outLabel[0] = 'F';
2869                                         strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2870                                         outLabel[4] = 0;
2871                                         break;
2872                                 case kGTOType_F7:
2873                                         outLabel[0] = 'F';
2874                                         strncpy(outLabel + 1, type_f7[comp_idx], 30);
2875                     outLabel[31] = 0;
2876                                         break;
2877                                 case kGTOType_G:
2878                                         outLabel[0] = 'G';
2879                                         strncpy(outLabel + 1, type_g[comp_idx], 30);
2880                     outLabel[31] = 0;
2881                                         break;
2882                                 case kGTOType_G9:
2883                                         outLabel[0] = 'G';
2884                                         strncpy(outLabel + 1, type_g9[comp_idx], 30);
2885                     outLabel[31] = 0;
2886                                         break;
2887                                 default:
2888                                         return -3;  /*  Unsupported orbital type (internal error) */
2889                         }
2890             if (shellp->add_exp > 0) {
2891                 /*  Additional exponent (JANPA extension): like "s*r2", "dxx-yy*r4", etc. */
2892                 int len = strlen(outLabel);
2893                 if (len < 28) {
2894                     snprintf(outLabel + len, 32 - len, "*r%d", shellp->add_exp);
2895                 }
2896                 outLabel[31] = 0;
2897             }
2898                         return 0;
2899                 }
2900         }
2901         return -4;  /*  comp_idx out of range? (internal error)  */
2902 }
2903
2904 /*  Set MO coefficients for idx-th MO (1-based)  */
2905 int
2906 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2907 {
2908         BasisSet *bset;
2909         int i, n;
2910         if (mol == NULL)
2911                 return -1;  /*  Molecule is empty  */
2912         bset = mol->bset;
2913         if (bset == NULL) {
2914                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2915                 if (bset == NULL)
2916                         return -2;  /*  Low memory  */
2917         }
2918         if (bset->nmos == 0) {
2919                 if (bset->nshells > 0) {
2920                         /*  Shell info is already set: calculate the number of MOs from there  */
2921                         for (i = n = 0; i < bset->nshells; i++)
2922                                 n += bset->shells[i].ncomp;
2923                         bset->ncomps = n;
2924                 } else if (ncomps > 0) {
2925                         bset->ncomps = ncomps;
2926                 }
2927                 if (bset->rflag == 0)
2928                         bset->nmos = bset->ncomps * 2;
2929                 else
2930                         bset->nmos = bset->ncomps;
2931                 if (bset->nmos <= 0)
2932                         return -3;  /*  Bad or inconsistent number of MOs  */
2933                 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2934                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2935                 if (bset->mo == NULL || bset->moenergies == NULL) {
2936                         if (bset->mo != NULL)
2937                                 free(bset->mo);
2938                         if (bset->moenergies != NULL)
2939                                 free(bset->moenergies);
2940                         bset->mo = NULL;
2941                         bset->moenergies = NULL;
2942                         bset->nmos = 0;
2943                         return -2;  /*  Low memory  */
2944                 }
2945         }
2946         if (idx < 0)
2947                 idx = -idx + bset->ncomps;
2948         if (idx < 0 || idx > bset->nmos)
2949                 return -4;  /*  Bad MO index  */
2950         if (idx == 0)
2951                 idx = bset->nmos;  /*  Arbitrary vector  */
2952         else
2953                 idx--;
2954         if (energy != -1000000)
2955                 bset->moenergies[idx] = energy;
2956         if (ncomps < bset->ncomps)
2957                 return -5;  /*  Insufficient number of data provided  */
2958         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2959         if (bset->cns != NULL) {
2960                 /*  Clear the cached values  */
2961                 free(bset->cns);
2962                 bset->cns = NULL;
2963                 bset->ncns = 0;
2964         }
2965         return 0;
2966 }
2967
2968 /*  Get MO coefficients for idx-th MO (1-based)  */
2969 /*  Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e.  */
2970 /*  *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs  */
2971 /*  properly designates the memory size as an array of Doubles.  */
2972 int
2973 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2974 {
2975         BasisSet *bset;
2976         if (mol == NULL)
2977                 return -1;  /*  Molecule is empty  */
2978         bset = mol->bset;
2979         if (bset == NULL || bset->ncomps <= 0)
2980                 return -2;  /*  No basis set info  */
2981         if (idx < 0)
2982                 idx = -idx + bset->ncomps;
2983         if (idx < 0 || idx > bset->nmos)
2984                 return -3;  /*  MO index out of range  */
2985         if (idx == 0)
2986                 idx = bset->nmos;  /*  Arbitrary vector  */
2987         else
2988                 idx--;
2989         if (energy != NULL)
2990                 *energy = bset->moenergies[idx];
2991         if (ncoeffs != NULL && coeffs != NULL) {
2992                 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2993                         if (*coeffs != NULL)
2994                                 free(*coeffs);  /*  Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2995                         *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2996                         *ncoeffs = bset->ncomps;
2997                 }
2998                 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2999         }
3000         return 0;
3001 }
3002
3003 /*  Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
3004     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons   */
3005 int
3006 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
3007 {
3008         BasisSet *bset;
3009         if (mol == NULL || mol->natoms == 0)
3010                 return -1;  /*  Molecule is empty  */
3011         if (rflag < 0) {
3012                 if (mol->bset != NULL) {
3013                         BasisSetRelease(mol->bset);
3014                         mol->bset = NULL;
3015                 }
3016                 return 0;
3017         }
3018         bset = mol->bset;
3019         if (bset == NULL) {
3020                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3021                 if (bset == NULL)
3022                         return -2;  /*  Low memory  */
3023         }
3024         bset->natoms_bs = mol->natoms;
3025         bset->ne_alpha = ne_alpha;
3026         bset->ne_beta = ne_beta;
3027         bset->rflag = rflag;
3028         return 0;
3029 }
3030
3031 static void
3032 sSeparateTokens(char *inString, char **outPtr, int size)
3033 {
3034         char *p;
3035         int i;
3036         for (i = 0; i < size; i++) {
3037                 p = strtok((i == 0 ? inString : NULL), " \r\n");
3038                 if (p == NULL)
3039                         break;
3040                 outPtr[i] = p;
3041         }
3042         while (i < size) {
3043                 outPtr[i++] = NULL;
3044         }
3045 }
3046
3047 static int
3048 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3049 {
3050         char buf[256];
3051         Int i, n;
3052         *((void **)basep) = NULL;
3053         *countp = 0;
3054         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3055                 return 4;  /*  Out of memory  */
3056         n = 0;
3057         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3058                 char *tokens[16], *p;
3059                 sSeparateTokens(buf, tokens, 16);
3060                 for (i = 0; i < 16; i++) {
3061                         if (tokens[i] == NULL)
3062                                 break;
3063                         if (size == sizeof(Int)) {
3064                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3065                         } else if (size == sizeof(Double)) {
3066                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3067                         } else return -1;  /*  Internal error  */
3068                         if (tokens[i] == p || *p != 0)
3069                                 return 1;  /*  Non-digit character  */
3070                         if (++n == num) {
3071                                 if (i < 15 && tokens[i + 1] != NULL)
3072                                         return 2;  /*  Too many data  */
3073                                 return 0;  /*  All data are successfully read  */
3074                         }
3075                 }
3076         }
3077         return 3;  /*  Unexpected EOF  */                       
3078 }
3079
3080 //  Normalization constant for one gaussian component
3081 //  1/sqrt(Integral((Y(lm)*(r^n)*exp(-a*r*r))^2, for all r = (x, y, z)))
3082 //  where Y(lm) is a spherical harmonic function, r^n is an "additional exponent"
3083 //  required in expanded Molden file generated by JANPA, and a is the exponent
3084 //  of the gaussian component.
3085 //  The function Y(lm) is assumed so that its norm equals sqrt(4*pi/(2l+1))
3086 //  for each m in [-l..l].
3087 static double
3088 sGaussianNormalizationConstant(int l, double a, int n)
3089 {
3090     return 1.0/(sqrt(4 * PI / (2 * l + 1.0)) * sqrt(tgamma(l + n + 1.5) / (2.0 * pow(2.0 * a, l + n + 1.5))));
3091 }
3092
3093 static int
3094 sSetupGaussianCoefficients(BasisSet *bset)
3095 {
3096         ShellInfo *sp;
3097         PrimInfo *pp;
3098     int i, j, k, n;
3099         Double *dp, d;
3100         
3101         /*  Cache the contraction coefficients for efficient calculation  */
3102         /*  Sum up the number of components for all primitives  */
3103         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3104                 sp->cn_idx = k;
3105                 k += sp->nprim * sp->ncomp;
3106         }
3107         /*  Allocate memory for the cached values  */
3108         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3109                 return 1;
3110         /*  Iterate over all primitives  */
3111         dp = bset->cns;
3112         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3113                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3114             n = sp->add_exp;
3115                         switch (sp->sym) {
3116                                 case kGTOType_S:
3117                     // GNC(0,a,n) * r^n * exp(-a*r^2)
3118                 d = pp->C * sGaussianNormalizationConstant(0, pp->A, n);
3119                 *dp++ = d;
3120                     //{ printf("type_S: %g %g\n", d, pp->C * pow(pp->A, 0.75) * 0.71270547); }
3121                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3122                                         //*dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3123                                         break;
3124                                 case kGTOType_P:
3125                     // GNC(1,a,n) * [x|y|z] * r^n * exp(-a*r^2)
3126                     d = pp->C * sGaussianNormalizationConstant(1, pp->A, n);
3127                     //{ printf("type_P: %g %g\n", d, pp->C * pow(pp->A, 1.25) * 1.425410941); }
3128                     // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3129                                         // d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3130                                         *dp++ = d;
3131                                         *dp++ = d;
3132                                         *dp++ = d;
3133                                         break;
3134                                 case kGTOType_SP:
3135                     // GNC(0,a,n) * r^n * exp(-a*r^2)
3136                     *dp++ = d = pp->C * sGaussianNormalizationConstant(0, pp->A, n);
3137                     //{ printf("type_SP(s): %g %g\n", d, pp->C * pow(pp->A, 0.75) * 0.71270547); }
3138                     // GNC(1,a,n) * [x|y|z] * r^n * exp(-a*r^2)
3139                     d = pp->Csp * sGaussianNormalizationConstant(1, pp->A, n);
3140                     //{ printf("type_SP(p): %g %g\n", d, pp->Csp * pow(pp->A, 1.25) * 1.425410941); }
3141                                         //*dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3142                                         //d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3143                                         *dp++ = d;
3144                                         *dp++ = d;
3145                                         *dp++ = d;
3146                                         break;
3147                                 case kGTOType_D:
3148                     // GNC(2,a,n) * [xx|yy|zz] * r^n * exp(-a*r^2)
3149                     // GNC(2,a,n) * sqrt(3) * [xy|yz|zx] * r^n * exp(-a*r^2)
3150                     d = pp->C * sGaussianNormalizationConstant(2, pp->A, n);
3151                     //{ printf("type_D[0-2]: %g %g\n", d, pp->C * pow(pp->A, 1.75) * 1.645922781); }
3152                     //{ printf("type_D[3-5]: %g %g\n", d * sqrt(3), pp->C * pow(pp->A, 1.75) * 2.850821881); }
3153                     dp[0] = dp[1] = dp[2] = d;
3154                     dp[3] = dp[4] = dp[5] = d * sqrt(3);
3155                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3156                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3157                                         // d = pp->C * pow(pp->A, 1.75);
3158                                         //dp[0] = dp[1] = dp[2] = d * 1.645922781;
3159                                         //dp[3] = dp[4] = dp[5] = d * 2.850821881;
3160                                         dp += 6;
3161                                         break;
3162                                 case kGTOType_D5:
3163                     // D(0): GNC(2,a,n) * (1/2) * (3zz-rr) * r^n * exp(-a*r^2)
3164                     // D(+1): GNC(2,a,n) * sqrt(3) * xz * r^n * exp(-a*r^2)
3165                     // D(-1): GNC(2,a,n) * sqrt(3) * yz * r^n * exp(-a*r^2)
3166                     // D(+2): GNC(2,a,n) * (sqrt(3)/2) * (xx-yy) * r^n * exp(-a*r^2)
3167                     // D(-2): GNC(2,a,n) * sqrt(3) * xy * r^n * exp(-a*r^2)
3168                     d = pp->C * sGaussianNormalizationConstant(2, pp->A, n);
3169                     //{ printf("type_D5[0]: %g %g\n", d * 0.5, pp->C * pow(pp->A, 1.75) * 0.822961390); }
3170                     //{ printf("type_D5[1,2,4]: %g %g\n", d * sqrt(3), pp->C * pow(pp->A, 1.75) * 2.850821881); }
3171                     //{ printf("type_D5[3]: %g %g\n", d * sqrt(3) * 0.5, pp->C * pow(pp->A, 1.75) * 1.425410941); }
3172                     dp[0] = d * 0.5;
3173                     dp[1] = dp[2] = dp[4] = d * sqrt(3);
3174                     dp[3] = d * sqrt(3) * 0.5;
3175                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3176                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3177                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3178                                         //d = pp->C * pow(pp->A, 1.75);
3179                                         //dp[0] = d * 0.822961390;
3180                                         //dp[1] = dp[2] = dp[4] = d * 2.850821881;
3181                                         //dp[3] = d * 1.425410941;
3182                                         dp += 5;
3183                                         break;
3184                 case kGTOType_F:
3185                     // GNC(3,a,n) * [xxx|yyy|zzz] * r^n * exp(-a*r^2)
3186                     // GNC(3,a,n) * sqrt(5) * [xyy|xxy|xxz|xzz|yzz|yyz] * r^n * exp(-a*r^2)
3187                     // GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
3188                     d = pp->C * sGaussianNormalizationConstant(3, pp->A, n);
3189                     dp[0] = dp[1] = dp[2] = d;
3190                     dp[3] = dp[4] = dp[5] = dp[6] = dp[7] = dp[8] = d * sqrt(5);
3191                     dp[9] = d * sqrt(15);
3192                     dp += 10;
3193                     break;
3194                 case kGTOType_F7:
3195                     // F(0): GNC(3,a,n) * (1/2) * (5zzz-3zrr) * r^n * exp(-a*r^2)
3196                     // F(+1): GNC(3,a,n) * sqrt(3/8) * (5xzz-xrr) * r^n * exp(-a*r^2)
3197                     // F(-1): GNC(3,a,n) * sqrt(3/8) * (5yzz-yrr) * r^n * exp(-a*r^2)
3198                     // F(+2): GNC(3,a,n) * sqrt(15/4) * (xxz-yyz) * r^n * exp(-a*r^2)
3199                     // F(-2): GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
3200                     // F(+3): GNC(3,a,n) * sqrt(5/8) * (xxx-3xyy) * r^n * exp(-a*r^2)
3201                     // F(-3): GNC(3,a,n) * sqrt(5/8) * (3xxy-yyy) * r^n * exp(-a*r^2)
3202                     d = pp->C * sGaussianNormalizationConstant(3, pp->A, n);
3203                     dp[0] = d * 0.5;
3204                     dp[1] = dp[2] = d * sqrt(3/8.0);
3205                     dp[3] = d * sqrt(15/4.0);
3206                     dp[4] = d * sqrt(15);
3207                     dp[5] = dp[6] = d * sqrt(5/8.0);
3208                     dp += 7;
3209                     break;
3210                 case kGTOType_G:
3211                     // GNC(4,a,n) * [xxxx|yyyy|zzzz] * exp(-a*r^2)
3212                     // GNC(4,a,n) * sqrt(7) * [xxxy|xxxz|yyyx|yyyz|zzzx|zzzy] * exp(-a*r^2)
3213                     // GNC(4,a,n) * sqrt(35/3) * [xxyy|xxzz|yyzz] * exp(-a*r^2)
3214                     // GNC(4,a,n) * sqrt(35) * [xxyz|yyzx|zzxy] * exp(-a*r^2)
3215                     d = pp->C * sGaussianNormalizationConstant(4, pp->A, n);
3216                     dp[0] = dp[1] = dp[2] = d;
3217                     dp[3] = dp[4] = dp[5] = dp[6] = dp[7] = dp[8] = d * sqrt(7);
3218                     dp[9] = dp[10] = dp[11] = d * sqrt(35/3.0);
3219                     dp[12] = dp[13] = dp[14] = d * sqrt(35);
3220                     dp += 15;
3221                     break;
3222                 case kGTOType_G9:
3223                     // G(0): GNC(4,a,n) * (1/8) * (35zzzz-30zzrr+3rrrr) * exp(-a*r^2)
3224                     // G(+1): GNC(4,a,n) * sqrt(5/8) * (7xzzz-3xzrr) * exp(-a*r^2)
3225                     // G(-1): GNC(4,a,n) * sqrt(5/8) * (7yzzz-3yzrr) * exp(-a*r^2)
3226                     // G(+2): GNC(4,a,n) * sqrt(5/16) * (xx-yy)(7zz-rr) * exp(-a*r^2)
3227                     // G(-2): GNC(4,a,n) * sqrt(5/4) * (7xyzz-xyrr) * exp(-a*r^2)
3228                     // G(+3): GNC(4,a,n) * sqrt(35/8) * (xxxz-3xyyz) * exp(-a*r^2)
3229                     // G(-3): GNC(4,a,n) * sqrt(35/8) * (3xxyz-yyyz) * exp(-a*r^2)
3230                     // G(+4): GNC(4,a,n) * sqrt(35/64) * (xxxx-6xxyy+yyyy) * exp(-a*r^2)
3231                     // G(-4): GNC(4,a,n) * sqrt(35/4) * (xxxy-xyyy) * exp(-a*r^2)
3232                     d = pp->C * sGaussianNormalizationConstant(4, pp->A, n);
3233                     dp[0] = d * 0.125;
3234                     dp[1] = dp[2] = d * sqrt(5/8.0);
3235                     dp[3] = d * sqrt(5/16.0);
3236                     dp[4] = d * sqrt(5/4.0);
3237                     dp[5] = dp[6] = d * sqrt(35/8.0);
3238                     dp[7] = d * sqrt(35/64.0);
3239                     dp[8] = d * sqrt(35/4.0);
3240                     dp += 9;
3241                     break;
3242                         }
3243                 }
3244         }
3245         return 0;
3246 }
3247
3248 int
3249 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3250 {
3251         FILE *fp;
3252         char buf[1024];
3253         int lineNumber;
3254         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3255         BasisSet *bset;
3256         ShellInfo *sp;
3257         PrimInfo *pp;
3258         Int nary;
3259         Int *iary;
3260         Double *dary;
3261         Atom *ap;
3262 /*      Vector *vp; */
3263         Double w;
3264
3265         *errbuf = NULL;
3266         if (mp == NULL)
3267                 mp = MoleculeNew();
3268         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3269         if (bset == NULL)
3270                 goto panic;
3271         mp->bset = bset;
3272         fp = fopen(fname, "rb");
3273         if (fp == NULL) {
3274                 s_append_asprintf(errbuf, "Cannot open file");
3275                 return 1;
3276         }
3277         lineNumber = 0;
3278         natoms = nbasis = -1;
3279         mxbond = 0;
3280         ncomps = 0;
3281         nelec = 0;
3282         nprims = 0;
3283         nary = 0;
3284         iary = NULL;
3285         dary = NULL;
3286         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3287                 char *tokens[16];
3288                 char *p = buf + 41;
3289                 if (lineNumber == 2) {
3290                         /*  job info line  */
3291                         if (buf[10] == 'U')
3292                                 bset->rflag = 0;  /*  UHF  */
3293                         else if (buf[11] == 'O')
3294                                 bset->rflag = 2;  /*  ROHF  */
3295                         else bset->rflag = 1; /*  RHF  */
3296                         continue;
3297                 }
3298                 while (p > buf && *p == ' ')
3299                         p--;
3300                 p[1] = 0;
3301                 sSeparateTokens(buf + 42, tokens, 16);
3302                 if (strcmp(buf, "Number of atoms") == 0) {
3303                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3304                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3305                                 retval = 2;
3306                                 goto cleanup;
3307                         }
3308                         bset->natoms_bs = natoms;
3309                         /*  Allocate atom records (all are empty for now)  */
3310                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3311                         /*  Also allocate atom position array for MO calculations  */
3312                 /*      AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3313                         /*  Also allocate nuclear charge array  */
3314                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3315                 } else if (strcmp(buf, "Number of electrons") == 0) {
3316                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3317                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3318                                 retval = 2;
3319                                 goto cleanup;
3320                         }
3321                         nelec = i;
3322                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3323                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3324                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3325                                 retval = 2;
3326                                 goto cleanup;
3327                         }
3328                         bset->ne_alpha = i;
3329                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3330                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3331                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3332                                 retval = 2;
3333                                 goto cleanup;
3334                         }
3335                         bset->ne_beta = i;
3336                         if (bset->ne_alpha + bset->ne_beta != nelec) {
3337                                 s_append_asprintf(errbuf, "Line %d: sum of alpha (%d) and beta (%d) electrons does not match the number of electrons (%d)", lineNumber, (int)bset->ne_alpha, (int)bset->ne_beta, (int)nelec);
3338                                 retval = 2;
3339                                 goto cleanup;
3340                         }
3341                 } else if (strcmp(buf, "Number of basis functions") == 0) {
3342                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3343                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3344                                 retval = 2;
3345                                 goto cleanup;
3346                         }
3347                 } else if (strcmp(buf, "Atomic numbers") == 0) {
3348                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3349                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3350                                 retval = 2;
3351                                 goto cleanup;
3352                         }
3353                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3354                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3355                                 retval = 2;
3356                                 goto cleanup;
3357                         }
3358                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3359                                 ap->atomicNumber = iary[i];
3360                                 bset->nuccharges[i] = iary[i];
3361                                 ElementToString(ap->atomicNumber, ap->element);
3362                                 memmove(ap->aname, ap->element, 4);
3363                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3364                                         ap->weight = w;
3365                         }
3366                         free(iary);
3367                         iary = NULL;
3368                 } else if (strcmp(buf, "Nuclear charges") == 0) {
3369                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3370                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3371                                 retval = 2;
3372                                 goto cleanup;
3373                         }
3374                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3375                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3376                                 retval = 2;
3377                                 goto cleanup;
3378                         }
3379                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3380                                 bset->nuccharges[i] = dary[i];
3381                         }
3382                         free(iary);
3383                         iary = NULL;
3384                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3385                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3386                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3387                                 retval = 2;
3388                                 goto cleanup;
3389                         }
3390                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3391                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3392                                 retval = 2;
3393                                 goto cleanup;
3394                         }
3395                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3396                                 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3397                                 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3398                                 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3399                         }
3400                         free(dary);
3401                         dary = NULL;
3402                 } else if (strcmp(buf, "MxBond") == 0) {
3403                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3404                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3405                                 retval = 2;
3406                                 goto cleanup;
3407                         }
3408                 } else if (strcmp(buf, "IBond") == 0) {
3409                         Int *bonds;
3410                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3411                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3412                                 retval = 2;
3413                                 goto cleanup;
3414                         }
3415                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3416                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3417                                 retval = 2;
3418                                 goto cleanup;
3419                         }
3420                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3421                         for (i = 0; i < natoms; i++) {
3422                                 for (j = k = 0; j < mxbond; j++) {
3423                                         n = iary[i * mxbond + j] - 1;
3424                                         if (n > i) {
3425                                                 /*  Connect atom i and atom n  */
3426                                                 bonds[k++] = i;
3427                                                 bonds[k++] = n;
3428                                         }
3429                                 }
3430                                 if (k > 0) {
3431                                         bonds[k] = kInvalidIndex;
3432                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3433                                 }
3434                         }
3435                         free(iary);
3436                         free(bonds);
3437                         iary = NULL;
3438                 } else if (strcmp(buf, "Shell types") == 0) {
3439                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3440                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3441                                 retval = 2;
3442                                 goto cleanup;
3443                         }
3444                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3445                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3446                                 retval = 2;
3447                                 goto cleanup;
3448                         }
3449                         /*  Allocate ShellInfo table and store shell type information  */
3450                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3451                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3452                                 switch (iary[i]) {
3453                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
3454                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
3455                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3456                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
3457                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3458                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
3459                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3460                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
3461                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3462                                         default:
3463                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3464                                                 retval = 2;
3465                                                 goto cleanup;
3466                                 }
3467                                 sp->m_idx = n;
3468                                 n += sp->ncomp;
3469                         }
3470                         bset->ncomps = ncomps = n;
3471                         free(iary);
3472                         iary = NULL;
3473                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3474                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3475                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3476                                 retval = 2;
3477                                 goto cleanup;
3478                         }
3479                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3480                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3481                                 retval = 2;
3482                                 goto cleanup;
3483                         }
3484                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3485                                 sp->nprim = iary[i];
3486                                 sp->p_idx = n;
3487                                 n += sp->nprim;
3488                         }
3489                         nprims = n;
3490                         free(iary);
3491                         iary = NULL;
3492                 } else if (strcmp(buf, "Shell to atom map") == 0) {
3493                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3494                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3495                                 retval = 2;
3496                                 goto cleanup;
3497                         }
3498                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3499                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3500                                 retval = 2;
3501                                 goto cleanup;
3502                         }
3503                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3504                                 sp->a_idx = iary[i] - 1;
3505                         }
3506                         free(iary);
3507                         iary = NULL;
3508                 } else if (strcmp(buf, "Primitive exponents") == 0) {
3509                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3510                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3511                                 retval = 2;
3512                                 goto cleanup;
3513                         }
3514                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3515                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3516                                 retval = 2;
3517                                 goto cleanup;
3518                         }
3519                         /*  Allocate PrimInfo table  */
3520                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3521                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3522                                 pp->A = dary[i];
3523                         }
3524                         free(dary);
3525                         dary = NULL;
3526                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3527                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3528                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3529                                 retval = 2;
3530                                 goto cleanup;
3531                         }
3532                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3533                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3534                                 retval = 2;
3535                                 goto cleanup;
3536                         }
3537                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3538                                 pp->C = dary[i];
3539                         }
3540                         free(dary);
3541                         dary = NULL;
3542                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3543                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3544                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3545                                 retval = 2;
3546                                 goto cleanup;
3547                         }
3548                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3549                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3550                                 retval = 2;
3551                                 goto cleanup;
3552                         }
3553                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3554                                 pp->Csp = dary[i];
3555                         }
3556                         free(dary);
3557                         dary = NULL;
3558                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3559                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3560                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3561                                 retval = 2;
3562                                 goto cleanup;
3563                         }
3564                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3565                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3566                                 retval = 2;
3567                                 goto cleanup;
3568                         }
3569                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3570                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3571                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3572                                 retval = 2;
3573                                 goto cleanup;
3574                         }
3575                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3576                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3577                                 retval = 2;
3578                                 goto cleanup;
3579                         }
3580                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3581                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3582                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3583                                 retval = 2;
3584                                 goto cleanup;
3585                         }
3586                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3587                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3588                                 retval = 2;
3589                                 goto cleanup;
3590                         }
3591                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3592                         bset->nmos = ncomps * 2;
3593                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3594                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3595                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3596                         free(dary);
3597                         dary = NULL;
3598                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3599                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3600                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3601                                 retval = 2;
3602                                 goto cleanup;
3603                         }
3604                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3605                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3606                                 retval = 2;
3607                                 goto cleanup;
3608                         }
3609                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
3610                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3611                         free(dary);
3612                         dary = NULL;
3613                 } else if (strcmp(buf, "Total SCF Density") == 0) {
3614                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3615                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3616                                 retval = 2;
3617                                 goto cleanup;
3618                         }
3619                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3620                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3621                                 retval = 2;
3622                                 goto cleanup;
3623                         }
3624                 }
3625         }
3626         if (mp->natoms == 0) {
3627                 s_append_asprintf(errbuf, "Atom information is missing");
3628                 retval = 2;
3629                 goto cleanup;
3630         }
3631         if (bset->shells == NULL || bset->priminfos == NULL) {
3632                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3633                 retval = 2;
3634                 goto cleanup;
3635         }
3636         if (bset->mo == NULL) {
3637                 s_append_asprintf(errbuf, "MO coefficients were not found");
3638                 retval = 2;
3639                 goto cleanup;
3640         }
3641         if (sSetupGaussianCoefficients(bset) != 0) {
3642                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3643                 retval = 2;
3644                 goto cleanup;
3645         }
3646         mp->nframes = -1;
3647         retval = 0;
3648 cleanup:
3649         fclose(fp);
3650         if (iary != NULL)
3651                 free(iary);
3652         if (dary != NULL)
3653                 free(dary);
3654         if (retval != 0) {
3655                 if (mp->bset != NULL) {
3656                         BasisSetRelease(mp->bset);
3657                         mp->bset = NULL;
3658                 }
3659         }
3660         return retval;
3661 panic:
3662         Panic("low memory while reading fchk file %s", fname);
3663         return -1; /* not reached */    
3664 }
3665
3666 int
3667 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3668 {
3669         FILE *fp;
3670         int newmol = 0;
3671         char buf[1024];
3672         int lineNumber, i, j, k, len, natoms = 0;
3673         int nframes = 0;
3674         int n1;
3675         int retval = 0;
3676         int ival[8];
3677         double dval[8];
3678         char sval[16];
3679         Vector *vbuf = NULL;
3680         IntGroup *ig;
3681         int optimizing = 0, status = 0;
3682         
3683         *errbuf = NULL;
3684         if (mol == NULL) {
3685                 mol = MoleculeNew();
3686         }
3687         if (mol->natoms == 0)
3688                 newmol = 1;
3689
3690         fp = fopen(fname, "rb");
3691         if (fp == NULL) {
3692                 s_append_asprintf(errbuf, "Cannot open file");
3693                 return 1;
3694         }
3695         
3696         /*  ESP is cleared (not undoable!)  */
3697         if (mol->elpots != NULL) {
3698                 free(mol->elpots);
3699                 mol->elpots = NULL;
3700                 mol->nelpots = 0;
3701         }
3702         
3703         lineNumber = 0;
3704         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3705         redo:
3706                 n1 = 0;
3707                 if (strncmp(buf, " $DATA", 6) == 0) {
3708                         /*  Initial geometry  */
3709                         if (!newmol) {
3710                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3711                         }
3712                         i = 0;
3713                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3714                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3715                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3716                                 if (strncmp(buf, " $END", 5) == 0)
3717                                         break;
3718                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3719                                         s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3720                                         retval = 2;
3721                                         goto exit_loop;
3722                                 }
3723                                 if (newmol) {
3724                                         Atom a;
3725                                         memset(&a, 0, sizeof(a));
3726                                         strncpy(a.aname, sval, 4);
3727                                         a.r.x = dval[1];
3728                                         a.r.y = dval[2];
3729                                         a.r.z = dval[3];
3730                                         a.atomicNumber = (Int)dval[0];
3731                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3732                                         a.type = AtomTypeEncodeToUInt(a.element);
3733                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3734                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3735                                 } else {
3736                                         Atom *ap;
3737                                         if (i >= mol->natoms) {
3738                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3739                                                 retval = 3;
3740                                                 goto exit_loop;
3741                                         }
3742                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3743                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3744                                                 retval = 4;
3745                                                 goto exit_loop;
3746                                         }
3747                                         vbuf[i].x = dval[1];
3748                                         vbuf[i].y = dval[2];
3749                                         vbuf[i].z = dval[3];
3750                                 }
3751                                 /*  Skip until a blank line is found  */
3752                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3753                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3754                                         for (j = 0; buf[j] == ' '; j++);
3755                                         if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3756                                                 break;
3757                                 }
3758                                 i++;
3759                         }
3760                         natoms = i;
3761                         if (!newmol) {
3762                                 /*  Set atom positions  */
3763                                 IntGroup *ig;
3764                                 if (natoms < mol->natoms) {
3765                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3766                                         retval = 5;
3767                                         goto exit_loop;
3768                                 }
3769                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3770                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3771                                 IntGroupRelease(ig);
3772                         }
3773                         if (vbuf == NULL)
3774                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3775                         nframes = MoleculeGetNumberOfFrames(mol);
3776                         if (status < 0)
3777                                 break;
3778                         continue;
3779                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3780                         /*  Skip until the separator line is read (three or four lines)  */
3781                         i = 0;
3782                         do {
3783                                 if (i++ >= 4) {
3784                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3785                                         retval = 6;
3786                                         goto exit_loop;
3787                                 }
3788                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3789                         } while (strstr(buf, "----------------------------") == NULL);
3790                         for (i = 0; i < natoms; i++) {
3791                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3792                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3793                                         retval = 6;
3794                                         goto exit_loop;
3795                                 }
3796                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3797                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3798                                         retval = 6;
3799                                         goto exit_loop;
3800                                 }
3801                                 vbuf[i].x = dval[1];
3802                                 vbuf[i].y = dval[2];
3803                                 vbuf[i].z = dval[3];
3804                         }
3805                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3806                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3807                         IntGroupRelease(ig);
3808                         nframes++;
3809                         if (n1 == 0)
3810                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3811                         else
3812                                 optimizing = 0;
3813                         continue;
3814                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3815                         if (mol->bset == NULL) {
3816                                 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3817                                 if (i != 0) {
3818                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3819                                         retval = 8;
3820                                         goto exit_loop;
3821                                 }
3822                         }
3823                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3824                         Double *coeffs;
3825                         /*  Read the vec group  */
3826                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3827                                 continue;  /*  Just ignore  */
3828                         if (optimizing)
3829                                 continue;  /*  Ignore VEC group during optimization  */
3830                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3831                         if (coeffs == NULL) {
3832                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3833                                 retval = 9;
3834                                 goto exit_loop;
3835                         }
3836                         i = k = 0;
3837                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3838                                 len = strlen(buf);
3839                                 if (strncmp(buf, " $END", 5) == 0)
3840                                         break;
3841                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3842                                         strncpy(sval, buf + j, 15);
3843                                         sval[15] = 0;
3844                                         coeffs[k] = strtod(sval, NULL);
3845                                         k++;
3846                                         if ((k % 5) == 0)
3847                                                 break;
3848                                 }
3849                                 if (k < mol->bset->ncomps)
3850                                         continue;
3851                                 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3852                                 if (j != 0) {
3853                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3854                                         free(coeffs);
3855                                         retval = 10;
3856                                         goto exit_loop;
3857                                 }
3858                                 i++;
3859                                 k = 0;
3860                         }
3861                         if (status < 0)
3862                                 break;
3863                         continue;
3864                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3865                         i = 0;
3866                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3867                                 Elpot *ep;
3868                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3869                                         continue;
3870                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3871                                         break;
3872                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3873                                 ep->pos.x = dval[0];
3874                                 ep->pos.y = dval[1];
3875                                 ep->pos.z = dval[2];
3876                                 ep->esp = dval[3];
3877                                 i++;
3878                         }
3879                         if (status > 0)
3880                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3881                         else break;    /*  End of file encountered or interrupted */
3882                 }  /*  TODO: read MOLPLT info if present  */
3883         }
3884         if (status < 0) {
3885                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3886                 retval = 11;
3887         }
3888 exit_loop:
3889         if (vbuf != NULL)
3890                 free(vbuf);
3891         if (mol->natoms > 0)
3892                 retval = 0;  /*  Return the partially constructed molecule  */
3893         if (newmol && mol->nbonds == 0) {
3894                 /*  Guess bonds  */
3895                 Int nbonds, *bonds;
3896                 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3897                 if (nbonds > 0) {
3898                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3899                         free(bonds);
3900                 }
3901         }
3902         return 0;
3903 }
3904
3905 int
3906 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3907 {
3908         int retval;
3909         if (ftype == NULL || *ftype == 0) {
3910                 const char *cp;
3911                 cp = strrchr(fname, '.');
3912                 if (cp != NULL)
3913                         ftype = cp + 1;
3914                 else {
3915                         cp = guessMoleculeType(fname);
3916                         if (strcmp(cp, "???") != 0)
3917                                 ftype = cp;
3918                 }
3919         }
3920         if (strcasecmp(ftype, "pdb") == 0) {
3921                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3922         }
3923         if (retval != 0) {
3924                 /*  Try all formats once again  */
3925                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3926         }
3927         return retval;
3928 }
3929
3930 int
3931 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3932 {
3933         FILE *fp;
3934         char buf[1024];
3935         char *p;
3936         int lineNumber;
3937         int i, j, new_unit, retval;
3938         Atom *ap;
3939         IntGroup *ig;
3940         Vector *vp = NULL;
3941         Int ibuf[12];
3942         Int entries = 0;
3943         retval = 0;
3944         *errbuf = NULL;
3945         fp = fopen(fname, "rb");
3946         if (fp == NULL) {
3947                 s_append_asprintf(errbuf, "Cannot open file");
3948                 return -1;
3949         }
3950 /*      flockfile(fp); */
3951         if (mp->natoms == 0)
3952                 new_unit = 1;
3953         else {
3954                 /*  Allocate buffer for undo-capable modification  */
3955                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3956                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3957                         /*  Retain current position if the atom info is missing in the input file  */
3958                         vp[i] = ap->r;
3959                 }
3960                 new_unit = 0;
3961         }
3962         lineNumber = 0;
3963         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3964                 if (strncmp(buf, "END", 3) == 0)
3965                         break;
3966                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3967                         struct {
3968                                 Int serial, intCharge, resSeq;
3969                                 Vector r;
3970                                 Double occ, temp;
3971                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3972                         } w;
3973                         memset(&w, 0, sizeof(w));
3974                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3975                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3976                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3977                         if (w.atomName[0] == 0) {
3978                                 continue;  /*  Atom name is empty  */
3979                         }
3980                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3981                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3982                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3983                         } else {
3984                                 w.resSeq = atoi(w.resSeqStr);
3985                         }
3986                         if (w.element[0] == 0) {
3987                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3988                                 for (p = w.atomName; *p != 0; p++) {
3989                                         if (isalpha(*p) && *p != '_') {
3990                                                 w.element[0] = toupper(*p);
3991                                                 if (isalpha(p[1]) && p[1] != '_') {
3992                                                         w.element[1] = toupper(p[1]);
3993                                                         w.element[2] = 0;
3994                                                 } else {
3995                                                         w.element[1] = 0;
3996                                                 }
3997                                                 break;
3998                                         }
3999                                 }
4000                         }
4001                         if (w.occStr[0] == 0)
4002                                 w.occ = 1.0;
4003                         else
4004                                 w.occ = atof(w.occStr);
4005                         if (w.serial <= 0) {
4006                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
4007                                 retval = 1;
4008                                 goto abort;
4009                         }
4010                         w.serial--;  /*  The internal atom number is 0-based  */
4011                         if (w.serial >= mp->natoms) {
4012                                 if (new_unit) {
4013                                         /*  Create a new atom entry  */
4014                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
4015                                 } else {
4016                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
4017                                         retval = 1;
4018                                         goto abort;
4019                                 }
4020                         }
4021                         if (new_unit) {
4022                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
4023                                 ap->r = w.r;
4024                                 ap->occupancy = w.occ;
4025                                 ap->tempFactor = w.temp;
4026                                 if (w.segName[0] == 0)
4027                                         strncpy(w.segName, "MAIN", 4);
4028                                 strncpy(ap->segName, w.segName, 4);
4029                                 ap->resSeq = w.resSeq;
4030                                 strncpy(ap->resName, w.resName, 4);
4031                                 strncpy(ap->aname, w.atomName, 4);
4032                                 strncpy(ap->element, w.element, 2);
4033                                 ap->element[2] = 0;
4034                                 ap->atomicNumber = ElementToInt(ap->element);
4035                                 ap->type = AtomTypeEncodeToUInt(ap->element);
4036                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
4037                                 ap->intCharge = w.intCharge;
4038                                 if (ap->resSeq > 0) {
4039                                         if (ap->resSeq < mp->nresidues) {
4040                                                 /*  Update the resName according to residues[]  */
4041                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
4042                                         } else {
4043                                                 /*  Register the resName to residues[]  */
4044                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
4045                                         }
4046                                 } else {
4047                                         ap->resSeq = 0;
4048                                         strcpy(ap->resName, "XXX");
4049                                         if (mp->nresidues == 0)
4050                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
4051                                 }
4052                                 i = ElementToInt(ap->element);
4053                                 if (i >= 0)
4054                                         ap->weight = gElementParameters[i].weight;
4055                         } else {
4056                                 /*  Not a new unit: only the atom position is updated  */
4057                                 vp[w.serial] = w.r;
4058                         }
4059                         entries++;
4060                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
4061                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
4062                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
4063                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
4064                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
4065                         if (i >= 2) {
4066                                 Int bbuf[25];
4067                                 int bi;
4068                                 for (j = 0; j < i; j++) {
4069                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
4070                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
4071                                                 retval = 1;
4072                                                 goto abort;
4073                                         } else if (ibuf[j] == 0)
4074                                                 break;
4075                                 }
4076                                 i = j;
4077                                 if (i < 2)
4078                                         continue;
4079                                 for (j = 1, bi = 0; j < i; j++) {
4080                                         if (ibuf[0] < ibuf[j]) {
4081                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
4082                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
4083                                                 } else {
4084                                                         bbuf[bi * 2] = ibuf[0] - 1;
4085                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
4086                                                         bi++;
4087                                                 }
4088                                         }
4089                                 }
4090                                 if (bi == 0)
4091                                         continue;
4092                                 bbuf[bi * 2] = -1;
4093                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
4094                                 if (retval < 0) {
4095                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
4096                                         retval = 1;
4097                                         goto abort;
4098                                 }
4099                         }
4100                 }
4101         }
4102 /*      funlockfile(fp); */
4103         fclose(fp);
4104         if (new_unit) {
4105                 /*  Renumber atoms if some atom number is unoccupied  */
4106                 int *old2new, oldidx, newidx;
4107                 old2new = (int *)calloc(sizeof(int), mp->natoms);
4108                 if (old2new == NULL) {
4109                         s_append_asprintf(errbuf, "Out of memory");
4110                         retval = 1;
4111                         goto abort;
4112                 }
4113                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
4114                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
4115                         if (ap->aname[0] != 0) {
4116                                 old2new[oldidx] = newidx;
4117                                 if (oldidx > newidx)
4118                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
4119                                 newidx++;
4120                         }
4121                 }
4122                 mp->natoms = newidx;
4123                 if (oldidx > newidx) {
4124                         /*  Renumber the connects and bonds  */
4125                         Int *cp;
4126                         for (i = 0; i < mp->natoms; i++) {
4127                                 ap = ATOM_AT_INDEX(mp->atoms, i);
4128                                 cp = AtomConnectData(&ap->connect);
4129                                 for (j = 0; j < ap->connect.count; j++) {
4130                                         cp[j] = old2new[cp[j]];
4131                                 }
4132                         }
4133                         for (i = 0; i < mp->nbonds * 2; i++) {
4134                                 mp->bonds[i] = old2new[mp->bonds[i]];
4135                         }
4136                 }
4137                 retval = MoleculeRebuildTablesFromConnects(mp);
4138                 if (retval != 0) {
4139                         /*  This error may not happen  */
4140                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
4141                         retval = 1;
4142                         goto abort;
4143                 }
4144                 /*  Undo action: delete all atoms  */
4145                 {
4146                         MolAction *act;
4147                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4148                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
4149                         act->frame = mp->cframe;
4150                         MolActionCallback_registerUndo(mp, act);
4151                         MolActionRelease(act);
4152                         IntGroupRelease(ig);
4153                 }
4154         } else {
4155                 /*  Set the new atom positions  */
4156                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4157                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4158                 IntGroupRelease(ig);
4159                 free(vp);
4160                 vp = NULL;
4161         }
4162         mp->nframes = -1;  /*  Should be recalculated later  */
4163         if (entries == 0)
4164                 return 1;  /*  No atoms  */
4165         return 0;
4166         abort:
4167         if (fp != NULL) {
4168         /*      funlockfile(fp); */
4169                 fclose(fp);
4170         }
4171         if (vp != NULL)
4172                 free(vp);
4173         if (entries == 0)
4174                 return 1;  /*  Maybe different format?  */
4175         return retval;
4176 }
4177
4178 int
4179 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4180 {
4181         DcdRecord dcd;
4182         SFloat32 *xp, *yp, *zp;
4183         Vector *vp, *cp;
4184         IntGroup *ig;
4185         int n, errcount = 0;
4186         *errbuf = NULL;
4187         if (mp == NULL || mp->natoms == 0) {
4188                 s_append_asprintf(errbuf, "Molecule is empty");
4189                 return 1;
4190         }
4191         n = DcdOpen(fname, &dcd);
4192         if (n != 0) {
4193                 switch (n) {
4194                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4195                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4196                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4197                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4198                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4199                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
4200                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4201                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4202                 }
4203                 errcount++;
4204         } else {
4205                 if (dcd.natoms == 0) {
4206                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4207                         errcount++;
4208                 } else if (dcd.nframes == 0) {
4209                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
4210                         errcount++;
4211                 }
4212         }
4213         if (errcount > 0) {
4214                 if (n == 0)
4215                         DcdClose(&dcd);
4216                 return 1;
4217         }
4218
4219         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4220         if (dcd.nextra)
4221                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4222         else cp = NULL;
4223         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4224         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4225         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4226         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4227         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4228                 s_append_asprintf(errbuf, "Cannot allocate memory");
4229                 if (vp) free(vp);
4230                 if (cp) free(cp);
4231                 if (xp) free(xp);
4232                 if (yp) free(yp);
4233                 if (zp) free(zp);
4234                 if (ig) IntGroupRelease(ig);
4235                 return 1;
4236         }
4237         for (n = 0; n < dcd.nframes; n++) {
4238                 int i;
4239                 Vector *vpp;
4240                 SFloat32 dcdcell[6];
4241                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4242                         s_append_asprintf(errbuf, "Read error in dcd file");
4243                         goto exit;
4244                 }
4245                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4246                         vpp->x = xp[i];
4247                         vpp->y = yp[i];
4248                         vpp->z = zp[i];
4249                 }
4250                 if (cp != NULL) {
4251                         Double sing;
4252                         vpp = &cp[n * 4];
4253                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
4254                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
4255                         if (dcdcell[1] < -1.0 || dcdcell[1] > 1.0 || dcdcell[3] < -1.0 || dcdcell[3] > 1.0 || dcdcell[4] < -1.0 || dcdcell[4] > 1.0) {
4256                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
4257                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
4258                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
4259                         }
4260                         /*  a axis lies along the cartesian x axis  */
4261                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4262                         vpp[0].x = dcdcell[0];
4263                         vpp[0].y = 0;
4264                         vpp[0].z = 0;
4265                         vpp[1].x = dcdcell[2] * dcdcell[1];
4266                         vpp[1].y = dcdcell[2] * sing;
4267                         vpp[1].z = 0;
4268                         vpp[2].x = dcdcell[5] * dcdcell[3];
4269                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4270                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4271                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4272                         if (mp->cell == NULL) {
4273                                 /*  Create periodicity if not present  */
4274                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4275                         }
4276                 }
4277         }
4278         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4279                 s_append_asprintf(errbuf, "Cannot insert frames");
4280         mp->startStep = dcd.nstart;
4281         mp->stepsPerFrame = dcd.ninterval;
4282         mp->psPerStep = dcd.delta;
4283 exit:
4284         DcdClose(&dcd);
4285         if (cp != NULL)
4286                 free(cp);
4287         free(vp);
4288         free(xp);
4289         free(yp);
4290         free(zp);
4291         IntGroupRelease(ig);
4292         if (errcount == 0)
4293                 return 0;
4294         else return 1;
4295 }
4296
4297 int
4298 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4299 {
4300         FILE *fp;
4301         char buf[1024];
4302         int lineNumber;
4303         int i, retval;
4304         Vector v[3], vv;
4305         double d[3];
4306         int n, flag;
4307         char flags[3];
4308         *errbuf = NULL;
4309         fp = fopen(fname, "rb");
4310         if (fp == NULL) {
4311                 s_append_asprintf(errbuf, "Cannot open file");
4312                 return -1;
4313         }
4314         errbuf[0] = 0;
4315         lineNumber = 0;
4316         retval = 0;
4317         flags[0] = flags[1] = flags[2] = 0;
4318         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4319                 if (strncmp(buf, "Bounding box:", 13) == 0) {
4320                         for (i = 0; i < 3; i++) {
4321                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4322                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4323                                         retval = 1;
4324                                         goto abort;
4325                                 }
4326                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4327                                 if (n < 3) {
4328                                         vv.x = vv.y = vv.z = 0.0;
4329                                         switch (i) {
4330                                                 case 0: vv.x = d[0]; break;
4331                                                 case 1: vv.y = d[0]; break;
4332                                                 case 2: vv.z = d[0]; break;
4333                                         }
4334                                         if (n == 1 || (n == 2 && d[1] != 0.0))
4335                                                 flags[i] = 1;
4336                                 } else {
4337                                         vv.x = d[0];
4338                                         vv.y = d[1];
4339                                         vv.z = d[2];
4340                                         if (n == 4)
4341                                                 flags[i] = (flag != 0);
4342                                         else
4343                                                 flags[i] = (VecLength2(vv) != 0);
4344                                 }
4345                                 v[i] = vv;
4346                         }
4347                         if (mp->cell != NULL)
4348                                 vv = mp->cell->origin;
4349                         else
4350                                 vv.x = vv.y = vv.z = 0.0;
4351                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4352                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4353                         if (mp->cell != NULL) {
4354                                 v[0] = mp->cell->axes[0];
4355                                 v[1] = mp->cell->axes[1];
4356                                 v[2] = mp->cell->axes[2];
4357                                 memmove(flags, mp->cell->flags, 3);
4358                         } else {
4359                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4360                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4361                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4362                                 flags[0] = flags[1] = flags[2] = 1.0;
4363                         }
4364                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4365                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4366                                 retval = 1;
4367                                 goto abort;
4368                         }
4369                         vv.x = d[0];
4370                         vv.y = d[1];
4371                         vv.z = d[2];
4372                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4373                 }
4374         }
4375         fclose(fp);
4376         return 0;
4377 abort:
4378         if (fp != NULL)
4379                 fclose(fp);
4380         return retval;
4381 }
4382                         
4383 int
4384 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4385 {
4386         int retval;
4387         *errbuf = NULL;
4388         if (ftype == NULL || *ftype == 0) {
4389                 const char *cp;
4390                 cp = strrchr(fname, '.');
4391                 if (cp != NULL)
4392                         ftype = cp + 1;
4393                 else {
4394                         cp = guessMoleculeType(fname);
4395                         if (strcmp(cp, "???") != 0)
4396                                 ftype = cp;
4397                 }
4398         }
4399         if (strcasecmp(ftype, "psf") == 0) {
4400                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4401         } else if (strcasecmp(ftype, "pdb") == 0) {
4402                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4403         } else if (strcasecmp(ftype, "tep") == 0) {
4404                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4405         } else {
4406                 s_append_asprintf(errbuf, "The file format should be specified");
4407                 retval = 1;
4408         }
4409         if (retval == 0)
4410                 MoleculeSetPath(mp, fname);
4411         return retval;
4412 }
4413
4414 int
4415 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4416 {
4417         FILE *fp;
4418         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4419         Atom *ap;
4420         char *p;
4421         char bufs[6][8];
4422
4423         *errbuf = NULL;
4424         fp = fopen(fname, "wb");
4425         if (fp == NULL) {
4426                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4427                 return 1;
4428         }
4429         errbuf[0] = 0;
4430
4431         nframes = MoleculeFlushFrames(mp);
4432
4433         fprintf(fp, "!:atoms\n");
4434         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4435         n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4436         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4437                 strncpy(bufs[0], ap->segName, 4);
4438                 bufs[0][4] = 0;
4439                 strncpy(bufs[1], ap->resName, 4);
4440                 bufs[1][4] = 0;
4441                 strncpy(bufs[2], ap->aname, 4);
4442                 bufs[2][4] = 0;
4443                 AtomTypeDecodeToString(ap->type, bufs[3]);
4444                 bufs[3][6] = 0;
4445                 strncpy(bufs[4], ap->element, 4);
4446                 bufs[4][2] = 0;
4447                 for (j = 0; j < 5; j++) {
4448                         if (bufs[j][0] == 0) {
4449                                 bufs[j][0] = '_';
4450                                 bufs[j][1] = 0;
4451                         }
4452                         for (k = 0; k < 6; k++) {
4453                                 if (bufs[j][k] == 0)
4454                                         break;
4455                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4456                                         bufs[j][k] = '_';
4457                         }
4458                 }
4459                 if (SYMOP_ALIVE(ap->symop))
4460                         n1++;
4461                 if (ap->fix_force != 0)
4462                         n2++;
4463                 if (ap->mm_exclude || ap->periodic_exclude)
4464                         n3++;
4465                 if (ap->aniso != NULL)
4466                         n_aniso++;
4467                 if (ap->anchor != NULL)
4468                         nanchors++;
4469                 if (ap->uff_type[0] != 0)
4470                         n_uff++;
4471                 fprintf(fp, "%d %s %d %s %s %s %.5f %.5f %s %d %f %f %d\n", i, bufs[0], ap->resSeq, bufs[1], bufs[2], bufs[3], ap->charge, ap->weight, bufs[4], ap->atomicNumber, ap->occupancy, ap->tempFactor, ap->intCharge);
4472         }
4473         fprintf(fp, "\n");
4474         
4475         if (n_uff > 0) {
4476                 fprintf(fp, "!:uff_type\n");
4477                 fprintf(fp, "! idx uff_type\n");
4478                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4479                         fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4480                 }
4481                 fprintf(fp, "\n");
4482         }
4483         
4484         if (n1 > 0) {
4485                 fprintf(fp, "!:atoms_symop\n");
4486                 fprintf(fp, "! idx symop symbase\n");
4487                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4488                         int n;
4489                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4490                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4491                 }
4492                 fprintf(fp, "\n");
4493         }
4494         
4495         if (n2 > 0) {
4496                 fprintf(fp, "!:atoms_fix\n");
4497                 fprintf(fp, "! idx fix_force fix_pos\n");
4498                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4499                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4500                 }
4501                 fprintf(fp, "\n");
4502         }
4503         
4504         if (n3 > 0) {
4505                 fprintf(fp, "!:mm_exclude\n");
4506                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4507                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4508                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4509                 }
4510                 fprintf(fp, "\n");
4511         }
4512         
4513         if (nanchors > 0) {
4514                 fprintf(fp, "!:pi_anchor\n");
4515                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4516                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4517                         Int *ip;
4518                         if (ap->anchor == NULL)
4519                                 continue;
4520                         k = ap->anchor->connect.count;
4521                         ip = AtomConnectData(&ap->anchor->connect);
4522                         fprintf(fp, "%d %d\n", i, k);
4523                         for (j = 0; j < k; j++) {
4524                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4525                         }
4526                 }
4527                 fprintf(fp, "\n");
4528         }
4529                                 
4530         n1 = nframes;
4531         if (n1 > 0)
4532                 n2 = mp->cframe;
4533         else
4534                 n2 = 0;
4535         for (i = 0; (i == n2 || i < n1); i++) {
4536                 fprintf(fp, "!:positions ; frame %d\n", i);
4537                 fprintf(fp, "! idx x y z [sx sy sz]\n");
4538                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4539                         Vector *vp;
4540                         Byte sig_flag = 0;
4541                         if (i != n2 && i < ap->nframes)
4542                                 vp = ap->frames + i;
4543                         else {
4544                                 vp = &(ap->r);
4545                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4546                                         sig_flag = 1;
4547                         }
4548                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4549                         if (sig_flag) {
4550                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4551                         }
4552                         fprintf(fp, "\n");
4553                 }
4554                 fprintf(fp, "\n");
4555         }
4556         
4557         if (mp->nbonds > 0) {
4558                 fprintf(fp, "!:bonds\n");
4559                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4560                 for (i = 0; i < mp->nbonds; i++) {
4561                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4562                 }
4563                 fprintf(fp, "\n");
4564         }
4565
4566         if (mp->nbondOrders > 0) {
4567                 fprintf(fp, "!:bond_orders\n");
4568                 fprintf(fp, "! order1 order2 order3 order4\n");
4569                 for (i = 0; i < mp->nbondOrders; i++) {
4570                         fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4571                 }
4572                 fprintf(fp, "\n");
4573         }
4574         
4575         if (mp->nangles > 0) {
4576                 fprintf(fp, "!:angles\n");
4577                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4578                 for (i = 0; i < mp->nangles; i++) {
4579                         fprintf(fp, "%d %d %d%c", mp->angles[i * 3], mp->angles[i * 3 + 1], mp->angles[i * 3 + 2], (i % 3 == 2 || i == mp->nangles - 1 ? '\n' : ' '));
4580                 }
4581                 fprintf(fp, "\n");
4582         }
4583         
4584         if (mp->ndihedrals > 0) {
4585                 fprintf(fp, "!:dihedrals\n");
4586                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4587                 for (i = 0; i < mp->ndihedrals; i++) {
4588                         fprintf(fp, "%d %d %d %d%c", mp->dihedrals[i * 4], mp->dihedrals[i * 4 + 1], mp->dihedrals[i * 4 + 2], mp->dihedrals[i * 4 + 3], (i % 2 == 1 || i == mp->ndihedrals - 1 ? '\n' : ' '));
4589                 }
4590                 fprintf(fp, "\n");
4591         }
4592         
4593         if (mp->nimpropers > 0) {
4594                 fprintf(fp, "!:impropers\n");
4595                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4596                 for (i = 0; i < mp->nimpropers; i++) {
4597                         fprintf(fp, "%d %d %d %d%c", mp->impropers[i * 4], mp->impropers[i * 4 + 1], mp->impropers[i * 4 + 2], mp->impropers[i * 4 + 3], (i % 2 == 1 || i == mp->nimpropers - 1 ? '\n' : ' '));
4598                 }
4599                 fprintf(fp, "\n");
4600         }
4601         
4602         if (mp->cell != NULL) {
4603                 fprintf(fp, "!:xtalcell\n");
4604                 fprintf(fp, "! a b c alpha beta gamma\n");
4605                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4606                 fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cell[0], mp->cell->cell[1], mp->cell->cell[2], mp->cell->cell[3], mp->cell->cell[4], mp->cell->cell[5]);
4607                 fprintf(fp, "\n");
4608
4609                 fprintf(fp, "!:periodic_box\n");
4610                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz; fa fb fc [sigma; sa sb sc s_alpha s_beta s_gamma]\n");
4611                 for (i = 0; i < 3; i++)
4612                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4613                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4614                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4615                 if (mp->cell->has_sigma) {
4616                         fprintf(fp, "%f %f %f %f %f %f\n", mp->cell->cellsigma[0], mp->cell->cellsigma[1], mp->cell->cellsigma[2], mp->cell->cellsigma[3], mp->cell->cellsigma[4], mp->cell->cellsigma[5]);
4617                 }
4618                 fprintf(fp, "\n");
4619         }
4620         
4621         if (mp->nframe_cells > 0) {
4622                 fprintf(fp, "!:frame_periodic_boxes\n");
4623                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4624                 for (i = 0; i < mp->nframe_cells * 4; i++) {
4625                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4626                 }
4627                 fprintf(fp, "\n");
4628         }
4629         
4630         if (mp->nsyms > 0) {
4631                 fprintf(fp, "!:symmetry_operations\n");
4632                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4633                 for (i = 0; i < mp->nsyms; i++) {
4634                         Transform *tp = mp->syms + i;
4635                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4636                         for (j = 0; j < 12; j++)
4637                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4638                 }
4639                 fprintf(fp, "\n");
4640         }
4641         
4642         if (n_aniso > 0) {
4643                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4644                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4645                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4646                         if (ap->aniso != NULL) {
4647                                 Double *bp = ap->aniso->bij;
4648                                 fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g%s\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], (ap->aniso->has_bsig ? " 1" : ""));
4649                                 if (ap->aniso->has_bsig) {
4650                                         bp = ap->aniso->bsig;
4651                                         fprintf(fp, "%14.8g %14.8g %14.8g %14.8g %14.8g %14.8g\n", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
4652                                 }
4653                         } else {
4654                                 fprintf(fp, "0 0 0 0 0 0\n");
4655                         }
4656                 }
4657                 fprintf(fp, "\n");              
4658         }
4659         
4660         if (mp->arena != NULL) {
4661                 MDArena *arena = mp->arena;
4662                 fprintf(fp, "!:md_parameters\n");
4663                 fprintf(fp, "log_file %s\n", arena->log_result_name);
4664                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4665                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4666                 fprintf(fp, "force_file %s\n", arena->force_result_name);
4667                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4668                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4669                 fprintf(fp, "step %d\n", arena->step);
4670                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4671                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4672                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4673                 fprintf(fp, "timestep %g\n", arena->timestep);
4674                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4675                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4676                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4677                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4678                 fprintf(fp, "temperature %g\n", arena->temperature);
4679                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4680                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4681                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4682                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4683                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4684                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4685                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4686                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4687                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4688                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4689                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4690                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4691                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4692                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4693                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4694                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4695                 if (arena->nalchem_flags > 0) {
4696                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4697                         for (i = 0; i < arena->nalchem_flags; i++) {
4698                                 if (i % 60 == 0)
4699                                         fputc('\n', fp);
4700                                 else if (i % 10 == 0)
4701                                         fputc(' ', fp);
4702                                 fputc('0' + arena->alchem_flags[i], fp);
4703                         }
4704                         fputc('\n', fp);
4705                 }
4706                 if (arena->pressure != NULL) {
4707                         Double *dp;
4708                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4709                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4710                         dp = arena->pressure->apply;
4711                         fprintf(fp, "pressure %g %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7], dp[8]);
4712                         dp = arena->pressure->cell_flexibility;
4713                         fprintf(fp, "pressure_cell_flexibility %g %g %g %g %g %g %g %g\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7]);
4714                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4715                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4716                 }
4717                 fprintf(fp, "\n");
4718
4719                 if (mp->par != NULL) {
4720                         Parameter *par = mp->par;
4721                         fprintf(fp, "!:parameters\n");
4722                         ParameterAppendToFile(par, fp);
4723                         fprintf(fp, "\n");
4724                 }
4725                 
4726                 fprintf(fp, "!:velocity\n");
4727                 fprintf(fp, "! idx vx vy vz\n");
4728                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4729                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4730                 }
4731                 fprintf(fp, "\n");
4732
4733                 fprintf(fp, "!:force\n");
4734                 fprintf(fp, "! idx fx fy fz\n");
4735                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4736                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4737                 }
4738                 fprintf(fp, "\n");
4739         }
4740         
4741         if (mp->mview != NULL) {
4742                 double f[4];
4743                 if (mp->mview->track != NULL) {
4744                         fprintf(fp, "!:trackball\n");
4745                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4746                         f[0] = TrackballGetScale(mp->mview->track);
4747                         fprintf(fp, "%f\n", f[0]);
4748                         TrackballGetTranslate(mp->mview->track, f);
4749                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4750                         TrackballGetRotate(mp->mview->track, f);
4751                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4752                         fprintf(fp, "\n");
4753                 }
4754                 fprintf(fp, "!:view\n");
4755                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4756                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4757                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4758                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4759                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4760                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4761                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4762                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4763                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4764                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4765                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4766                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4767                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4768                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4769                 if (mp->mview->atomRadius != 0.2)
4770                         fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4771                 if (mp->mview->bondRadius != 0.1)
4772                         fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4773                 if (mp->mview->atomResolution != 12)
4774                         fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4775                 if (mp->mview->bondResolution != 8)
4776                         fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4777                 fprintf(fp, "\n");
4778         }
4779
4780         if (mp->nmolprops > 0) {
4781                 MolProp *prp;
4782                 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4783                         /*  Encode the property name if necessary  */
4784                         char enc[1024];
4785                         n1 = n2 = 0;
4786                         for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4787                                 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4788                                         enc[n1++] = *p;
4789                                         n2 = n1;
4790                                 } else {
4791                                         sprintf(enc + n1, "%%%02x", *p);
4792                                         n1 += 3;
4793                                 }
4794                         }
4795                         if (*p == 0)
4796                                 enc[n1] = 0;
4797                         else {
4798                                 enc[n2] = 0; /* Truncate after last ASCII character */
4799                                 n1 = n2;
4800                         }
4801                         if (n1 == 0) {
4802                                 sprintf(enc, "prop_%d", i + 1);
4803                                 n1 = strlen(enc);
4804                         }
4805                         fprintf(fp, "!:property ; %s\n", enc);
4806                         for (j = 0; j < nframes; j++) {
4807                                 fprintf(fp, "%.18g\n", prp->propvals[j]);
4808                         }
4809                         fprintf(fp, "\n");
4810                 }
4811         }
4812         
4813         if (mp->bset != NULL) {
4814                 /*  Gaussian primitive info  */
4815                 ShellInfo *sp;
4816                 PrimInfo *pp;
4817                 fprintf(fp, "!:gaussian_primitives\n");
4818                 fprintf(fp, "! sym nprims a_idx [add_exp]; A C Csp\n");
4819                 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4820                         switch (sp->sym) {
4821                                 case kGTOType_S:  p = "S";  break;
4822                                 case kGTOType_P:  p = "P";  break;
4823                                 case kGTOType_SP: p = "SP"; break;
4824                                 case kGTOType_D:  p = "D";  break;
4825                                 case kGTOType_D5: p = "D5"; break;
4826                                 case kGTOType_F:  p = "F";  break;
4827                                 case kGTOType_F7: p = "F7"; break;
4828                                 case kGTOType_G:  p = "G";  break;
4829                                 case kGTOType_G9: p = "G9"; break;
4830                                 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4831                         }
4832             if (sp->add_exp == 0)
4833                 fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4834             else
4835                 fprintf(fp, "%s %d %d %d\n", p, sp->nprim, sp->a_idx, sp->add_exp);
4836                         pp = mp->bset->priminfos + sp->p_idx;
4837                         for (j = 0; j < sp->nprim; j++, pp++) {
4838                                 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4839                         }
4840                 }
4841                 fprintf(fp, "\n");
4842                 
4843                 /*  MO info  */
4844                 fprintf(fp, "!:mo_info\n");
4845                 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4846                 switch (mp->bset->rflag) {
4847                         case 0: p = "UHF"; break;
4848                         case 1: p = "RHF"; break;
4849                         case 2: p = "ROHF"; break;
4850                         default: p = "(unknown)"; break;
4851                 }
4852                 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4853                 fprintf(fp, "\n");
4854
4855                 /*  MO coefficients  */
4856                 fprintf(fp, "!:mo_coefficients\n");
4857                 for (i = 0; i < mp->bset->nmos; i++) {
4858                         fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4859                         for (j = 0; j < mp->bset->ncomps; j++) {
4860                                 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4861                         }
4862                 }
4863                 fprintf(fp, "\n");
4864         }
4865
4866         if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4867                 MainViewGraphic *gp;
4868                 fprintf(fp, "!:graphics\n");
4869                 for (i = 0; i < mp->mview->ngraphics; i++) {
4870                         gp = mp->mview->graphics + i;
4871                         switch (gp->kind) {
4872                                 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4873                                 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4874                                 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4875                                 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4876                                 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4877                                 default: fprintf(fp, "unknown\n"); break;
4878                         }
4879                         fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4880                         fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4881                         fprintf(fp, "%d\n", gp->npoints);
4882                         for (j = 0; j < gp->npoints; j++)
4883                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4884                         fprintf(fp, "%d\n", gp->nnormals);
4885                         for (j = 0; j < gp->nnormals; j++)
4886                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4887                 }
4888                 fprintf(fp, "\n");
4889         }
4890         
4891         /*  Plug-in in the Ruby world  */
4892         {
4893                 char *outMessage;
4894         /*  Save NBO information  */
4895         if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4896                                       "proc { nbo2msbfstring rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4897             if (strncmp(outMessage, "Plug-in error", 13) == 0) {
4898                 s_append_asprintf(errbuf, "%s", outMessage);
4899             }
4900             fputs(outMessage, fp);
4901             free(outMessage);
4902         }
4903         /*  Other Plug-in  */
4904                 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4905                                                                           "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4906                         if (outMessage[0] != 0) {
4907                                 if (strncmp(outMessage, "Plug-in error", 13) == 0) {
4908                                         s_append_asprintf(errbuf, "%s", outMessage);
4909                                 } else {
4910                                         fprintf(fp, "%s\n", outMessage);
4911                                 }
4912                         }
4913                         free(outMessage);
4914                 }
4915         }
4916         
4917         fclose(fp);
4918         return 0;
4919 }
4920
4921 int
4922 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4923 {
4924         FILE *fp;
4925         int i;
4926         Atom *ap;
4927         *errbuf = NULL;
4928         fp = fopen(fname, "wb");
4929         if (fp == NULL) {
4930                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4931                 return 1;
4932         }
4933         fprintf(fp, "PSF\n\n");
4934         fprintf(fp, "       1 !NTITLE\n");
4935         fprintf(fp, " REMARKS FILENAME=\n");
4936         fprintf(fp, "\n");
4937         
4938         /*  Atoms  */
4939         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4940         for (i = 0; i < mp->natoms; i++) {
4941                 const char *fmt;
4942                 ap = ATOM_AT_INDEX(mp->atoms, i);
4943                 fprintf(fp, "%8d ", i + 1);
4944                 if (ap->resSeq >= 10000) {
4945                         fmt = "%-3.3s %-5d ";
4946                 } else {
4947                         fmt = "%-4.4s %-4d ";
4948                 }
4949                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4950                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4951                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4952         }
4953         fprintf(fp, "\n");
4954         
4955         /*  Bonds  */
4956         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4957         for (i = 0; i < mp->nbonds * 2; i++) {
4958                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4959                 if (i % 8 == 7)
4960                         fprintf(fp, "\n");
4961         }
4962         if (i % 8 != 0)
4963                 fprintf(fp, "\n");
4964         fprintf(fp, "\n");
4965         
4966         /*  Angles  */
4967         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4968         for (i = 0; i < mp->nangles * 3; i++) {
4969                 fprintf(fp, "%8d", mp->angles[i] + 1);
4970                 if (i % 9 == 8)
4971                         fprintf(fp, "\n");
4972         }
4973         if (i % 9 != 0)
4974                 fprintf(fp, "\n");
4975         fprintf(fp, "\n");
4976         
4977         /*  Dihedrals  */
4978         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4979         for (i = 0; i < mp->ndihedrals * 4; i++) {
4980                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4981                 if (i % 8 == 7)
4982                         fprintf(fp, "\n");
4983         }
4984         if (i % 8 != 0)
4985                 fprintf(fp, "\n");
4986         fprintf(fp, "\n");
4987         
4988         /*  Dihedrals  */
4989         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4990         for (i = 0; i < mp->nimpropers * 4; i++) {
4991                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4992                 if (i % 8 == 7)
4993                         fprintf(fp, "\n");
4994         }
4995         if (i % 8 != 0)
4996                 fprintf(fp, "\n");
4997         fprintf(fp, "\n");
4998         
4999         fprintf(fp, "%8d !NDON: donors\n\n", 0);
5000         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
5001         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
5002         for (i = 0; i < mp->natoms; i++) {
5003                 fprintf(fp, "%8d", 0);
5004                 if (i % 8 == 7)
5005                         fprintf(fp, "\n");
5006         }
5007         if (i % 8 != 0)
5008                 fprintf(fp, "\n");
5009         fprintf(fp, "\n");
5010         fprintf(fp, "%8d !NGRP: groups\n", 1);
5011         fprintf(fp, "       0       0       0\n");
5012         fprintf(fp, "\n");
5013         
5014         i = strlen(fname);
5015         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
5016                 /*  Extended psf (with coordinates and other info)  */
5017                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
5018                 for (i = 0; i < mp->natoms; i++) {
5019                         Vector r;
5020                         ap = ATOM_AT_INDEX(mp->atoms, i);
5021                         r = ap->r;
5022                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
5023                 }
5024                 fprintf(fp, "\n");
5025         }
5026                 
5027         fclose(fp);
5028         return 0;
5029 }
5030
5031 int
5032 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
5033 {
5034         FILE *fp;
5035         int i, j;
5036         Atom *ap;
5037         *errbuf = NULL;
5038         fp = fopen(fname, "wb");
5039         if (fp == NULL) {
5040                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5041                 return 1;
5042         }
5043         for (i = 0; i < mp->natoms; i++) {
5044                 char buf[6];
5045                 ap = ATOM_AT_INDEX(mp->atoms, i);
5046                 if (ap->resSeq >= 10000) {
5047                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
5048                 } else {
5049                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
5050                 }
5051                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
5052                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
5053                                         "%-4.4s%-2.2s%-2d\n",
5054                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
5055                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
5056                         ap->segName, ap->element, ap->intCharge);
5057         }
5058         for (i = 0; i < mp->natoms; i++) {
5059                 Int *cp;
5060                 ap = ATOM_AT_INDEX(mp->atoms, i);
5061                 cp = AtomConnectData(&ap->connect);
5062                 for (j = 0; j < ap->connect.count; j++) {
5063                         if (j % 4 == 0) {
5064                                 if (j > 0)
5065                                         fprintf(fp, "\n");
5066                                 fprintf(fp, "CONECT%5d", i + 1);
5067                         }
5068                         fprintf(fp, "%5d", cp[j] + 1);
5069                 }
5070                 if (j > 0)
5071                         fprintf(fp, "\n");
5072         }
5073         fprintf(fp, "END\n");
5074         fclose(fp);
5075         return 0;
5076 }
5077
5078 int
5079 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
5080 {
5081         DcdRecord dcd;
5082         SFloat32 *xp, *yp, *zp;
5083         int n;
5084         *errbuf = NULL;
5085         if (mp == NULL || mp->natoms == 0) {
5086                 s_append_asprintf(errbuf, "Molecule is empty");
5087                 return 1;
5088         }
5089         memset(&dcd, 0, sizeof(dcd));
5090         dcd.natoms = mp->natoms;
5091         dcd.nframes = MoleculeGetNumberOfFrames(mp);
5092         if (dcd.nframes == 0) {
5093                 s_append_asprintf(errbuf, "no frame is present");
5094                 return 1;
5095         }
5096         dcd.nstart = mp->startStep;
5097         dcd.ninterval = mp->stepsPerFrame;
5098         if (dcd.ninterval == 0)
5099                 dcd.ninterval = 1;
5100         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
5101         if (mp->cell != NULL)
5102                 dcd.nextra = 1;
5103         dcd.delta = mp->psPerStep;
5104         if (dcd.delta == 0.0)
5105                 dcd.delta = 1.0;
5106         dcd.ncharmver = 24;
5107         n = DcdCreate(fname, &dcd);
5108         if (n != 0) {
5109                 if (n < 0)
5110                         s_append_asprintf(errbuf, "Cannot create dcd file");
5111                 else
5112                         s_append_asprintf(errbuf, "Cannot write dcd header");
5113                 DcdClose(&dcd);
5114                 return 1;
5115         }
5116         
5117         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5118         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5119         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
5120         if (xp == NULL || yp == NULL || zp == NULL) {
5121                 s_append_asprintf(errbuf, "Cannot allocate memory");
5122                 if (xp) free(xp);
5123                 if (yp) free(yp);
5124                 if (zp) free(zp);
5125                 DcdClose(&dcd);
5126                 return 1;
5127         }
5128         for (n = 0; n < dcd.nframes; n++) {
5129                 int i;
5130                 Atom *ap;
5131                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5132                         Vector r;
5133                         if (ap->frames == NULL || n >= ap->nframes)
5134                                 r = ap->r;
5135                         else
5136                                 r = ap->frames[n];
5137                         xp[i] = r.x;
5138                         yp[i] = r.y;
5139                         zp[i] = r.z;
5140                 }
5141                 if (i < dcd.natoms) {
5142                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
5143                         memset(xp + i, 0, sz);
5144                         memset(yp + i, 0, sz);
5145                         memset(zp + i, 0, sz);
5146                 }
5147                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
5148                         Vector *cp = &(mp->frame_cells[n * 4]);
5149                         dcd.globalcell[0] = VecLength(cp[0]);
5150                         dcd.globalcell[2] = VecLength(cp[1]);
5151                         dcd.globalcell[5] = VecLength(cp[2]);
5152                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
5153                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
5154                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
5155                 }                       
5156                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
5157                         s_append_asprintf(errbuf, "Write error in dcd file");
5158                         goto exit;
5159                 }
5160         }
5161         
5162 exit:
5163         DcdClose(&dcd);
5164         free(xp);
5165         free(yp);
5166         free(zp);
5167         if (errbuf[0] == 0)
5168                 return 0;
5169         else return 1;
5170 }
5171
5172 int
5173 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5174 {
5175         FILE *fp;
5176         int i;
5177         Vector v;
5178         *errbuf = NULL;
5179         fp = fopen(fname, "wb");
5180         if (fp == NULL) {
5181                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5182                 return 1;
5183         }
5184         if (mp->cell != NULL) {
5185                 fprintf(fp, "Bounding box:\n");
5186                 for (i = 0; i < 3; i++) {
5187                         v = mp->cell->axes[i];
5188                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5189                 }
5190                 fprintf(fp, "Bounding box origin:\n");
5191                 v = mp->cell->origin;
5192                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5193         }
5194         fclose(fp);
5195         return 0;
5196 }
5197                 
5198  static int
5199 sCompareByElement(const void *ap, const void *bp)
5200 {
5201         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5202 }
5203
5204 static int
5205 sMakeAdc(int n, int base, Symop symop)
5206 {
5207         int an, sym;
5208         if (SYMOP_ALIVE(symop)) {
5209                 an = base;
5210                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5211         } else {
5212                 an = n;
5213                 sym = 55501;
5214         }
5215         return (an + 1) * 100000 + sym;
5216 }
5217
5218 static int
5219 sCompareAdc(const void *ap, const void *bp)
5220 {
5221         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5222         if (n == 0)
5223                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5224         return n;
5225 }
5226
5227 static void
5228 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5229 {
5230         int i, j, k, an, sym;
5231         Atom *ap;
5232         Int *adc;
5233         adc = (Int *)malloc(sizeof(Int) * natoms);
5234         if (adc == NULL)
5235                 return;
5236         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5237                 if (ap->exflags & kAtomHiddenFlag)
5238                         continue;
5239                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5240         }
5241         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5242         
5243         /*  Create the atom list  */
5244         an = sym = -1;
5245         for (i = j = k = 0; i < natoms; i++) {
5246                 int an1 = adc[i] / 100000;
5247                 int sym1 = adc[i] % 100000;
5248                 if (sym == sym1 && an1 == an + 1) {
5249                         /*  Continuous  */
5250                         an = an1;
5251                         k++;
5252                         continue;
5253                 }
5254                 if (k > 0)
5255                         /*  Output the last atom with a minus sign  */
5256                         adc[j++] = -(an * 100000 + sym);
5257                 /*  Output this atom  */
5258                 adc[j++] = adc[i];
5259                 an = an1;
5260                 sym = sym1;
5261                 k = 0;
5262         }
5263         if (k > 0)
5264                 adc[j++] = -(an * 100000 + sym);
5265         
5266         /*  Create the instruction cards  */
5267         for (i = k = 0; i < j; i++) {
5268                 if (k == 0)
5269                         fprintf(fp, "      401");
5270                 fprintf(fp, "%9d", adc[i]);
5271                 k++;
5272                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5273                         fprintf(fp, "\n");
5274                         k = 0;
5275                 }
5276         }
5277         free(adc);
5278 }
5279
5280 static int
5281 sEllipsoidType(int an)
5282 {
5283         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5284 }
5285
5286 static void
5287 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5288 {
5289         int i;
5290         Atom *ap;
5291         int etype, elast, istart, ilast, n1, n2;
5292         elast = istart = ilast = -1;
5293         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5294                 if (i < natoms) {
5295                         if (SYMOP_ALIVE(ap->symop))
5296                                 continue;
5297                         if (ap->exflags & kAtomHiddenFlag)
5298                                 continue;
5299                         etype = sEllipsoidType(ap->atomicNumber);
5300                         if (elast < 0) {
5301                                 istart = ilast = i;
5302                                 elast = etype;
5303                                 continue;
5304                         } else if (elast == etype && ilast == i - 1) {
5305                                 ilast++;
5306                                 continue;
5307                         }
5308                 }
5309                 /*  Output the instruction card for the 'last' block of atoms  */
5310                 switch (etype) {
5311                         case 2:
5312                                 n1 = 4; n2 = 0; break;
5313                         case 3:
5314                                 n1 = 4; n2 = 5; break;
5315                         default:
5316                                 n1 = 1; n2 = 0; break;
5317                 }
5318                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
5319                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
5320                 elast = etype;
5321                 ilast = istart = i;
5322         }
5323 }
5324
5325 static int
5326 sCompareBondType(const void *ap, const void *bp)
5327 {
5328         /*  Descending order  */
5329         return *((int *)bp) - *((int *)ap);
5330 }
5331
5332 static void
5333 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5334 {
5335         Atom *ap, *ap2;
5336         char buf[96];
5337         int i, j, n[5], an, count, n1, n2, k;
5338         Int *cp;
5339         Int nexbonds;
5340         Int *exbonds;
5341         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5342         static const int sBondShade[4] = {5, 3, 1, 1};
5343
5344         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5345         n[4] = natoms;
5346         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5347                 an = ap->atomicNumber;
5348                 if (an < 2)
5349                         n[3] = i;
5350                 if (an < 10)
5351                         n[2] = i;
5352                 if (an < 18)
5353                         n[1] = i;
5354         }
5355         nexbonds = 0;
5356         exbonds = NULL;
5357         count = 0;
5358
5359         if (overlap_correction)
5360                 strcpy(buf, "  2  1001    0.000\n");
5361         else
5362                 strcpy(buf, "  2   812\n");
5363         
5364         for (i = 0; i < 4; i++) {
5365                 for (j = i; j < 4; j++) {
5366                         /*  Examine bonds between "group i" and "group j"  */
5367                         Vector dr;
5368                         double d;
5369                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
5370                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
5371                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
5372                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
5373                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5374                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5375                                         if (n1 == n2)
5376                                                 continue;
5377                                         VecSub(dr, ap->r, ap2->r);
5378                                         d = VecLength(dr);
5379                                         cp = AtomConnectData(&ap->connect);
5380                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5381                                                 if (cp[k] == n2)
5382                                                         break;
5383                                         }
5384                                         if (k >= 0) {
5385                                                 /*  n1 and n2 are bound  */
5386                                                 if (d < min_bond)
5387                                                         min_bond = d;
5388                                                 if (d > max_bond)
5389                                                         max_bond = d;
5390                                         } else {
5391                                                 /*  n1 and n2 are not bound  */
5392                                                 if (d < min_nonbond)
5393                                                         min_nonbond = d;
5394                                         }
5395                                 }
5396                         }
5397                         if (min_bond == 10000.0)
5398                                 continue;  /*  No bonds between these groups  */
5399                         min_bond *= 0.9;
5400                         if (max_bond + 0.002 < min_nonbond)
5401                                 max_bond += 0.002;
5402                         else {
5403                                 max_bond = min_nonbond - 0.002;
5404                                 /*  Some bonds may be omitted, so scan all bonds again  */
5405                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5406                                         cp = AtomConnectData(&ap->connect);
5407                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5408                                                 n2 = cp[k];
5409                                                 if (n2 < n[j] || n2 >= n[j + 1])
5410                                                         continue;
5411                                                 ap2 = atoms + n2;
5412                                                 VecSub(dr, ap->r, ap2->r);
5413                                                 d = VecLength(dr);
5414                                                 if (d > max_bond) {
5415                                                         /*  This bond should be explicitly defined  */
5416                                                         Int adc1, adc2;
5417                                                         if (count_exbond == 0) {
5418                                                                 adc1 = -(i + 1);  /*  Bond type  */
5419                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5420                                                         }
5421                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5422                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5423                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5424                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5425                                                         count_exbond++;
5426                                                 }
5427                                         }
5428                                 }
5429                         }
5430                         /*  Output the last instruction card  */
5431                         fputs(buf, fp);
5432                         /*  Make a new trailer card  */
5433                         snprintf(buf, sizeof(buf), "  2      %3d%3d%3d%3d%3d%6.3f%6.3f%6.3f\n", n[i]+1, n[i+1], n[j]+1, n[j+1], sBondShade[i], min_bond, max_bond, sBondRad[i]);
5434                         count++;
5435                 }
5436         }
5437         if (count > 0) {
5438                 /*  Output the last trailer card  */
5439                 buf[2] = ' ';
5440                 fputs(buf, fp);
5441         }
5442         if (nexbonds > 0) {
5443                 if (count == 0 && overlap_correction) {
5444                         /*  1001 card is not yet written, so write it  */
5445                         buf[2] = ' ';
5446                         fputs(buf, fp);
5447                 }
5448                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
5449                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
5450                 i = 1;  /*  Index for exbonds[]  */
5451                 j = 0;  /*  Count in this block  */
5452                 while (i <= nexbonds) {
5453                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5454                                 /*  End of block  */
5455                                 buf[2] = '2';
5456                                 fputs(buf, fp);
5457                                 /*  The trailer card  */
5458                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
5459                                 if (i == nexbonds)
5460                                         break;
5461                                 if (exbonds[i] < 0)
5462                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
5463                                 j = 0;
5464                         } else if (j > 0 && j % 3 == 0) {
5465                                 buf[2] = '1';
5466                                 fputs(buf, fp);
5467                         }
5468                         n1 = exbonds[i++];
5469                         n2 = exbonds[i++];
5470                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5471                         j++;
5472                 }
5473                 free(exbonds);
5474         }
5475 }
5476
5477 int
5478 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5479 {
5480         FILE *fp;
5481         int i, j, natoms, *ip;
5482         Int *cp;
5483         Atom *ap, *atoms, **app;
5484         Double *dp;
5485         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5486         
5487         *errbuf = NULL;
5488
5489         /*  Create sorted array of atoms  */
5490         natoms = mp->natoms;
5491         atoms = (Atom *)calloc(sizeof(Atom), natoms);
5492         app = (Atom **)calloc(sizeof(Atom *), natoms);
5493         ip = (int *)calloc(sizeof(int), natoms);
5494         if (atoms == NULL || app == NULL || ip == NULL) {
5495                 s_append_asprintf(errbuf, "Cannot allocate memory");
5496                 return 1;
5497         }
5498         /*  Sort the atom pointer by atomic number  */
5499         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5500                 app[i] = ap;
5501         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5502         for (i = 0; i < natoms; i++) {
5503                 /*  ip[old_index] is new_index  */
5504                 ip[app[i] - mp->atoms] = i;
5505         }
5506         /*  Copy the atom record to atoms[]  */
5507         /*  The 'v' member contains crystallographic coordinates  */
5508         /*  The connection table and symbase are renumbered  */
5509         /*  Hidden flags are modified to reflect the visibility in the MainView  */
5510         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5511                 AtomDuplicateNoFrame(ap, app[i]);
5512         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
5513                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5514                 cp = AtomConnectData(&ap->connect);
5515                 for (j = ap->connect.count - 1; j >= 0; j--) {
5516                         cp[j] = ip[cp[j]];
5517                 }
5518                 if (SYMOP_ALIVE(ap->symop))
5519                         ap->symbase = ip[ap->symbase];
5520                 if (MainView_isAtomHidden(mp->mview, i)) {
5521                         ap->exflags |= kAtomHiddenFlag;
5522                 } else {
5523                         ap->exflags &= ~kAtomHiddenFlag;
5524                 }
5525         }
5526         free(ip);
5527         free(app);
5528         
5529         fp = fopen(fname, "wb");
5530         if (fp == NULL) {
5531                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5532                 return 1;
5533         }
5534
5535         /*  Title line  */
5536         fprintf(fp, "Generated by Molby\n");
5537         
5538         /*  XtalCell  */
5539         if (mp->cell != NULL) {
5540                 dp = mp->cell->cell;
5541         } else {
5542                 dp = sUnit;
5543         }
5544         fprintf(fp, "%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]);
5545         
5546         /*  Symmetry operations  */
5547         if (mp->nsyms > 0) {
5548                 for (i = 0; i < mp->nsyms; i++) {
5549                         dp = mp->syms[i];
5550                         fprintf(fp, "%c%14g%3g%3g%3g%15g%3g%3g%3g%15g%3g%3g%3g\n", (i == mp->nsyms - 1 ? '1' : ' '), dp[9], dp[0], dp[1], dp[2], dp[10], dp[3], dp[4], dp[5], dp[11], dp[6], dp[7], dp[8]);
5551                 }
5552         } else {
5553                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
5554         }
5555         
5556         /*  Atoms  */
5557         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5558                 /*  The 'v' field contains crystallographic coordinates  */
5559                 fprintf(fp, " %4.4s%22s%9.4f%9.4f%9.4f%9d\n", ap->aname, "", ap->v.x, ap->v.y, ap->v.z, 0);
5560                 if (ap->aniso != NULL) {
5561                         dp = ap->aniso->bij;
5562                         fprintf(fp, " %8.5f%9.6f%9.6f%9.6f%9.6f%9.6f%9d\n", dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], 0);
5563                 } else {
5564                         Double temp = ap->tempFactor;
5565                         if (temp <= 0)
5566                                 temp = 1.2;
5567                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5568                 }
5569         }
5570         /*  Special points  */
5571         {
5572                 Vector camera, lookat, up, xvec, yvec, zvec;
5573                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5574                 VecSub(zvec, lookat, camera);
5575                 VecCross(xvec, zvec, up);
5576                 NormalizeVec(&xvec, &xvec);
5577                 NormalizeVec(&yvec, &up);
5578                 VecInc(xvec, lookat);
5579                 VecInc(yvec, lookat);
5580                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5581                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5582                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5583                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
5584                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5585                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
5586                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5587                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
5588                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5589                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
5590                 fprintf(fp, "1%8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5591         }
5592         
5593         /*  Instructions  */
5594         fprintf(fp, "      201\n");
5595         fprintf(fp, "      205       12\n");
5596         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
5597         sOutputAtomListInstructions(fp, natoms, atoms);
5598         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5599         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
5600         fprintf(fp, "      604                               1.538\n");
5601
5602         sOutputBondInstructions(fp, natoms, atoms, 1);
5603         sOutputAtomTypeInstructions(fp, natoms, atoms);
5604         sOutputBondInstructions(fp, natoms, atoms, 0);
5605
5606         for (i = 0; i < natoms; i++) {
5607                 AtomClean(atoms + i);
5608         }
5609         free(atoms);
5610
5611         fprintf(fp, "      202\n");
5612         fprintf(fp, "  0    -1\n");
5613         fclose(fp);
5614         return 0;
5615 }
5616
5617 void
5618 MoleculeDump(Molecule *mol)
5619 {
5620         int i, j;
5621         Int *cp;
5622         Atom *ap;
5623         for (i = 0; i < mol->natoms; i++) {
5624                 char buf1[8];
5625                 ap = ATOM_AT_INDEX(mol->atoms, i);
5626                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5627                 fprintf(stderr, "%4d %-7s %-4.6s %-4.6s %-2.2s %7.3f %7.3f %7.3f %6.3f [", i, buf1, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->element, ap->r.x, ap->r.y, ap->r.z, ap->charge);
5628                 cp = AtomConnectData(&ap->connect);
5629                 for (j = 0; j < ap->connect.count; j++) {
5630                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5631                 }
5632                 fprintf(stderr, "]\n");
5633         }
5634 }
5635
5636 #pragma mark ====== MD support (including modification of Molecule) ======
5637
5638 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5639         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5640     If retmsg is not NULL, a message describing the problem is returned there. This message
5641     must be free'd by the caller.  */
5642 int
5643 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5644 {
5645         const char *msg;
5646         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5647         Int missing = 0;
5648         IntGroup *ig1, *ig2, *ig3;
5649         MDArena *arena = mol->arena;
5650
5651         if (arena == NULL) {
5652                 md_arena_new(mol);
5653                 arena = mol->arena;
5654         } else if (arena->xmol != mol)
5655                 md_arena_set_molecule(arena, mol);
5656
5657         arena->is_initialized = 0;
5658         
5659         /*  Rebuild the tables  */
5660         ig1 = ig2 = ig3 = NULL;
5661         nangles = MoleculeFindMissingAngles(mol, &angles);
5662         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5663         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5664         if (nangles > 0) {
5665                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5666                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5667                 free(angles);
5668                 IntGroupRelease(ig1);
5669         }
5670         if (ndihedrals > 0) {
5671                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5672                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5673                 free(dihedrals);
5674                 IntGroupRelease(ig2);
5675         }
5676         if (nimpropers > 0) {
5677                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5678                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5679                 free(impropers);
5680                 IntGroupRelease(ig3);
5681         }
5682         
5683         {
5684                 /*  Update the path information of the molecule before MD setup  */
5685                 char *buf = (char *)malloc(4096);
5686                 MoleculeCallback_pathName(mol, buf, sizeof buf);
5687                 MoleculeSetPath(mol, buf);
5688                 free(buf);
5689         }
5690                 
5691         /*  Prepare parameters and internal information  */
5692         msg = md_prepare(arena, check_only);
5693         
5694         /*  Some parameters are missing?  */
5695         if (msg != NULL) {
5696                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5697                         missing = 1;
5698                 else {
5699                         if (retmsg != NULL)
5700                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5701                         return -1;
5702                 }
5703         }
5704         
5705         /*  The local parameter list is updated  */
5706         {
5707                 Int parType, idx;
5708                 if (mol->par == NULL)
5709                         mol->par = ParameterNew();
5710                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5711                         /*  Delete global and undefined parameters  */
5712                         UnionPar *up, *upbuf;
5713                         Int nparams, count;
5714                         ig1 = IntGroupNew();
5715                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5716                                 if (up->bond.src != 0)
5717                                         IntGroupAdd(ig1, idx, 1);
5718                         }
5719                         if (IntGroupGetCount(ig1) > 0)
5720                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5721                         IntGroupRelease(ig1);
5722                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
5723                         nparams = ParameterGetCountForType(arena->par, parType);
5724                         if (nparams == 0)
5725                                 continue;
5726                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5727                         ig1 = IntGroupNew();
5728                         ig2 = IntGroupNew();
5729                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5730                                 if (up->bond.src > 0)
5731                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
5732                                 else if (up->bond.src < 0)
5733                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5734                         }
5735                         if ((count = IntGroupGetCount(ig1)) > 0) {
5736                                 /*  Insert global parameters (at the top)  */
5737                                 ParameterCopy(arena->par, parType, upbuf, ig1);
5738                                 ig3 = IntGroupNewWithPoints(0, count, -1);
5739                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5740                                 IntGroupRelease(ig3);
5741                         }
5742                         if ((count = IntGroupGetCount(ig2)) > 0) {
5743                                 /*  Insert undefined parameters (at the bottom)  */
5744                                 ParameterCopy(arena->par, parType, upbuf, ig2);
5745                                 idx = ParameterGetCountForType(mol->par, parType);
5746                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
5747                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5748                                 IntGroupRelease(ig3);
5749                         }
5750                         IntGroupRelease(ig2);
5751                         IntGroupRelease(ig1);
5752                         free(upbuf);
5753                 }
5754                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5755         }
5756         
5757         if (missing) {
5758                 if (retmsg != NULL)
5759                         *retmsg = strdup(msg);
5760                 return 1;
5761         } else return 0;
5762 }
5763
5764 #pragma mark ====== Serialize ======
5765
5766 Molecule *
5767 MoleculeDeserialize(const char *data, Int length, Int *timep)
5768 {
5769         Molecule *mp;
5770         Parameter *par;
5771         Atom *ap;
5772 /*      int result; */
5773
5774         mp = MoleculeNew();
5775         if (mp == NULL)
5776                 goto out_of_memory;
5777         par = ParameterNew();
5778         if (par == NULL)
5779                 goto out_of_memory;
5780
5781         while (length >= 12) {
5782                 const char *ptr = data + 8 + sizeof(Int);
5783                 int len = *((const Int *)(data + 8));
5784                 int i, j, n;
5785                 if (strcmp(data, "ATOM") == 0) {
5786                         n = len / gSizeOfAtomRecord;
5787                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5788                         memmove(mp->atoms, ptr, len);
5789                 } else if (strcmp(data, "ANISO") == 0) {
5790                         n = len / (sizeof(Int) + sizeof(Aniso));
5791                         for (i = 0; i < n; i++) {
5792                                 j = *((const Int *)ptr);
5793                                 if (j < 0 || j >= mp->natoms)
5794                                         goto bad_format;
5795                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5796                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5797                                 if (ap->aniso == NULL)
5798                                         goto out_of_memory;
5799                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5800                                 ptr += sizeof(Int) + sizeof(Aniso);
5801                         }
5802                 } else if (strcmp(data, "FRAME") == 0) {
5803                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5804                                 if (ap->nframes == 0)
5805                                         continue;
5806                                 n = ap->nframes;
5807                                 ap->frames = NULL;
5808                                 ap->nframes = 0;
5809                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5810                                 if (ap->frames == NULL)
5811                                         goto out_of_memory;
5812                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5813                                 ptr += sizeof(Vector) * ap->nframes;
5814                         }
5815                 } else if (strcmp(data, "EXTCON") == 0) {
5816                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5817                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5818                                         continue;
5819                                 n = ap->connect.count;
5820                                 ap->connect.count = 0;
5821                                 ap->connect.u.ptr = NULL;
5822                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5823                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5824                                 ptr += sizeof(Int) * n;
5825                         }
5826                 } else if (strcmp(data, "BOND") == 0) {
5827                         n = len / (sizeof(Int) * 2);
5828                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5829                         memmove(mp->bonds, ptr, len);
5830                 } else if (strcmp(data, "ANGLE") == 0) {
5831                         n = len / (sizeof(Int) * 3);
5832                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5833                         memmove(mp->angles, ptr, len);
5834                 } else if (strcmp(data, "DIHED") == 0) {
5835                         n = len / (sizeof(Int) * 4);
5836                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5837                         memmove(mp->dihedrals, ptr, len);
5838                 } else if (strcmp(data, "IMPROP") == 0) {
5839                         n = len / (sizeof(Int) * 4);
5840                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5841                         memmove(mp->impropers, ptr, len);
5842                 } else if (strcmp(data, "RESIDUE") == 0) {
5843                         n = len / 4;
5844                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5845                         memmove(mp->residues, ptr, len);
5846                 } else if (strcmp(data, "CELL") == 0) {
5847                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5848                         if (mp->cell == NULL)
5849                                 goto out_of_memory;
5850                         memmove(mp->cell, ptr, sizeof(XtalCell));
5851                 } else if (strcmp(data, "SYMOP") == 0) {
5852                         n = len / sizeof(Transform);
5853                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5854                         memmove(mp->syms, ptr, len);
5855                 } else if (strcmp(data, "ANCHOR") == 0) {
5856                         const char *ptr2 = ptr + len;
5857                         while (ptr < ptr2) {
5858                                 PiAnchor an;
5859                                 memset(&an, 0, sizeof(an));
5860                                 i = *((Int *)ptr);
5861                                 if (i >= 0 && i < mp->natoms) {
5862                                         n = *((Int *)(ptr + sizeof(Int)));
5863                                         AtomConnectResize(&(an.connect), n);
5864                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5865                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5866                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5867                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5868                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5869                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5870                                 }
5871                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5872                         }
5873                 } else if (strcmp(data, "TIME") == 0) {
5874                         if (timep != NULL)
5875                                 *timep = *((Int *)ptr);
5876                 } else if (strcmp(data, "BONDPAR") == 0) {
5877                         mp->par = par;
5878                         n = len / sizeof(BondPar);
5879                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5880                         memmove(par->bondPars, ptr, len);
5881                 } else if (strcmp(data, "ANGPAR") == 0) {
5882                         mp->par = par;
5883                         n = len / sizeof(AnglePar);
5884                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5885                         memmove(par->anglePars, ptr, len);
5886                 } else if (strcmp(data, "DIHEPAR") == 0) {
5887                         mp->par = par;
5888                         n = len / sizeof(TorsionPar);
5889                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5890                         memmove(par->dihedralPars, ptr, len);
5891                 } else if (strcmp(data, "IMPRPAR") == 0) {
5892                         mp->par = par;
5893                         n = len / sizeof(TorsionPar);
5894                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5895                         memmove(par->improperPars, ptr, len);
5896                 } else if (strcmp(data, "VDWPAR") == 0) {
5897                         mp->par = par;
5898                         n = len / sizeof(VdwPar);
5899                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5900                         memmove(par->vdwPars, ptr, len);
5901                 } else if (strcmp(data, "VDWPPAR") == 0) {
5902                         mp->par = par;
5903                         n = len / sizeof(VdwPairPar);
5904                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5905                         memmove(par->vdwpPars, ptr, len);
5906                 } else if (strcmp(data, "VCUTPAR") == 0) {
5907                         mp->par = par;
5908                         n = len / sizeof(VdwCutoffPar);
5909                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5910                         memmove(par->vdwCutoffPars, ptr, len);
5911                 }
5912                 len += 8 + sizeof(Int);
5913                 data += len;
5914                 length -= len;
5915         }
5916         if (mp->par == NULL)
5917                 ParameterRelease(par);
5918 /*      result = MoleculeRebuildTablesFromConnects(mp);
5919         if (result != 0)
5920                 goto bad_format; */
5921         return mp;
5922         
5923   out_of_memory:
5924         Panic("Low memory while deserializing molecule data");
5925         return NULL; /* Not reached */
5926
5927   bad_format:
5928         Panic("internal error: bad format during deserializing molecule data");
5929         return NULL; /* Not reached */
5930 }
5931
5932 char *
5933 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5934 {
5935         char *ptr, *p;
5936         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5937         Atom *ap;
5938
5939         /*  Array of atoms  */
5940         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5941         ptr = (char *)malloc(len);
5942         if (ptr == NULL)
5943                 goto out_of_memory;
5944         memmove(ptr, "ATOM\0\0\0\0", 8);
5945         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5946         p = ptr + 8 + sizeof(Int);
5947         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5948         naniso = nframes = nconnects = nanchors = 0;
5949         for (i = 0; i < mp->natoms; i++) {
5950                 ap = ATOM_AT_INDEX(p, i);
5951                 if (ap->aniso != NULL) {
5952                         naniso++;
5953                         ap->aniso = NULL;
5954                 }
5955                 if (ap->frames != NULL) {
5956                         nframes += ap->nframes;
5957                         ap->frames = NULL;
5958                 }
5959                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5960                         nconnects += ap->connect.count;
5961                         ap->connect.u.ptr = NULL;
5962                 }
5963                 if (ap->anchor != NULL) {
5964                         nanchors++;
5965                         ap->anchor = NULL;
5966                 }
5967         }
5968         len_all = len;
5969
5970         /*  Array of aniso  */
5971         if (naniso > 0) {
5972                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5973                 ptr = (char *)realloc(ptr, len_all + len);
5974                 if (ptr == NULL)
5975                         goto out_of_memory;
5976                 p = ptr + len_all;
5977                 memmove(p, "ANISO\0\0\0", 8);
5978                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5979                 p += 8 + sizeof(Int);
5980                 for (i = 0; i < mp->natoms; i++) {
5981                         ap = ATOM_AT_INDEX(mp->atoms, i);
5982                         if (ap->aniso != NULL) {
5983                                 *((Int *)p) = i;
5984                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5985                                 p += sizeof(Int) + sizeof(Aniso);
5986                         }
5987                 }
5988                 len_all += len;
5989         }
5990         
5991         /*  Array of frames  */
5992         if (nframes > 0) {
5993                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5994                 ptr = (char *)realloc(ptr, len_all + len);
5995                 if (ptr == NULL)
5996                         goto out_of_memory;
5997                 p = ptr + len_all;
5998                 memmove(p, "FRAME\0\0\0", 8);
5999                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
6000                 p += 8 + sizeof(Int);
6001                 for (i = 0; i < mp->natoms; i++) {
6002                         ap = ATOM_AT_INDEX(mp->atoms, i);
6003                         if (ap->frames != NULL) {
6004                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
6005                                 p += sizeof(Vector) * ap->nframes;
6006                         }
6007                 }
6008                 len_all += len;
6009         }
6010         
6011         /*  Array of connects  */
6012         if (nconnects > 0) {
6013                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
6014                 ptr = (char *)realloc(ptr, len_all + len);
6015                 if (ptr == NULL)
6016                         goto out_of_memory;
6017                 p = ptr + len_all;
6018                 memmove(p, "EXTCON\0\0", 8);
6019                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
6020                 p += 8 + sizeof(Int);
6021                 for (i = 0; i < mp->natoms; i++) {
6022                         ap = ATOM_AT_INDEX(mp->atoms, i);
6023                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
6024                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
6025                                 p += sizeof(Int) * ap->connect.count;
6026                         }
6027                 }
6028                 len_all += len;
6029         }
6030         
6031         /*  Bonds, angles, dihedrals, impropers  */
6032         if (mp->nbonds > 0) {
6033                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
6034                 ptr = (char *)realloc(ptr, len_all + len);
6035                 if (ptr == NULL)
6036                         goto out_of_memory;
6037                 p = ptr + len_all;
6038                 memmove(p, "BOND\0\0\0\0", 8);
6039                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
6040                 p += 8 + sizeof(Int);
6041                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
6042                 len_all += len;
6043         }
6044         if (mp->nangles > 0) {
6045                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
6046                 ptr = (char *)realloc(ptr, len_all + len);
6047                 if (ptr == NULL)
6048                         goto out_of_memory;
6049                 p = ptr + len_all;
6050                 memmove(p, "ANGLE\0\0\0", 8);
6051                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
6052                 p += 8 + sizeof(Int);
6053                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
6054                 len_all += len;
6055         }
6056         if (mp->ndihedrals > 0) {
6057                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
6058                 ptr = (char *)realloc(ptr, len_all + len);
6059                 if (ptr == NULL)
6060                         goto out_of_memory;
6061                 p = ptr + len_all;
6062                 memmove(p, "DIHED\0\0\0", 8);
6063                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
6064                 p += 8 + sizeof(Int);
6065                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
6066                 len_all += len;
6067         }
6068         if (mp->nimpropers > 0) {
6069                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
6070                 ptr = (char *)realloc(ptr, len_all + len);
6071                 if (ptr == NULL)
6072                         goto out_of_memory;
6073                 p = ptr + len_all;
6074                 memmove(p, "IMPROP\0\0", 8);
6075                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
6076                 p += 8 + sizeof(Int);
6077                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
6078                 len_all += len;
6079         }
6080         
6081         /*  Array of residues  */
6082         if (mp->nresidues > 0) {
6083                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
6084                 ptr = (char *)realloc(ptr, len_all + len);
6085                 if (ptr == NULL)
6086                         goto out_of_memory;
6087                 p = ptr + len_all;
6088                 memmove(p, "RESIDUE\0", 8);
6089                 *((Int *)(p + 8)) = 4 * mp->nresidues;
6090                 p += 8 + sizeof(Int);
6091                 memmove(p, mp->residues, 4 * mp->nresidues);
6092                 len_all += len;
6093         }
6094
6095         /*  Unit cell  */
6096         if (mp->cell != NULL) {
6097                 len = 8 + sizeof(Int) + sizeof(XtalCell);
6098                 ptr = (char *)realloc(ptr, len_all + len);
6099                 if (ptr == NULL)
6100                         goto out_of_memory;
6101                 p = ptr + len_all;
6102                 memmove(p, "CELL\0\0\0\0", 8);
6103                 *((Int *)(p + 8)) = sizeof(XtalCell);
6104                 p += 8 + sizeof(Int);
6105                 memmove(p, mp->cell, sizeof(XtalCell));
6106                 len_all += len;
6107         }
6108         
6109         /*  Symmetry operations  */
6110         if (mp->nsyms > 0) {
6111                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
6112                 ptr = (char *)realloc(ptr, len_all + len);
6113                 if (ptr == NULL)
6114                         goto out_of_memory;
6115                 p = ptr + len_all;
6116                 memmove(p, "SYMOP\0\0\0", 8);
6117                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
6118                 p += 8 + sizeof(Int);
6119                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
6120                 len_all += len;
6121         }
6122         
6123         /*  Pi-anchors  */
6124         if (nanchors > 0) {
6125                 /*  Estimate the necessary storage first  */
6126                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
6127                 len = 8 + sizeof(Int);
6128                 for (i = 0; i < mp->natoms; i++) {
6129                         ap = ATOM_AT_INDEX(mp->atoms, i);
6130                         if (ap->anchor != NULL)
6131                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
6132                 }
6133                 ptr = (char *)realloc(ptr, len_all + len);
6134                 if (ptr == NULL)
6135                         goto out_of_memory;
6136                 p = ptr + len_all;
6137                 memmove(p, "ANCHOR\0\0", 8);
6138                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
6139                 p += 8 + sizeof(Int);
6140                 for (i = 0; i < mp->natoms; i++) {
6141                         Int count, *ip;
6142                         ap = ATOM_AT_INDEX(mp->atoms, i);
6143                         if (ap->anchor != NULL) {
6144                                 count = ap->anchor->connect.count;
6145                                 *((Int *)p) = i;
6146                                 *((Int *)(p + sizeof(Int))) = count;
6147                                 p += sizeof(Int) * 2;
6148                                 ip = AtomConnectData(&(ap->anchor->connect));
6149                                 memmove(p, ip, sizeof(Int) * count);
6150                                 p += sizeof(Int) * count;
6151                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
6152                                 p += sizeof(Double) * count;
6153                         }
6154                 }
6155                 len_all += len;
6156         }
6157         
6158         /*  Parameters  */
6159         if (mp->par != NULL) {
6160                 int type;
6161                 for (type = kFirstParType; type <= kLastParType; type++) {
6162                         const char *parname;
6163                         Int parsize, parcount;
6164                         void *parptr;
6165                         switch (type) {
6166                                 case kBondParType:
6167                                         parname = "BONDPAR\0";
6168                                         parsize = sizeof(BondPar);
6169                                         parcount = mp->par->nbondPars;
6170                                         parptr = mp->par->bondPars;
6171                                         break;
6172                                 case kAngleParType:
6173                                         parname = "ANGPAR\0\0";
6174                                         parsize = sizeof(AnglePar);
6175                                         parcount = mp->par->nanglePars;
6176                                         parptr = mp->par->anglePars;
6177                                         break;
6178                                 case kDihedralParType:
6179                                         parname = "DIHEPAR\0";
6180                                         parsize = sizeof(TorsionPar);
6181                                         parcount = mp->par->ndihedralPars;
6182                                         parptr = mp->par->dihedralPars;
6183                                         break;
6184                                 case kImproperParType:
6185                                         parname = "IMPRPAR\0";
6186                                         parsize = sizeof(TorsionPar);
6187                                         parcount = mp->par->nimproperPars;
6188                                         parptr = mp->par->improperPars;
6189                                         break;
6190                                 case kVdwParType:
6191                                         parname = "VDWPAR\0\0";
6192                                         parsize = sizeof(VdwPar);
6193                                         parcount = mp->par->nvdwPars;
6194                                         parptr = mp->par->vdwPars;
6195                                         break;
6196                                 case kVdwPairParType:
6197                                         parname = "VDWPPAR\0";
6198                                         parsize = sizeof(VdwPairPar);
6199                                         parcount = mp->par->nvdwpPars;
6200                                         parptr = mp->par->vdwpPars;
6201                                         break;
6202                                 case kVdwCutoffParType:
6203                                         parname = "VCUTPAR\0";
6204                                         parsize = sizeof(VdwCutoffPar);
6205                                         parcount = mp->par->nvdwCutoffPars;
6206                                         parptr = mp->par->vdwCutoffPars;
6207                                         break;
6208                                 default:
6209                                         continue;
6210                         }
6211                         if (parcount > 0) {
6212                                 len = 8 + sizeof(Int) + parsize * parcount;
6213                                 ptr = (char *)realloc(ptr, len_all + len);
6214                                 if (ptr == NULL)
6215                                         goto out_of_memory;
6216                                 p = ptr + len_all;
6217                                 memmove(p, parname, 8);
6218                                 *((Int *)(p + 8)) = parsize * parcount;
6219                                 p += 8 + sizeof(Int);
6220                                 memmove(p, parptr, parsize * parcount);
6221                                 len_all += len;
6222                         }
6223                 }
6224         }
6225         
6226         /*  Time stamp  */
6227         {
6228                 time_t tm = time(NULL);
6229                 len = 8 + sizeof(Int) + sizeof(Int);
6230                 ptr = (char *)realloc(ptr, len_all + len);
6231                 if (ptr == NULL)
6232                         goto out_of_memory;
6233                 p = ptr + len_all;
6234                 memmove(p, "TIME\0\0\0\0", 8);
6235                 *((Int *)(p + 8)) = sizeof(Int);
6236                 p += 8 + sizeof(Int);
6237                 *((Int *)p) = (Int)tm;
6238                 len_all += len;
6239                 if (timep != NULL)
6240                         *timep = (Int)tm;
6241         }
6242         
6243         if (outLength != NULL)
6244                 *outLength = len_all;
6245         return ptr;
6246
6247   out_of_memory:
6248     Panic("Low memory while serializing a molecule data");
6249         return NULL; /* Not reached */  
6250 }
6251
6252 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6253
6254 static IntGroup *
6255 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6256 {
6257         int i, j;
6258         Int *ip;
6259         IntGroup *gp = NULL;
6260         if (atomgroup == NULL)
6261                 return NULL;
6262         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6263                 for (j = 0; j < nsize; j++) {
6264                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6265                                 if (gp == NULL)
6266                                         gp = IntGroupNew();
6267                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6268                                         Panic("Low memory while searching %s", msg);
6269                                 break;
6270                         }
6271                 }
6272         }
6273         return gp;
6274 }
6275
6276 IntGroup *
6277 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6278 {
6279         if (mp == NULL)
6280                 return NULL;
6281         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6282 }
6283
6284 IntGroup *
6285 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6286 {
6287         if (mp == NULL)
6288                 return NULL;
6289         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6290 }
6291
6292 IntGroup *
6293 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6294 {
6295         if (mp == NULL)
6296                 return NULL;
6297         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6298 }
6299
6300 IntGroup *
6301 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6302 {
6303         if (mp == NULL)
6304                 return NULL;
6305         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6306 }
6307
6308 static IntGroup *
6309 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6310 {
6311         int i, j;
6312         Int *ip;
6313         IntGroup *gp = NULL;
6314         if (atomgroup == NULL)
6315                 return NULL;
6316         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6317                 int k = -1;
6318                 for (j = 0; j < nsize; j++) {
6319                         int kk;
6320                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6321                         if (k < 0)
6322                                 k = kk;
6323                         else if (k != kk) {
6324                                 /*  This bond etc. crosses the atom group border  */
6325                                 if (gp == NULL)
6326                                         gp = IntGroupNew();
6327                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6328                                         Panic("Low memory while searching %s", msg);
6329                                 break;
6330                         }
6331                 }
6332         }
6333         return gp;
6334 }
6335
6336 IntGroup *
6337 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6338 {
6339         if (mp == NULL)
6340                 return NULL;
6341         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6342 }
6343
6344 IntGroup *
6345 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6346 {
6347         if (mp == NULL)
6348                 return NULL;
6349         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6350 }
6351
6352 IntGroup *
6353 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6354 {
6355         if (mp == NULL)
6356                 return NULL;
6357         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6358 }
6359
6360 IntGroup *
6361 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6362 {
6363         if (mp == NULL)
6364                 return NULL;
6365         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6366 }
6367
6368 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6369     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
6370 /*  Find atoms within the given "distance" from the given position.  */
6371 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6372  the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6373  the van der Waals radius of the atom at the given position. */
6374 /*  Index is the atom index of the given atom; it is only used in returning the "bond" array
6375  to the caller. If index is negative, then (-index) is the real atom index, and
6376  only atoms with lower indices than (-index) are looked for.  */
6377 int
6378 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6379 {
6380         Int n2, j, nlim, newbond[2];
6381         Double a2, alim;
6382         Vector dr, r2;
6383         if (index < 0) {
6384                 nlim = index = -index;
6385         } else {
6386                 nlim = mp->natoms;
6387         }
6388         for (j = 0; j < nlim; j++) {
6389                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6390                 if (index == j)
6391                         continue;
6392                 n2 = bp->atomicNumber;
6393                 if (n2 >= 0 && n2 < gCountElementParameters)
6394                         a2 = gElementParameters[n2].radius;
6395                 else a2 = gElementParameters[6].radius;
6396                 r2 = bp->r;
6397                 VecSub(dr, *vp, r2);
6398                 if (limit < 0)
6399                         alim = -limit;
6400                 else
6401                         alim = limit * (radius + a2);
6402                 if (VecLength2(dr) < alim * alim) {
6403                         newbond[0] = index;
6404                         newbond[1] = j;
6405                         /*      MoleculeAddBonds(mp, 1, newbonds); */
6406                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6407                 }
6408         }
6409         return 0;
6410 }
6411
6412 /*  Guess the bonds from the coordinates  */
6413 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6414     the threshold distance is given by the sum of van der Waals radii times limit.  */
6415 int
6416 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6417 {
6418         Int nbonds, *bonds, i, newbond[2];
6419         Atom *ap;
6420         nbonds = 0;
6421         bonds = NULL;
6422         if (limit == 0.0)
6423                 limit = 1.2;
6424         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6425                 Vector r = ap->r;
6426                 Int an = ap->atomicNumber;
6427                 Double rad;
6428                 if (an >= 0 && an < gCountElementParameters)
6429                         rad = gElementParameters[an].radius;
6430                 else rad = gElementParameters[6].radius;
6431                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6432         }
6433         if (nbonds > 0) {
6434                 newbond[0] = kInvalidIndex;
6435                 newbond[1] = 0;
6436                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6437                 nbonds--;
6438         }
6439         if (outNbonds != NULL)
6440                 *outNbonds = nbonds;
6441         if (outBonds != NULL)
6442                 *outBonds = bonds;
6443         return 0;
6444 }
6445
6446 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
6447 int
6448 MoleculeRebuildTablesFromConnects(Molecule *mp)
6449 {
6450         int i, j, k, retval;
6451         Atom *ap;
6452         Int ibuf[6], *cp;
6453         
6454         __MoleculeLock(mp);
6455
6456         /*  Find bonds   */
6457         if (mp->nbonds == 0) {
6458                 for (i = 0; i < mp->natoms; i++) {
6459                         ap = ATOM_AT_INDEX(mp->atoms, i);
6460                         cp = AtomConnectData(&ap->connect);
6461                         for (j = 0; j < ap->connect.count; j++) {
6462                                 k = cp[j];
6463                                 if (i >= k)
6464                                         continue;
6465                                 ibuf[0] = i;
6466                                 ibuf[1] = k;
6467                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
6468                                     bonds are already in sync  */
6469                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6470                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
6471                                 if (retval != 0)
6472                                         goto abort; */
6473                         }
6474                 }
6475         }
6476         
6477         /*  Find angles  */
6478         if (mp->nangles == 0) {
6479                 for (i = 0; i < mp->natoms; i++) {
6480                         ap = ATOM_AT_INDEX(mp->atoms, i);
6481                         cp = AtomConnectData(&ap->connect);
6482                         for (j = 0; j < ap->connect.count; j++) {
6483                                 for (k = j + 1; k < ap->connect.count; k++) {
6484                                         ibuf[0] = cp[j];
6485                                         ibuf[1] = i;
6486                                         ibuf[2] = cp[k];
6487                                         ibuf[3] = -1;
6488                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
6489                                         if (retval < 0)
6490                                                 goto abort;
6491                                 }
6492                         }
6493                 }
6494         }
6495         
6496         /*  Find dihedrals  */
6497         if (mp->ndihedrals == 0) {
6498                 for (i = 0; i < mp->natoms; i++) {
6499                         ap = ATOM_AT_INDEX(mp->atoms, i);
6500                         cp = AtomConnectData(&ap->connect);
6501                         for (j = 0; j < ap->connect.count; j++) {
6502                                 int jj, kk, mm, m;
6503                                 Atom *apjj;
6504                                 Int *cpjj;
6505                                 jj = cp[j];
6506                                 if (i >= jj)
6507                                         continue;
6508                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6509                                 cpjj = AtomConnectData(&apjj->connect);
6510                                 for (k = 0; k < ap->connect.count; k++) {
6511                                         if (k == j)
6512                                                 continue;
6513                                         kk = cp[k];
6514                                         for (m = 0; m < apjj->connect.count; m++) {
6515                                                 mm = cpjj[m];
6516                                                 if (mm == i || mm == kk)
6517                                                         continue;
6518                                                 ibuf[0] = kk;
6519                                                 ibuf[1] = i;
6520                                                 ibuf[2] = jj;
6521                                                 ibuf[3] = mm;
6522                                                 ibuf[4] = -1;
6523                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6524                                                 if (retval < 0)
6525                                                         goto abort;
6526                                         }
6527                                 }
6528                         }
6529                 }
6530         }
6531         
6532         /*  Find impropers  */
6533         if (mp->nimpropers == 0) {
6534                 for (i = 0; i < mp->natoms; i++) {
6535                         int i1, i2, i4, n1, n2, n4;
6536                         ap = ATOM_AT_INDEX(mp->atoms, i);
6537                         cp = AtomConnectData(&ap->connect);
6538                         for (i1 = 0; i1 < ap->connect.count; i1++) {
6539                                 n1 = cp[i1];
6540                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6541                                         n2 = cp[i2];
6542                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6543                                                 n4 = cp[i4];
6544                                                 ibuf[0] = n1;
6545                                                 ibuf[1] = n2;
6546                                                 ibuf[2] = i;
6547                                                 ibuf[3] = n4;
6548                                                 ibuf[4] = -1;
6549                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6550                                                 if (retval < 0)
6551                                                         goto abort;
6552                                         }
6553                                 }
6554                         }
6555                 }
6556         }
6557
6558         mp->needsMDRebuild = 1;
6559         __MoleculeUnlock(mp);
6560         return 0;
6561
6562   abort:
6563         __MoleculeUnlock(mp);
6564         return retval;
6565 }
6566
6567 int
6568 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6569 {
6570         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6571         if (AtomConnectHasEntry(&ap1->connect, idx2))
6572                 return 1;
6573         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6574                 return 2;
6575         else return 0;
6576 }
6577
6578 #pragma mark ====== Atom names ======
6579
6580 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
6581 int
6582 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6583 {
6584         int i, j, lasti;
6585         Atom *ap;
6586         if (mp == NULL || mp->natoms == 0)
6587                 return -1;
6588         lasti = -1;
6589         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6590                 if (ap->resSeq == resno) {
6591                         lasti = i;
6592                         if (j++ == n1)
6593                                 return i;
6594                 }
6595         }
6596         if (n1 == -1)
6597                 return lasti; /* max */
6598         return -1;
6599 }
6600
6601 int
6602 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6603 {
6604     int n;
6605     char *p;
6606         n = strtol(s, &p, 0);
6607         if (p > s) {
6608                 while (isspace(*p))
6609                         p++;
6610                 if (*p == 0) {
6611                   resName[0] = 0;
6612                   *resSeq = -1;
6613                   atomName[0] = 0;
6614                   return n;
6615                 }
6616         }
6617
6618         if ((p = strchr(s, ':')) != NULL) {
6619                 /*  Residue is specified  */
6620                 char *pp;
6621                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6622                         /*  Residue number is also specified  */
6623                         char *ppp;
6624                         n = pp - s;
6625                         *resSeq = strtol(pp + 1, &ppp, 0);
6626                         if (ppp == pp + 1)
6627                                 return -2;  /*  Bad format  */
6628                         while (isspace(*ppp))
6629                                 ppp++;
6630                         if (ppp != p)
6631                                 return -2;  /*  Bad format  */
6632                 } else {
6633                         *resSeq = -1;
6634                         /*  Check whether the "residue name" is an integer  */
6635                         n = strtol(s, &pp, 0);
6636                         if (pp > s) {
6637                                 while (isspace(*pp))
6638                                         pp++;
6639                                 if (*pp == 0 || *pp == ':') {
6640                                         *resSeq = n;
6641                                         if (*resSeq < 0)
6642                                                 return -2;  /*  Bad format  */
6643                                 }
6644                         }
6645                         if (*resSeq >= 0)
6646                                 n = 0;
6647                         else
6648                                 n = p - s;
6649                 }
6650                 if (n >= sizeof(resName))
6651                         n = sizeof(resName) - 1;
6652                 strncpy(resName, s, n);
6653                 resName[n] = 0;
6654                 p++;
6655         } else {
6656                 resName[0] = 0;
6657                 *resSeq = -1;
6658                 p = (char *)s;
6659         }
6660         strncpy(atomName, p, 4);
6661         atomName[4] = 0;
6662         return 0;
6663 }
6664
6665 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
6666 int
6667 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6668 {
6669         char resName[6];
6670         int resSeq, n;
6671         char atomName[6];
6672         /*      char *p; */
6673
6674         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6675         if (atomName[0] == 0) {
6676           if (n >= mp->natoms)
6677             n = -1;  /* Out of range */
6678           return n;
6679         }
6680         for (n = 0; n < mp->natoms; n++) {
6681                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6682                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6683                         && (resSeq < 0 || ap->resSeq == resSeq)
6684                         && strncmp(atomName, ap->aname, 4) == 0) {
6685                         return n;
6686                 }
6687         }
6688         return -1;  /*  Not found  */
6689 }
6690
6691 void
6692 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6693 {
6694         Atom *ap;
6695         int n;
6696         if (mp == NULL || index < 0 || index >= mp->natoms) {
6697                 buf[0] = 0;
6698                 return;
6699         }
6700         ap = mp->atoms + index;
6701         if (ap->resSeq != 0) {
6702                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6703                 buf += n;
6704                 bufsize -= n;
6705         }
6706         snprintf(buf, bufsize, "%.4s", ap->aname);
6707 }
6708
6709 #pragma mark ====== Selection ======
6710
6711 static void
6712 sMoleculeNotifyChangeSelection(Molecule *mp)
6713 {
6714         /*  TODO: Finer control of notification types may be necessary  */
6715         MoleculeCallback_notifyModification(mp, 0);
6716 }
6717
6718 void
6719 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6720 {
6721         if (mp == NULL)
6722                 return;
6723         if (select != NULL)
6724                 IntGroupRetain(select);
6725         if (mp->selection != NULL)
6726                 IntGroupRelease(mp->selection);
6727         mp->selection = select;
6728         sMoleculeNotifyChangeSelection(mp);
6729 }
6730
6731 IntGroup *
6732 MoleculeGetSelection(Molecule *mp)
6733 {
6734         if (mp == NULL)
6735                 return NULL;
6736         else return mp->selection;
6737 }
6738
6739 void
6740 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6741 {
6742         if (mp->selection == NULL)
6743                 mp->selection = IntGroupNew();
6744         if (!extending)
6745                 IntGroupClear(mp->selection);
6746         IntGroupAdd(mp->selection, n1, 1);
6747         sMoleculeNotifyChangeSelection(mp);
6748 }
6749
6750 void
6751 MoleculeUnselectAtom(Molecule *mp, int n1)
6752 {
6753         if (mp->selection != NULL)
6754                 IntGroupRemove(mp->selection, n1, 1);
6755         sMoleculeNotifyChangeSelection(mp);
6756 }
6757
6758 void
6759 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6760 {
6761         if (mp->selection == NULL)
6762                 mp->selection = IntGroupNew();
6763         IntGroupReverse(mp->selection, n1, 1);
6764         sMoleculeNotifyChangeSelection(mp);
6765 }
6766
6767 int
6768 MoleculeIsAtomSelected(Molecule *mp, int n1)
6769 {
6770         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6771                 return 1;
6772         else return 0;
6773 }
6774
6775 int
6776 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6777 {
6778         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6779                 return 1;
6780         else return 0;
6781 }
6782
6783 IntGroup *
6784 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6785 {
6786         int status;
6787         IntGroup *remain, *ig1, *ig2;
6788         ig1 = ig2 = NULL;
6789         remain = IntGroupNewFromIntGroup(remove);
6790         if (remain == NULL)
6791                 status = -1;
6792         else
6793                 status = IntGroupReverse(remain, 0, mp->natoms);
6794         if (status == 0) {
6795                 ig1 = IntGroupNew();
6796                 if (ig1 == NULL)
6797                         status = -1;
6798                 else
6799                         status = IntGroupDifference(selection, remove, ig1);
6800         }
6801         if (status == 0) {
6802                 ig2 = IntGroupNew();
6803                 if (ig2 == NULL)
6804                         status = -1;
6805                 else
6806                         status = IntGroupDeconvolute(ig1, remain, ig2);
6807         }
6808         if (remain != NULL)
6809                 IntGroupRelease(remain);
6810         if (ig1 != NULL)
6811                 IntGroupRelease(ig1);
6812         if (status == 0)
6813                 return ig2;
6814         else {
6815                 if (ig2 != NULL)
6816                         IntGroupRelease(ig2);
6817                 return NULL;
6818         }
6819 }
6820
6821 #pragma mark ====== Atom Equivalence ======
6822
6823 struct sEqList {
6824         int i[2];
6825         struct sEqList *next;
6826         struct sEqList *link;
6827 };
6828
6829 static struct sEqList *sListBase = NULL;
6830 static struct sEqList *sListFree = NULL;
6831
6832 static struct sEqList *
6833 sAllocEqList(void)
6834 {
6835         struct sEqList *lp;
6836         if (sListFree != NULL) {
6837                 lp = sListFree;
6838                 sListFree = lp->next;
6839                 lp->i[0] = lp->i[1] = 0;
6840                 lp->next = NULL;
6841                 return lp;
6842         }
6843         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6844         lp->link = sListBase;
6845         sListBase = lp;
6846         return lp;
6847 }
6848
6849 static void
6850 sFreeEqList(struct sEqList *list)
6851 {
6852         list->next = sListFree;
6853         sListFree = list;
6854 }
6855
6856 static void
6857 sDeallocateEqLists(void)
6858 {
6859         struct sEqList *lp, *lp_link;
6860         for (lp = sListBase; lp != NULL; lp = lp_link) {
6861                 lp_link = lp->link;
6862                 free(lp);
6863         }
6864         sListBase = NULL;
6865         sListFree = NULL;
6866 }
6867
6868 static int
6869 sExistInEqList(int i, int idx, struct sEqList *list)
6870 {
6871         while (list != NULL) {
6872                 if (list->i[idx] == i)
6873                         return 1;
6874                 list = list->next;
6875         }
6876         return 0;
6877 }
6878
6879 static struct sEqList *
6880 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6881 {
6882         Atom *api, *apj;
6883         struct sEqList *list1, *list2;
6884         Int ii, jj, ni, nj, *cpi, *cpj;
6885         api = ATOM_AT_INDEX(mol->atoms, i);
6886         apj = ATOM_AT_INDEX(mol->atoms, j);
6887         if (api->atomicNumber != apj->atomicNumber)
6888                 return NULL;
6889         list1 = sAllocEqList();
6890         if (list1 == NULL)
6891                 return NULL;
6892         list1->i[0] = i;
6893         list1->i[1] = j;
6894         list1->next = list;
6895         if (i == j || (db[i] != NULL && db[i] == db[j]))
6896                 return list1;
6897         cpi = AtomConnectData(&api->connect);
6898         cpj = AtomConnectData(&apj->connect);
6899         for (ni = 0; ni < api->connect.count; ni++) {
6900                 ii = cpi[ni];
6901                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6902                         continue;
6903                 if (sExistInEqList(ii, 0, list1))
6904                         continue;
6905                 list2 = NULL;
6906                 for (nj = 0; nj < apj->connect.count; nj++) {
6907                         jj = cpj[nj];
6908                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6909                                 continue;
6910                         if (sExistInEqList(jj, 1, list1))
6911                                 continue;
6912                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6913                         if (list2 != NULL)
6914                                 break;
6915                 }
6916                 if (list2 == NULL) {
6917                         sFreeEqList(list1);
6918                         return NULL;    /*  No equivalent to ii  */
6919                 }
6920                 list1 = list2;      /*  ii is OK, try next  */
6921         }
6922         return list1;
6923 }
6924
6925 int
6926 sDBInclude(Int *ip, int i)
6927 {
6928         int j;
6929         if (ip == NULL)
6930                 return -1;
6931         for (j = ip[0] - 1; j >= 0; j--) {
6932                 if (ip[j] == i)
6933                         return j;
6934         }
6935         return -1;
6936 }
6937
6938 Int *
6939 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6940 {
6941         Int **db;  /*  List of equivalents for each atom  */
6942         Int *ip, *result;
6943         Atom *api, *apj, *apk;
6944         Int *cpi, *cpj, *ibuf, nibuf;
6945         int i, j, k, ii, jj, kk;
6946         if (mol == NULL || mol->natoms == 0)
6947                 return NULL;
6948         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6949         ibuf = NULL;
6950         nibuf = 0;
6951
6952         /*  Find the equivalent univalent atoms  */
6953         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6954                 if (api->connect.count < 2)
6955                         continue;
6956                 cpi = AtomConnectData(&api->connect);
6957                 for (j = 0; j < api->connect.count; j++) {
6958                         Int n;
6959                         n = 0;
6960                         jj = cpi[j];
6961                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6962                                 continue;
6963                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6964                         n++;
6965                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6966                         if (apj->connect.count != 1 || db[jj] != NULL)
6967                                 continue;
6968                         cpj = AtomConnectData(&apj->connect);
6969                         for (k = j + 1; k < api->connect.count; k++) {
6970                                 kk = cpj[k];
6971                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6972                                         continue;
6973                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6974                                 if (apk->connect.count != 1 || db[kk] != NULL)
6975                                         continue;
6976                                 if (apj->atomicNumber == apk->atomicNumber) {
6977                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6978                                         n++;
6979                                 }
6980                         }
6981                         if (n > 1) {
6982                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6983                                 if (ip == NULL)
6984                                         return NULL;
6985                                 ip[0] = n;
6986                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6987                                 for (k = 0; k < n; k++)
6988                                         db[ip[k + 1]] = ip;
6989                         }
6990                 }
6991         }
6992         if (ibuf != NULL) {
6993                 free(ibuf);
6994                 ibuf = NULL;
6995         }
6996         
6997         /*  Try matching (i,j) pair  */
6998         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6999                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
7000                         continue;
7001                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
7002                         struct sEqList *list;
7003                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
7004                                 continue;
7005                         if (api->atomicNumber != apj->atomicNumber)
7006                                 continue;  /*  Different elements do not match  */
7007                         if (db[i] != NULL && db[i] == db[j])
7008                                 continue;  /*  Already equivalent  */
7009                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
7010                         if (list == NULL)
7011                                 continue;  /*  (i,j) do not match  */
7012                         while (list != NULL) {
7013                                 ii = list->i[0];
7014                                 jj = list->i[1];
7015                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
7016                                         /*  Merge db[ii] and db[jj]  */
7017                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
7018                                         ip = (Int *)calloc(sizeof(Int), k + 1);
7019                                         if (ip == NULL)
7020                                                 return NULL;  /*  Out of memory  */
7021                                         if (db[ii] == NULL) {
7022                                                 ip[1] = ii;
7023                                                 k = 2;
7024                                         } else {
7025                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
7026                                                 k = db[ii][0] + 1;
7027                                         }
7028                                         if (db[jj] == NULL) {
7029                                                 ip[k++] = jj;
7030                                         } else {
7031                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
7032                                                 k += db[jj][0];
7033                                         }
7034                                         ip[0] = k - 1;
7035                                         /*  Free old ones  */
7036                                         if (db[ii] != NULL)
7037                                                 free(db[ii]);
7038                                         if (db[jj] != NULL)
7039                                                 free(db[jj]);
7040                                         for (k = 0; k < ip[0]; k++)
7041                                                 db[ip[k + 1]] = ip;
7042                                         if (0) {
7043                                                 /*  For debug  */
7044                                                 printf("(%d,%d) matched: ", ii, jj);
7045                                                 for (k = 0; k < ip[0]; k++) {
7046                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
7047                                                 }
7048                                                 printf("]\n");
7049                                         }
7050                                 }
7051                                 list = list->next;
7052                         }
7053                 }
7054         }
7055         
7056         /*  Record the equivalent atoms with the lowest index for each atom  */
7057         result = (Int *)calloc(sizeof(Int), mol->natoms);
7058         for (i = 0; i < mol->natoms; i++)
7059                 result[i] = -1;
7060         for (i = 0; i < mol->natoms; i++) {
7061                 if (result[i] >= 0 || (ip = db[i]) == NULL)
7062                         continue;
7063                 k = mol->natoms;
7064                 for (j = 0; j < ip[0]; j++) {
7065                         kk = ip[j + 1];
7066                         if (kk < k)
7067                                 k = kk;
7068                 }
7069                 for (j = 0; j < ip[0]; j++) {
7070                         result[ip[j + 1]] = k;
7071                         db[ip[j + 1]] = NULL;
7072                 }
7073                 free(ip);
7074         }
7075         sDeallocateEqLists();
7076         return result;
7077 }
7078
7079 #pragma mark ====== Symmetry expansion ======
7080
7081 int
7082 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
7083 {
7084         Transform t;
7085         if (mp == NULL || mp->cell == NULL)
7086                 return -1;
7087         if (symop.sym >= mp->nsyms && symop.sym != 0)
7088                 return -2;
7089         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
7090         (*tf)[9] += symop.dx;
7091         (*tf)[10] += symop.dy;
7092         (*tf)[11] += symop.dz;
7093         if (is_cartesian) {
7094                 TransformMul(t, *tf, mp->cell->rtr);
7095                 TransformMul(*tf, mp->cell->tr, t);
7096         }
7097         return 0;
7098 }
7099
7100 int
7101 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
7102 {
7103         Transform t;
7104         int i, j, n[3];
7105         if (mp == NULL || mp->cell == NULL)
7106                 return -1;
7107         if (is_cartesian) {
7108                 TransformMul(t, tf, mp->cell->tr);
7109                 TransformMul(t, mp->cell->rtr, t);
7110         } else {
7111                 memmove(t, tf, sizeof(Transform));
7112         }
7113         for (i = 0; i < mp->nsyms || i == 0; i++) {
7114                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
7115                 for (j = 0; j < 9; j++) {
7116                         if (fabs((*tp)[j] - t[j]) > 1e-4)
7117                                 break;
7118                 }
7119                 if (j == 9) {
7120                         for (j = 9; j < 12; j++) {
7121                                 double f1 = t[j] - (*tp)[j];
7122                                 double f2 = floor(f1 + 0.5);
7123                                 if (fabs(f1 - f2) > 1e-4)
7124                                         break;
7125                                 n[j - 9] = f2;
7126                         }
7127                         if (j == 12) {
7128                                 /*  Found  */
7129                                 symop->sym = i;
7130                                 symop->dx = n[0];
7131                                 symop->dy = n[1];
7132                                 symop->dz = n[2];
7133                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
7134                                 return 0;
7135                         }
7136                 }
7137         }
7138         return -3;  /*  Not found  */
7139 }
7140
7141 int
7142 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
7143 {
7144         if (mp == NULL)
7145                 return 1;
7146         if (symop.sym >= mp->nsyms && symop.sym != 0)
7147                 return 2;
7148         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
7149                 TransformVec(vpout, mp->cell->rtr, vpin);
7150                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
7151                 vpout->x += symop.dx;
7152                 vpout->y += symop.dy;
7153                 vpout->z += symop.dz;
7154                 TransformVec(vpout, mp->cell->tr, vpout);
7155         } else {
7156                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
7157                 vpout->x += symop.dx;
7158                 vpout->y += symop.dy;
7159                 vpout->z += symop.dz;
7160         }
7161         return 0;
7162 }
7163
7164 /*  Add expanded atoms. Returns the number of newly created atoms.
7165         If indices is non-NULL, it should be an array of Int with at least 
7166         IntGroupGetCount(group) entries, and on return it contains the
7167     indices of the expanded atoms (may be existing atoms if the expanded
7168     atoms are already present)
7169     If allowOverlap is non-zero, then the new atom is created even when the
7170     coordinates coincide with the some other atom (special position) of the
7171     same element; otherwise, such atom will not be created and the existing
7172     atom is returned in indices[].  */
7173 int
7174 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7175 {
7176         int i, n, n0, n1, n2, base, count, *table;
7177         Atom *ap;
7178         IntGroupIterator iter;
7179         Transform tr, t1;
7180         Symop symop1;
7181         Atom *ap2;
7182         Vector nr, dr;
7183         
7184         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7185                 return -1;
7186         if (symop.sym != 0 && symop.sym >= mp->nsyms)
7187                 return -2;
7188
7189         /*  Create atoms, with avoiding duplicates  */
7190         n0 = n1 = mp->natoms;
7191         table = (int *)malloc(sizeof(int) * n0);
7192         if (table == NULL)
7193                 return -3;
7194         for (i = 0; i < n0; i++)
7195                 table[i] = -1;
7196         IntGroupIteratorInit(group, &iter);
7197         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7198         __MoleculeLock(mp);
7199         for (i = 0; i < count; i++) {
7200                 n = IntGroupIteratorNext(&iter);
7201                 ap = ATOM_AT_INDEX(mp->atoms, n);
7202                 if (SYMOP_ALIVE(ap->symop)) {
7203                         /*  Calculate the cumulative symop  */
7204                         Transform tr2;
7205                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7206                         TransformMul(tr2, tr, t1);
7207                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7208                                 if (indices != NULL)
7209                                         indices[i] = -1;
7210                                 continue;  /*  Skip this atom  */
7211                         }
7212                         base = ap->symbase;
7213                 } else {
7214                         symop1 = symop;
7215                         base = n;
7216                 }
7217
7218                 /*  Calculate the expande position  */
7219                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7220                 
7221                 /*  Is this expansion already present?  */
7222                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7223                         /*  Symmetry operation and the base atom are the same  */
7224                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7225                                 break;
7226                         /*  Atomic number and the position are the same  */
7227                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7228                                 VecSub(dr, ap2->r, nr);
7229                                 if (VecLength2(dr) < 1e-6)
7230                                         break;
7231                         }
7232                 }
7233                 if (n2 < n0) {
7234                         /*  If yes, then skip it  */
7235                         if (indices != NULL)
7236                                 indices[i] = n2;
7237                         continue;
7238                 } else {
7239                         /*  Create a new atom  */
7240                         Atom newAtom;
7241                         AtomDuplicate(&newAtom, ap);
7242                         MoleculeCreateAnAtom(mp, &newAtom, -1);
7243                         AtomClean(&newAtom);
7244                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7245                         ap2->r = nr;
7246                         ap2->symbase = base;
7247                         ap2->symop = symop1;
7248                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7249                         table[n] = n1;  /*  The index of the new atom  */
7250                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
7251                         if (indices != NULL)
7252                                 indices[i] = n1;
7253                         n1++;
7254                 }
7255         }
7256         IntGroupIteratorRelease(&iter);
7257
7258         /*  Create bonds  */
7259         for (i = n0; i < n1; i++) {
7260                 Int b[2], j;
7261                 ap = ATOM_AT_INDEX(mp->atoms, i);
7262                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7263                         /*  For each connected atom, look for the transformed atom  */
7264                         Int *cp;
7265                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7266                         cp = AtomConnectData(&ap2->connect);
7267                         n2 = ap2->connect.count;
7268                         for (n = 0; n < n2; n++) {
7269                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7270                                 nr = apn->r;
7271                                 TransformVec(&nr, tr, &nr);
7272                                 /*  Look for the bonded atom transformed by ap->symop  */
7273                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7274                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7275                                                 break;
7276                                         VecSub(dr, nr, ap2->r);
7277                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7278                                                 break;
7279                                 }
7280                                 if (j < mp->natoms) {
7281                                         /*  Bond i-j is created  */
7282                                         b[0] = i;
7283                                         b[1] = j;
7284                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7285                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
7286                                 }
7287                         }
7288                 }
7289         }
7290         mp->needsMDRebuild = 1;
7291         __MoleculeUnlock(mp);
7292         free(table);
7293         return n1 - n0;  /*  The number of added atoms  */
7294 }
7295
7296 /*  Recalculate the coordinates of symmetry expanded atoms.
7297     (Also recalculate the positions of pi-anchor atoms)
7298         Returns the number of affected atoms.
7299     If group is non-NULL, only the expanded atoms whose base atoms are in the
7300     given group are considered.
7301         If groupout and vpout are non-NULL, the indices of the affected atoms
7302         and the original positions are returned (for undo operation).
7303         The pointers returned in *groupout and *vpout must be released and 
7304         free()'ed by the caller  */
7305 int
7306 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7307 {
7308         int i, count;
7309         Atom *ap, *bp;
7310         Vector nr, dr;
7311         IntGroup *ig = NULL;
7312         Vector *vp = NULL;
7313         
7314         if (mp == NULL || mp->natoms == 0)
7315                 return 0;
7316
7317         __MoleculeLock(mp);
7318         count = 0;
7319         if (mp->nsyms != 0) {
7320                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7321                         if (!SYMOP_ALIVE(ap->symop))
7322                                 continue;
7323                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7324                                 continue;
7325                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7326                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7327                         VecSub(dr, nr, ap->r);
7328                         if (VecLength2(dr) < 1e-20)
7329                                 continue;
7330                         if (groupout != NULL) {
7331                                 if (ig == NULL) {
7332                                         ig = IntGroupNew();
7333                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7334                                 }
7335                                 vp[count] = ap->r;
7336                                 IntGroupAdd(ig, i, 1);
7337                         }
7338                         ap->r = nr;
7339                         count++;
7340                 }
7341         }
7342         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7343                 Int *ip, j, n;
7344                 if (ap->anchor == NULL)
7345                         continue;
7346                 if (group != NULL) {
7347                         if (IntGroupLookup(group, i, NULL) == 0) {
7348                                 n = ap->anchor->connect.count;
7349                                 ip = AtomConnectData(&(ap->anchor->connect));
7350                                 for (j = 0; j < n; j++) {
7351                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
7352                                                 break;
7353                                 }
7354                                 if (j == n)
7355                                         continue;  /*  This pi-anchor should not be modified  */
7356                         }
7357                 }
7358                 nr = ap->r;
7359                 MoleculeCalculatePiAnchorPosition(mp, i);
7360                 VecSub(dr, nr, ap->r);
7361                 if (VecLength2(dr) < 1e-20) {
7362                         ap->r = nr;  /*  No change  */
7363                         continue;
7364                 }
7365                 if (groupout != NULL) {
7366                         if (ig == NULL) {
7367                                 ig = IntGroupNew();
7368                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7369                         }
7370                         vp[count] = nr;
7371                         IntGroupAdd(ig, i, 1);
7372                 }
7373                 count++;
7374         }
7375         mp->needsMDCopyCoordinates = 1;
7376         __MoleculeUnlock(mp);
7377
7378         if (count > 0) {
7379                 if (groupout != NULL && vpout != NULL) {
7380                         *groupout = ig;
7381                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7382                 } else {
7383                         IntGroupRelease(ig);
7384                         free(vp);
7385                 }
7386         } else {
7387                 if (groupout != NULL && vpout != NULL) {
7388                         *groupout = NULL;
7389                         *vpout = NULL;
7390                 }
7391         }
7392         return count;
7393 }
7394
7395 #pragma mark ====== Show/hide atoms ======
7396
7397 static void
7398 sMoleculeNotifyChangeAppearance(Molecule *mp)
7399 {
7400         /*  TODO: Finer control of notification types may be necessary  */
7401         MoleculeCallback_notifyModification(mp, 0);
7402 }
7403
7404
7405 static void
7406 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7407 {
7408         int i;
7409         if (mp == NULL || mp->selection == NULL)
7410                 return;
7411         for (i = 0; i < mp->natoms; i++) {
7412                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7413                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7414                         IntGroupRemove(mp->selection, i, 1);
7415         }
7416         sMoleculeNotifyChangeAppearance(mp);
7417 }
7418
7419 int
7420 MoleculeShowAllAtoms(Molecule *mp)
7421 {
7422         int i;
7423         if (mp == NULL)
7424                 return 0;
7425         for (i = 0; i < mp->natoms; i++) {
7426                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7427                 ap->exflags &= ~kAtomHiddenFlag;
7428         }
7429         sMoleculeNotifyChangeAppearance(mp);
7430         return 1;
7431 }
7432
7433 int
7434 MoleculeShowReverse(Molecule *mp)
7435 {
7436         int i;
7437         if (mp == NULL)
7438                 return 0;
7439         for (i = 0; i < mp->natoms; i++) {
7440                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7441                 ap->exflags ^= kAtomHiddenFlag;
7442         }
7443         sMoleculeUnselectHiddenAtoms(mp);
7444         sMoleculeNotifyChangeAppearance(mp);
7445         return 1;
7446 }
7447
7448 int
7449 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7450 {
7451         int i;
7452         if (mp == NULL || ig == NULL)
7453                 return 0;
7454         for (i = 0; i < mp->natoms; i++) {
7455                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7456                 if (ap->exflags & kAtomHiddenFlag)
7457                         continue;  /*  Already hidden  */
7458                 if (IntGroupLookupPoint(ig, i) >= 0)
7459                         ap->exflags |= kAtomHiddenFlag;
7460         }
7461         sMoleculeUnselectHiddenAtoms(mp);
7462         sMoleculeNotifyChangeAppearance(mp);
7463         return 1;
7464 }
7465
7466 #pragma mark ====== Reversible Editing ======
7467
7468 /*
7469 static void
7470 sMoleculeNotifyModification(Molecule *mp)
7471 {
7472         **  TODO: Finer control of notification types may be necessary  **
7473         MoleculeCallback_notifyModification(mp, 0);
7474 }
7475 */
7476
7477 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7478 int
7479 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7480 {
7481         int n1, n2, n3, i;
7482         if (where == NULL) {
7483                 /*  Append the new objects at the end  */
7484                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7485                 return 0;
7486         }
7487         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
7488         n2 = nobjs;                    /*  Position to get old object  */
7489         n3 = n1 + n2;                  /*  Position to place new/old object  */
7490         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7491                 int start = IntGroupGetStartPoint(where, i);
7492                 int end = IntGroupGetEndPoint(where, i);
7493                 if (end < n3) {
7494                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
7495                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7496                         n2 = end - (n3 - n2);
7497                         n3 = end;
7498                 }
7499                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
7500                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7501                 n3 -= end - start;
7502                 n1 -= end - start;
7503         }
7504         return 0;
7505 }
7506
7507 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7508 int
7509 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7510 {
7511         int n1, n2, n3, start, end, i;
7512         if (where == NULL || IntGroupGetCount(where) == 0)
7513                 return 0;  /*  No operation  */
7514         if (objs == NULL || nobjs == 0)
7515                 return 1;  /*  Bad argument  */
7516         n1 = 0;  /*  Position to move remaining elements to */
7517         n2 = 0;  /*  Position to move remaining elements from  */
7518         n3 = 0;  /*  Position to move removed elements to  */
7519         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7520                 end = IntGroupGetEndPoint(where, i);
7521                 if (n2 < start) {
7522                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
7523                         if (n1 < n2)
7524                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7525                         n1 += start - n2;
7526                         n2 = start;
7527                 }
7528                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
7529                 if (clip != NULL)
7530                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7531                 n3 += (end - start);
7532                 n2 += (end - start);
7533         }
7534         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
7535         if (nobjs > n2)
7536                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7537         return 0;
7538 }
7539
7540 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7541 int
7542 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7543 {
7544         int n1, start, end, i;
7545         if (objs == NULL || where == NULL)
7546                 return 1;  /*  Bad argument  */
7547         n1 = 0;  /*  Position to move removed elements to  */
7548         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7549                 end = IntGroupGetEndPoint(where, i);
7550                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
7551                 if (clip != NULL)
7552                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7553                 n1 += (end - start);
7554         }
7555         return 0;
7556 }
7557
7558 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7559    (Use AtomDuplicate() first) */
7560 int
7561 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7562 {
7563     Atom *ap1, *api;
7564         int i;
7565         if (mp == NULL || ap == NULL || mp->noModifyTopology)
7566                 return -1;
7567         __MoleculeLock(mp);
7568         if (pos < 0 || pos >= mp->natoms)
7569                 pos = mp->natoms;
7570         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7571         if (ap1 == NULL)
7572                 goto error;  /*  Out of memory  */
7573         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7574         if (pos < mp->natoms - 1) {
7575                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7576         }
7577         if (AtomDuplicate(ap1, ap) == NULL) {
7578                 /*  Cannot duplicate: restore the original state  */
7579                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7580                 mp->natoms--;
7581                 goto error;
7582         }
7583         ap1->connect.count = 0;
7584         if (ap1->resSeq >= mp->nresidues)
7585                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7586         if (ap1->resName[0] == 0)
7587           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7588         if (ap1->segName[0] == 0)
7589           strncpy(ap1->segName, "MAIN", 4);
7590         if (pos < mp->natoms - 1) {
7591                 /*  Renumber the connect table, bonds, angles, etc. */
7592                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7593                         int j;
7594                         Int *cp;
7595                         cp = AtomConnectData(&api->connect);
7596                         for (j = 0; j < api->connect.count; j++) {
7597                                 if (cp[j] >= pos)
7598                                         cp[j]++;
7599                         }
7600                         if (api->anchor != NULL) {
7601                                 cp = AtomConnectData(&api->anchor->connect);
7602                                 for (j = 0; j < api->anchor->connect.count; j++) {
7603                                         if (cp[j] >= pos)
7604                                                 cp[j]++;
7605                                 }
7606                         }
7607                 }
7608                 for (i = 0; i < mp->nbonds * 2; i++) {
7609                         if (mp->bonds[i] >= pos)
7610                                 mp->bonds[i]++;
7611                 }
7612                 for (i = 0; i < mp->nangles * 3; i++) {
7613                         if (mp->angles[i] >= pos)
7614                                 mp->angles[i]++;
7615                 }
7616                 for (i = 0; i < mp->ndihedrals * 4; i++) {
7617                         if (mp->dihedrals[i] >= pos)
7618                                 mp->dihedrals[i]++;
7619                 }
7620                 for (i = 0; i < mp->nimpropers * 4; i++) {
7621                         if (mp->impropers[i] >= pos)
7622                                 mp->impropers[i]++;
7623                 }
7624         }
7625         mp->nframes = -1;  /*  Should be recalculated later  */
7626         MoleculeIncrementModifyCount(mp);
7627         mp->needsMDRebuild = 1;
7628         __MoleculeUnlock(mp);
7629         return pos;
7630 error:
7631         __MoleculeUnlock(mp);
7632         return -1;
7633 }
7634
7635 #if defined(DEBUG)
7636
7637 static int s_error_count;
7638
7639 static int
7640 s_fprintf(FILE *fp, const char *fmt, ...)
7641 {
7642         va_list va;
7643         va_start(va, fmt);
7644         s_error_count++;
7645         return vfprintf(fp, fmt, va);
7646 }
7647
7648 int
7649 MoleculeCheckSanity(Molecule *mol)
7650 {
7651         const char *fail = "Sanity check failure";
7652         Int i, j, *ip, c[4];
7653         Atom *ap;
7654         s_error_count = 0;
7655         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7656                 if (ap->resSeq >= mol->nresidues)
7657                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7658                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7659                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7660                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7661                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7662                 ip = AtomConnectData(&ap->connect);
7663                 for (j = 0; j < ap->connect.count; j++) {
7664                         if (ip[j] < 0 || ip[j] >= mol->natoms)
7665                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7666                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7667                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7668                 }
7669         }
7670         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7671                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7672                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7673                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7674                         s_fprintf(stderr, "%s: bond %d %d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[0], ip[1]);
7675         }
7676         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7677                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7678                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7679                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7680                 if (c[0] == 0)
7681                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0]);
7682                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7683                 if (c[1] == 0)
7684                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[2]);
7685                 if (c[0] == 2 && c[1] == 2)
7686                         s_fprintf(stderr, "%s: angle %d %d-%d-%d but bonds %d-%d and %d-%d are both virtual\n", fail, i, ip[0], ip[1], ip[2], ip[1], ip[0], ip[1], ip[2]);
7687         }
7688         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7689                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
7690                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7691                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7692                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7693                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7694                 if (c[0] == 0)
7695                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[0]);
7696                 if (c[1] == 0)
7697                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[1], ip[2]);
7698                 if (c[2] == 0)
7699                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
7700         }
7701         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7702                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms || ip[3] < 0 || ip[3] >= mol->natoms)
7703                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7704                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7705                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7706                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7707                 if (c[0] == 0)
7708                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[0]);
7709                 if (c[1] == 0)
7710                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[1]);
7711                 if (c[2] == 0)
7712                         s_fprintf(stderr, "%s: improper %d %d-%d-%d-%d but atom %d has no connect %d\n", fail, i, ip[0], ip[1], ip[2], ip[3], ip[2], ip[3]);
7713         }
7714         return s_error_count;
7715 }
7716 #endif
7717
7718 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7719 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7720 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7721 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7722     separately by other undo actions.  */
7723 int
7724 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7725 {
7726         Int nsrc, ndst;
7727         Int i, j, n1, n2, n3, n4, *cp;
7728         Int *new2old, *old2new;
7729         IntGroup *ig;
7730         Atom *ap;
7731         MolAction *act;
7732         
7733         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7734                 return 0;  /*  Do nothing  */
7735
7736         if (dst->noModifyTopology)
7737                 return 1;  /*  Prohibited operation  */
7738
7739         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7740                 return 1;  /*  Bad parameter  */
7741
7742         if (nactions != NULL)
7743                 *nactions = 0;
7744         if (actions != NULL)
7745                 *actions = NULL;
7746         act = NULL;
7747
7748         __MoleculeLock(dst);
7749
7750         nsrc = src->natoms;
7751         ndst = dst->natoms;
7752         if (resSeqOffset < 0)
7753                 resSeqOffset = 0;
7754
7755         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7756             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7757         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7758         if (new2old == NULL)
7759                 goto panic;
7760         old2new = new2old + ndst + nsrc;
7761         n1 = 0;  /*  dst index  */
7762         n2 = 0;  /*  src index  */
7763         n3 = 0;  /*  "merged" index  */
7764         i = 0;
7765         while (n1 < ndst || n2 < nsrc) {
7766                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7767                         n4 = ndst - n1;
7768                 else n4 -= n3;
7769                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7770                 for (j = 0; j < n4; j++) {
7771                         old2new[n1 + j] = n3 + j;
7772                         new2old[n3 + j] = n1 + j;
7773                 }
7774                 n3 += n4;
7775                 n1 += n4;
7776                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7777                         n4 = nsrc - n2;
7778                 /*  n4 elements from src[n2] will go to merged[n3]  */
7779                 for (j = 0; j < n4; j++) {
7780                         old2new[ndst + n2 + j] = n3 + j;
7781                         new2old[n3 + j] = ndst + n2 + j;
7782                 }
7783                 n3 += n4;
7784                 n2 += n4;
7785                 i++;
7786         }
7787
7788         /*  Expand the destination array  */
7789         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7790                 goto panic;
7791
7792         /*  Move the atoms  */
7793         if (where == NULL) {
7794                 /*  Duplicate atoms to the end of the destination array  */
7795                 for (i = 0; i < nsrc; i++) {
7796                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7797                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7798                                 goto panic;
7799                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7800                                 AtomConnectResize(&ap->connect, 0);
7801                 }
7802         } else {
7803                 /*  Duplicate to a temporary storage and then insert  */
7804                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7805                 if (tempatoms == NULL)
7806                         goto panic;
7807                 for (i = 0; i < nsrc; i++) {
7808                         ap = ATOM_AT_INDEX(tempatoms, i);
7809                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7810                                 goto panic;
7811                         if (forUndo)  /*  See above  */
7812                                 AtomConnectResize(&ap->connect, 0);                             
7813                 }
7814                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7815                         goto panic;
7816                 free(tempatoms);
7817         }
7818         dst->natoms = ndst + nsrc;
7819
7820         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7821         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7822                 if (new2old[i] < ndst) {
7823                         /*  This atom is from dst  */
7824                         n1 = 0;
7825                 } else {
7826                         /*  This atom is from src  */
7827                         n1 = ndst;  /*  Offset to the internal number  */
7828                         if (ap->resSeq != 0)
7829                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7830                 }
7831                 cp = AtomConnectData(&ap->connect);
7832                 for (j = 0; j < ap->connect.count; j++)
7833                         cp[j] = old2new[cp[j] + n1];
7834                 if (SYMOP_ALIVE(ap->symop))
7835                         ap->symbase = old2new[ap->symbase + n1];
7836                 if (ap->anchor != NULL) {
7837                         cp = AtomConnectData(&ap->anchor->connect);
7838                         for (j = 0; j < ap->anchor->connect.count; j++)
7839                                 cp[j] = old2new[cp[j] + n1];
7840                 }
7841         }
7842         
7843         /*  Move the bonds, angles, dihedrals, impropers  */
7844         for (i = 0; i < 4; i++) {
7845                 Int *nitems, *nitems_src;
7846                 Int **items, **items_src;
7847                 Int nsize;  /*  Number of Ints in one element  */
7848                 switch (i) {
7849                         case 0:
7850                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7851                         case 1:
7852                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7853                         case 2:
7854                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7855                         case 3:
7856                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7857                 }
7858                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7859                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7860                 if (forUndo) {
7861                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7862                             by undo actions  */
7863                         n1 = *nitems;
7864                         n2 = 0;
7865                 } else {
7866                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7867                         n1 = *nitems;
7868                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7869                         n2 = *nitems_src;
7870                         /*  Expand the array  */
7871                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7872                                 goto panic;
7873                         /*  Copy the items  */
7874                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7875                         if (i == 0) {
7876                                 /*  Copy the bond order info if present */
7877                                 Int nn1 = dst->nbondOrders;
7878                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7879                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7880                                                 goto panic;
7881                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7882                                         if (src->bondOrders != NULL)
7883                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7884                                 }
7885                         }
7886                 }
7887                 /*  Renumber  */
7888                 for (j = 0; j < n1 * nsize; j++)
7889                         (*items)[j] = old2new[(*items)[j]];
7890                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7891                         (*items)[j] = old2new[(*items)[j] + ndst];
7892                 if (forUndo == 0 && actions != NULL) {
7893                         ig = IntGroupNewWithPoints(n1, n2, -1);
7894                         switch (i) {
7895                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7896                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7897                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7898                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7899                         }
7900                         IntGroupRelease(ig);
7901                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7902                         act = NULL;
7903                 }
7904         }
7905         
7906         /*  Renumber existing parameters  */
7907         if (dst->par != NULL) {
7908                 int type;
7909                 for (type = kFirstParType; type <= kLastParType; type++) {
7910                         UnionPar *up1;
7911                         n1 = ParameterGetCountForType(dst->par, type);
7912                         for (i = 0; i < n1; i++) {
7913                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7914                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7915                         }
7916                 }
7917         }
7918
7919         /*  Merge parameters from src  */
7920         if (src->par != NULL && forUndo == 0) {
7921                 UnionPar *up1, *up2;
7922                 int type;
7923                 if (dst->par == NULL)
7924                         dst->par = ParameterNew();
7925                 else {
7926                         /*  Renumber existing parameters  */
7927                         for (type = kFirstParType; type <= kLastParType; type++) {
7928                                 n1 = ParameterGetCountForType(dst->par, type);
7929                                 for (i = 0; i < n1; i++) {
7930                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7931                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7932                                 }
7933                         }
7934                 }
7935                 ig = IntGroupNew();
7936                 for (type = kFirstParType; type <= kLastParType; type++) {
7937                         n1 = ParameterGetCountForType(src->par, type);
7938                         n2 = ParameterGetCountForType(dst->par, type);
7939                         if (n1 == 0)
7940                                 continue;
7941                         /*  Determine which parameter should be copied from src to dst  */
7942                         for (i = 0; i < n1; i++) {
7943                                 UInt types[4];
7944                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7945                                 n3 = ParameterGetAtomTypes(type, up1, types);
7946                                 for (j = 0; j < n3; j++) {
7947                                         /*  If it includes explicit atom index, then it should be copied  */
7948                                         if (types[j] < kAtomTypeMinimum) {
7949                                                 IntGroupAdd(ig, i, 1);
7950                                                 break;
7951                                         }
7952                                 }
7953                                 if (j == n3) {
7954                                         for (j = 0; j < n2; j++) {
7955                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7956                                                 if (ParameterCompare(up1, up2, type))
7957                                                         break;
7958                                         }
7959                                         if (j >= n2)
7960                                                 /*  This is an unknown parameter; should be copied  */
7961                                                 IntGroupAdd(ig, i, 1);
7962                                 }
7963                         }
7964                         n1 = IntGroupGetCount(ig);
7965                         if (n1 == 0)
7966                                 continue;
7967                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7968                         if (up1 == NULL)
7969                                 goto panic;
7970                         /*  Copy parameters and renumber indices if necessary  */
7971                         for (i = j = 0; i < n1; i++) {
7972                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7973                                 if (up2 == NULL)
7974                                         continue;
7975                                 up1[j] = *up2;
7976                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7977                                 j++;
7978                         }
7979                         /*  Merge parameters  */
7980                         IntGroupClear(ig);
7981                         IntGroupAdd(ig, n2, j);
7982                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7983                                 goto panic;
7984                         if (actions != NULL) {
7985                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7986                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7987                                 act = NULL;
7988                         }
7989                         IntGroupClear(ig);
7990                         free(up1);
7991                 }
7992                 IntGroupRelease(ig);
7993         }
7994         
7995         /*  Copy the residues if necessary  */
7996         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7997             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7998                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7999         if (forUndo == 0) {
8000                 n1 = dst->nresidues;
8001                 if (1 + resSeqOffset < n1) {
8002                         n2 = n1;
8003                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
8004                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
8005                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
8006                                 goto panic;
8007                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
8008                         if (nactions != NULL) {
8009                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
8010                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8011                                 act = NULL;
8012                         }
8013                 }
8014         }
8015
8016         MoleculeCleanUpResidueTable(dst);
8017         
8018         free(new2old);
8019         dst->nframes = -1;  /*  Should be recalculated later  */
8020
8021         MoleculeIncrementModifyCount(dst);
8022         dst->needsMDRebuild = 1;
8023         __MoleculeUnlock(dst);
8024         return 0;
8025
8026   panic:
8027         __MoleculeUnlock(dst);
8028     Panic("Low memory while adding atoms");
8029         return 1;  /*  Not reached  */
8030 }
8031
8032 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
8033     (The nactions/actions array must be initialized by the caller)  */
8034 static int
8035 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
8036 {
8037         Int nsrc, ndst, nsrcnew;
8038         Int i, j, n1, n2, n3, n4, *cp;
8039         Int *new2old, *old2new;
8040         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
8041         Molecule *dst;
8042         Atom *ap, *dst_ap;
8043         UnionPar *up;
8044         MolAction *act;
8045
8046         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
8047                 /*  Do nothing  */
8048                 if (dstp != NULL)
8049                         *dstp = NULL;
8050                 return 0;
8051         }
8052         
8053         if (src->noModifyTopology && moveFlag)
8054                 return 1;  /*  Prohibit editing  */
8055
8056         if ((ndst = IntGroupGetCount(where)) > src->natoms)
8057                 return 1;  /*  Bad parameter  */
8058
8059         __MoleculeLock(src);
8060         
8061         act = NULL;
8062         
8063         nsrc = src->natoms;
8064         nsrcnew = nsrc - ndst;
8065         if (resSeqOffset < 0)
8066                 resSeqOffset = 0;
8067
8068         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
8069             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
8070         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
8071         if (new2old == NULL)
8072                 goto panic;
8073         old2new = new2old + nsrc;
8074         n1 = 0;  /*  src index  */
8075         n2 = 0;  /*  dst index  */
8076         n3 = 0;  /*  src index after "unmerge"  */
8077         i = 0;
8078         while (n1 < nsrc || n2 < ndst) {
8079                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
8080                         n4 = nsrc - n1;
8081                 else n4 -= n1;
8082                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
8083                 for (j = 0; j < n4; j++) {
8084                         old2new[n1 + j] = n3 + j;
8085                         new2old[n3 + j] = n1 + j;
8086                 }
8087                 n3 += n4;
8088                 n1 += n4;
8089                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
8090                         n4 = nsrc - n1;
8091                 /*  n4 elements from src[n1] will go to dst[n2]  */
8092                 for (j = 0; j < n4; j++) {
8093                         old2new[n1 + j] = nsrcnew + n2 + j;
8094                         new2old[nsrcnew + n2 + j] = n1 + j;
8095                 }
8096                 n1 += n4;
8097                 n2 += n4;
8098                 i++;
8099         }
8100
8101         /*  Atoms to remain in the source group  */
8102         if (moveFlag) {
8103                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
8104                 IntGroupRemoveIntGroup(remain_g, where);
8105         } else remain_g = NULL;
8106         
8107         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
8108         if (src->par != NULL) {
8109                 dst_par_g = IntGroupNew();
8110                 if (moveFlag)
8111                         remove_par_g = IntGroupNew();
8112                 else remove_par_g = NULL;
8113                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8114                         n2 = ParameterGetCountForType(src->par, n1);
8115                         if (n2 == 0)
8116                                 continue;
8117                         for (i = 0; i < n2; i++) {
8118                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8119                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
8120                                         /*  This parameter is to be copied to dst  */
8121                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
8122                                 }
8123                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
8124                                         /*  This parameter is to be removed  */
8125                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
8126                                 }
8127                         }
8128                 }
8129         } else dst_par_g = remove_par_g = NULL;
8130         
8131         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
8132             src anc dst  */
8133         if (moveFlag) {
8134                 Int ibufsize, *ibuf, flag_i, flag_j;
8135                 ibufsize = 8;
8136                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
8137                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8138                         if (ap->anchor == NULL)
8139                                 continue;
8140                         flag_i = (old2new[i] < nsrcnew);
8141                         cp = AtomConnectData(&ap->anchor->connect);
8142                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8143                                 flag_j = (old2new[cp[j]] < nsrcnew);
8144                                 if (flag_i == flag_j) {
8145                                         if (n1 >= ibufsize) {
8146                                                 ibufsize += 8;
8147                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
8148                                         }
8149                                         ibuf[n1++] = cp[j];
8150                                 }
8151                         }
8152                         if (n1 < j) {
8153                                 /*  Need to modify the pi anchor list  */
8154                                 if (n1 <= 1)
8155                                         n1 = 0;
8156                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
8157                         }
8158                 }
8159         }
8160         
8161         /*  Make a new molecule  */
8162         if (dstp != NULL) {
8163                 dst = MoleculeNew();
8164                 if (dst == NULL)
8165                         goto panic;
8166                 /*  Expand the destination array  */
8167                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8168                         goto panic;
8169                 dst_ap = dst->atoms;
8170         } else {
8171                 dst = NULL;
8172                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8173                 if (dst_ap == NULL)
8174                         goto panic;
8175         }
8176         
8177         /*  Move the atoms  */
8178         if (moveFlag) {
8179                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8180                         goto panic;
8181                 src->natoms = nsrcnew;
8182                 if (dst == NULL) {
8183                         /*  The atom record must be deallocated correctly  */
8184                         for (i = 0; i < ndst; i++)
8185                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8186                 }
8187         } else {
8188                 if (dst != NULL) {
8189                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8190                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8191                 }
8192         }
8193         
8194         if (dst == NULL) {
8195                 /*  The dummy destination array is no longer needed  */
8196                 free(dst_ap);
8197                 dst_ap = NULL;
8198         }
8199         
8200         /*  Renumber the atom indices in connect[] (src) */
8201         if (moveFlag) {
8202                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8203                         cp = AtomConnectData(&ap->connect);
8204                         for (j = n1 = 0; j < ap->connect.count; j++) {
8205                                 n2 = old2new[cp[j]];
8206                                 if (n2 < nsrcnew)
8207                                         cp[n1++] = n2;
8208                         }
8209                         AtomConnectResize(&ap->connect, n1);
8210                         if (ap->anchor != NULL) {
8211                                 cp = AtomConnectData(&ap->anchor->connect);
8212                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8213                                         n2 = old2new[cp[j]];
8214                                         if (n2 < nsrcnew)
8215                                                 cp[n1++] = n2;
8216                                 }
8217                                 if (n1 != ap->anchor->connect.count) {
8218                                         /*  This should not happen!!  */
8219                                         AtomConnectResize(&ap->anchor->connect, n1);
8220                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8221                                         if (n1 == 0) {
8222                                                 free(ap->anchor->coeffs);
8223                                                 free(ap->anchor);
8224                                                 ap->anchor = NULL;
8225                                         }
8226                                 }
8227                         }
8228                 }
8229         }
8230         
8231         /*  Renumber the atom indices in connect[] (dst)  */
8232         if (dst != NULL) {
8233                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8234                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8235                                 ap->resSeq -= resSeqOffset;
8236                         else ap->resSeq = 0;
8237                         cp = AtomConnectData(&ap->connect);
8238                         for (j = n1 = 0; j < ap->connect.count; j++) {
8239                                 n2 = old2new[cp[j]] - nsrcnew;
8240                                 if (n2 >= 0)
8241                                         cp[n1++] = n2;
8242                         }
8243                         AtomConnectResize(&ap->connect, n1);
8244                         if (ap->anchor != NULL) {
8245                                 cp = AtomConnectData(&ap->anchor->connect);
8246                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8247                                         n2 = old2new[cp[j]] - nsrcnew;
8248                                         if (n2 >= 0)
8249                                                 cp[n1++] = n2;
8250                                 }
8251                                 if (n1 != ap->anchor->connect.count) {
8252                                         /*  This can happen, and the anchor info is silently modified  */
8253                                         if (n1 <= 1) {
8254                                                 AtomConnectResize(&ap->anchor->connect, 0);
8255                                                 free(ap->anchor->coeffs);
8256                                                 free(ap->anchor);
8257                                                 ap->anchor = NULL;
8258                                         } else {
8259                                                 Double d;
8260                                                 AtomConnectResize(&ap->anchor->connect, n1);
8261                                                 d = 0.0;
8262                                                 for (j = 0; j < n1; j++)
8263                                                         d += ap->anchor->coeffs[j];
8264                                                 for (j = 0; j < n1; j++)
8265                                                         ap->anchor->coeffs[j] /= d;
8266                                                 MoleculeCalculatePiAnchorPosition(dst, i);
8267                                         }
8268                                 }
8269                         }
8270                 }
8271         }
8272
8273         /*  Separate the bonds, angles, dihedrals, impropers  */
8274         /*  TODO: Improper torsions should also be copied!  */
8275         move_g = IntGroupNew();
8276         if (move_g == NULL)
8277                 goto panic;
8278         for (i = 3; i >= 0; i--) {
8279                 Int *nitems, *nitems_dst;
8280                 Int **items, **items_dst;
8281                 Int nsize;  /*  Number of Ints in one element  */
8282                 unsigned char *counts;
8283                 del_g = IntGroupNew();
8284                 switch (i) {
8285                         case 0:
8286                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8287                         case 1:
8288                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8289                         case 2:
8290                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8291                         case 3:
8292                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8293                         default:
8294                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
8295                 }
8296                 if (dst != NULL) {
8297                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8298                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8299                 } else {
8300                         nitems_dst = NULL;
8301                         items_dst = NULL;
8302                 }
8303                 counts = (unsigned char *)calloc(1, *nitems);
8304                 /*  Find the entries that should be moved to dst  */
8305                 n2 = 0;
8306                 for (j = 0; j < *nitems * nsize; j++) {
8307                         n1 = old2new[(*items)[j]];
8308                         if (n1 >= nsrcnew)
8309                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
8310                 }
8311                 for (j = n2 = n3 = 0; j < *nitems; j++) {
8312                         if (counts[j] > 0) {
8313                                 /*  Remove from src  */
8314                                 n2++;
8315                                 if (IntGroupAdd(del_g, j, 1) != 0)
8316                                         goto panic;
8317                                 if (counts[j] == nsize) {
8318                                         /*  Move to dst  */
8319                                         n3++;
8320                                         if (IntGroupAdd(move_g, j, 1) != 0)
8321                                                 goto panic;
8322                                 }
8323                         }
8324                 }
8325                 if (n2 > 0) {
8326                         /*  Expand the destination array  */
8327                         if (items_dst != NULL && n3 > 0) {
8328                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8329                                         goto panic;
8330                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8331                                         goto panic;
8332                                 if (i == 0 && src->bondOrders != NULL) {
8333                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8334                                                 goto panic;
8335                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8336                                                 goto panic;
8337                                 }
8338                         }
8339                         /*  Remove from src  */
8340                         if (moveFlag && forUndo == 0) {
8341                                 if (nactions != NULL) {
8342                                         Int k, *ip;
8343                                         Double *dp;
8344                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8345                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8346                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8347                                         if (i == 0 && src->bondOrders != NULL) {
8348                                                 dp = (Double *)malloc(sizeof(Double) * n2);
8349                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8350                                                         dp[j] = src->bondOrders[k];
8351                                         } else dp = NULL;
8352                                         switch (i) {
8353                                                 case 0:
8354                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8355                                                 case 1:
8356                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8357                                                 case 2:
8358                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8359                                                 case 3:
8360                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8361                                         }
8362                                         if (act != NULL) {
8363                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8364                                                 act = NULL;
8365                                         }
8366                                         free(ip);
8367                                         if (dp != NULL) {
8368                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8369                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8370                                                 act = NULL;
8371                                                 free(dp);
8372                                         }
8373                                 }
8374                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8375                                         goto panic;
8376                                 (*nitems) -= n2;
8377                         }
8378                 }
8379                 /*  Renumber the entries  */
8380                 if (moveFlag) {
8381                         for (j = 0; j < *nitems * nsize; j++) {
8382                                 (*items)[j] = old2new[(*items)[j]];
8383                         }
8384                 }
8385                 if (items_dst != NULL) {
8386                         for (j = 0; j < *nitems_dst * nsize; j++) {
8387                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8388                         }
8389                 }
8390                 free(counts);
8391                 IntGroupClear(move_g);
8392                 IntGroupRelease(del_g);
8393         }
8394         IntGroupRelease(move_g);
8395         
8396         /*  Copy the residues  */
8397         if (dst != NULL) {
8398                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
8399                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
8400                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8401                         goto panic;
8402                 if (n1 > 1) {
8403                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8404                 }
8405         }
8406
8407         /*  Copy the parameters to dst */
8408         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8409                 IntGroup *dst_new_g = IntGroupNew();
8410                 Int dst_par_count[kLastParType - kFirstParType + 1];
8411                 if (dst_new_g == NULL)
8412                         goto panic;
8413                 for (i = 0; i <= kLastParType - kFirstParType; i++)
8414                         dst_par_count[i] = 0;
8415                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8416                 if (up == NULL)
8417                         goto panic;
8418                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8419                         goto panic;
8420                 /*  Renumber the explicit atom indices  */
8421                 for (i = 0; i < nsrc; i++)
8422                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
8423                 for (i = 0; i < n2; i++) {
8424                         /*  Renumber the indices, and count the number of parameters for each type  */
8425                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8426                         dst_par_count[n1 - kFirstParType]++;
8427                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8428                 }
8429                 for (i = 0; i < nsrc; i++)
8430                         old2new[i] += nsrcnew;
8431                 if (dst->par == NULL)
8432                         dst->par = ParameterNew();
8433                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8434                         if (dst_par_count[i] > 0)
8435                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8436                 }
8437                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8438                         goto panic;
8439                 free(up);
8440                 IntGroupRelease(dst_new_g);
8441         }
8442         IntGroupRelease(dst_par_g);
8443
8444         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
8445             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
8446         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8447                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8448                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8449                 if (nactions != NULL) {
8450                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8451                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8452                         act = NULL;
8453                 }
8454                 free(up);
8455         }
8456         IntGroupRelease(remove_par_g);
8457         
8458         /*  Renumber the parameter records remaining in the src  */
8459         if (moveFlag) {
8460                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8461                         n2 = ParameterGetCountForType(src->par, n1);
8462                         for (i = 0; i < n2; i++) {
8463                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8464                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8465                         }
8466                 }
8467         }
8468
8469         /*  Clean up  */
8470         IntGroupRelease(remain_g);
8471         MoleculeCleanUpResidueTable(src);
8472         if (dst != NULL)
8473                 MoleculeCleanUpResidueTable(dst);
8474         free(new2old);
8475
8476         src->nframes = -1;  /*  Should be recalculated later  */
8477         if (dst != NULL)
8478                 dst->nframes = -1;  /*  Should be recalculated later  */
8479
8480         
8481         if (dstp != NULL)
8482                 *dstp = dst;
8483
8484         MoleculeIncrementModifyCount(src);
8485         src->needsMDRebuild = 1;
8486         __MoleculeUnlock(src);
8487         
8488         return 0;
8489
8490   panic:
8491         __MoleculeUnlock(src);
8492 /*    Panic("Low memory while removing atoms"); */
8493         return -1;
8494 }
8495
8496 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
8497     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
8498         in which case the moved atoms are discarded.  */
8499 int
8500 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8501 {
8502         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8503 }
8504
8505 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
8506         'where' are copied from src to a new molecule, which is returned as *dstp.
8507     If dummyFlag is non-zero, then the atoms that are not included in the group 
8508         but are connected to any atoms in the group are converted to "dummy" atoms 
8509         (i.e. with element "Du" and names beginning with an underscore) and included 
8510         in the new molecule object.  */
8511 int
8512 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8513 {
8514         int retval;
8515
8516         /*  Extract the fragment  */
8517         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8518         if (retval != 0)
8519                 return retval;
8520
8521         if (dummyFlag) {
8522
8523                 /*  Search bonds crossing the molecule border  */
8524                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8525                 if (ig != NULL) {
8526                         IntGroupIterator iter;
8527                         Int i, idx;
8528                         idx = 1;
8529                         IntGroupIteratorInit(ig, &iter);
8530                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8531                                 /*  The atoms at the border  */
8532                                 Int n1, n2, nn[3];
8533                                 Atom a, *ap;
8534                                 n1 = src->bonds[i*2];
8535                                 n2 = src->bonds[i*2+1];
8536                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8537                                         int w = n1;
8538                                         n1 = n2;
8539                                         n2 = w;
8540                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8541                                                 continue;  /*  Actually this is an internal error  */
8542                                 }
8543                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
8544                                 /*  Create a new dummy atom with the same segment/residue info with n1
8545                                     and the same position as n2  */
8546                                 ap = ATOM_AT_INDEX(src->atoms, n1);
8547                                 memset(&a, 0, gSizeOfAtomRecord);
8548                                 a.segSeq = ap->segSeq;
8549                                 memmove(a.segName, ap->segName, 4);
8550                                 a.resSeq = ap->resSeq;
8551                                 memmove(a.resName, ap->resName, 4);
8552                                 ElementToString(0, a.element);  /*  "Du"  */
8553                                 snprintf(a.aname, 4, "_%d", idx++);
8554                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8555                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
8556                                     of the new dummy atom in the new molecule  */
8557                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8558                                 /*  Connect nn1 and nn2  */
8559                                 nn[2] = kInvalidIndex;
8560                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8561                         }
8562                         IntGroupIteratorRelease(&iter);
8563                         IntGroupRelease(ig);
8564                 }
8565         }
8566         
8567         return 0;
8568 }
8569
8570 int
8571 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8572 {
8573         Int nangles, ndihedrals;
8574         Int *angles, *dihedrals;
8575         Int i, j, k, kk, n1, n2, cn1, cn2;
8576         Int *cp1, *cp2;
8577         Int temp[4];
8578         Atom *ap1, *ap2, *ap3;
8579         
8580         if (mp == NULL || bonds == NULL || nbonds <= 0)
8581                 return 0;
8582         if (mp->noModifyTopology)
8583                 return -4;  /*  Prohibited operation  */
8584
8585         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
8586
8587         __MoleculeLock(mp);
8588
8589         n1 = mp->nbonds;
8590         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8591                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8592                 __MoleculeUnlock(mp);
8593                 return -4;  /*  Out of memory  */
8594         }
8595         if (mp->bondOrders != NULL) {
8596                 /*  Expand the bond order info (all new entries are zero)  */
8597                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8598                 if (dp == NULL)
8599                         return -4;
8600                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8601                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8602                         __MoleculeUnlock(mp);
8603                         free(dp);
8604                         return -4;
8605                 }
8606                 free(dp);
8607         }
8608         
8609         angles = dihedrals = NULL;
8610         nangles = ndihedrals = 0;
8611         
8612         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
8613         for (i = 0; i < nbonds; i++) {
8614                 
8615                 /*  One entry at time  */
8616                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
8617                 n1 = bonds[i * 2];
8618                 n2 = bonds[i * 2 + 1];
8619                 
8620                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8621                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8622                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8623                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8624         
8625                 /*  Add angles and dihedrals  */
8626                 if (autoGenerate) {
8627                         AtomConnect *ac1, *ac2;
8628                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
8629                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
8630                                 for (j = 0; j < 4; j++) {
8631                                         switch (j) {
8632                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
8633                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8634                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
8635                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8636                                         }
8637                                         cp1 = AtomConnectData(ac1);
8638                                         cn1 = ac1->count;
8639                                         for (k = 0; k < cn1; k++) {
8640                                                 temp[2] = cp1[k];
8641                                                 if (temp[2] == temp[0])
8642                                                         continue;
8643                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8644                                                 if (ap3->anchor != NULL) {
8645                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
8646                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8647                                                                 continue;
8648                                                 }
8649                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8650                                                         goto panic;
8651                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
8652                                                 if (j == 1 || j == 3)
8653                                                         continue;
8654                                                 cp2 = AtomConnectData(&ap3->connect);
8655                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
8656                                                         temp[3] = cp2[kk];
8657                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8658                                                                 continue;
8659                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8660                                                                 goto panic;
8661                                                 }
8662                                                 if (ap3->anchor != NULL) {
8663                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
8664                                                         /*  for Y, only the first constitute atom is considered  */
8665                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
8666                                                         temp[3] = cp2[0];
8667                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8668                                                                 continue;
8669                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8670                                                                 goto panic;
8671                                                 }
8672                                         }
8673                                 }
8674                         }
8675                         /*  X-N1-N2-X dihedrals  */
8676                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8677                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
8678                         if (ap1->anchor == NULL) {
8679                                 ac1 = &ap1->connect;
8680                                 cn1 = ac1->count;
8681                         } else {
8682                                 ac1 = &ap1->anchor->connect;
8683                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8684                         }
8685                         if (ap2->anchor == NULL) {
8686                                 ac2 = &ap2->connect;
8687                                 cn2 = ac2->count;
8688                         } else {
8689                                 ac2 = &ap2->anchor->connect;
8690                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8691                         }
8692                         temp[1] = n1;
8693                         temp[2] = n2;
8694                         cp1 = AtomConnectData(ac1);
8695                         cp2 = AtomConnectData(ac2);
8696                         for (j = 0; j < cn1; j++) {
8697                                 temp[0] = cp1[j];
8698                                 if (temp[0] == temp[2])
8699                                         continue;
8700                                 for (k = 0; k < cn2; k++) {
8701                                         temp[3] = cp2[k];
8702                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8703                                                 continue;
8704                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8705                                                 goto panic;
8706                                 }
8707                         }
8708                 }
8709         }
8710         
8711         if (angles != NULL) {
8712                 temp[0] = kInvalidIndex;
8713                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8714                         goto panic;
8715                 MoleculeAddAngles(mp, angles, NULL);
8716                 free(angles);
8717         }
8718         if (dihedrals != NULL) {
8719                 temp[0] = kInvalidIndex;
8720                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8721                         goto panic;
8722                 MoleculeAddDihedrals(mp, dihedrals, NULL);
8723                 free(dihedrals);
8724         }
8725
8726         MoleculeIncrementModifyCount(mp);
8727         mp->needsMDRebuild = 1;
8728         __MoleculeUnlock(mp);
8729
8730         return nbonds;
8731
8732   panic:
8733         __MoleculeUnlock(mp);
8734         Panic("Low memory while adding bonds");
8735         return -1;  /*  Not reached  */
8736 }
8737
8738 /*  Delete bonds  */
8739 /*  The deleted angles and dihedrals are stored in outRemoval.  */
8740 /*  (*outRemoval) is an array of integers, containing:
8741       [0..na*3-1]: the angle indices
8742       [na*3..na*3+nd*4-1]: the dihedral indices
8743           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8744     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8745           the angle indices are included as they are,
8746       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8747       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8748     Note: the removed bond indices are not returned, because the caller should already know them.  */
8749 int
8750 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8751 {
8752         Int i, j, n1, n2, nw;
8753         Int *ip, *jp, na, nd, ni;
8754         IntGroup *ag, *dg, *ig;
8755         Atom *ap;
8756         IntGroupIterator iter;
8757
8758         if (mp == NULL)
8759                 return 0;
8760         if (mp->noModifyTopology)
8761                 return -4;  /*  Prohibited operation  */
8762
8763         __MoleculeLock(mp);
8764
8765         /*  Update connects[]  */
8766         IntGroupIteratorInit(where, &iter);
8767         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8768                 n1 = mp->bonds[i * 2];
8769                 n2 = mp->bonds[i * 2 + 1];
8770                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8771                 ip = AtomConnectData(&ap->connect);
8772                 for (j = 0; j < ap->connect.count; j++) {
8773                         if (ip[j] == n2) {
8774                                 AtomConnectDeleteEntry(&ap->connect, j);
8775                                 break;
8776                         }
8777                 }
8778                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8779                 ip = AtomConnectData(&ap->connect);
8780                 for (j = 0; j < ap->connect.count; j++) {
8781                         if (ip[j] == n1) {
8782                                 AtomConnectDeleteEntry(&ap->connect, j);
8783                                 break;
8784                         }
8785                 }
8786         }
8787         
8788         /*  Remove bonds, angles, dihedrals, impropers  */
8789         ag = IntGroupNew();
8790         dg = ig = NULL;
8791         na = nd = ni = 0;
8792         
8793         nw = IntGroupGetCount(where);
8794         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8795         j = 0;
8796         IntGroupIteratorReset(&iter);
8797         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8798                 jp[j++] = mp->bonds[i * 2];
8799                 jp[j++] = mp->bonds[i * 2 + 1];
8800         }
8801         IntGroupIteratorRelease(&iter);
8802
8803         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8804                 for (j = 0; j < nw; j++) {
8805                         n1 = jp[j * 2];
8806                         n2 = jp[j * 2 + 1];
8807                         if ((ip[0] == n1 && ip[1] == n2)
8808                                 || (ip[1] == n1 && ip[0] == n2)
8809                                 || (ip[1] == n1 && ip[2] == n2)
8810                                 || (ip[2] == n1 && ip[1] == n2)) {
8811                                 if (IntGroupAdd(ag, i, 1) != 0)
8812                                         goto panic;
8813                                 na++;
8814                                 break;
8815                         }
8816                 }
8817         }
8818         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8819                 for (j = 0; j < nw; j++) {
8820                         n1 = jp[j * 2];
8821                         n2 = jp[j * 2 + 1];
8822                         if ((ip[0] == n1 && ip[1] == n2)
8823                          || (ip[1] == n1 && ip[0] == n2)
8824                          || (ip[1] == n1 && ip[2] == n2)
8825                          || (ip[2] == n1 && ip[1] == n2)
8826                          || (ip[2] == n1 && ip[3] == n2)
8827                          || (ip[3] == n1 && ip[2] == n2)) {
8828                                 if (dg == NULL)
8829                                         dg = IntGroupNew();
8830                                 if (IntGroupAdd(dg, i, 1) != 0)
8831                                         goto panic;
8832                                 nd++;
8833                                 break;
8834                         }
8835                 }
8836         }
8837         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8838                 for (j = 0; j < nw; j++) {
8839                         n1 = jp[j * 2];
8840                         n2 = jp[j * 2 + 1];
8841                         if ((ip[0] == n1 && ip[2] == n2)
8842                          || (ip[1] == n1 && ip[2] == n2)
8843                          || (ip[3] == n1 && ip[2] == n2)
8844                          || (ip[0] == n2 && ip[2] == n1)
8845                          || (ip[1] == n2 && ip[2] == n1)
8846                          || (ip[3] == n2 && ip[2] == n1)) {
8847                                 if (ig == NULL)
8848                                         ig = IntGroupNew();
8849                                 if (IntGroupAdd(ig, i, 1) != 0)
8850                                         goto panic;
8851                                 ni++;
8852                                 break;
8853                         }
8854                 }
8855         }
8856         free(jp);
8857         
8858         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8859                 goto panic;
8860         mp->nbonds -= IntGroupGetCount(where);
8861         if (mp->nbonds == 0) {
8862                 free(mp->bonds);
8863                 mp->bonds = NULL;
8864         }
8865         if (mp->bondOrders != NULL) {
8866                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8867                         goto panic;
8868                 mp->nbondOrders -= IntGroupGetCount(where);
8869                 if (mp->nbondOrders == 0) {
8870                         free(mp->bondOrders);
8871                         mp->bondOrders = NULL;
8872                 }
8873         }
8874         if (na == 0 && nd == 0 && ni == 0)
8875                 ip = NULL;
8876         else
8877                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8878         if (na > 0)
8879                 MoleculeDeleteAngles(mp, ip, ag);
8880         if (nd > 0)
8881                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8882         if (ni > 0)
8883                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8884         if (ip != NULL) {
8885                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8886                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8887                 IntGroupAddIntGroup(ag, dg);
8888                 IntGroupAddIntGroup(ag, ig);
8889                 IntGroupRelease(dg);
8890                 IntGroupRelease(ig);
8891         }
8892
8893         if (IntGroupGetCount(ag) == 0) {
8894                 IntGroupRelease(ag);
8895                 ag = NULL;
8896         }
8897         
8898         *outRemoved = ip;
8899         *outRemovedPos = ag;
8900
8901         MoleculeIncrementModifyCount(mp);
8902         mp->needsMDRebuild = 1;
8903         __MoleculeUnlock(mp);
8904
8905         return na * 3 + nd * 4 + ni * 4;
8906
8907   panic:
8908         __MoleculeUnlock(mp);
8909         Panic("Low memory while removing bonds");
8910         return -1;  /*  Not reached  */
8911 }
8912
8913 int
8914 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8915 {
8916         Int i, j;
8917         IntGroupIterator iter;
8918         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8919                 return 0;
8920         if (mp->noModifyTopology)
8921                 return -4;  /*  Prohibited operation  */
8922         if (mp->bondOrders == NULL) {
8923                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8924                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8925         }
8926         IntGroupIteratorInit(where, &iter);
8927         j = 0;
8928         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8929                 if (i >= mp->nbondOrders)
8930                         break;
8931                 mp->bondOrders[i] = orders[j++];
8932         }
8933         IntGroupIteratorRelease(&iter);
8934         return 0;
8935 }
8936
8937 int
8938 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8939 {
8940         Int i, j;
8941         IntGroupIterator iter;
8942         if (mp == NULL || mp->nbonds == 0)
8943                 return 0;
8944         if (mp->bondOrders == NULL) {
8945                 /*  Returns all zero  */
8946                 i = IntGroupGetCount(where);
8947                 for (j = 0; j < i; j++)
8948                         outOrders[j] = 0.0;
8949         } else {
8950                 IntGroupIteratorInit(where, &iter);
8951                 j = 0;
8952                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8953                         if (i < mp->nbondOrders)
8954                                 outOrders[j] = mp->bondOrders[i];
8955                         else outOrders[j] = 0.0;
8956                         j++;
8957                 }
8958         }
8959         return 0;
8960 }
8961
8962 int
8963 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8964 {
8965         int n1, nc;
8966         if (mp == NULL || angles == NULL)
8967                 return 0;
8968         if (mp->noModifyTopology)
8969                 return -4;  /*  Prohibited operation  */
8970
8971         __MoleculeLock(mp);
8972         if (where != NULL)
8973                 nc = IntGroupGetCount(where);
8974         else {
8975                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8976                         ;
8977                 nc = n1;
8978         }
8979         if (nc > 0) {
8980                 n1 = mp->nangles;
8981                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8982                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8983                         __MoleculeUnlock(mp);
8984                         Panic("Low memory while adding angles");
8985                 }
8986         }
8987         mp->needsMDRebuild = 1;
8988         __MoleculeUnlock(mp);
8989         return nc;
8990 }
8991
8992 int
8993 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8994 {
8995         int nc;
8996         if (mp == NULL || where == NULL)
8997                 return 0;
8998         if (mp->noModifyTopology)
8999                 return -4;  /*  Prohibited operation  */
9000         __MoleculeLock(mp);
9001         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
9002                 __MoleculeUnlock(mp);
9003                 Panic("Bad argument while deleting angles");
9004         }
9005         mp->nangles -= (nc = IntGroupGetCount(where));
9006         if (mp->nangles == 0) {
9007                 free(mp->angles);
9008                 mp->angles = NULL;
9009         }
9010         mp->needsMDRebuild = 1;
9011         __MoleculeUnlock(mp);
9012         return nc;
9013 }
9014
9015 int
9016 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
9017 {
9018         int n1, nc;
9019         if (mp == NULL || dihedrals == NULL)
9020                 return 0;
9021         if (mp->noModifyTopology)
9022                 return -4;  /*  Prohibited operation  */
9023         if (where != NULL)
9024                 nc = IntGroupGetCount(where);
9025         else {
9026                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
9027                         ;
9028                 nc = n1;
9029         }
9030         if (nc <= 0)
9031                 return 0;
9032         n1 = mp->ndihedrals;
9033         __MoleculeLock(mp);
9034         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
9035         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
9036                 __MoleculeUnlock(mp);
9037                 Panic("Low memory while adding dihedrals");
9038         }
9039         mp->needsMDRebuild = 1;
9040         __MoleculeUnlock(mp);
9041         return nc;
9042 }
9043
9044 int
9045 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
9046 {       
9047         int nc;
9048         if (mp == NULL || where == NULL)
9049                 return 0;
9050         if (mp->noModifyTopology)
9051                 return -4;  /*  Prohibited operation  */
9052         __MoleculeLock(mp);
9053         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
9054                 __MoleculeUnlock(mp);
9055                 Panic("Internal error: bad argument while deleting dihedrals");
9056         }
9057         mp->ndihedrals -= (nc = IntGroupGetCount(where));
9058         if (mp->ndihedrals == 0) {
9059                 free(mp->dihedrals);
9060                 mp->dihedrals = NULL;
9061         }
9062         mp->needsMDRebuild = 1;
9063         __MoleculeUnlock(mp);
9064         return nc;
9065 }
9066
9067 int
9068 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
9069 {
9070         int n1, nc;
9071         if (mp == NULL || impropers == NULL)
9072                 return 0;
9073         if (mp->noModifyTopology)
9074                 return -4;  /*  Prohibited operation  */
9075         if (where != NULL)
9076                 nc = IntGroupGetCount(where);
9077         else {
9078                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
9079                         ;
9080                 nc = n1;
9081         }
9082         if (nc <= 0)
9083                 return 0;
9084         n1 = mp->nimpropers;
9085         __MoleculeLock(mp);
9086         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
9087         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
9088                 __MoleculeUnlock(mp);
9089                 Panic("Low memory while adding impropers");
9090         }
9091         mp->needsMDRebuild = 1;
9092         __MoleculeUnlock(mp);
9093         return nc;
9094 }
9095
9096 int
9097 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
9098 {
9099         int nc;
9100         if (mp == NULL || where == NULL)
9101                 return 0;
9102         if (mp->noModifyTopology)
9103                 return -4;  /*  Prohibited operation  */
9104         __MoleculeLock(mp);
9105         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
9106                 __MoleculeUnlock(mp);
9107                 Panic("Internal error: bad argument while deleting impropers");
9108         }
9109         mp->nimpropers -= (nc = IntGroupGetCount(where));
9110         if (mp->impropers == NULL) {
9111                 free(mp->impropers);
9112                 mp->impropers = NULL;
9113         }
9114         __MoleculeUnlock(mp);
9115         return nc;
9116 }
9117
9118 int
9119 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
9120 {
9121         Int i, *ip;
9122         if (mp == NULL || mp->bonds == NULL)
9123                 return -1;
9124         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
9125                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
9126                         return i;
9127         }
9128         return -1;
9129 }
9130
9131 int
9132 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
9133 {
9134         Int i, *ip;
9135         if (mp == NULL || mp->angles == NULL)
9136                 return -1;
9137         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
9138                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
9139                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
9140                         return i;
9141         }
9142         return -1;
9143 }
9144
9145 int
9146 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9147 {
9148         Int i, *ip;
9149         if (mp == NULL || mp->dihedrals == NULL)
9150                 return -1;
9151         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
9152                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
9153                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
9154                         return i;
9155         }
9156         return -1;
9157 }
9158
9159 int
9160 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9161 {
9162         Int i, *ip;
9163         if (mp == NULL || mp->impropers == NULL)
9164                 return -1;
9165         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9166                 if (n3 != ip[2])
9167                         continue;
9168                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9169                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9170                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9171                         return i;
9172         }
9173         return -1;
9174 }
9175
9176 /*  Remove the bond at bondIndex and create two dummy atoms instead.
9177     The dummy atoms are placed at the end of atoms[], and the residue
9178         numbers are the same as the root atoms (i.e. the atoms to which
9179         the dummy atoms are connected). The indices are returned in
9180         dummyIndices[0,1].  */
9181 int
9182 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9183 {
9184         Int roots[3], newBonds[5];
9185         Vector dr;
9186         Atom *rootp[2];
9187         Atom na[2], *nap;
9188         int i, natoms;
9189         IntGroup *ig;
9190         if (mp == NULL || mp->noModifyTopology)
9191                 return 0;
9192         if (bondIndex < 0 || bondIndex >= mp->nbonds)
9193                 return -1;
9194         roots[0] = mp->bonds[bondIndex * 2];
9195         roots[1] = mp->bonds[bondIndex * 2 + 1];
9196         roots[2] = kInvalidIndex;
9197         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9198         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9199         VecSub(dr, rootp[0]->r, rootp[1]->r);
9200         for (i = 0; i < 2; i++) {
9201                 float w;
9202                 nap = &na[i];
9203                 memmove(nap, rootp[i], sizeof(na));
9204                 nap->aname[0] = '*';
9205                 strcpy(nap->element, "Du");
9206                 nap->type = 0;
9207                 nap->charge = nap->weight = 0.0;
9208                 nap->atomicNumber = 0;
9209                 nap->connect.count = 0;
9210                 w = (i == 0 ? 0.4 : -0.4);
9211                 VecScaleInc(nap->r, dr, w);
9212                 VecZero(nap->v);
9213                 VecZero(nap->f);
9214                 nap->intCharge = 0;
9215                 nap->exflags = 0;
9216         }
9217
9218         /*  Expand atoms array and append the dummy atoms at the end  */
9219         __MoleculeLock(mp);
9220         natoms = mp->natoms;
9221         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9222                 goto panic;
9223         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9224         dummyIndices[0] = natoms;
9225         dummyIndices[1] = natoms + 1;
9226
9227         /*  Remove the old bond and create new bonds  */
9228         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9229         if (ig == NULL)
9230                 goto panic;
9231         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9232         IntGroupRelease(ig);
9233         newBonds[0] = roots[0];
9234         newBonds[1] = dummyIndices[0];
9235         newBonds[2] = roots[1];
9236         newBonds[3] = dummyIndices[1];
9237         newBonds[4] = kInvalidIndex;
9238         
9239         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9240         mp->needsMDRebuild = 1;
9241         __MoleculeUnlock(mp);
9242         return i;
9243
9244 panic:
9245         __MoleculeUnlock(mp);
9246         Panic("Low memory during creating dummy atoms");
9247         return 1;
9248 }
9249
9250 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9251     a bond between the two root atoms. The value bondIndex is used as a
9252         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9253         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9254         is ignored and the new bond is stored at the end of bonds[].  */
9255 int
9256 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9257 {
9258         return 0;
9259 }
9260
9261 /*
9262 Int
9263 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9264 {
9265         Int n1, *np1;
9266         if (mol == NULL || mol->noModifyTopology)
9267                 return -1;
9268         n1 = mol->nangles;
9269         np1 = mol->angles;
9270         mol->nangles = 0;
9271         mol->angles = NULL;
9272         if (nangles > 0) {
9273                 __MoleculeLock(mol);
9274                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9275                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9276                 mol->needsMDRebuild = 1;
9277                 __MoleculeUnlock(mol);
9278         }
9279         *outAngles = np1;
9280         return n1;
9281 }
9282                                                 
9283 Int
9284 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9285 {
9286         Int n1, *np1;
9287         if (mol == NULL || mol->noModifyTopology)
9288                 return -1;
9289         n1 = mol->ndihedrals;
9290         np1 = mol->dihedrals;
9291         mol->ndihedrals = 0;
9292         mol->dihedrals = NULL;
9293         if (ndihedrals > 0) {
9294                 __MoleculeLock(mol);
9295                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9296                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9297                 mol->needsMDRebuild = 1;
9298                 __MoleculeUnlock(mol);
9299         }
9300         *outDihedrals = np1;
9301         return n1;
9302 }
9303
9304 Int
9305 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9306 {
9307         Int n1, *np1;
9308         if (mol == NULL || mol->noModifyTopology)
9309                 return -1;
9310         n1 = mol->nimpropers;
9311         np1 = mol->impropers;
9312         mol->nimpropers = 0;
9313         mol->impropers = NULL;
9314         if (nimpropers > 0) {
9315                 __MoleculeLock(mol);
9316                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9317                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9318                 mol->needsMDRebuild = 1;
9319                 __MoleculeUnlock(mol);
9320         }
9321         *outImpropers = np1;
9322         return n1;
9323 }
9324 */
9325
9326 Int
9327 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9328 {
9329         Int i, j, k, *ip;
9330         Atom *ap;
9331         Int nangles;
9332         Int *angles;
9333         
9334         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9335                 return 0;  /*  molecule is empty  */
9336         if (mol->noModifyTopology)
9337                 return -1;
9338         nangles = 0;
9339         angles = NULL;
9340         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9341                 Int *cp = AtomConnectData(&ap->connect);
9342                 if (ap->anchor != NULL)
9343                         continue;
9344                 for (j = 0; j < ap->connect.count; j++) {
9345                         Int j0 = cp[j];
9346                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9347                                 continue;
9348                         for (k = j + 1; k < ap->connect.count; k++) {
9349                                 Int k0 = cp[k];
9350                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9351                                         continue;
9352                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9353                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9354                                         ip[0] = j0;
9355                                         ip[1] = i;
9356                                         ip[2] = k0;
9357                                 }
9358                         }
9359                 }
9360         }
9361         if (nangles > 0) {
9362                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9363                 ip[0] = -1;
9364                 nangles--;
9365         }
9366         if (outAngles != NULL)
9367                 *outAngles = angles;
9368         return nangles;
9369 }
9370
9371 Int
9372 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9373 {
9374         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9375         Atom *ap2, *ap3;
9376         Int ndihedrals;
9377         Int *dihedrals;
9378         
9379         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9380                 return 0;  /*  molecule is empty  */
9381         ndihedrals = 0;
9382         dihedrals = NULL;
9383         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9384                 Int i1, i3, i4, *ip;
9385                 if (ap2->anchor != NULL)
9386                         continue;
9387                 cp2 = AtomConnectData(&ap2->connect);
9388                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9389                         n3 = cp2[i3];
9390                         if (n2 > n3)
9391                                 continue;
9392                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9393                         if (ap3->anchor != NULL)
9394                                 continue;
9395                         cp3 = AtomConnectData(&ap3->connect);
9396                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
9397                                 n1 = cp2[i1];
9398                                 if (n1 == n3)
9399                                         continue;
9400                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9401                                         continue;
9402                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9403                                         n4 = cp3[i4];
9404                                         if (n2 == n4 || n1 == n4)
9405                                                 continue;
9406                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9407                                                 continue;
9408                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9409                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9410                                                 ip[0] = n1;
9411                                                 ip[1] = n2;
9412                                                 ip[2] = n3;
9413                                                 ip[3] = n4;
9414                                         }
9415                                 }
9416                         }
9417                 }
9418         }
9419         if (ndihedrals > 0) {
9420                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9421                 ip[0] = -1;
9422                 ndihedrals--;
9423         }
9424         if (outDihedrals != NULL)
9425                 *outDihedrals = dihedrals;
9426         return ndihedrals;
9427 }
9428
9429 Int
9430 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9431 {
9432         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9433         Parameter *par = mol->par;
9434         Atom *ap, *ap3;
9435         Int nimpropers;
9436         Int *impropers;
9437         
9438         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9439                 return 0;  /*  molecule is empty  */
9440         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9441                 return 0;  /*  No improper parameters are defined  */
9442         nimpropers = 0;
9443         impropers = NULL;
9444         ap = mol->atoms;
9445         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9446                 Int i1, i2, i4, found, *ip;
9447                 t3 = ap3->type;
9448                 cp = AtomConnectData(&ap3->connect);
9449                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9450                         n1 = cp[i1];
9451                         t1 = ATOM_AT_INDEX(ap, n1)->type;
9452                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9453                                 n2 = cp[i2];
9454                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
9455                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9456                                         n4 = cp[i4];
9457                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
9458                                         found = 0;
9459                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9460                                                 found = 1;
9461                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9462                                                 found = 1;
9463                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9464                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9465                                                 ip[0] = n1;
9466                                                 ip[1] = n2;
9467                                                 ip[2] = n3;
9468                                                 ip[3] = n4;
9469                                         }
9470                                 }
9471                         }
9472                 }
9473         }
9474         if (nimpropers > 0) {
9475                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9476                 ip[0] = -1;
9477                 nimpropers--;
9478         }
9479         if (outImpropers != NULL)
9480                 *outImpropers = impropers;
9481         return nimpropers;
9482 }
9483
9484 #pragma mark ====== Residues ======
9485
9486 void
9487 MoleculeCleanUpResidueTable(Molecule *mp)
9488 {
9489         int i, maxres;
9490         Atom *ap;
9491         if (mp == NULL || mp->natoms == 0)
9492                 return;
9493         maxres = 0;
9494         __MoleculeLock(mp);
9495         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9496                 if (ap->resSeq >= maxres)
9497                         maxres = ap->resSeq + 1;
9498                 if (ap->resSeq < mp->nresidues) {
9499                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9500                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9501                 } else {
9502                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9503                 }
9504         }
9505         if (maxres < mp->nresidues)
9506                 mp->nresidues = maxres;
9507         __MoleculeUnlock(mp);
9508 }
9509
9510 /*  Change the number of residues. If nresidues is greater than the current value,
9511     then the array mp->residues is expanded with null names. If nresidues is smaller
9512         than the current value, mp->nresidues is set to the smallest possible value
9513         that is no smaller than nresidues and larger than any of the resSeq values.  */
9514 int
9515 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9516 {
9517         int n;
9518         if (mp == NULL)
9519                 return 0;
9520         if (mp->nresidues == nresidues)
9521                 return nresidues;
9522         else if (mp->nresidues < nresidues) {
9523                 __MoleculeLock(mp);
9524                 n = mp->nresidues;
9525                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9526                 while (n < nresidues)
9527                         mp->residues[n++][0] = 0;
9528                 __MoleculeUnlock(mp);
9529                 return nresidues;
9530         } else {
9531                 int i;
9532                 Atom *ap;
9533                 n = nresidues;
9534                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9535                         if (ap->resSeq >= n)
9536                                 n = ap->resSeq + 1;
9537                 }
9538                 mp->nresidues = n;
9539                 return n;
9540         }
9541 }
9542
9543 int
9544 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9545 {
9546         IntGroupIterator iter;
9547         int withArray, resSeq, maxSeq;
9548         int i, j;
9549         Atom *ap;
9550         
9551         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
9552         if (((uintptr_t)resSeqs & 1) == 0) {
9553                 withArray = 1;
9554                 resSeq = 0;
9555         } else {
9556                 withArray = 0;
9557                 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9558         }
9559         
9560         IntGroupIteratorInit(group, &iter);
9561
9562         /*  Change resSeqs  */
9563         maxSeq = 0;
9564         j = 0;
9565         __MoleculeLock(mp);
9566         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9567                 ap = ATOM_AT_INDEX(mp->atoms, i);
9568                 if (withArray)
9569                         resSeq = resSeqs[j++];
9570                 if (resSeq > maxSeq)
9571                         maxSeq = resSeq;
9572                 ap->resSeq = resSeq;
9573         }
9574         __MoleculeUnlock(mp);
9575
9576         /*  Expand array if necessary  */
9577         if (maxSeq >= mp->nresidues)
9578                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9579
9580         /*  Synchronize resName and residues[]  */
9581         __MoleculeLock(mp);
9582         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9583                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9584                         continue;
9585                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9586         }
9587         IntGroupIteratorRelease(&iter);
9588         __MoleculeUnlock(mp);
9589         
9590         MoleculeIncrementModifyCount(mp);
9591         
9592         return 0;
9593 }
9594
9595 int
9596 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9597 {
9598         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9599 }
9600
9601 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9602     specifies the mp->nresidues after modifying the residue numbers.
9603         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9604         the table of residue names is not touched. */
9605 int
9606 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9607 {
9608         int i, maxSeq, nmodatoms;
9609         Atom *ap;
9610         IntGroupIterator iter;
9611         IntGroupIteratorInit(group, &iter);
9612         maxSeq = 0;
9613         if (nresidues < 0)
9614                 nresidues = mp->nresidues;
9615         nmodatoms = 0;
9616         __MoleculeLock(mp);
9617         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9618                 ap = ATOM_AT_INDEX(mp->atoms, i);
9619                 ap->resSeq += offset;
9620                 if (ap->resSeq < 0) {
9621                         /*  Bad argument; undo change and returns this index + 1  */
9622                         int bad_index = i;
9623                         ap->resSeq -= offset;
9624                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9625                                 ap = ATOM_AT_INDEX(mp->atoms, i);
9626                                 ap->resSeq -= offset;
9627                         }
9628                         IntGroupIteratorRelease(&iter);
9629                         return bad_index + 1;
9630                 }
9631                 if (ap->resSeq > maxSeq)
9632                         maxSeq = ap->resSeq;
9633                 nmodatoms++;
9634         }
9635         if (maxSeq >= nresidues)
9636                 nresidues = maxSeq + 1;
9637         if (offset < 0 && nmodatoms == mp->natoms) {
9638                 /*  Shift the residue names downward  */
9639                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9640         }
9641         __MoleculeUnlock(mp);
9642         MoleculeChangeNumberOfResidues(mp, nresidues);
9643         if (offset > 0 && nmodatoms == mp->natoms) {
9644                 /*  Shift the residue names upward  */
9645                 __MoleculeLock(mp);
9646                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9647                 __MoleculeUnlock(mp);
9648         }
9649         IntGroupIteratorRelease(&iter);
9650
9651         MoleculeIncrementModifyCount(mp);
9652         
9653         return 0;
9654 }
9655
9656 /*  Change residue names for the specified residue numbers. Names is an array of
9657     chars containing argc*4 characters, and every 4 characters represent a
9658         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
9659         names to be handled as a C string.  */
9660 int
9661 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9662 {
9663         int i, maxSeq;
9664         Atom *ap;
9665         maxSeq = 0;
9666         for (i = 0; i < argc; i++) {
9667                 if (maxSeq < resSeqs[i])
9668                         maxSeq = resSeqs[i];
9669         }
9670         if (maxSeq >= mp->nresidues)
9671                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9672         __MoleculeLock(mp);
9673         for (i = 0; i < argc; i++) {
9674                 char *p = mp->residues[resSeqs[i]];
9675                 int j;
9676                 strncpy(p, names + i * 4, 4);
9677                 for (j = 0; j < 4; j++) {
9678                         if (p[j] >= 0 && p[j] < 0x20)
9679                                 p[j] = 0;
9680                 }
9681         }
9682         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9683                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9684         }
9685         __MoleculeUnlock(mp);
9686
9687         MoleculeIncrementModifyCount(mp);
9688         
9689         return 0;
9690 }
9691
9692 /*  Returns the maximum residue number actually used  */
9693 int
9694 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9695 {
9696         int i, maxSeq;
9697         Atom *ap;
9698         maxSeq = -1;
9699         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9700                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9701                         continue;
9702                 if (ap->resSeq > maxSeq)
9703                         maxSeq = ap->resSeq;
9704         }
9705         return maxSeq;
9706 }
9707
9708 /*  Returns the minimum residue number actually used  */
9709 int
9710 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9711 {
9712         int i, minSeq;
9713         Atom *ap;
9714         minSeq = ATOMS_MAX_NUMBER;
9715         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9716                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9717                         continue;
9718                 if (ap->resSeq < minSeq)
9719                         minSeq = ap->resSeq;
9720         }
9721         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9722 }
9723
9724 #pragma mark ====== Sort by Residues ======
9725
9726 static int
9727 sAtomSortComparator(const void *a, const void *b)
9728 {
9729         const Atom *ap, *bp;
9730         ap = *((const Atom **)a);
9731         bp = *((const Atom **)b);
9732         if (ap->resSeq == bp->resSeq) {
9733                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
9734                 if (ap < bp)
9735                         return -1;
9736                 else if (ap > bp)
9737                         return 1;
9738                 else return 0;
9739         } else {
9740                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
9741                 if (ap->resSeq == 0)
9742                         return 1;
9743                 else if (bp->resSeq == 0)
9744                         return -1;
9745                 else if (ap->resSeq < bp->resSeq)
9746                         return -1;
9747                 else if (ap->resSeq > bp->resSeq)
9748                         return 1;
9749                 else return 0;
9750         }
9751 }
9752
9753 static void
9754 sMoleculeReorder(Molecule *mp)
9755 {
9756         int i, res, prevRes;
9757         Atom **apArray;
9758         Int *old2new;
9759         Atom *newAtoms;
9760         if (mp == NULL || mp->natoms <= 1)
9761                 return;
9762
9763         /*  Sort the atoms, bonds, etc. */
9764         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9765         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9766         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9767         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9768                 Panic("Low memory during reordering atoms");
9769         for (i = 0; i < mp->natoms; i++)
9770                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9771
9772         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9773         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9774         
9775         /*  Make a table of 'which atom becomes which'  */
9776         for (i = 0; i < mp->natoms; i++) {
9777                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9778                 old2new[j] = i;
9779         }
9780         
9781         /*  Renumber the bonds, etc.  */
9782         for (i = 0; i < mp->nbonds * 2; i++) {
9783                 mp->bonds[i] = old2new[mp->bonds[i]];
9784         }
9785         for (i = 0; i < mp->nangles * 3; i++) {
9786                 mp->angles[i] = old2new[mp->angles[i]];
9787         }
9788         for (i = 0; i < mp->ndihedrals * 4; i++) {
9789                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9790         }
9791         for (i = 0; i < mp->nimpropers * 4; i++) {
9792                 mp->impropers[i] = old2new[mp->impropers[i]];
9793         }
9794         for (i = 0; i < mp->natoms; i++) {
9795                 Int *ip, j;
9796                 ip = AtomConnectData(&(apArray[i]->connect));
9797                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9798                         *ip = old2new[*ip];
9799         }
9800         
9801         /*  Renumber the residues so that the residue numbers are contiguous  */
9802         res = prevRes = 0;
9803         for (i = 0; i < mp->natoms; i++) {
9804                 if (apArray[i]->resSeq == 0)
9805                         break;
9806                 if (apArray[i]->resSeq != prevRes) {
9807                         res++;
9808                         prevRes = apArray[i]->resSeq;
9809                         if (prevRes != res) {
9810                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9811                         }
9812                 }
9813                 apArray[i]->resSeq = res;
9814         }
9815         mp->nresidues = res + 1;
9816
9817         /*  Sort the atoms and copy back to atoms[] */
9818         for (i = 0; i < mp->natoms; i++) {
9819                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9820         }
9821         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9822         
9823         /*  Free the locally allocated storage  */
9824         free(newAtoms);
9825         free(old2new);
9826         free(apArray);
9827 }
9828
9829 /*  Renumber atoms  */
9830 int
9831 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9832 {
9833         Int *old2new, i, j, retval;
9834         Atom *saveAtoms;
9835         if (mp == NULL)
9836                 return 0;
9837         if (mp->noModifyTopology)
9838                 return -1;
9839         if (old2new_out != NULL)
9840                 old2new = old2new_out;
9841         else
9842                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9843         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9844         if (old2new == NULL || saveAtoms == NULL)
9845                 Panic("Low memory during reordering atoms");
9846         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9847         __MoleculeLock(mp);
9848         for (i = 0; i < mp->natoms; i++)
9849                 old2new[i] = -1;
9850         for (i = 0; i < isize && i < mp->natoms; i++) {
9851                 j = new2old[i];
9852                 if (j < 0 || j >= mp->natoms) {
9853                         retval = 1; /* Out of range */
9854                         goto end;
9855                 }
9856                 if (old2new[j] != -1) {
9857                         retval = 2;  /*  Duplicate entry  */
9858                         goto end;
9859                 }
9860                 old2new[j] = i;
9861         }
9862         if (i < mp->natoms) {
9863                 for (j = 0; j < mp->natoms; j++) {
9864                         if (old2new[j] != -1)
9865                                 continue;
9866                         old2new[j] = i++;
9867                 }
9868         }
9869         if (i != mp->natoms) {
9870                 retval = 3;  /*  Internal inconsistency  */
9871                 goto end;
9872         }
9873
9874         /*  Renumber the bonds, etc.  */
9875         for (i = 0; i < mp->nbonds * 2; i++) {
9876                 mp->bonds[i] = old2new[mp->bonds[i]];
9877         }
9878         for (i = 0; i < mp->nangles * 3; i++) {
9879                 mp->angles[i] = old2new[mp->angles[i]];
9880         }
9881         for (i = 0; i < mp->ndihedrals * 4; i++) {
9882                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9883         }
9884         for (i = 0; i < mp->nimpropers * 4; i++) {
9885                 mp->impropers[i] = old2new[mp->impropers[i]];
9886         }
9887         /*  Renumber the connection table and pi anchor table  */
9888         for (i = 0; i < mp->natoms; i++) {
9889                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9890                 Int *ip = AtomConnectData(&ap->connect);
9891                 for (j = 0; j < ap->connect.count; j++, ip++)
9892                         *ip = old2new[*ip];
9893                 if (ap->anchor != NULL) {
9894                         ip = AtomConnectData(&ap->anchor->connect);
9895                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9896                                 *ip = old2new[*ip];
9897                 }
9898         }
9899         
9900         if (mp->par != NULL) {
9901                 /*  Renumber the parameters  */
9902                 int n;
9903                 for (j = kFirstParType; j <= kLastParType; j++) {
9904                         n = ParameterGetCountForType(mp->par, j);
9905                         for (i = 0; i < n; i++) {
9906                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9907                                 if (up != NULL)
9908                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9909                         }
9910                 }
9911         }
9912         
9913         /*  Renumber the atoms  */
9914         for (i = 0; i < mp->natoms; i++)
9915                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9916         retval = 0;
9917         
9918         MoleculeIncrementModifyCount(mp);
9919         mp->needsMDRebuild = 1;
9920
9921   end:
9922         __MoleculeUnlock(mp);
9923         free(saveAtoms);
9924         if (old2new_out == NULL)
9925                 free(old2new);
9926         return retval;
9927 }
9928
9929 #pragma mark ====== Coordinate Transform ======
9930
9931 void
9932 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9933 {
9934         int i;
9935         Atom *ap;
9936         Symop new_symop;
9937         Transform rtr, symtr;
9938         if (mp == NULL || tr == NULL)
9939                 return;
9940         TransformInvert(rtr, tr);
9941         __MoleculeLock(mp);
9942         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9943                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9944                         TransformVec(&ap->r, tr, &ap->r);
9945                         if (!SYMOP_ALIVE(ap->symop))
9946                                 continue;
9947                         /*  Transform symop  */
9948                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9949                                 continue;
9950                         TransformMul(symtr, tr, symtr);
9951                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9952                                 TransformMul(symtr, symtr, rtr);
9953                 } else {
9954                         if (!SYMOP_ALIVE(ap->symop))
9955                                 continue;
9956                         /*  Transform symop if the base atom is transformed  */
9957                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9958                                 continue;
9959                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9960                                 continue;
9961                         TransformMul(symtr, symtr, rtr);
9962                 }
9963                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9964                         continue;
9965                 ap->symop = new_symop;
9966         }
9967         mp->needsMDCopyCoordinates = 1;
9968         __MoleculeUnlock(mp);
9969         sMoleculeNotifyChangeAppearance(mp);
9970 }
9971
9972 /*
9973 void
9974 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9975 {
9976         int i;
9977         Atom *ap;
9978         if (mp == NULL || tr == NULL)
9979                 return;
9980         __MoleculeLock(mp);
9981         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9982                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9983                         continue;
9984                 TransformVec(&ap->r, tr, &ap->r);
9985         }
9986         mp->needsMDCopyCoordinates = 1;
9987         __MoleculeUnlock(mp);
9988         sMoleculeNotifyChangeAppearance(mp);
9989 }
9990 */
9991
9992 void
9993 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9994 {
9995         Transform tr;
9996         if (mp == NULL || vp == NULL)
9997                 return;
9998         memset(tr, 0, sizeof(tr));
9999         tr[0] = tr[4] = tr[8] = 1.0;
10000         tr[9] = vp->x;
10001         tr[10] = vp->y;
10002         tr[11] = vp->z;
10003         MoleculeTransform(mp, tr, group);
10004 }
10005
10006 void
10007 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
10008 {
10009         Transform tr;
10010         TransformForRotation(tr, axis, angle, center);
10011         MoleculeTransform(mp, tr, group);
10012 }
10013
10014 int
10015 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
10016 {
10017         int i;
10018         Atom *ap;
10019         Double w;
10020         if (mp == NULL || center == NULL)
10021                 return 1;
10022         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
10023                 return 2;   /*  Empty molecule  */
10024         w = 0.0;
10025         center->x = center->y = center->z = 0.0;
10026         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10027                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
10028                         continue;
10029                 VecScaleInc(*center, ap->r, ap->weight);
10030                 w += ap->weight;
10031         }
10032         if (w < 1e-7)
10033                 return 3;  /*  Atomic weights are not defined?  */
10034         w = 1.0 / w;
10035         VecScaleSelf(*center, w);
10036         return 0;
10037 }
10038
10039 int
10040 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
10041 {
10042         Vector vmin, vmax;
10043         int i;
10044         Atom *ap;
10045         if (mp == NULL)
10046                 return 1;
10047         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
10048                 return 2;   /*  Empty molecule  */
10049         vmin.x = vmin.y = vmin.z = 1e50;
10050         vmax.x = vmax.y = vmax.z = -1e50;
10051         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10052                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
10053                         continue;
10054                 if (vmin.x > ap->r.x)
10055                         vmin.x = ap->r.x;
10056                 if (vmin.y > ap->r.y)
10057                         vmin.y = ap->r.y;
10058                 if (vmin.z > ap->r.z)
10059                         vmin.z = ap->r.z;
10060                 if (vmax.x < ap->r.x)
10061                         vmax.x = ap->r.x;
10062                 if (vmax.y < ap->r.y)
10063                         vmax.y = ap->r.y;
10064                 if (vmax.z < ap->r.z)
10065                         vmax.z = ap->r.z;
10066         }
10067         if (min != NULL)
10068                 *min = vmin;
10069         if (max != NULL)
10070                 *max = vmax;
10071         return 0;       
10072 }
10073
10074 #pragma mark ====== Measurements ======
10075
10076 Double
10077 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
10078 {
10079         Vector r1, r2;
10080 /*      if (mp->is_xtal_coord) {
10081                 TransformVec(&r1, mp->cell->tr, vp1);
10082                 TransformVec(&r2, mp->cell->tr, vp2);
10083         } else */ {
10084                 r1 = *vp1;
10085                 r2 = *vp2;
10086         }
10087         VecDec(r1, r2);
10088         return VecLength(r1);
10089 }
10090
10091 Double
10092 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
10093 {
10094         Vector r1, r2, r3;
10095         double w;
10096 /*      if (mp->is_xtal_coord) {
10097                 TransformVec(&r1, mp->cell->tr, vp1);
10098                 TransformVec(&r2, mp->cell->tr, vp2);
10099                 TransformVec(&r3, mp->cell->tr, vp3);
10100         } else */ {
10101                 r1 = *vp1;
10102                 r2 = *vp2;
10103                 r3 = *vp3;
10104         }
10105         VecDec(r1, r2);
10106         VecDec(r3, r2);
10107         w = VecLength(r1) * VecLength(r3);
10108         if (w < 1e-20)
10109                 return NAN;
10110         return acos(VecDot(r1, r3) / w) * kRad2Deg;
10111 }
10112
10113 Double
10114 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
10115 {
10116         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
10117         double w1, w2, w3;
10118 /*      if (mp->is_xtal_coord) {
10119                 TransformVec(&r1, mp->cell->tr, vp1);
10120                 TransformVec(&r2, mp->cell->tr, vp2);
10121                 TransformVec(&r3, mp->cell->tr, vp3);
10122                 TransformVec(&r4, mp->cell->tr, vp4);
10123         } else */ {
10124                 r1 = *vp1;
10125                 r2 = *vp2;
10126                 r3 = *vp3;
10127                 r4 = *vp4;
10128         }
10129         VecSub(r21, r1, r2);
10130         VecSub(r32, r2, r3);
10131         VecSub(r43, r3, r4);
10132         VecCross(v1, r21, r32);
10133         VecCross(v2, r32, r43);
10134         VecCross(v3, r32, v1);
10135         w1 = VecLength(v1);
10136         w2 = VecLength(v2);
10137         w3 = VecLength(v3);
10138         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
10139                 return NAN;
10140         } else {
10141                 w1 = 1.0 / w1;
10142                 w2 = 1.0 / w2;
10143                 w3 = 1.0 / w3;
10144                 VecScaleSelf(v1, w1);
10145                 VecScaleSelf(v2, w2);
10146                 VecScaleSelf(v3, w3);
10147                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
10148         }
10149 }
10150
10151 #pragma mark ====== XtalCell Parameters ======
10152
10153 void
10154 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
10155 {
10156         if (mp->cell != NULL) {
10157                 TransformVec(dst, mp->cell->tr, src);
10158         } else *dst = *src;
10159 }
10160
10161 void
10162 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10163 {
10164         if (mp->cell != NULL) {
10165                 TransformVec(dst, mp->cell->rtr, src);
10166         } else *dst = *src;
10167 }
10168
10169 int
10170 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10171 {
10172         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10173         int n1, n2, n3;
10174         Vector *vp1, *vp2, *vp3;
10175         Vector v1, v2;
10176
10177         if (cp == NULL)
10178                 return 0;
10179         for (n1 = 0; n1 < 3; n1++) {
10180                 if (cp->flags[n1] != 0)
10181                         break;
10182         }
10183         if (n1 == 3) {
10184                 /*  All directions are non-periodic  */
10185                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10186                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10187         } else {
10188                 n2 = (n1 + 1) % 3;
10189                 n3 = (n1 + 2) % 3;
10190                 vp1 = &(cp->axes[n1]);
10191                 vp2 = &(cp->axes[n2]);
10192                 vp3 = &(cp->axes[n3]);
10193                 cp->tr[n1*3] = vp1->x;
10194                 cp->tr[n1*3+1] = vp1->y;
10195                 cp->tr[n1*3+2] = vp1->z;
10196                 cp->tr[9] = cp->origin.x;
10197                 cp->tr[10] = cp->origin.y;
10198                 cp->tr[11] = cp->origin.z;
10199                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10200                         /*  1-dimensional or 2-dimensional system  */
10201                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10202                          possible with a single matrix  */
10203                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10204                                 /*  1-dimensional  */
10205                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10206                                 VecCross(v1, *vp1, xvec);
10207                                 VecCross(v2, *vp1, yvec);
10208                                 if (VecLength2(v1) < VecLength2(v2))
10209                                         v1 = v2;
10210                                 VecCross(v2, *vp1, v1);
10211                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10212                                         return -1;   /*  Non-regular transform  */
10213                         } else if (cp->flags[n2] == 0) {
10214                                 v2 = *vp3;
10215                                 VecCross(v1, v2, *vp1);
10216                                 if (NormalizeVec(&v1, &v1))
10217                                         return -1;  /*  Non-regular transform  */
10218                         } else {
10219                                 v1 = *vp2;
10220                                 VecCross(v2, *vp1, v1);
10221                                 if (NormalizeVec(&v2, &v2))
10222                                         return -1;  /*  Non-regular transform  */
10223                         }
10224                         cp->tr[n2*3] = v1.x;
10225                         cp->tr[n2*3+1] = v1.y;
10226                         cp->tr[n2*3+2] = v1.z;
10227                         cp->tr[n3*3] = v2.x;
10228                         cp->tr[n3*3+1] = v2.y;
10229                         cp->tr[n3*3+2] = v2.z;
10230                 } else {
10231                         VecCross(v1, *vp1, *vp2);
10232                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
10233                                 return -1;  /*  Non-regular transform  */
10234                         cp->tr[n2*3] = vp2->x;
10235                         cp->tr[n2*3+1] = vp2->y;
10236                         cp->tr[n2*3+2] = vp2->z;
10237                         cp->tr[n3*3] = vp3->x;
10238                         cp->tr[n3*3+1] = vp3->y;
10239                         cp->tr[n3*3+2] = vp3->z;
10240                 }
10241         }
10242         if (TransformInvert(cp->rtr, cp->tr))
10243                 return -1;  /*  Non-regular transform  */
10244
10245         /*  Calculate the reciprocal cell parameters  */
10246         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10247         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10248         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10249         cp->rcell[3] = acos((cp->rtr[1] * cp->rtr[2] + cp->rtr[4] * cp->rtr[5] + cp->rtr[7] * cp->rtr[8]) / (cp->rcell[1] * cp->rcell[2])) * kRad2Deg;
10250         cp->rcell[4] = acos((cp->rtr[2] * cp->rtr[0] + cp->rtr[5] * cp->rtr[3] + cp->rtr[8] * cp->rtr[6]) / (cp->rcell[2] * cp->rcell[0])) * kRad2Deg;
10251         cp->rcell[5] = acos((cp->rtr[0] * cp->rtr[1] + cp->rtr[3] * cp->rtr[4] + cp->rtr[6] * cp->rtr[7]) / (cp->rcell[0] * cp->rcell[1])) * kRad2Deg;
10252         
10253         if (calc_abc) {
10254                 /*  Calculate a, b, c, alpha, beta, gamma  */
10255                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10256                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10257                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10258                 cp->cell[3] = acos((cp->tr[3] * cp->tr[6] + cp->tr[4] * cp->tr[7] + cp->tr[5] * cp->tr[8]) / (cp->cell[1] * cp->cell[2])) * kRad2Deg;
10259                 cp->cell[4] = acos((cp->tr[6] * cp->tr[0] + cp->tr[7] * cp->tr[1] + cp->tr[8] * cp->tr[2]) / (cp->cell[2] * cp->cell[0])) * kRad2Deg;
10260                 cp->cell[5] = acos((cp->tr[0] * cp->tr[3] + cp->tr[1] * cp->tr[4] + cp->tr[2] * cp->tr[5]) / (cp->cell[0] * cp->cell[1])) * kRad2Deg;
10261         }
10262         return 0;
10263 }
10264
10265 void
10266 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10267 {
10268         XtalCell *cp;
10269         int i;
10270         Atom *ap;
10271         Transform cmat;
10272         if (mp == NULL)
10273                 return;
10274         __MoleculeLock(mp);
10275         memset(&cmat, 0, sizeof(Transform));
10276         if (mp->cell != NULL)
10277                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10278         else
10279                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10280         if (a == 0.0) {
10281                 if (mp->cell != NULL) {
10282                         free(mp->cell);
10283                         mp->needsMDRebuild = 1;
10284                 }
10285                 mp->cell = NULL;
10286         } else {
10287                 cp = mp->cell;
10288                 if (cp == NULL) {
10289                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10290                         if (cp == NULL)
10291                                 Panic("Low memory during setting cell parameters");
10292                         mp->cell = cp;
10293                         mp->needsMDRebuild = 1;
10294                 }
10295                 /*  alpha, beta, gamma are in degree  */
10296                 cp->cell[0] = a;
10297                 cp->cell[1] = b;
10298                 cp->cell[2] = c;
10299                 cp->cell[3] = alpha;
10300                 cp->cell[4] = beta;
10301                 cp->cell[5] = gamma;
10302                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10303                         /*  c unique (hexagonal etc.)  */
10304                         Double cosa, cosb, sinb, cosg;
10305                         cosa = cos(alpha * kDeg2Rad);
10306                         cosb = cos(beta * kDeg2Rad);
10307                         sinb = sin(beta * kDeg2Rad);
10308                         cosg = cos(gamma * kDeg2Rad);
10309                         cp->axes[0].x = a * sinb;
10310                         cp->axes[0].y = 0;
10311                         cp->axes[0].z = a * cosb;
10312                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10313                         cp->axes[1].z = b * cosa;
10314                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10315                         cp->axes[2].x = 0;
10316                         cp->axes[2].y = 0;
10317                         cp->axes[2].z = c;
10318                 } else {
10319                         /*  b unique  */
10320                         Double cosg, sing, cosa, cosb;
10321                         cosa = cos(alpha * kDeg2Rad);
10322                         cosb = cos(beta * kDeg2Rad);
10323                         cosg = cos(gamma * kDeg2Rad);
10324                         sing = sin(gamma * kDeg2Rad);
10325                         cp->axes[0].x = a * sing;
10326                         cp->axes[0].y = a * cosg;
10327                         cp->axes[0].z = 0;
10328                         cp->axes[1].x = 0;
10329                         cp->axes[1].y = b;
10330                         cp->axes[1].z = 0;
10331                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10332                         cp->axes[2].y = c * cosa;
10333                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10334                 }
10335                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10336                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10337                 MoleculeCalculateCellFromAxes(cp, 0);
10338                 TransformMul(cmat, cp->tr, cmat);
10339         }
10340         
10341         /*  Update the coordinates (if requested)  */
10342         if (convertCoordinates) {
10343                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10344                         TransformVec(&(ap->r), cmat, &(ap->r));
10345                 }
10346         }
10347         
10348         /*  Update the anisotropic parameters  */
10349         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10350                 Aniso *anp = ap->aniso;
10351                 if (anp != NULL) {
10352                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10353                 }
10354         }
10355         __MoleculeUnlock(mp);
10356         sMoleculeNotifyChangeAppearance(mp);
10357 }
10358
10359 void
10360 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10361 {
10362         Double d, dx;
10363         int u = 0;
10364         const Double log2 = 0.693147180559945;
10365         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
10366         Transform m1, m2;
10367         Aniso *anp;
10368         XtalCell *cp;
10369         Vector axis[3];
10370         Double val[3];
10371         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10372                 return;
10373         anp = mp->atoms[n1].aniso;
10374         __MoleculeLock(mp);
10375         if (anp == NULL) {
10376                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10377                 if (anp == NULL) {
10378                         __MoleculeUnlock(mp);
10379                         Panic("Low memory during setting anisotropic atom parameters");
10380                 }
10381                 mp->atoms[n1].aniso = anp;
10382         }
10383         switch (type) {
10384                 case 1: d = 1; dx = 0.5; break;
10385                 case 2: d = log2; dx = log2; break;
10386                 case 3: d = log2; dx = log2 * 0.5; break;
10387                 case 4: u = 1; d = 0.25; dx = 0.25; break;
10388                 case 5: u = 1; d = 0.25; dx = 0.125; break;
10389                 case 8: u = 1; d = pi22; dx = pi22; break;
10390                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10391                 case 10: d = pi22; dx = pi22; break;
10392                 default: d = dx = 1; break;
10393         }
10394         anp->bij[0] = x11 * d;
10395         anp->bij[1] = x22 * d;
10396         anp->bij[2] = x33 * d;
10397         anp->bij[3] = x12 * dx;
10398         anp->bij[4] = x13 * dx;
10399         anp->bij[5] = x23 * dx;
10400         if (sigmaptr != NULL) {
10401                 anp->has_bsig = 1;
10402                 anp->bsig[0] = sigmaptr[0] * d;
10403                 anp->bsig[1] = sigmaptr[1] * d;
10404                 anp->bsig[2] = sigmaptr[2] * d;
10405                 anp->bsig[3] = sigmaptr[3] * dx;
10406                 anp->bsig[4] = sigmaptr[4] * dx;
10407                 anp->bsig[5] = sigmaptr[5] * dx;
10408         } else {
10409                 anp->has_bsig = 0;
10410                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10411         }
10412         cp = mp->cell;
10413         if (cp != NULL && u == 1) {
10414                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10415                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10416                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10417                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10418                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10419                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10420                 if (sigmaptr != NULL) {
10421                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10422                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10423                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10424                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10425                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10426                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10427                 }
10428         }
10429         
10430         /*  Calculate the principal axes (in Cartesian coordinates)  */
10431         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
10432                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10433                 in which x and z are the crystal-space and cartesian coordinates. */
10434         m1[0] = anp->bij[0] / pi22;
10435         m1[4] = anp->bij[1] / pi22;
10436         m1[8] = anp->bij[2] / pi22;
10437         m1[1] = m1[3] = anp->bij[3] / pi22;
10438         m1[2] = m1[6] = anp->bij[4] / pi22;
10439         m1[5] = m1[7] = anp->bij[5] / pi22;
10440         MatrixInvert(m1, m1);
10441         if (cp != NULL) {
10442                 memmove(m2, cp->rtr, sizeof(Mat33));
10443                 MatrixMul(m1, m1, m2);
10444                 MatrixTranspose(m2, m2);
10445                 MatrixMul(m1, m2, m1);
10446         }
10447         MatrixSymDiagonalize(m1, val, axis);
10448         for (u = 0; u < 3; u++) {
10449         anp->eigval[u] = val[u];
10450                 if (val[u] < 0) {
10451                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10452                         val[u] = 0.001;
10453                 } else {
10454                         val[u] = 1 / sqrt(val[u]);
10455                 }
10456                 anp->pmat[u*3] = axis[u].x * val[u];
10457                 anp->pmat[u*3+1] = axis[u].y * val[u];
10458                 anp->pmat[u*3+2] = axis[u].z * val[u];
10459         }
10460         __MoleculeUnlock(mp);
10461 }
10462
10463 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10464 void
10465 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10466 {
10467         Atom *ap, *ap2;
10468         Transform t1, t2;
10469         if (mp == NULL || idx < 0 || idx >= mp->natoms)
10470                 return;
10471         ap = ATOM_AT_INDEX(mp->atoms, idx);
10472         if (!SYMOP_ALIVE(ap->symop))
10473                 return;
10474         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10475         if (ap2->aniso == NULL) {
10476                 if (ap->aniso != NULL) {
10477                         free(ap->aniso);
10478                         ap->aniso = NULL;
10479                 }
10480                 return;
10481         }
10482         if (ap->aniso == NULL)
10483                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10484         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10485                 /*  Just copy the aniso parameters  */
10486                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10487                 return;
10488         }
10489         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10490         t1[9] = t1[10] = t1[11] = 0.0;
10491         memset(t2, 0, sizeof(Transform));
10492         t2[0] = ap2->aniso->bij[0];
10493         t2[4] = ap2->aniso->bij[1];
10494         t2[8] = ap2->aniso->bij[2];
10495         t2[1] = t2[3] = ap2->aniso->bij[3];
10496         t2[2] = t2[6] = ap2->aniso->bij[4];
10497         t2[5] = t2[7] = ap2->aniso->bij[5];
10498         TransformMul(t2, t1, t2);
10499         TransformInvert(t1, t1);
10500         TransformMul(t2, t2, t1);
10501         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10502 }
10503
10504 int
10505 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10506 {
10507         static Vector zeroVec = {0, 0, 0};
10508         XtalCell b;
10509         Transform cmat;
10510         int i, n;
10511         Atom *ap;
10512         if (mp == NULL)
10513                 return 0;
10514         if (mp->cell != NULL)
10515                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10516         else
10517                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10518         if (ax == NULL) {
10519                 if (mp->cell != NULL) {
10520                         free(mp->cell);
10521                         mp->needsMDRebuild = 1;
10522                 }
10523                 mp->cell = NULL;
10524                 return 0;
10525         }       
10526         memset(&b, 0, sizeof(b));
10527         b.axes[0] = (ax != NULL ? *ax : zeroVec);
10528         b.axes[1] = (ay != NULL ? *ay : zeroVec);
10529         b.axes[2] = (az != NULL ? *az : zeroVec);
10530         b.origin = *ao;
10531         memmove(b.flags, periodic, 3);
10532         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10533                 return -1;
10534         __MoleculeLock(mp);
10535         if (mp->cell == NULL) {
10536                 mp->needsMDRebuild = 1;
10537         } else {
10538                 if (mp->cell->has_sigma) {
10539                         /*  Keep the sigma  */
10540                         b.has_sigma = 1;
10541                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10542                 }
10543                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10544                         mp->needsMDRebuild = 1;
10545                 }
10546                 free(mp->cell);
10547         }
10548         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10549         if (mp->cell != NULL) {
10550                 memmove(mp->cell, &b, sizeof(XtalCell));
10551                 TransformMul(cmat, b.tr, cmat);
10552                 /*  Update the coordinates (if requested)  */
10553                 if (convertCoordinates) {
10554                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10555                                 TransformVec(&(ap->r), cmat, &(ap->r));
10556                         }
10557                 }
10558                 
10559                 /*  Update the anisotropic parameters  */
10560                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10561                         Aniso *anp = ap->aniso;
10562                         if (anp != NULL) {
10563                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10564                         }
10565                 }
10566                 n = 0;
10567         } else n = -2;  /*  Out of memory  */
10568         __MoleculeUnlock(mp);
10569         sMoleculeNotifyChangeAppearance(mp);
10570         return n;
10571 }
10572
10573 #pragma mark ====== Fragment manipulation ======
10574
10575 static void
10576 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10577 {
10578         Atom *ap;
10579         Int i, *cp, idx2;
10580         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10581                 return;
10582         IntGroupAdd(result, idx, 1);
10583         ap = ATOM_AT_INDEX(mp->atoms, idx);
10584         cp = AtomConnectData(&ap->connect);
10585         for (i = 0; i < ap->connect.count; i++) {
10586                 idx2 = cp[i];
10587                 if (IntGroupLookup(result, idx2, NULL))
10588                         continue;
10589                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10590                         continue;  /*  bond between two pi_anchors is ignored  */
10591                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10592         }
10593         if (ap->anchor != NULL) {
10594                 cp = AtomConnectData(&ap->anchor->connect);
10595                 for (i = 0; i < ap->anchor->connect.count; i++) {
10596                         idx2 = cp[i];
10597                         if (IntGroupLookup(result, idx2, NULL))
10598                                 continue;
10599                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
10600                 }
10601         }
10602 }
10603
10604 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10605     not containing the atoms in exatoms  */
10606 IntGroup *
10607 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10608 {
10609         IntGroup *result;
10610         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10611                 return NULL;
10612         result = IntGroupNew();
10613         sMoleculeFragmentSub(mp, n1, result, exatoms);
10614         return result;
10615 }
10616
10617 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10618     not containing the atoms n2, n3, ... (terminated by -1)  */
10619 IntGroup *
10620 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10621 {
10622         int i;
10623         IntGroup *exatoms, *result;
10624         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10625                 return NULL;
10626         exatoms = IntGroupNew();
10627         for (i = 0; i < argc; i++)
10628                 IntGroupAdd(exatoms, argv[i], 1);
10629         result = IntGroupNew();
10630         sMoleculeFragmentSub(mp, n1, result, exatoms);
10631         IntGroupRelease(exatoms);
10632         return result;
10633 }
10634
10635 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10636     not containing the atoms in exatoms  */
10637 IntGroup *
10638 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10639 {
10640         IntGroupIterator iter;
10641         IntGroup *result;
10642         int i;
10643         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10644                 return NULL;
10645         IntGroupIteratorInit(inatoms, &iter);
10646         result = IntGroupNew();
10647         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10648                 sMoleculeFragmentSub(mp, i, result, exatoms);
10649         }
10650         IntGroupIteratorRelease(&iter);
10651         return result;
10652 }
10653
10654 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10655     group is bound to the rest of the molecule via only one bond.
10656         If the result is true, then the atoms belonging to the (only) bond are returned
10657         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10658         and n2 can be NULL, if those informations are not needed.  */
10659 int
10660 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10661 {
10662         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10663         Atom *ap;
10664         if (mp == NULL || mp->natoms == 0 || group == NULL)
10665                 return 0;  /*  Invalid arguments  */
10666         bond_count = 0;
10667         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10668                 i2 = IntGroupGetEndPoint(group, i);
10669                 for (j = i1; j < i2; j++) {
10670                         if (j < 0 || j >= mp->natoms)
10671                                 return 0;  /*  Invalid atom group  */
10672                         ap = ATOM_AT_INDEX(mp->atoms, j);
10673                         cp = AtomConnectData(&ap->connect);
10674                         for (k = 0; k < ap->connect.count; k++) {
10675                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10676                                         continue;  /*  Ignore bond between two pi_anchors  */
10677                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10678                                         bond_count++;
10679                                         nval1 = j;
10680                                         nval2 = cp[k];
10681                                         if (bond_count > 1)
10682                                                 return 0;  /*  Too many bonds  */
10683                                 }
10684                         }
10685                         if (ap->anchor != NULL) {
10686                                 cp = AtomConnectData(&ap->anchor->connect);
10687                                 for (k = 0; k < ap->anchor->connect.count; k++) {
10688                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
10689                                                 bond_count++;
10690                                                 nval1 = j;
10691                                                 nval2 = cp[k];
10692                                                 if (bond_count > 1)
10693                                                         return 0;  /*  Too many bonds  */
10694                                         }
10695                                 }                                       
10696                         }
10697                 }
10698         }
10699         if (bond_count == 1) {
10700                 if (n1 != NULL)
10701                         *n1 = nval1;
10702                 if (n2 != NULL)
10703                         *n2 = nval2;
10704                 return 1;
10705         } else {
10706                 return 0;
10707         }       
10708 }
10709
10710 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
10711     is said to be 'rotatable' when either of the following conditions are met; (1)
10712         the group is detachable, or (2) the group consists of two bonded atoms that define
10713         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10714         (either a new IntGroup or 'group' with incremented reference count; thus the caller
10715         is responsible for releasing the returned value).  */
10716 int
10717 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10718 {
10719         int i1, i2;
10720         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10721                 if (rotGroup != NULL) {
10722                         IntGroupRetain(group);
10723                         *rotGroup = group;
10724                 }
10725                 return 1;
10726         }
10727         if (group != NULL && IntGroupGetCount(group) == 2) {
10728                 i1 = IntGroupGetNthPoint(group, 0);
10729                 i2 = IntGroupGetNthPoint(group, 1);
10730                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10731                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10732                         if (frag == NULL)
10733                                 return 0;
10734                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10735                         if (i1 == 0) {
10736                                 IntGroupRelease(frag);
10737                                 if (rotGroup != NULL)
10738                                         *rotGroup = NULL;
10739                                 return 0;
10740                         }
10741                         if (rotGroup != NULL)
10742                                 *rotGroup = frag;
10743                         else if (frag != NULL)
10744                                 IntGroupRelease(frag);
10745                         return i1;
10746                 }
10747         }
10748         return 0;
10749 }
10750
10751 #pragma mark ====== Multiple frame ======
10752
10753 int
10754 MoleculeGetNumberOfFrames(Molecule *mp)
10755 {
10756         if (mp == NULL)
10757                 return 0;
10758         if (mp->nframes <= 0) {
10759                 /*  Recalculate  */
10760                 int i, n;
10761                 Atom *ap;
10762                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10763                         if (ap->nframes > n)
10764                                 n = ap->nframes;
10765                 }
10766                 if (n == 0)
10767                         n = 1;
10768                 mp->nframes = n;
10769         }
10770         return mp->nframes;
10771 }
10772
10773 int
10774 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10775 {
10776         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10777         Vector *tempv, *vp;
10778         Atom *ap;
10779         MolProp *prp;
10780         Double *dp;
10781         
10782         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10783                 return -1;
10784
10785         n_old = MoleculeGetNumberOfFrames(mp);
10786         n_new = n_old + count;
10787         last_inserted = IntGroupGetNthPoint(group, count - 1);
10788         if (n_new <= last_inserted) {
10789                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10790                 n_new += exframes;
10791         } else exframes = 0;
10792
10793         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10794         if (tempv == NULL)
10795                 return -1;
10796
10797         __MoleculeLock(mp);
10798
10799         /*  Copy back the current coordinates  */
10800         /*  No change in the current coordinates, but the frame buffer is updated  */
10801         MoleculeSelectFrame(mp, mp->cframe, 1); 
10802         
10803         /*  Expand ap->frames for all atoms  */
10804         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10805                 Int n = ap->nframes;
10806                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10807                 if (ap->frames == NULL) {
10808                         __MoleculeUnlock(mp);
10809                         return -1;
10810                 }
10811                 for (j = n; j < n_new; j++)
10812                         ap->frames[j] = ap->r;
10813         }
10814         if (mp->cell != NULL) {
10815                 j = mp->nframe_cells;
10816                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10817                 for (i = j; i < n_new; i++) {
10818                         /*  Set the current cell parameters to the expanded frames  */
10819                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10820                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10821                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10822                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10823                 }
10824         }
10825         
10826         /*  Expand propvals for all properties  */
10827         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10828                 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10829                 if (dp == NULL) {
10830                         __MoleculeUnlock(mp);
10831                         return -1;
10832                 }
10833                 for (j = n_old; j < n_new; j++)
10834                         dp[j] = 0.0;
10835                 prp->propvals = dp;
10836         }
10837         
10838         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10839         /*  s = t = 0,  */
10840         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10841             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10842                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10843                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10844                 ...
10845                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10846                 At last, s will become n_old and t will become count.  */
10847         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10848                 int s, t, ns, ne, mult;
10849                 Vector cr;
10850                 ne = s = t = 0;
10851                 if (i == mp->natoms) {
10852                         if (mp->cell == NULL || mp->frame_cells == NULL)
10853                                 continue;
10854                         vp = mp->frame_cells;
10855                         mult = 4;
10856                 } else if (i < mp->natoms) {
10857                         cr = ap->r;
10858                         vp = ap->frames;
10859                         mult = 1;
10860                 } else {
10861                         dp = prp->propvals;
10862                 }
10863                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10864                         if (ns > ne) {
10865                                 if (i <= mp->natoms)
10866                                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10867                                 else
10868                                         memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne)); 
10869                                 s += ns - ne;
10870                         }
10871                         ne = IntGroupGetEndPoint(group, j);
10872                         while (ns < ne) {
10873                                 if (i == mp->natoms) {
10874                                         if (inFrameCell != NULL) {
10875                                                 tempv[ns * 4] = inFrameCell[t * 4];
10876                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10877                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10878                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10879                                         } else {
10880                                                 tempv[ns * 4] = mp->cell->axes[0];
10881                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10882                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10883                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10884                                         }
10885                                 } else if (i < mp->natoms) {
10886                                         if (inFrame != NULL)
10887                                                 tempv[ns] = inFrame[natoms * t + i];
10888                                         else
10889                                                 tempv[ns] = cr;
10890                                 } else {
10891                                         ((Double *)tempv)[ns] = 0.0;
10892                                 }
10893                                 t++;
10894                                 ns++;
10895                         }
10896                 }
10897                 if (n_new > ne) {
10898                         if (i <= mp->natoms)
10899                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10900                         else
10901                                 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10902                         s += n_new - ne;
10903                 }
10904                 if (i < mp->natoms)
10905                         ap->nframes = n_new;
10906                 if (i <= mp->natoms) {
10907                         memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10908                         if (i < mp->natoms) {
10909                                 ap->nframes = n_new;
10910                                 ap = ATOM_NEXT(ap);
10911                         }
10912                 } else {
10913                         memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10914                         prp++;
10915                 }
10916         }
10917         free(tempv);
10918         mp->nframes = n_new;
10919         MoleculeSelectFrame(mp, last_inserted, 0);
10920         MoleculeIncrementModifyCount(mp);
10921         __MoleculeUnlock(mp);
10922         return count;
10923 }
10924
10925 int
10926 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10927 {
10928         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10929         Vector *tempv, *vp;
10930         Atom *ap;
10931         MolProp *prp;
10932         IntGroup *group, *group2;
10933
10934         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10935                 return -1;
10936
10937         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10938         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10939         if (mp->cell != NULL && mp->frame_cells != NULL)
10940                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10941
10942         n_old = MoleculeGetNumberOfFrames(mp);
10943         if (n_old == 1)
10944                 return -2;  /*  Cannot delete last frame  */
10945
10946         group = IntGroupNew();
10947         group2 = IntGroupNewWithPoints(0, n_old, -1);
10948         IntGroupIntersect(inGroup, group2, group);
10949         IntGroupRelease(group2);
10950         count = IntGroupGetCount(group);
10951         n_new = n_old - count;
10952         if (n_new < 1) {
10953                 IntGroupRelease(group);
10954                 return -2;  /*  Trying to delete too many frames  */
10955         }
10956         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10957         if (tempv == NULL) {
10958                 IntGroupRelease(group);
10959                 return -1;
10960         }
10961
10962         __MoleculeLock(mp);
10963
10964         /*  Copy back the current coordinates  */
10965         /*  No change in the current coordinates, but the frame buffer is updated  */
10966         MoleculeSelectFrame(mp, mp->cframe, 1); 
10967
10968         /*  Determine which frame should be selected after removal is completed  */
10969         {
10970                 int n1;
10971                 if (IntGroupLookup(group, mp->cframe, &i)) {
10972                         /*  cframe will be removed  */
10973                         n1 = IntGroupGetStartPoint(group, i) - 1;
10974                         if (n1 < 0)
10975                                 n1 = IntGroupGetEndPoint(group, i);
10976                 } else n1 = mp->cframe;
10977                 /*  Change to that frame  */
10978                 MoleculeSelectFrame(mp, n1, 0);
10979                 group2 = IntGroupNewFromIntGroup(group);
10980                 IntGroupReverse(group2, 0, n_old);
10981                 new_cframe = IntGroupLookupPoint(group2, n1);
10982                 if (new_cframe < 0)
10983                         return -3;  /*  This cannot happen  */
10984                 IntGroupRelease(group2);
10985         }
10986
10987         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10988         /*  s = t = 0, */
10989         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10990             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10991                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10992                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10993                 ...
10994                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10995                 At last, s will become n_new and t will become count.  */
10996         nframes = 0;
10997         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10998                 int s, t, j, ns, ne;
10999                 int mult;
11000                 /*  if i == mp->natoms, mp->frame_cells is handled  */
11001                 if (i == mp->natoms) {
11002                         if (mp->cell == NULL || mp->frame_cells == NULL)
11003                                 continue;
11004                         mult = 4 * sizeof(Vector);
11005                         vp = mp->frame_cells;
11006                         old_count = n_old;
11007                 } else if (i < mp->natoms) {
11008                         mult = sizeof(Vector);
11009                         vp = ap->frames;
11010                         if (vp == NULL) {
11011                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
11012                                 if (ap->frames == NULL) {
11013                                         __MoleculeUnlock(mp);
11014                                         return -1;
11015                                 }
11016                                 vp = ap->frames;
11017                         }
11018                         old_count = ap->nframes;
11019                 } else {
11020                         mult = sizeof(Double);
11021                         vp = (Vector *)prp->propvals;
11022                         old_count = n_old;
11023                 }
11024
11025                 /*  Copy vp to tempv  */
11026                 memset(tempv, 0, mult * n_old);
11027                 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
11028                 ne = ns = s = t = 0;
11029                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
11030                         if (ns > n_old)
11031                                 ns = n_old;
11032                         if (ns > ne) {
11033                                 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
11034                                 s += ns - ne;
11035                         }
11036                         ne = IntGroupGetEndPoint(group, j);
11037                         if (ne > n_old)
11038                                 ne = n_old;
11039                         while (ns < ne) {
11040                                 if (i < mp->natoms)
11041                                         outFrame[natoms * t + i] = tempv[ns];
11042                                 else if (i == mp->natoms) {
11043                                         if (outFrameCell != NULL) {
11044                                                 outFrameCell[t * 4] = tempv[ns * 4];
11045                                                 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
11046                                                 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
11047                                                 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
11048                                         }
11049                                 }
11050                                 t++;
11051                                 ns++;
11052                         }
11053                 }
11054                 if (n_old > ne) {
11055                         memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
11056                         s += n_old - ne;
11057                 }
11058                 if (i < mp->natoms)
11059                         ap->nframes = s;
11060                 if (nframes < s)
11061                         nframes = s;
11062                 if (s <= 1) {
11063                         if (i < mp->natoms) {
11064                                 free(ap->frames);
11065                                 ap->frames = NULL;
11066                                 ap->nframes = 0;
11067                         } else if (i == mp->natoms) {
11068                                 free(mp->frame_cells);
11069                                 mp->frame_cells = NULL;
11070                                 mp->nframe_cells = 0;
11071                         } else {
11072                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
11073                         }
11074                 } else {
11075                         if (i < mp->natoms) {
11076                                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
11077                                 ap->nframes = s;
11078                         } else if (i == mp->natoms) {
11079                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
11080                                 mp->nframe_cells = s;
11081                         } else {
11082                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
11083                         }
11084                 }
11085                 if (i < mp->natoms) {
11086                         ap = ATOM_NEXT(ap);
11087                 } else if (i > mp->natoms) {
11088                         prp++;
11089                 }
11090         }
11091         free(tempv);
11092         mp->nframes = nframes;
11093         
11094         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
11095 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
11096         MoleculeSelectFrame(mp, new_cframe, 0);
11097
11098         IntGroupRelease(group);
11099
11100         MoleculeIncrementModifyCount(mp);
11101         __MoleculeUnlock(mp);
11102         return count;
11103 }
11104
11105 int
11106 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
11107 {
11108         int i, cframe, nframes, modified;
11109         Atom *ap;
11110         cframe = mp->cframe;
11111         nframes = MoleculeGetNumberOfFrames(mp);
11112         if (frame == -1)
11113                 frame = mp->cframe;
11114         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
11115                 return -1;
11116         modified = 0;
11117         __MoleculeLock(mp);
11118         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11119                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
11120                         /*  Write the current coordinate back to the frame array  */
11121                         ap->frames[cframe] = ap->r;
11122                 }
11123                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
11124                         /*  Read the coordinate from the frame array  */
11125                         ap->r = ap->frames[frame];
11126                         modified = 1;
11127                 }
11128         }
11129
11130         if (mp->cell != NULL && mp->frame_cells != NULL) {
11131                 /*  Write the current cell back to the frame_cells array  */
11132                 if (copyback && cframe >= 0) {
11133                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
11134                         vp[0] = mp->cell->axes[0];
11135                         vp[1] = mp->cell->axes[1];
11136                         vp[2] = mp->cell->axes[2];
11137                         vp[3] = mp->cell->origin;
11138                 }
11139                 /*  Set the cell from the frame array  */
11140                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
11141                         MoleculeSetPeriodicBox(mp, &mp->frame_cells[frame * 4], &mp->frame_cells[frame * 4 + 1], &mp->frame_cells[frame * 4 + 2], &mp->frame_cells[frame * 4 + 3], mp->cell->flags, 0);
11142                         modified = 1;
11143                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
11144                 }
11145         }
11146         mp->cframe = frame;
11147         if (modified)
11148                 mp->needsMDCopyCoordinates = 1;
11149         __MoleculeUnlock(mp);
11150         sMoleculeNotifyChangeAppearance(mp);
11151         return frame;
11152 }
11153
11154 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
11155     Returns the number of frames.  */
11156 int
11157 MoleculeFlushFrames(Molecule *mp)
11158 {
11159         int nframes = MoleculeGetNumberOfFrames(mp);
11160         if (nframes > 1)
11161                 MoleculeSelectFrame(mp, mp->cframe, 1);
11162         return nframes;
11163 }
11164
11165 int
11166 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11167 {
11168         Int *ip, i, j, n, nframes;
11169         Double *dp;
11170         Atom *ap;
11171         MolProp *prp;
11172         if (mp == NULL || old_idx == NULL)
11173                 return 0;
11174         nframes = MoleculeGetNumberOfFrames(mp);
11175         MoleculeFlushFrames(mp);
11176         ip = (Int *)malloc(sizeof(Int) * nframes);
11177         if (ip == NULL)
11178                 return -1;  /*  Out of memory  */
11179         memset(ip, 0, sizeof(Int) * nframes);
11180         /*  Check the argument  */
11181         for (i = 0; i < nframes; i++) {
11182                 j = old_idx[i];
11183                 if (j < 0 || j >= nframes || ip[j] != 0) {
11184                         free(ip);
11185                         return -2;  /*  Bad argument  */
11186                 }
11187                 ip[j] = 1;
11188         }
11189         free(ip);
11190         dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11191         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11192                 for (j = 0; j < nframes; j++) {
11193                         n = old_idx[j];
11194                         if (i < mp->natoms) {
11195                                 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11196                         } else if (i == mp->natoms) {
11197                                 if (mp->cell != NULL) {
11198                                         if (n < mp->nframe_cells && mp->frame_cells != NULL)
11199                                                 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11200                                         else {
11201                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11202                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11203                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11204                                                 ((Vector *)dp)[j * 4] = mp->cell->origin;
11205                                         }
11206                                 }
11207                         } else {
11208                                 dp[j] = prp->propvals[n];
11209                         }
11210                 }
11211                 for (j = 0; j < nframes; j++) {
11212                         if (i < mp->natoms) {
11213                                 if (ap->nframes <= j)
11214                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11215                                 ap->frames[j] = ((Vector *)dp)[j];
11216                         } else if (i == mp->natoms) {
11217                                 if (mp->cell != NULL) {
11218                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11219                                         memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11220                                 }
11221                         } else {
11222                                 prp->propvals[j] = dp[j];
11223                         }
11224                 }
11225                 if (i < mp->natoms)
11226                         ap = ATOM_NEXT(ap);
11227                 else if (i > mp->natoms)
11228                         prp++;
11229         }
11230         free(dp);
11231         MoleculeSelectFrame(mp, mp->cframe, 0);
11232         return 0;
11233 }
11234
11235 #pragma mark ====== Molecule Propeties ======
11236
11237 int
11238 MoleculeCreateProperty(Molecule *mp, const char *name)
11239 {
11240         int i;
11241         MolProp *prp;
11242         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11243                 if (strcmp(prp->propname, name) == 0)
11244                         return -(i + 1);
11245         }
11246         prp = (MolProp *)calloc(sizeof(MolProp), 1);
11247         if (prp == NULL)
11248                 return -10000;
11249         prp->propname = strdup(name);
11250         if (prp->propname == NULL)
11251                 return -10000;
11252         i = MoleculeGetNumberOfFrames(mp);
11253         prp->propvals = (Double *)calloc(sizeof(Double), i);
11254         if (prp->propvals == NULL)
11255                 return -10000;
11256         AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11257         free(prp);
11258         return mp->nmolprops - 1;
11259 }
11260
11261 int
11262 MoleculeLookUpProperty(Molecule *mp, const char *name)
11263 {
11264         int i;
11265         MolProp *prp;
11266         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11267                 if (strcmp(prp->propname, name) == 0)
11268                         return i;
11269         }
11270         return -1;
11271 }
11272
11273 int
11274 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11275 {
11276         if (idx >= 0 && idx < mp->nmolprops) {
11277                 free(mp->molprops[idx].propname);
11278                 free(mp->molprops[idx].propvals);
11279                 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11280                 return idx;
11281         }
11282         return -1;
11283 }
11284
11285 int
11286 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11287 {
11288         IntGroupIterator iter;
11289         int i, n, nframes;
11290         if (idx < 0 || idx >= mp->nmolprops)
11291                 return -1;
11292         IntGroupIteratorInit(ig, &iter);
11293         nframes = MoleculeGetNumberOfFrames(mp);
11294         n = 0;
11295         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11296                 if (i >= nframes)
11297                         break;
11298                 mp->molprops[idx].propvals[i] = values[n];
11299                 n++;
11300         }
11301         IntGroupIteratorRelease(&iter);
11302         return n;
11303 }
11304
11305 int
11306 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11307 {
11308         IntGroupIterator iter;
11309         int i, n, nframes;
11310         if (idx < 0 || idx >= mp->nmolprops)
11311                 return -1;
11312         IntGroupIteratorInit(ig, &iter);
11313         nframes = MoleculeGetNumberOfFrames(mp);
11314         n = 0;
11315         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11316                 if (i >= nframes)
11317                         break;
11318                 outValues[n] = mp->molprops[idx].propvals[i];
11319                 n++;
11320         }
11321         IntGroupIteratorRelease(&iter);
11322         return n;
11323 }
11324
11325 #pragma mark ====== Pi Atoms ======
11326
11327 static inline void
11328 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11329 {
11330         Int *cp, j, n;
11331         Atom *ap2;
11332         cp = AtomConnectData(&ap->anchor->connect);
11333         n = ap->anchor->connect.count;
11334         VecZero(ap->r);
11335         for (j = 0; j < n; j++) {
11336                 Double w = ap->anchor->coeffs[j];
11337                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11338                 VecScaleInc(ap->r, ap2->r, w);
11339         }       
11340 }
11341
11342 void
11343 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11344 {
11345         Int i;
11346         Atom *ap;
11347         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11348                 if (ap->anchor == NULL)
11349                         continue;
11350                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11351         }
11352 }
11353
11354 void
11355 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11356 {
11357         Atom *ap;
11358         if (mol == NULL || idx < 0 || idx >= mol->natoms)
11359                 return;
11360         ap = ATOM_AT_INDEX(mol->atoms, idx);
11361         if (ap->anchor == NULL)
11362                 return;
11363         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11364 }
11365
11366 int
11367 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11368 {
11369         Atom *ap;
11370         Int *ip, i, j, n, *np;
11371         Double d;
11372         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11373                 return -1;  /*  Invalid argument  */
11374         if (weights != NULL) {
11375                 d = 0.0;
11376                 for (i = 0; i < nentries; i++) {
11377                         if (weights[i] <= 0.0) {
11378                                 return 10;  /*  Weights must be positive  */
11379                         }
11380                         d += weights[i];
11381                 }
11382                 d = 1.0 / d;
11383         } else d = 1.0 / nentries;
11384         ap = ATOM_AT_INDEX(mol->atoms, idx);
11385         if (ap->anchor != NULL) {
11386                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
11387                 IntGroup *bg, *ag, *dg, *ig;
11388                 Int *ibuf, ibufsize;
11389                 MolAction *act;
11390                 bg = ag = dg = ig = NULL;
11391                 ip = AtomConnectData(&ap->anchor->connect);
11392                 for (i = 0; i < ap->anchor->connect.count; i++) {
11393                         n = ip[i];
11394                         for (j = 0; j < nentries; j++) {
11395                                 if (n == entries[j])
11396                                         break;
11397                         }
11398                         if (j == nentries) {
11399                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
11400                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11401                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11402                                                 if (bg == NULL)
11403                                                         bg = IntGroupNew();
11404                                                 IntGroupAdd(bg, j, 1);
11405                                         }
11406                                 }
11407                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11408                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11409                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11410                                                 if (ag == NULL)
11411                                                         ag = IntGroupNew();
11412                                                 IntGroupAdd(ag, j, 1);
11413                                         }
11414                                 }
11415                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11416                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11417                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11418                                                 if (dg == NULL)
11419                                                         dg = IntGroupNew();
11420                                                 IntGroupAdd(dg, j, 1);
11421                                         }
11422                                 }
11423                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11424                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11425                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11426                                                 if (ig == NULL)
11427                                                         ig = IntGroupNew();
11428                                                 IntGroupAdd(ig, j, 1);
11429                                         }
11430                                 }
11431                         }
11432                 }
11433                 ibuf = NULL;
11434                 ibufsize = 0;
11435                 if (ig != NULL) {
11436                         /*  Delete impropers (with undo info) */
11437                         i = IntGroupGetCount(ig);
11438                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11439                         MoleculeDeleteImpropers(mol, ibuf, ig);
11440                         if (nUndoActions != NULL && undoActions != NULL) {
11441                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11442                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11443                         }
11444                         IntGroupRelease(ig);
11445                 }
11446                 if (dg != NULL) {
11447                         /*  Delete dihedrals (with undo info)  */
11448                         i = IntGroupGetCount(dg);
11449                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11450                         MoleculeDeleteDihedrals(mol, ibuf, dg);
11451                         if (nUndoActions != NULL && undoActions != NULL) {
11452                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11453                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11454                         }
11455                         IntGroupRelease(dg);
11456                 }
11457                 if (ag != NULL) {
11458                         /*  Delete angles (with undo info) */
11459                         i = IntGroupGetCount(ag);
11460                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11461                         MoleculeDeleteAngles(mol, ibuf, ag);
11462                         if (nUndoActions != NULL && undoActions != NULL) {
11463                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11464                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11465                         }
11466                         IntGroupRelease(ag);
11467                 }
11468                 if (bg != NULL) {
11469                         /*  Delete bonds (with undo info) */
11470                         i = IntGroupGetCount(bg);
11471                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11472                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11473                         if (nUndoActions != NULL && undoActions != NULL) {
11474                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11475                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11476                         }
11477                         IntGroupRelease(bg);
11478                 }
11479         } else {
11480                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11481         }
11482         AtomConnectResize(&ap->anchor->connect, nentries);
11483         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11484         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11485         if (weights != NULL) {
11486                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11487                 for (i = 0; i < nentries; i++)
11488                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
11489         } else {
11490                 for (i = 0; i < nentries; i++)
11491                         ap->anchor->coeffs[i] = d;
11492         }
11493         MoleculeCalculatePiAnchorPosition(mol, idx);
11494         return 0;
11495 }
11496
11497 #pragma mark ====== MO calculation ======
11498
11499 /*  Calculate an MO value for a single point.  */
11500 /*  Index is the MO number (1-based); 0 denotes "arbitrary vector"  */
11501 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
11502 static Double
11503 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11504 {
11505         ShellInfo *sp;
11506         PrimInfo *pp;
11507         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11508         Int i, j;
11509         /*  Cache dr and |dr|^2  */
11510         if (index == 0)
11511                 index = bset->nmos + 1;
11512         for (i = 0; i < mp->natoms; i++) {
11513                 Vector r;
11514                 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11515                 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11516                 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11517                 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11518                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11519         }
11520         /*  Iterate over all shells  */
11521         val = 0.0;
11522         mobasep = bset->mo + (index - 1) * bset->ncomps;
11523         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11524         Double rn;
11525         pp = bset->priminfos + sp->p_idx;
11526                 cnp = bset->cns + sp->cn_idx;
11527                 if (sp->a_idx >= mp->natoms)
11528                         return 0.0; /*  This may happen when molecule is edited after setting up MO info  */
11529                 tmpp = tmp + sp->a_idx * 4;
11530                 mop = mobasep + sp->m_idx;
11531         if (sp->add_exp == 0)
11532             rn = 1.0;
11533         else
11534             rn = pow(tmpp[3], sp->add_exp * 0.5);
11535                 switch (sp->sym) {
11536                         case kGTOType_S: {
11537                                 tval = 0;
11538                                 for (j = 0; j < sp->nprim; j++) {
11539                                         tval += *cnp++ * rn * exp(-pp->A * tmpp[3]);
11540                                         pp++;
11541                                 }
11542                                 val += mop[0] * tval;
11543                                 break;
11544                         }
11545                         case kGTOType_P: {
11546                                 Double x, y, z;
11547                                 x = y = z = 0;
11548                                 for (j = 0; j < sp->nprim; j++) {
11549                                         tval = rn * exp(-pp->A * tmpp[3]);
11550                                         x += *cnp++ * tval;
11551                                         y += *cnp++ * tval;
11552                                         z += *cnp++ * tval;
11553                                         pp++;
11554                                 }
11555                                 x *= mop[0] * tmpp[0];
11556                                 y *= mop[1] * tmpp[1];
11557                                 z *= mop[2] * tmpp[2];
11558                                 val += x + y + z;
11559                                 break;
11560                         }
11561                         case kGTOType_SP: {
11562                                 Double t, x, y, z;
11563                                 t = x = y = z = 0;
11564                                 for (j = 0; j < sp->nprim; j++) {
11565                                         tval = rn * exp(-pp->A * tmpp[3]);
11566                                         t += *cnp++ * tval;
11567                                         x += *cnp++ * tval;
11568                                         y += *cnp++ * tval;
11569                                         z += *cnp++ * tval;
11570                                         pp++;
11571                                 }
11572                                 t *= mop[0];
11573                                 x *= mop[1] * tmpp[0];
11574                                 y *= mop[2] * tmpp[1];
11575                                 z *= mop[3] * tmpp[2];
11576                                 val += t + x + y + z;
11577                 break;
11578                         }
11579                         case kGTOType_D: {
11580                                 Double xx, yy, zz, xy, xz, yz;
11581                                 xx = yy = zz = xy = xz = yz = 0;
11582                                 for (j = 0; j < sp->nprim; j++) {
11583                                         tval = rn * exp(-pp->A * tmpp[3]);
11584                                         xx += *cnp++ * tval;
11585                                         yy += *cnp++ * tval;
11586                                         zz += *cnp++ * tval;
11587                                         xy += *cnp++ * tval;
11588                                         xz += *cnp++ * tval;
11589                                         yz += *cnp++ * tval;
11590                                         pp++;
11591                                 }
11592                                 xx *= mop[0] * tmpp[0] * tmpp[0];
11593                                 yy *= mop[1] * tmpp[1] * tmpp[1];
11594                                 zz *= mop[2] * tmpp[2] * tmpp[2];
11595                                 xy *= mop[3] * tmpp[0] * tmpp[1];
11596                                 xz *= mop[4] * tmpp[0] * tmpp[2];
11597                                 yz *= mop[5] * tmpp[1] * tmpp[2];
11598                                 val += xx + yy + zz + xy + xz + yz;
11599                                 break;
11600                         }
11601                         case kGTOType_D5: {
11602                                 Double d0, d1p, d1n, d2p, d2n;
11603                                 d0 = d1p = d1n = d2p = d2n = 0;
11604                                 for (j = 0; j < sp->nprim; j++) {
11605                                         tval = rn * exp(-pp->A * tmpp[3]);
11606                                         d0 += *cnp++ * tval;
11607                                         d1p += *cnp++ * tval;
11608                                         d1n += *cnp++ * tval;
11609                                         d2p += *cnp++ * tval;
11610                                         d2n += *cnp++ * tval;
11611                                         pp++;
11612                                 }
11613                 // D(0): GNC(2,a,n) * (1/2) * (3zz-rr) * r^n * exp(-a*r^2)
11614                 // D(+1): GNC(2,a,n) * sqrt(3) * xz * r^n * exp(-a*r^2)
11615                 // D(-1): GNC(2,a,n) * sqrt(3) * yz * r^n * exp(-a*r^2)
11616                 // D(+2): GNC(2,a,n) * (sqrt(3)/2) * (xx-yy) * r^n * exp(-a*r^2)
11617                 // D(-2): GNC(2,a,n) * sqrt(3) * xy * r^n * exp(-a*r^2)
11618                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11619                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
11620                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
11621                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11622                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
11623                                 val += d0 + d1p + d1n + d2p + d2n;
11624                                 break;
11625                         }
11626             case kGTOType_F: {
11627                 Double xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz;
11628                 xxx = yyy = zzz = xyy = xxy = xxz = xzz = yzz = yyz = xyz = 0;
11629                 for (j = 0; j < sp->nprim; j++) {
11630                     tval = rn * exp(-pp->A * tmpp[3]);
11631                     xxx += *cnp++ * tval;
11632                     yyy += *cnp++ * tval;
11633                     zzz += *cnp++ * tval;
11634                     xyy += *cnp++ * tval;
11635                     xxy += *cnp++ * tval;
11636                     xxz += *cnp++ * tval;
11637                     xzz += *cnp++ * tval;
11638                     yzz += *cnp++ * tval;
11639                     yyz += *cnp++ * tval;
11640                     xyz += *cnp++ * tval;
11641                     pp++;
11642                 }
11643                 xxx *= mop[0] * tmpp[0] * tmpp[0] * tmpp[0];
11644                 yyy *= mop[1] * tmpp[1] * tmpp[1] * tmpp[1];
11645                 zzz *= mop[2] * tmpp[2] * tmpp[2] * tmpp[2];
11646                 xyy *= mop[3] * tmpp[0] * tmpp[1] * tmpp[1];
11647                 xxy *= mop[4] * tmpp[0] * tmpp[0] * tmpp[1];
11648                 xxz *= mop[5] * tmpp[0] * tmpp[0] * tmpp[2];
11649                 xzz *= mop[6] * tmpp[0] * tmpp[2] * tmpp[2];
11650                 yzz *= mop[7] * tmpp[1] * tmpp[2] * tmpp[2];
11651                 yyz *= mop[8] * tmpp[1] * tmpp[1] * tmpp[2];
11652                 xyz *= mop[9] * tmpp[0] * tmpp[1] * tmpp[2];
11653                 val += xxx + yyy + zzz + xyy + xxy + xxz + xzz + yzz + yyz + xyz;
11654                 break;
11655             }
11656             case kGTOType_F7: {
11657                 Double f0, f1p, f1n, f2p, f2n, f3p, f3n;
11658                 f0 = f1p = f1n = f2p = f2n = f3p = f3n = 0;
11659                 for (j = 0; j < sp->nprim; j++) {
11660                     tval = rn * exp(-pp->A * tmpp[3]);
11661                     f0 += *cnp++ * tval;
11662                     f1p += *cnp++ * tval;
11663                     f1n += *cnp++ * tval;
11664                     f2p += *cnp++ * tval;
11665                     f2n += *cnp++ * tval;
11666                     f3p += *cnp++ * tval;
11667                     f3n += *cnp++ * tval;
11668                     pp++;
11669                 }
11670                 // F(0): GNC(3,a,n) * (1/2) * (5zzz-3zrr) * r^n * exp(-a*r^2)
11671                 // F(+1): GNC(3,a,n) * sqrt(3/8) * (5xzz-xrr) * r^n * exp(-a*r^2)
11672                 // F(-1): GNC(3,a,n) * sqrt(3/8) * (5yzz-yrr) * r^n * exp(-a*r^2)
11673                 // F(+2): GNC(3,a,n) * sqrt(15/4) * (xxz-yyz) * r^n * exp(-a*r^2)
11674                 // F(-2): GNC(3,a,n) * sqrt(15) * xyz * r^n * exp(-a*r^2)
11675                 // F(+3): GNC(3,a,n) * sqrt(5/8) * (xxx-3xyy) * r^n * exp(-a*r^2)
11676                 // F(-3): GNC(3,a,n) * sqrt(5/8) * (3xxy-yyy) * r^n * exp(-a*r^2)
11677                 f0 *= mop[0] * tmpp[2] * (5 * tmpp[2] * tmpp[2] - 3 * tmpp[3]);
11678                 f1p *= mop[1] * tmpp[0] * (5 * tmpp[2] * tmpp[2] - tmpp[3]);
11679                 f1n *= mop[2] * tmpp[1] * (5 * tmpp[2] * tmpp[2] - tmpp[3]);
11680                 f2p *= mop[3] * tmpp[2] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11681                 f2n *= mop[4] * tmpp[0] * tmpp[1] * tmpp[2];
11682                 f3p *= mop[5] * tmpp[0] * (tmpp[0] * tmpp[0] - 3 * tmpp[1] * tmpp[1]);
11683                 f3n *= mop[6] * tmpp[1] * (3 * tmpp[0] * tmpp[0] - tmpp[2] * tmpp[2]);
11684                 val += f0 + f1p + f1n + f2p + f2n + f3p + f3n;
11685                 break;
11686             }
11687             case kGTOType_G: {
11688                 Double xxxx, yyyy, zzzz, xxxy, xxxz, yyyx, yyyz, zzzx, zzzy, xxyy, xxzz, yyzz, xxyz, yyxz, zzxy;
11689                 xxxx = yyyy = zzzz = xxxy = xxxz = yyyx = yyyz = zzzx = zzzy = xxyy = xxzz = yyzz = xxyz = yyxz = zzxy = 0;
11690                 for (j = 0; j < sp->nprim; j++) {
11691                     tval = rn * exp(-pp->A * tmpp[3]);
11692                     xxxx += *cnp++ * tval;
11693                     yyyy += *cnp++ * tval;
11694                     zzzz += *cnp++ * tval;
11695                     xxxy += *cnp++ * tval;
11696                     xxxz += *cnp++ * tval;
11697                     yyyx += *cnp++ * tval;
11698                     yyyz += *cnp++ * tval;
11699                     zzzx += *cnp++ * tval;
11700                     zzzy += *cnp++ * tval;
11701                     xxyy += *cnp++ * tval;
11702                     xxzz += *cnp++ * tval;
11703                     yyzz += *cnp++ * tval;
11704                     xxyz += *cnp++ * tval;
11705                     yyxz += *cnp++ * tval;
11706                     zzxy += *cnp++ * tval;
11707                     pp++;
11708                 }
11709                 xxxx *= mop[0] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[0];
11710                 yyyy *= mop[1] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[1];
11711                 zzzz *= mop[2] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[2];
11712                 xxxy *= mop[3] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[1];
11713                 xxxz *= mop[4] * tmpp[0] * tmpp[0] * tmpp[0] * tmpp[2];
11714                 yyyx *= mop[5] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[0];
11715                 yyyz *= mop[6] * tmpp[1] * tmpp[1] * tmpp[1] * tmpp[2];
11716                 zzzx *= mop[7] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[0];
11717                 zzzy *= mop[8] * tmpp[2] * tmpp[2] * tmpp[2] * tmpp[1];
11718                 xxyy *= mop[9] * tmpp[0] * tmpp[0] * tmpp[1] * tmpp[1];
11719                 xxzz *= mop[10] * tmpp[0] * tmpp[0] * tmpp[2] * tmpp[2];
11720                 yyzz *= mop[11] * tmpp[1] * tmpp[1] * tmpp[2] * tmpp[2];
11721                 xxyz *= mop[12] * tmpp[0] * tmpp[0] * tmpp[1] * tmpp[2];
11722                 yyxz *= mop[13] * tmpp[1] * tmpp[1] * tmpp[0] * tmpp[2];
11723                 zzxy *= mop[14] * tmpp[2] * tmpp[2] * tmpp[0] * tmpp[1];
11724                 val += xxxx + yyyy + zzzz + xxxy + xxxz + yyyx + yyyz + zzzx + zzzy + xxyy + xxzz + yyzz + xxyz + yyxz + zzxy;
11725                 break;
11726             }
11727             case kGTOType_G9: {
11728                 Double g0, g1p, g1n, g2p, g2n, g3p, g3n, g4p, g4n;
11729                 Double xx = tmpp[0] * tmpp[0];
11730                 Double yy = tmpp[1] * tmpp[1];
11731                 Double zz = tmpp[2] * tmpp[2];
11732                 Double rr = tmpp[3];
11733                 g0 = g1p = g1n = g2p = g2n = g3p = g3n = g4p = g4n = 0;
11734                 for (j = 0; j < sp->nprim; j++) {
11735                     tval = rn * exp(-pp->A * tmpp[3]);
11736                     g0 += *cnp++ * tval;
11737                     g1p += *cnp++ * tval;
11738                     g1n += *cnp++ * tval;
11739                     g2p += *cnp++ * tval;
11740                     g2n += *cnp++ * tval;
11741                     g3p += *cnp++ * tval;
11742                     g3n += *cnp++ * tval;
11743                     g4p += *cnp++ * tval;
11744                     g4n += *cnp++ * tval;
11745                     pp++;
11746                 }
11747                 // G(0): GNC(4,a,n) * (1/8) * (35zzzz-30zzrr+3rrrr) * r^n * exp(-a*r^2)
11748                 // G(+1): GNC(4,a,n) * sqrt(5/8) * (7xzzz-3xzrr) * r^n * exp(-a*r^2)
11749                 // G(-1): GNC(4,a,n) * sqrt(5/8) * (7yzzz-3yzrr) * r^n * exp(-a*r^2)
11750                 // G(+2): GNC(4,a,n) * sqrt(5/16) * (xx-yy)(7zz-rr) * r^n * exp(-a*r^2)
11751                 // G(-2): GNC(4,a,n) * sqrt(5/4) * (7xyzz-xyrr) * r^n * exp(-a*r^2)
11752                 // G(+3): GNC(4,a,n) * sqrt(35/8) * (xxxz-3xyyz) * r^n * exp(-a*r^2)
11753                 // G(-3): GNC(4,a,n) * sqrt(35/8) * (3xxyz-yyyz) * r^n * exp(-a*r^2)
11754                 // G(+4): GNC(4,a,n) * sqrt(35/64) * (xxxx-6xxyy+yyyy) * r^n * exp(-a*r^2)
11755                 // G(-4): GNC(4,a,n) * sqrt(35/4) * (xxxy-xyyy) * r^n * exp(-a*r^2)
11756                 g0 *= mop[0] * (35 * zz * zz - 30 * zz * rr + 3 * rr * rr);
11757                 g1p *= mop[1] * tmpp[0] * tmpp[2] * (7 * zz - 3 * rr);
11758                 g1n *= mop[2] * tmpp[1] * tmpp[2] * (7 * zz - 3 * rr);
11759                 g2p *= mop[3] * (xx - yy) * (7 * zz - rr);
11760                 g2n *= mop[4] * tmpp[0] * tmpp[1] * (7 * zz - rr);
11761                 g3p *= mop[5] * tmpp[0] * tmpp[2] * (xx - 3 * yy);
11762                 g3n *= mop[6] * tmpp[1] * tmpp[2] * (3 * xx - yy);
11763                 g4p *= mop[7] * (xx * xx - 6 * xx * yy + yy * yy);
11764                 g4n *= mop[8] * tmpp[0] * tmpp[1] * (xx - yy);
11765                 val += g0 + g1p + g1n + g2p + g2n + g3p + g3n + g4p + g4n;
11766                 break;
11767             }
11768                 }
11769         }
11770         return val;
11771 }
11772
11773 /*  Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520)  */
11774 /*  mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11775 int
11776 MoleculeCalcMO(Molecule *mp, Int mono, const Vector *op, const Vector *dxp, const Vector *dyp, const Vector *dzp, Int nx, Int ny, Int nz, int (*callback)(double progress, void *ref), void *ref)
11777 {
11778         int ix, iy, iz, n, nn;
11779         Cube *cp;
11780         Double *tmp;
11781         if (mp == NULL || mp->bset == NULL)
11782                 return -1;
11783         if (mp->bset->cns == NULL) {
11784                 if (sSetupGaussianCoefficients(mp->bset) != 0)
11785                         return -1;
11786         }
11787         if (mp->bset->natoms_bs > mp->natoms)
11788                 return -3;  /*  Number of atoms is smaller than expected (internal error)  */
11789         
11790         cp = (Cube *)calloc(sizeof(Cube), 1);
11791         if (cp == NULL) {
11792                 return -1;
11793         }
11794         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11795         if (cp->dp == NULL) {
11796                 free(cp);
11797                 return -1;
11798         }
11799         cp->idn = mono;
11800         cp->origin = *op;
11801         cp->dx = *dxp;
11802         cp->dy = *dyp;
11803         cp->dz = *dzp;
11804         cp->nx = nx;
11805         cp->ny = ny;
11806         cp->nz = nz;
11807         
11808         /*  TODO: use multithread  */
11809         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11810         if (tmp == NULL) {
11811                 free(cp->dp);
11812                 free(cp);
11813                 return -1;
11814         }
11815         n = nn = 0;
11816         for (ix = 0; ix < nx; ix++) {
11817                 Vector p;
11818                 for (iy = 0; iy < ny; iy++) {
11819                         for (iz = 0; iz < nz; iz++) {
11820                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11821                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11822                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11823                                 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11824                         }
11825                         if (callback != NULL && n - nn > 100) {
11826                                 nn = n;
11827                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11828                                         free(cp->dp);
11829                                         free(cp);
11830                                         free(tmp);
11831                                         return -2;  /*  User interrupt  */
11832                                 }
11833                         }
11834                 }
11835         }
11836         free(tmp);
11837
11838         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11839         return mp->bset->ncubes - 1;
11840 }
11841
11842 /*  Output values are in angstrom unit (changed from bohr unit: 20140520)  */
11843 int
11844 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11845 {
11846         int i;
11847         Vector rmin, rmax, r;
11848         Double dr, dx, dy, dz;
11849         Atom *ap;
11850         if (mp == NULL || mp->bset == NULL)
11851                 return -1;
11852         if (npoints <= 0)
11853                 npoints = 1000000;
11854         rmin.x = rmin.y = rmin.z = 1e10;
11855         rmax.x = rmax.y = rmax.z = -1e10;
11856         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11857                 dr = RadiusForAtomicNumber(ap->atomicNumber);
11858                 r = ap->r;
11859                 if (dr == 0.0)
11860                         dr = 1.0;
11861                 dr = dr * 3.0 + 2.0;
11862                 if (rmin.x > r.x - dr)
11863                         rmin.x = r.x - dr;
11864                 if (rmin.y > r.y - dr)
11865                         rmin.y = r.y - dr;
11866                 if (rmin.z > r.z - dr)
11867                         rmin.z = r.z - dr;
11868                 if (rmax.x < r.x + dr)
11869                         rmax.x = r.x + dr;
11870                 if (rmax.y < r.y + dr)
11871                         rmax.y = r.y + dr;
11872                 if (rmax.z < r.z + dr)
11873                         rmax.z = r.z + dr;
11874         }
11875         dx = rmax.x - rmin.x;
11876         dy = rmax.y - rmin.y;
11877         dz = rmax.z - rmin.z;
11878         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11879         *nx = floor(dx / dr + 0.5);
11880         *ny = floor(dy / dr + 0.5);
11881         *nz = floor(dz / dr + 0.5);
11882         if (*nx == 0)
11883                 *nx = 1;
11884         if (*ny == 0)
11885                 *ny = 1;
11886         if (*nz == 0)
11887                 *nz = 1;
11888         *op = rmin;
11889         xp->x = yp->y = zp->z = dr;
11890         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11891         return 0;
11892 }
11893
11894 const Cube *
11895 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11896 {
11897         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11898                 return NULL;
11899         return mp->bset->cubes[index];
11900 }
11901
11902 int
11903 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11904 {
11905         int i;
11906         if (mp == NULL || mp->bset == NULL)
11907                 return -1;
11908         for (i = 0; i < mp->bset->ncubes; i++) {
11909                 if (mp->bset->cubes[i]->idn == mono)
11910                         return i;
11911         }
11912         return -1;
11913 }
11914
11915 int
11916 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11917 {
11918         int n;
11919         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11920                 return -1;
11921         CubeRelease(mp->bset->cubes[index]);
11922         if (index < n - 1)
11923                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11924         if (--(mp->bset->ncubes) == 0) {
11925                 free(mp->bset->cubes);
11926                 mp->bset->cubes = NULL;
11927         }
11928         return mp->bset->ncubes;
11929 }
11930
11931 int
11932 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11933 {
11934         const Cube *cp;
11935         int i, j, k, n;
11936         FILE *fp;
11937         if (mp == NULL || mp->bset == NULL)
11938                 return -1;  /*  Molecule or the basis set information is empty  */
11939         cp = MoleculeGetCubeAtIndex(mp, index);
11940         if (cp == NULL)
11941                 return -2;  /*  MO not yet calculated  */
11942         fp = fopen(fname, "wb");
11943         if (fp == NULL)
11944                 return -3;  /*  Cannot create file  */
11945
11946         /*  Comment lines  */
11947         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11948         fprintf(fp, " MO coefficients\n");
11949         
11950         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11951                         cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11952         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11953                         cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11954         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11955                         cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11956         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11957                         cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11958         
11959         /*  Atomic information  */
11960         for (i = 0; i < mp->natoms; i++) {
11961                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11962                 /*  The second number should actually be the effective charge  */
11963                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11964                                 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11965         }
11966         fprintf(fp, "%5d%5d\n", 1, 1);
11967         
11968         /*  3D data  */
11969         for (i = n = 0; i < cp->nx; i++) {
11970                 for (j = 0; j < cp->ny; j++) {
11971                         for (k = 0; k < cp->nz; k++) {
11972                                 /*  On Windows, the "%e" format writes the exponent in 3 digits, but
11973                                     this is not standard. So we avoid using %e  */
11974                                 Double d = cp->dp[n++];
11975                                 int exponent;
11976                                 Double base;
11977                                 if (d >= -1.0e-90 && d <= 1.0e-90) {
11978                                         exponent = 0;
11979                                         base = 0.0;
11980                                 } else {
11981                                         exponent = (int)floor(log10(fabs(d)));
11982                                         base = d * pow(10, -1.0 * exponent);
11983                                 }
11984                                 fprintf(fp, " %8.5fe%+03d", base, exponent);
11985                         /*      fprintf(fp, " %12.5e", d); */
11986                                 if (k == cp->nz - 1 || k % 6 == 5)
11987                                         fprintf(fp, "\n");
11988                         }
11989                 }
11990         }
11991         fclose(fp);
11992         return 0;
11993 }
11994
11995 #pragma mark ====== Marching Cube (for isosurface) ======
11996
11997 MCube *
11998 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
11999 {
12000         MCube *mc = mol->mcube;
12001         int i;
12002         float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
12003         if (mc != NULL) {
12004                 free(mc->dp);
12005                 free(mc->radii);
12006                 free(mc->c[0].fp);
12007                 free(mc->c[0].cubepoints);
12008                 free(mc->c[0].triangles);
12009                 free(mc->c[1].fp);
12010                 free(mc->c[1].cubepoints);
12011                 free(mc->c[1].triangles);
12012                 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
12013                 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
12014                 free(mc);
12015                 mol->mcube = NULL;
12016         }
12017         if (nx > 0 && ny > 0 && nz > 0) {
12018                 mc = (MCube *)calloc(sizeof(MCube), 1);
12019                 mc->idn = -1;
12020                 /*  round up to nearest 4N+1 integer  */
12021                 dx *= nx;
12022                 dy *= ny;
12023                 dz *= nz;
12024                 mc->nx = (nx + 2) / 4 * 4 + 1;
12025                 mc->ny = (ny + 2) / 4 * 4 + 1;
12026                 mc->nz = (nz + 2) / 4 * 4 + 1;
12027                 mc->dx = dx / mc->nx;
12028                 mc->dy = dy / mc->ny;
12029                 mc->dz = dz / mc->nz;
12030                 mc->origin = *origin;
12031                 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
12032                 if (mc->dp == NULL) {
12033                         free(mc);
12034                         return NULL;
12035                 }
12036                 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
12037                 if (mc->radii == NULL) {
12038                         free(mc->dp);
12039                         free(mc);
12040                         return NULL;
12041                 }
12042                 mc->nradii = mol->natoms;
12043                 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
12044                 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
12045                 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
12046                         free(mc->c[0].fp);
12047                         free(mc->c[1].fp);
12048                         free(mc->dp);
12049                         free(mc->radii);
12050                         free(mc);
12051                         return NULL;
12052                 }
12053                 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
12054                         mc->dp[i] = DBL_MAX;
12055                 }
12056                 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
12057                 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
12058                 mol->mcube = mc;
12059         }
12060         MoleculeCallback_notifyModification(mol, 0);
12061         return mol->mcube;
12062 }
12063
12064 static int sMarchingCubeTable[256][16] = {
12065         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12066         {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12067         {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12068         {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12069         {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12070         {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12071         {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12072         {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
12073         {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12074         {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12075         {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12076         {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
12077         {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12078         {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
12079         {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
12080         {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12081         {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12082         {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12083         {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12084         {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
12085         {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12086         {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
12087         {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
12088         {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
12089         {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12090         {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
12091         {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
12092         {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
12093         {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
12094         {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
12095         {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
12096         {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
12097         {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12098         {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12099         {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12100         {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
12101         {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12102         {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
12103         {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
12104         {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
12105         {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12106         {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
12107         {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
12108         {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
12109         {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
12110         {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
12111         {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
12112         {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
12113         {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12114         {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
12115         {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
12116         {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12117         {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
12118         {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
12119         {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
12120         {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
12121         {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
12122         {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
12123         {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
12124         {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
12125         {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
12126         {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
12127         {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
12128         {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12129         {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12130         {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12131         {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12132         {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
12133         {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12134         {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
12135         {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
12136         {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
12137         {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12138         {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
12139         {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
12140         {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
12141         {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
12142         {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
12143         {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
12144         {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
12145         {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12146         {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
12147         {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
12148         {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
12149         {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
12150         {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
12151         {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
12152         {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
12153         {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
12154         {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
12155         {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
12156         {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
12157         {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
12158         {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
12159         {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
12160         {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
12161         {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12162         {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
12163         {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
12164         {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
12165         {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
12166         {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
12167         {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12168         {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
12169         {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
12170         {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
12171         {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
12172         {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
12173         {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
12174         {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
12175         {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
12176         {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12177         {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
12178         {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
12179         {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
12180         {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
12181         {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
12182         {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
12183         {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
12184         {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12185         {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
12186         {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
12187         {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
12188         {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
12189         {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
12190         {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12191         {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
12192         {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12193         {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12194         {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12195         {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12196         {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
12197         {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12198         {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
12199         {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
12200         {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
12201         {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12202         {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
12203         {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
12204         {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
12205         {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
12206         {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
12207         {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
12208         {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
12209         {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12210         {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
12211         {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
12212         {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
12213         {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
12214         {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
12215         {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
12216         {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
12217         {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
12218         {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12219         {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
12220         {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
12221         {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
12222         {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
12223         {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
12224         {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12225         {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12226         {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
12227         {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
12228         {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
12229         {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
12230         {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
12231         {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
12232         {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
12233         {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
12234         {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
12235         {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
12236         {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
12237         {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
12238         {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
12239         {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
12240         {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
12241         {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12242         {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
12243         {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
12244         {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
12245         {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
12246         {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
12247         {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
12248         {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
12249         {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
12250         {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
12251         {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
12252         {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12253         {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
12254         {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
12255         {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12256         {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12257         {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12258         {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
12259         {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
12260         {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
12261         {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
12262         {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
12263         {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
12264         {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
12265         {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
12266         {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
12267         {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
12268         {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
12269         {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12270         {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
12271         {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
12272         {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12273         {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
12274         {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
12275         {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
12276         {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
12277         {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
12278         {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
12279         {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
12280         {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12281         {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
12282         {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
12283         {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
12284         {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
12285         {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
12286         {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12287         {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
12288         {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12289         {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
12290         {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
12291         {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
12292         {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
12293         {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
12294         {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
12295         {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
12296         {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
12297         {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
12298         {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
12299         {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
12300         {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12301         {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
12302         {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
12303         {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12304         {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12305         {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12306         {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
12307         {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
12308         {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12309         {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
12310         {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
12311         {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12312         {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12313         {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12314         {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12315         {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12316         {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12317         {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12318         {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12319         {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12320         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12321 };
12322
12323 /*  When estimating the boundary box of the orbital, the MO values are calculated
12324     from radial distance only, ignoring the angular part. This may lead to too small
12325     value for spherical basis set, because the real MO calculation assumes expression
12326     like (3zz-rr) * r^n * exp(-a*r^2) (for D(0)). In this case, the angular part is
12327     (3zz-rr)/rr, which can take a maximum value of 2.0 instead of 1.0. So, we need to
12328     set a multiplication factor for spherical orbitals to account for this problem. */
12329 /*  [0][]: D5, [1][]: F7, [2][]: G9  */
12330 /*  See sCalcMOPoint() for representation of each spherical orbitals  */
12331 static double sMultiplyFactors[3][10] = {
12332     { 2.0, 1.0, 1.0, 1.0, 1.0 },
12333     { 2.0, 4.0, 4.0, 1.0, 1.0, 2.0, 2.0 },
12334     { 8.0, 4.0, 4.0, 6.0, 6.0, 2.0, 2.0, 4.0, 1.0 }
12335 };
12336
12337 /*  Recalculate the MCube  */
12338 /*  If idn < 0, then the current grid settings and values are unchanged, and */
12339 /*  only the marching cubes are regenerated.  */
12340 int
12341 MoleculeUpdateMCube(Molecule *mol, int idn)
12342 {
12343     Int retval, step, sn, mocount;
12344         Int n, ix, iy, iz, nx, ny, nz;
12345         Int nn, iix, iiy, iiz;
12346         Int ncubepoints, c1, c2, c3;
12347         Int *ip;
12348         Double thres, *tmp, dd;
12349         Vector p;
12350         MCube *mc;
12351         MCubePoint *mcp;
12352         Atom *ap;
12353
12354         if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12355                 return -1;
12356         if (mol->bset->cns == NULL) {
12357                 if (sSetupGaussianCoefficients(mol->bset) != 0)
12358                         return -1;
12359         }
12360         if (mol->bset->natoms_bs > mol->natoms)
12361                 return -1;  /*  Number of atoms is smaller than expected  */
12362
12363         mc = mol->mcube;
12364         if (idn >= 0) {
12365                 ShellInfo *sp;
12366                 Double *mobasep, *mop, mopmax;
12367                 Double xmin, xmax, ymin, ymax, zmin, zmax;
12368         Double thres, rthres;
12369         Int k;
12370                 /*  Clear mcube values  */
12371                 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12372                         mc->dp[ix] = DBL_MAX;
12373                         mc->c[0].fp[ix] = 0;
12374                         mc->c[1].fp[ix] = 0;
12375                 }
12376                 mc->idn = idn;
12377                 /*  Estimate the orbital sizes  */
12378                 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12379                 if (mc->radii == NULL)
12380                         return -2;  /*  Out of memory  */
12381                 mc->nradii = mol->natoms;
12382
12383         /*  Reset the temporary array of radii  */
12384         memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12385         /*  Base pointer for MO coefficients  */
12386         mobasep = mol->bset->mo;
12387         mocount = 1;  /*  Scan for only one MO, except for total density  */
12388         thres = mc->thres * 0.2;  /*  Use lower threshold (for margin)  */
12389         if (mc->idn == 0)
12390             mobasep += mol->bset->nmos * mol->bset->ncomps;  /*  Arbitrary vector  */
12391         else if (mc->idn < mol->bset->nmos + 1)
12392             mobasep += (mc->idn - 1) * mol->bset->ncomps;
12393         else
12394             mocount = mol->bset->nmos;  /*  Total density; scan for all MO coefficients  */
12395         mopmax = 0.0;
12396         for (nn = 0; nn < mol->natoms; nn++) {
12397             /*  Find the suitable 'box' for each atom  */
12398             /*  Start from r = atomic_radius * 10, and scale r by 0.8 until fabs(p(r)) > thres */
12399             /*  Then, start from r/0.8=r*1.25, and decrease r by 0.025r until fabs(p(r)) > thres */
12400             Int pass = 1;
12401             Double dr = 0.0;
12402             Double *mp;
12403             rthres = RadiusForAtomicNumber(mol->atoms[nn].atomicNumber) * 10;
12404             while (1) {
12405                 /*  Calculate the MO value (or total density)  */
12406                 Double dd = 0.0;
12407                 for (n = 0; n < mocount; n++) {
12408                     /*  Iterate over all shells, skipping those unrelated to the current atom */
12409                     for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12410                         PrimInfo *pp;
12411                         Double *cnp;
12412                         if (sp->a_idx != nn)
12413                             continue;  /*  Skip this shell  */
12414                         pp = mol->bset->priminfos + sp->p_idx;
12415                         cnp = mol->bset->cns + sp->cn_idx;
12416                         mop = mobasep + (n * mol->bset->ncomps) + sp->m_idx;
12417                         k = sp->add_exp;
12418                         mp = NULL;
12419                         switch (sp->sym) {
12420                             case kGTOType_SP:
12421                             case kGTOType_P: k += 1; break;
12422                             case kGTOType_D: k += 2; break;
12423                             case kGTOType_D5: k += 2; mp = sMultiplyFactors[0]; break;
12424                             case kGTOType_F: k += 3; break;
12425                             case kGTOType_F7: k += 3; mp = sMultiplyFactors[1]; break;
12426                             case kGTOType_G: k += 4; break;
12427                             case kGTOType_G9: k += 4; mp = sMultiplyFactors[2]; break;
12428                         }
12429                         /*  Iterate over all components and primitives */
12430                         for (iy = 0; iy < sp->ncomp; iy++) {
12431                             Int kk = k;
12432                             Double ddd = 0.0;
12433                             if (sp->sym == kGTOType_SP && iy > 0)
12434                                 kk++;
12435                             for (iz = 0; iz < sp->nprim; iz++) {
12436                                 ddd += cnp[iz] * pow(rthres, kk) * exp(-pp[iz].A * rthres * rthres);
12437                             }
12438                             if (mp != NULL)
12439                                 ddd *= mp[iy];
12440                             if (mc->idn == mol->bset->nmos + 1)
12441                                 dd += mop[iy] * mop[iy] * ddd * ddd;
12442                             else
12443                                 dd += mop[iy] * fabs(ddd);
12444                         } /* end for iy */
12445                     } /* end for ix */
12446                 } /* end for n */
12447                 if (fabs(dd) > thres) {
12448                     /*  End of this pass  */
12449                     if (pass == 1) {
12450                         dr = rthres * 0.025;
12451                         rthres = rthres * 1.25;
12452                     } else {
12453                         if (dr < 0.01)
12454                             break;
12455                         rthres += dr;
12456                         dr = dr / 10;
12457                     }
12458                     pass++;
12459                     continue;
12460                 }
12461                 if (pass == 1) {
12462                     rthres *= 0.8;
12463                     if (rthres < 0.01) {
12464                         /* The orbital value is less than thres for (almost) all r */
12465                         rthres = 0.0;
12466                         break;
12467                     }
12468                 } else {
12469                     rthres -= dr;
12470                     if (rthres <= 0.0) {
12471                         /*  This cannot happen, but just in case  */
12472                         rthres = 0.0;
12473                         break;
12474                     }
12475                 }
12476             } /* end while */
12477             if (rthres > 0.0)
12478                 mc->radii[nn] = rthres * kBohr2Angstrom;
12479         } /* end loop nn */
12480         /*  Create box that encloses all atoms with radius mc->radii[nn] */
12481         xmin = ymin = zmin = 1e10;
12482         xmax = ymax = zmax = -1e10;
12483         for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12484             Double atomic_rad;
12485             dd = mc->radii[ix];
12486             atomic_rad = RadiusForAtomicNumber(ap->atomicNumber);
12487             if (dd == 0.0) {
12488                 mc->radii[ix] = atomic_rad;
12489                 continue;
12490             }
12491             if (atomic_rad > dd) {
12492                 dd = atomic_rad;
12493                 mc->radii[ix] = atomic_rad;
12494             }
12495             p = ap->r;
12496             if (p.x - dd < xmin)
12497                 xmin = p.x - dd;
12498             if (p.y - dd < ymin)
12499                 ymin = p.y - dd;
12500             if (p.z - dd < zmin)
12501                 zmin = p.z - dd;
12502             if (p.x + dd > xmax)
12503                 xmax = p.x + dd;
12504             if (p.y + dd > ymax)
12505                 ymax = p.y + dd;
12506             if (p.z + dd > zmax)
12507                 zmax = p.z + dd;
12508         }
12509         /*  Scale the box if specified so  */
12510         if (mc->expand > 0.0) {
12511             Double xexp, yexp, zexp;
12512             xexp = (xmax - xmin) * (mc->expand - 1.0) * 0.5;
12513             yexp = (ymax - ymin) * (mc->expand - 1.0) * 0.5;
12514             zexp = (zmax - zmin) * (mc->expand - 1.0) * 0.5;
12515             xmax += xexp;
12516             xmin -= xexp;
12517             ymax += yexp;
12518             ymin -= yexp;
12519             zmax += zexp;
12520             zmin -= zexp;
12521         }
12522         mc->origin.x = xmin;
12523         mc->origin.y = ymin;
12524         mc->origin.z = zmin;
12525         mc->dx = (xmax - xmin) / (mc->nx - 1);
12526         mc->dy = (ymax - ymin) / (mc->ny - 1);
12527         mc->dz = (zmax - zmin) / (mc->nz - 1);
12528
12529     }
12530     
12531         /*  Temporary work area  */
12532         tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12533         if (tmp == NULL)
12534                 return -2;
12535         
12536         /*  TODO: use multithread  */
12537         nx = mc->nx;
12538         ny = mc->ny;
12539         nz = mc->nz;
12540         step = 4;
12541         
12542 #if 1
12543         /*  Calculate points within certain distances from atoms  */
12544         for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12545         /*      dd = RadiusForAtomicNumber(ap->atomicNumber);
12546                 if (dd == 0.0)
12547                         dd = 1.0;
12548                 dd = dd * 1.5 + 1.0; */
12549                 dd = mc->radii[nn];
12550                 p.x = ap->r.x - dd - mc->origin.x;
12551                 p.y = ap->r.y - dd - mc->origin.y;
12552                 p.z = ap->r.z - dd - mc->origin.z;
12553                 c1 = p.x / mc->dx;
12554                 c2 = p.y / mc->dy;
12555                 c3 = p.z / mc->dz;
12556                 iix = c1 + ceil(dd * 2.0 / mc->dx);
12557                 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12558                 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12559                 if (c1 < 0)
12560                         c1 = 0;
12561                 if (c2 < 0)
12562                         c2 = 0;
12563                 if (c3 < 0)
12564                         c3 = 0;
12565                 if (iix >= nx)
12566                         iix = nx - 1;
12567                 if (iiy >= ny)
12568                         iiy = ny - 1;
12569                 if (iiz >= nz)
12570                         iiz = nz - 1;
12571                 for (ix = c1; ix <= iix; ix++) {
12572                         p.x = mc->origin.x + mc->dx * ix;
12573                         for (iy = c2; iy <= iiy; iy++) {
12574                                 p.y = mc->origin.y + mc->dy * iy;
12575                                 for (iz = c3; iz <= iiz; iz++) {
12576                                         n = (ix * ny + iy) * nz + iz;
12577                                         if (mc->dp[n] == DBL_MAX) {
12578                                                 p.z = mc->origin.z + mc->dz * iz;
12579                                                 if (mc->idn == mol->bset->nmos + 1) {
12580                                                         /*  Total electron density  */
12581                                                         Int ne_alpha, ne_beta;
12582                                                         mc->dp[n] = 0.0;
12583                                                         ne_alpha = mol->bset->ne_alpha;
12584                                                         ne_beta = mol->bset->ne_beta;
12585                                                         if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12586                                                                 /*  ROHF case: ensure ne_alpha >= ne_beta  */
12587                                                                 ne_beta = ne_alpha;
12588                                                                 ne_alpha = mol->bset->ne_beta;
12589                                                         }
12590                                                         for (sn = 1; sn <= ne_alpha; sn++) {
12591                                                                 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12592                                                                 dd = dd * dd;
12593                                                                 if (mol->bset->rflag != 0 && sn <= ne_beta)
12594                                                                         dd *= 2;
12595                                                                 mc->dp[n] += dd;
12596                                                         }
12597                                                         if (mol->bset->rflag == 0) {
12598                                                                 for (sn = 1; sn <= ne_beta; sn++) {
12599                                                                         dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12600                                                                         mc->dp[n] += dd * dd;
12601                                                                 }
12602                                                         }
12603                                                 } else {
12604                                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12605                                                 }
12606                                         }
12607                                 }
12608                         }
12609                 }
12610         }
12611         
12612 #else
12613         /*  (i * step, j * step, k * step)  */
12614         for (ix = 0; ix < nx; ix += step) {
12615                 for (iy = 0; iy < ny; iy += step) {
12616                         for (iz = 0; iz < nz; iz += step) {
12617                                 n = (ix * ny + iy) * nz + iz;
12618                                 if (mc->dp[n] == DBL_MAX) {
12619                                         p.x = mc->origin.x + mc->dx * ix;
12620                                         p.y = mc->origin.y + mc->dy * iy;
12621                                         p.z = mc->origin.z + mc->dz * iz;
12622                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12623                                 }
12624                                 n += step;
12625                         }
12626                 }
12627         }
12628         
12629         /*  Intermediate points  */
12630         for (step = 4; step > 1; step /= 2) {
12631                 hstep = step / 2;
12632                 for (sn = 0; sn <= 1; sn++) {
12633                         n = 0;
12634                         for (ix = 0; ix < nx - 1; ix += step) {
12635                                 for (iy = 0; iy < ny - 1; iy += step) {
12636                                         for (iz = 0; iz < nz - 1; iz += step) {
12637                                                 flags = 0;
12638                                                 thres = mc->thres * (sn == 0 ? 1 : -1);
12639                                                 n = (ix * ny + iy) * nz + iz;
12640                                                 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12641                                                         continue;
12642                                                 /*  (ix, iy, iz)  */
12643                                                 if (mc->dp[n] >= thres)
12644                                                         flags |= 1;
12645                                                 /*  (ix + step, iy, iz)  */
12646                                                 if (mc->dp[n + step * ny * nz] >= thres)
12647                                                         flags |= 2;
12648                                                 /*  (ix, iy + step, iz)  */
12649                                                 if (mc->dp[n + step * nz] >= thres)
12650                                                         flags |= 4;
12651                                                 /*  (ix + 4, iy + step, iz)  */
12652                                                 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12653                                                         flags |= 8;
12654                                                 /*  (ix, iy, iz + step)  */
12655                                                 if (mc->dp[n + step] >= thres)
12656                                                         flags |= 16;
12657                                                 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12658                                                         flags |= 32;
12659                                                 /*  (ix, iy + step, iz + step)  */
12660                                                 if (mc->dp[n + step * (nz + 1)] >= thres)
12661                                                         flags |= 64;
12662                                                 /*  (ix + step, iy + step, iz + step)  */
12663                                                 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12664                                                         flags |= 128;
12665                                                 if (flags != 0 && flags != 255) {
12666                                                         /*  Calc the intermediate points  */
12667                                                         for (iix = 0; iix <= step; iix += hstep) {
12668                                                                 for (iiy = 0; iiy <= step; iiy += hstep) {
12669                                                                         for (iiz = 0; iiz <= step; iiz += hstep) {
12670                                                                                 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12671                                                                                         continue;
12672                                                                                 nn = n + (iix * ny + iiy) * nz + iiz;
12673                                                                                 if (mc->dp[nn] == DBL_MAX) {
12674                                                                                         p.x = mc->origin.x + mc->dx * (ix + iix);
12675                                                                                         p.y = mc->origin.y + mc->dy * (iy + iiy);
12676                                                                                         p.z = mc->origin.z + mc->dz * (iz + iiz);
12677                                                                                         mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12678                                                                                 }
12679                                                                         }
12680                                                                 }
12681                                                         }
12682                                                 }
12683                                         }
12684                                 }
12685                         }
12686                 }
12687         }
12688         
12689 #endif
12690
12691         free(tmp);
12692         
12693         /*  Calculate vertex positions and normal vectors  */
12694         for (sn = 0; sn <= 1; sn++) {
12695                 n = 0;
12696                 thres = mc->thres * (sn == 0 ? 1 : -1);
12697                 VecZero(p);
12698                 for (ix = 0; ix < nx - 1; ix++) {
12699                         for (iy = 0; iy < ny - 1; iy++) {
12700                                 for (iz = 0; iz < nz - 1; iz++) {
12701                                         Double dd0, dd1;
12702                                         nn = (ix * ny + iy) * nz + iz;
12703                                         dd0 = mc->dp[nn];
12704                                         if (dd0 == DBL_MAX)
12705                                                 continue;
12706                                         if (0) {
12707                                                 dd1 = mc->dp[nn + ny * nz];
12708                                                 if (dd1 != DBL_MAX)
12709                                                         p.x = (dd1 - dd0) / mc->dx;
12710                                                 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12711                                                         p.x = (dd0 - dd1) / mc->dx;
12712                                                 else continue;  /*  Cannot define gradient  */
12713                                                 dd1 = mc->dp[nn + nz];
12714                                                 if (dd1 != DBL_MAX)
12715                                                         p.y = (dd1 - dd0) / mc->dy;
12716                                                 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12717                                                         p.y = (dd0 - dd1) / mc->dy;
12718                                                 else continue;
12719                                                 dd1 = mc->dp[nn + 1];
12720                                                 if (dd1 != DBL_MAX)
12721                                                         p.z = (dd1 - dd0) / mc->dz;
12722                                                 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12723                                                         p.z = (dd0 - dd1) / mc->dz;
12724                                                 else continue;
12725                                                 NormalizeVec(&p, &p);
12726                                         }
12727                                         if (n + 3 >= mc->c[sn].ncubepoints) {
12728                                                 /*  Expand cubepoints[] array  */
12729                                                 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12730                                                 if (mc->c[sn].cubepoints == NULL) {
12731                                                         mc->c[sn].ncubepoints = 0;
12732                                                         retval = -3;
12733                                                         goto end;
12734                                                 }
12735                                                 mc->c[sn].ncubepoints += 8192;
12736                                         }
12737                                         mcp = mc->c[sn].cubepoints + n;
12738                                         iix = (dd0 >= thres ? 1 : -1);
12739                                         /*  (x, y, z)->(x + 1, y, z)  */
12740                                         dd1 = mc->dp[nn + ny * nz];
12741                                         if (dd1 != DBL_MAX) {
12742                                                 iiy = (dd1 >= thres ? 1 : -1);
12743                                                 if (iix != iiy) {
12744                                                         /*  Register  */
12745                                                         mcp->key = nn * 3;
12746                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12747                                                         mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12748                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12749                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12750                                                         mcp->grad[0] = p.x;
12751                                                         mcp->grad[1] = p.y;
12752                                                         mcp->grad[2] = p.z;
12753                                                         mcp++;
12754                                                         n++;
12755                                                 }
12756                                         }
12757                                         /*  (x, y, z)->(x, y + 1, z)  */
12758                                         dd1 = mc->dp[nn + nz];
12759                                         if (dd1 != DBL_MAX) {
12760                                                 iiy = (dd1 >= thres ? 1 : -1);
12761                                                 if (iix != iiy) {
12762                                                         /*  Register  */
12763                                                         mcp->key = nn * 3 + 1;
12764                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12765                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12766                                                         mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12767                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12768                                                         mcp->grad[0] = p.x;
12769                                                         mcp->grad[1] = p.y;
12770                                                         mcp->grad[2] = p.z;
12771                                                         mcp++;
12772                                                         n++;
12773                                                 }
12774                                         }
12775                                         /*  (x, y, z)->(x, y, z + 1)  */
12776                                         dd1 = mc->dp[nn + 1];
12777                                         if (dd1 != DBL_MAX) {
12778                                                 iiy = (dd1 >= thres ? 1 : -1);
12779                                                 if (iix != iiy) {
12780                                                         /*  Register  */
12781                                                         mcp->key = nn * 3 + 2;
12782                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12783                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12784                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12785                                                         mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12786                                                         mcp->grad[0] = p.x;
12787                                                         mcp->grad[1] = p.y;
12788                                                         mcp->grad[2] = p.z;
12789                                                         mcp++;
12790                                                         n++;
12791                                                 }
12792                                         }
12793                                 }
12794                         }
12795                 }
12796                 if (n < mc->c[sn].ncubepoints)
12797                         mc->c[sn].cubepoints[n].key = -1;  /*  End mark  */
12798                 ncubepoints = n;
12799                 if (ncubepoints < 3) {
12800                         /*  Less than 3 points: no triangles  */
12801                         if (mc->c[sn].ntriangles > 0)
12802                                 mc->c[sn].triangles[0] = -1;  /*  End mark  */
12803                         continue;
12804                 }
12805                 
12806                 /*  Create triangle table  */
12807                 n = 0;
12808                 for (ix = 0; ix < nx - 1; ix++) {
12809                         for (iy = 0; iy < ny - 1; iy++) {
12810                                 for (iz = 0; iz < nz - 1; iz++) {
12811                                         nn = (ix * ny + iy) * nz + iz;
12812                                         iix = 0;
12813                                         if ((dd = mc->dp[nn]) == DBL_MAX)
12814                                                 continue;
12815                                         else if (dd >= thres)
12816                                                 iix |= 1;
12817                                         if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12818                                                 continue;
12819                                         else if (dd >= thres)
12820                                                 iix |= 2;
12821                                         if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12822                                                 continue;
12823                                         else if (dd >= thres)
12824                                                 iix |= 4;
12825                                         if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12826                                                 continue;
12827                                         else if (dd >= thres)
12828                                                 iix |= 8;
12829                                         if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12830                                                 continue;
12831                                         else if (dd >= thres)
12832                                                 iix |= 16;
12833                                         if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12834                                                 continue;
12835                                         else if (dd >= thres)
12836                                                 iix |= 32;
12837                                         if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12838                                                 continue;
12839                                         else if (dd >= thres)
12840                                                 iix |= 64;
12841                                         if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12842                                                 continue;
12843                                         else if (dd >= thres)
12844                                                 iix |= 128;
12845                                         for (iiy = 0; iiy < 15; iiy++) {
12846                                                 nn = sMarchingCubeTable[iix][iiy];
12847                                                 if (nn < 0)
12848                                                         break;
12849                                                 /*  key index for edges 0-11  */
12850                                                 switch (nn) {
12851                                                         case 0:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3;     break;
12852                                                         case 1:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12853                                                         case 2:  iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3;     break;
12854                                                         case 3:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12855                                                         case 4:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3;     break;
12856                                                         case 5:  iiz = (((ix + 1) * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12857                                                         case 6:  iiz = (( ix      * ny + iy + 1) * nz + iz + 1) * 3;     break;
12858                                                         case 7:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12859                                                         case 8:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12860                                                         case 9:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12861                                                         case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12862                                                         case 11: iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12863                                                         default:
12864                                                                 /*  Skip this triangle  */
12865                                                                 iiy = (iiy - iiy % 3) + 2;
12866                                                                 n = n - n % 3;
12867                                                                 continue;
12868                                                 }
12869                                                 /*  Look for the key index in cubepoints  */
12870                                                 c1 = 0;
12871                                                 c3 = ncubepoints - 1;
12872                                                 mcp = mc->c[sn].cubepoints;
12873                                                 while (1) {
12874                                                         int w;
12875                                                         /*  c1 is always less than c3  */
12876                                                         if (c1 + 1 == c3) {
12877                                                                 /*  end of search  */
12878                                                                 if (mcp[c1].key == iiz) {
12879                                                                         c2 = c1;
12880                                                                 } else if (mcp[c3].key == iiz) {
12881                                                                         c2 = c3;
12882                                                                 } else {
12883                                                                         c2 = -1;
12884                                                                 }
12885                                                                 break;
12886                                                         }
12887                                                         c2 = (c1 + c3) / 2;
12888                                                         w = mcp[c2].key - iiz;
12889                                                         if (w == 0)
12890                                                                 break;
12891                                                         if (w < 0) {
12892                                                                 c1 = c2;
12893                                                         } else {
12894                                                                 c3 = c2;
12895                                                         }
12896                                                 }
12897                                                 if (c2 < 0) {
12898                                                         /*  Not found: skip this triangle  */
12899                                                         iiy = (iiy - iiy % 3) + 2;
12900                                                         n = n - n % 3;
12901                                                         continue;
12902                                                 }
12903                                                 if (n + 1 >= mc->c[sn].ntriangles) {
12904                                                         /*  Expand triangles[] array  */
12905                                                         mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12906                                                         if (mc->c[sn].triangles == NULL) {
12907                                                                 mc->c[sn].ntriangles = 0;
12908                                                                 retval = -4;
12909                                                                 goto end;
12910                                                         }
12911                                                         mc->c[sn].ntriangles += 8192;
12912                                                 }
12913                                                 mc->c[sn].triangles[n] = c2;
12914                                                 n++;
12915                                         }
12916                                 }
12917                         }
12918                 }
12919                 if (n < mc->c[sn].ntriangles)
12920                         mc->c[sn].triangles[n] = -1;  /*  End mark  */
12921                 
12922                 /*  Estimate the normal vector  */
12923                 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12924                         Vector v[3];
12925                         for (ix = 0; ix < 3; ix++) {
12926                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12927                                 v[ix].x = mcp->pos[0];
12928                                 v[ix].y = mcp->pos[1];
12929                                 v[ix].z = mcp->pos[2];
12930                         }
12931                         VecDec(v[2], v[0]);
12932                         VecDec(v[1], v[0]);
12933                         VecCross(v[0], v[1], v[2]);
12934                         NormalizeVec(v, v);
12935                         for (ix = 0; ix < 3; ix++) {
12936                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12937                                 mcp->grad[0] += v[0].x;
12938                                 mcp->grad[1] += v[0].y;
12939                                 mcp->grad[2] += v[0].z;
12940                         }
12941                 }
12942                 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12943                         if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12944                                 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12945                                 if (mc->thres < 0.0)
12946                                         dd = -dd;
12947                                 mcp->grad[0] *= dd;
12948                                 mcp->grad[1] *= dd;
12949                                 mcp->grad[2] *= dd;
12950                         }
12951                 }
12952         }
12953         retval = 0;
12954         MoleculeCallback_notifyModification(mol, 0);
12955 end:
12956         /*  For debug  */
12957         if (0) {
12958                 char *MyAppCallback_getDocumentHomeDir(void);
12959                 FILE *fp;
12960                 char *s;
12961                 Double dmax, dmin;
12962                 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12963                 fp = fopen(s, "w");
12964                 dmax = -1e8;
12965                 dmin = 1e8;
12966                 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12967                         if (mc->dp[n] == DBL_MAX)
12968                                 continue;
12969                         if (dmax < mc->dp[n])
12970                                 dmax = mc->dp[n];
12971                         if (dmin > mc->dp[n])
12972                                 dmin = mc->dp[n];
12973                 }
12974                 dmax = fabs(dmax);
12975                 dmin = fabs(dmin);
12976                 if (dmax < dmin)
12977                         dmax = dmin;
12978                 dmax = 1.001 * dmax;
12979                 fprintf(fp, "thres = %g = 100\n", mc->thres);
12980                 for (iz = 0; iz < mc->nz; iz++) {
12981                         fprintf(fp, "z = %d\n", iz);
12982                         for (iy = 0; iy < mc->ny; iy++) {
12983                                 for (ix = 0; ix < mc->nx; ix++) {
12984                                         n = (ix * ny + iy) * nz + iz;
12985                                         dd = mc->dp[n];
12986                                         if (dd == DBL_MAX)
12987                                                 fprintf(fp, " XXX ");
12988                                         else {
12989                                                 dd = dd * 100 / mc->thres;
12990                                                 if (dd > 999.0)
12991                                                         dd = 999.0;
12992                                                 else if (dd < -999.0)
12993                                                         dd = -999.0;
12994                                                 fprintf(fp, "%4d ", (int)(dd));
12995                                         }
12996                                 }
12997                                 fprintf(fp, "\n");
12998                         }
12999                         fprintf(fp, "\n");
13000                 }
13001                 
13002                 for (sn = 0; sn <= 1; sn++) {
13003                         for (n = 0; n < mc->c[sn].ncubepoints; n++) {
13004                                 MCubePoint *mcp = mc->c[sn].cubepoints + n;
13005                                 nn = mcp->key;
13006                                 if (nn == -1)
13007                                         break;
13008                                 iix = nn % 3;
13009                                 iz = nn / 3 % mc->nz;
13010                                 iy = nn / (3 * mc->nz) % mc->ny;
13011                                 ix = nn / (3 * mc->nz * mc->ny);
13012                                 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
13013                                                 n, ix, iy, iz, iix,
13014                                                 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
13015                         }
13016                         for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
13017                                 if (mc->c[sn].triangles[n] < 0)
13018                                         break;
13019                                 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
13020                                                 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
13021                         }
13022                 }
13023                 fclose(fp);
13024         }
13025         
13026         return retval;
13027 }
13028
13029 void
13030 MoleculeDeallocateMCube(MCube *mcube)
13031 {
13032         free(mcube->dp);
13033         free(mcube->radii);
13034         free(mcube->c[0].cubepoints);
13035         free(mcube->c[0].triangles);
13036         free(mcube->c[1].cubepoints);
13037         free(mcube->c[1].triangles);
13038         free(mcube);
13039 }