OSDN Git Service

0dd48b11fa2957f764610c1995259e72fcc0c324
[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 err_exit;
863                                 }
864                                 if (i >= mp->natoms) {
865                                         s_append_asprintf(errbuf, "line %d: too many atomic symmetry info\n", lineNumber);
866                                         goto err_exit;
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 err_exit;
888                                 }
889                                 if (i >= mp->natoms) {
890                                         s_append_asprintf(errbuf, "line %d: too many fix atom info\n", lineNumber);
891                                         goto err_exit;
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 err_exit;
912                                 }
913                                 if (i >= mp->natoms) {
914                                         s_append_asprintf(errbuf, "line %d: too many uff type info\n", lineNumber);
915                                         goto err_exit;
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 err_exit;
933                                 }
934                                 if (i >= mp->natoms) {
935                                         s_append_asprintf(errbuf, "line %d: too many mm_exclude flags\n", lineNumber);
936                                         goto err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
984                                         }
985                                         if (dbuf[0] <= 0.0) {
986                                                 s_append_asprintf(errbuf, "line %d: the pi anchor weights should be positive", lineNumber);
987                                                 goto err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
1269                                 }
1270                                 if (i >= mp->natoms) {
1271                                         s_append_asprintf(errbuf, "line %d: too many anisotropic thermal parameters\n", lineNumber);
1272                                         goto err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
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 err_exit;
1537                                 }
1538                                 if (i >= mp->natoms) {
1539                                         s_append_asprintf(errbuf, "line %d: too many atom velocity records\n", lineNumber);
1540                                         goto err_exit;
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 err_exit;
1560                                 }
1561                                 if (i >= mp->natoms) {
1562                                         s_append_asprintf(errbuf, "line %d: too many atom force records\n", lineNumber);
1563                                         goto err_exit;
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 err_exit;
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 err_exit;
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 */
1726                                 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1727                                         s_append_asprintf(errbuf, "line %d: the gaussian primitive info cannot be read", lineNumber);
1728                                         goto err_exit;
1729                                 }
1730                                 if (strcasecmp(cbuf[0], "S") == 0) {
1731                                         ibuf[2] = 0;
1732                                 } else if (strcasecmp(cbuf[0], "P") == 0) {
1733                                         ibuf[2] = 1;
1734                                 } else if (strcasecmp(cbuf[0], "SP") == 0) {
1735                                         ibuf[2] = -1;
1736                                 } else if (strcasecmp(cbuf[0], "D") == 0) {
1737                                         ibuf[2] = 2;
1738                                 } else if (strcasecmp(cbuf[0], "D5") == 0) {
1739                                         ibuf[2] = -2;
1740                                 } else if (strcasecmp(cbuf[0], "F") == 0) {
1741                                         ibuf[2] = 3;
1742                                 } else if (strcasecmp(cbuf[0], "F7") == 0) {
1743                                         ibuf[2] = -3;
1744                                 } else if (strcasecmp(cbuf[0], "G") == 0) {
1745                                         ibuf[2] = 4;
1746                                 } else if (strcasecmp(cbuf[0], "G9") == 0) {
1747                                         ibuf[2] = -4;
1748                                 } else {
1749                                         s_append_asprintf(errbuf, "line %d: the gaussian primitive type %s is unknown", lineNumber, cbuf[0]);
1750                                         goto err_exit;
1751                                 }
1752                                 if (ibuf[0] <= 0) {
1753                                         s_append_asprintf(errbuf, "line %d: the number of primitive (%d) must be positive", lineNumber, ibuf[0]);
1754                                         goto err_exit;
1755                                 }
1756                                 if (ibuf[1] < 0 || ibuf[1] >= mp->natoms) {
1757                                         s_append_asprintf(errbuf, "line %d: the atom index (%d) is out of range", lineNumber, ibuf[1]);
1758                                         goto err_exit;
1759                                 }
1760                                 MoleculeAddGaussianOrbitalShell(mp, ibuf[1], ibuf[2], ibuf[0]);
1761                                 i = ibuf[0];
1762                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1763                                         if (buf[0] == '!')
1764                                                 continue;
1765                                         if (buf[0] == '\n')
1766                                                 break;
1767                                         if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1768                                                 s_append_asprintf(errbuf, "line %d: cannot read gaussian primitive coefficients", lineNumber);
1769                                                 goto err_exit;
1770                                         }
1771                                         MoleculeAddGaussianPrimitiveCoefficients(mp, dbuf[0], dbuf[1], dbuf[2]);
1772                                         if (--i == 0)
1773                                                 break;
1774                                 }
1775                                 if (buf[0] == '\n')
1776                                         break;
1777                         }
1778                         continue;
1779                 } else if (strcmp(buf, "!:mo_info") == 0) {
1780                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1781                                 if (buf[0] == '!')
1782                                         continue;
1783                                 if (buf[0] == '\n')
1784                                         break;
1785                                 if (sscanf(buf, "%6s %d %d", cbuf[0], &ibuf[0], &ibuf[1]) < 3) {
1786                                         s_append_asprintf(errbuf, "line %d: the MO info cannot be correctly read", lineNumber);
1787                                         goto err_exit;
1788                                 }
1789                                 if (strcasecmp(cbuf[0], "RHF") == 0) {
1790                                         ibuf[2] = 1;
1791                                 } else if (strcasecmp(cbuf[0], "ROHF") == 0) {
1792                                         ibuf[2] = 2;
1793                                 } else if (strcasecmp(cbuf[0], "UHF") == 0) {
1794                                         ibuf[2] = 0;
1795                                 } else {
1796                                         s_append_asprintf(errbuf, "line %d: unknown HF type: %s", lineNumber, cbuf[0]);
1797                                         goto err_exit;
1798                                 }
1799                                 if (ibuf[0] < 0 || ibuf[1] < 0) {
1800                                         s_append_asprintf(errbuf, "line %d: incorrect number of electrons", lineNumber);
1801                                         goto err_exit;
1802                                 }
1803                                 MoleculeSetMOInfo(mp, ibuf[2], ibuf[0], ibuf[1]);
1804                         }
1805                         continue;
1806                 } else if (strcmp(buf, "!:mo_coefficients") == 0) {
1807                         if (mp->bset == NULL || mp->bset->nshells == 0) {
1808                                 s_append_asprintf(errbuf, "line %d: the :gaussian_primitive section must come before :mo_coefficients", lineNumber);
1809                                 goto err_exit;
1810                         }
1811                         /*  Count the number of components  */
1812                         dp = (Double *)malloc(sizeof(Double) * mp->bset->ncomps);
1813                         i = 1;
1814                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1815                                 if (buf[0] == '!')
1816                                         continue;
1817                                 if (buf[0] == '\n')
1818                                         break;
1819                                 if (sscanf(buf, "MO %d %lf", &ibuf[0], &dbuf[6]) < 2) {
1820                                         s_append_asprintf(errbuf, "line %d: cannot read the MO index or energy", lineNumber);
1821                                         goto err_exit;
1822                                 }
1823                                 if (ibuf[0] != i) {
1824                                         s_append_asprintf(errbuf, "line %d: the MO index (%d) must be in ascending order", lineNumber, ibuf[0]);
1825                                         goto err_exit;
1826                                 }
1827                                 i = 0;
1828                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1829                                         j = sscanf(buf, "%lf %lf %lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3], &dbuf[4], &dbuf[5]);
1830                                         if (j == 0) {
1831                                                 s_append_asprintf(errbuf, "line %d: cannot read the MO coefficients", lineNumber);
1832                                                 goto err_exit;
1833                                         }
1834                                         for (k = 0; k < j; k++, i++) {
1835                                                 if (i >= mp->bset->ncomps) {
1836                                                         s_append_asprintf(errbuf, "line %d: too many MO coefficients", lineNumber);
1837                                                         goto err_exit;
1838                                                 }
1839                                                 dp[i] = dbuf[k];
1840                                         }
1841                                         if (i >= mp->bset->ncomps)
1842                                                 break;
1843                                 }
1844                                 i = MoleculeSetMOCoefficients(mp, ibuf[0], dbuf[6], mp->bset->ncomps, dp);
1845                                 if (i != 0) {
1846                                         s_append_asprintf(errbuf, "line %d: cannot set MO coefficients", lineNumber);
1847                                         goto err_exit;
1848                                 }
1849                                 i = ibuf[0] + 1;  /*  For next entry  */
1850                         }
1851                         continue;
1852                 } else if (strcmp(buf, "!:graphics") == 0) {
1853                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1854                                 MainViewGraphic *gp = NULL;
1855                                 if (buf[0] == '!')
1856                                         continue;
1857                                 if (buf[0] == '\n')
1858                                         break;
1859                                 if (mp->mview == NULL)
1860                                         continue;  /*  Skip  */
1861                         redo:
1862                                 if (strcmp(buf, "line\n") == 0) {
1863                                         ibuf[0] = kMainViewGraphicLine;
1864                                 } else if (strcmp(buf, "poly\n") == 0) {
1865                                         ibuf[0] = kMainViewGraphicPoly;
1866                                 } else if (strcmp(buf, "cylinder\n") == 0) {
1867                                         ibuf[0] = kMainViewGraphicCylinder;
1868                                 } else if (strcmp(buf, "cone\n") == 0) {
1869                                         ibuf[0] = kMainViewGraphicCone;
1870                                 } else if (strcmp(buf, "ellipsoid\n") == 0) {
1871                                         ibuf[0] = kMainViewGraphicEllipsoid;
1872                                 } else {
1873                                         continue;  /*  Skip  */
1874                                 }
1875                                 gp = (MainViewGraphic *)calloc(sizeof(MainViewGraphic), 1);
1876                                 gp->kind = ibuf[0];
1877                                 i = 0;
1878                                 while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1879                                         if (buf[0] == '!')
1880                                                 continue;
1881                                         if (buf[0] == '\n')
1882                                                 break;
1883                                         if (i == 0) {
1884                                                 if (sscanf(buf, "%d %d", &ibuf[0], &ibuf[1]) < 2) {
1885                                                         s_append_asprintf(errbuf, "line %d: the closed/visible flags cannot be read for graphic object", lineNumber);
1886                                                         goto err_exit;
1887                                                 }
1888                                                 gp->closed = ibuf[0];
1889                                                 gp->visible = ibuf[1];
1890                                         } else if (i == 1) {
1891                                                 if (sscanf(buf, "%lf %lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2], &dbuf[3]) < 4) {
1892                                                         s_append_asprintf(errbuf, "line %d: the color cannot be read for graphic object", lineNumber);
1893                                                         goto err_exit;
1894                                                 }
1895                                                 for (j = 0; j < 4; j++)
1896                                                         gp->rgba[j] = dbuf[j];
1897                                         } else if (i == 2) {
1898                                                 j = atoi(buf);
1899                                                 if (j < 0) {
1900                                                         s_append_asprintf(errbuf, "line %d: the number of control points must be non-negative", lineNumber);
1901                                                         goto err_exit;
1902                                                 }
1903                                                 if (j > 0)
1904                                                         NewArray(&gp->points, &gp->npoints, sizeof(GLfloat) * 3, j);
1905                                         } else if (i >= 3 && i < gp->npoints + 3) {
1906                                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1907                                                         s_append_asprintf(errbuf, "line %d: the control point cannot be read for graphic object", lineNumber);
1908                                                         goto err_exit;
1909                                                 }
1910                                                 j = (i - 3) * 3;
1911                                                 gp->points[j++] = dbuf[0];
1912                                                 gp->points[j++] = dbuf[1];
1913                                                 gp->points[j] = dbuf[2];
1914                                         } else if (i == gp->npoints + 3) {
1915                                                 j = atoi(buf);
1916                                                 if (j < 0) {
1917                                                         s_append_asprintf(errbuf, "line %d: the number of normals must be non-negative", lineNumber);
1918                                                         goto err_exit;
1919                                                 }
1920                                                 if (j > 0)
1921                                                         NewArray(&gp->normals, &gp->nnormals, sizeof(GLfloat) * 3, j);
1922                                         } else if (i >= gp->npoints + 4 && i < gp->npoints + gp->nnormals + 4) {
1923                                                 if (sscanf(buf, "%lf %lf %lf", &dbuf[0], &dbuf[1], &dbuf[2]) < 3) {
1924                                                         s_append_asprintf(errbuf, "line %d: the normal vector cannot be read for graphic object", lineNumber);
1925                                                         goto err_exit;
1926                                                 }
1927                                                 j = (i - gp->npoints - 4) * 3;
1928                                                 gp->normals[j++] = dbuf[0];
1929                                                 gp->normals[j++] = dbuf[1];
1930                                                 gp->normals[j] = dbuf[2];
1931                                         } else break;
1932                                         i++;
1933                                 }
1934                                 MainView_insertGraphic(mp->mview, -1, gp);
1935                                 free(gp);
1936                                 if (buf[0] == '\n' || buf[0] == 0)
1937                                         break;
1938                                 goto redo;
1939                         }
1940                         continue;
1941                 } else if (strncmp(buf, "!:@", 3) == 0) {
1942                         /*  Plug-in implemented in the ruby world  */
1943                         Int stringLen;
1944                         char *stringBuf, *returnString;
1945                         i = strlen(buf);
1946                         NewArray(&stringBuf, &stringLen, sizeof(char), i + 1);
1947                         strcpy(stringBuf, buf);
1948                         k = lineNumber;
1949                         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
1950                                 /*  The comment lines are _not_ skipped  */
1951                                 if (buf[0] == '\n')
1952                                         break;
1953                                 j = strlen(buf);
1954                                 AssignArray(&stringBuf, &stringLen, sizeof(char), i + j, NULL);
1955                                 strncpy(stringBuf + i, buf, j);
1956                                 i += j;
1957                         }
1958                         if (MolActionCreateAndPerform(mp, SCRIPT_ACTION("si;s"),
1959                                                                                   "proc { |i| loadmbsf_plugin(i) rescue \"line #{i}: #{$i.to_s}\" }",
1960                                                                                   stringBuf, k, &returnString) != 0) {
1961                                 s_append_asprintf(errbuf, "line %d: cannot invoke Ruby plugin", lineNumber);
1962                                 goto err_exit;
1963                         } else if (returnString[0] != 0) {
1964                                 s_append_asprintf(errbuf, "%s", returnString);
1965                                 goto err_exit;
1966                         }
1967                         free(stringBuf);
1968                         continue;
1969                 }
1970                 /*  Unknown sections are silently ignored  */
1971         }
1972
1973         MoleculeCleanUpResidueTable(mp);
1974         if (mp->arena != NULL)
1975                 md_arena_set_molecule(mp->arena, mp);
1976
1977         fclose(fp);
1978
1979 /*      if (mp->mview != NULL) {
1980                 if (mview_ibuf[0] != kUndefined)
1981                         mp->mview->showUnitCell = mview_ibuf[0];
1982                 if (mview_ibuf[1] != kUndefined)
1983                         mp->mview->showPeriodicBox = mview_ibuf[1];
1984                 if (mview_ibuf[2] != kUndefined)
1985                         mp->mview->showExpandedAtoms = mview_ibuf[2];
1986                 if (mview_ibuf[3] != kUndefined)
1987                         mp->mview->showEllipsoids = mview_ibuf[3];
1988                 if (mview_ibuf[4] != kUndefined)
1989                         mp->mview->showHydrogens = mview_ibuf[4];
1990                 if (mview_ibuf[5] != kUndefined)
1991                         mp->mview->showDummyAtoms = mview_ibuf[5];
1992                 if (mview_ibuf[6] != kUndefined)
1993                         mp->mview->showRotationCenter = mview_ibuf[6];
1994                 if (mview_ibuf[7] != kUndefined)
1995                         mp->mview->showGraphiteFlag = mview_ibuf[7];
1996                 if (mview_ibuf[8] != kUndefined)
1997                         mp->mview->showPeriodicImageFlag = mview_ibuf[8];
1998                 if (mview_ibuf[9] != kUndefined)
1999                         mp->mview->showGraphite = mview_ibuf[9];
2000                 if (mview_ibuf[10] != kUndefined && mview_ibuf[10] >= 6)
2001                         mp->mview->atomResolution = mview_ibuf[10];
2002                 if (mview_ibuf[11] != kUndefined && mview_ibuf[11] >= 4)
2003                         mp->mview->bondResolution = mview_ibuf[11];
2004                 for (i = 0; i < 6; i++) {
2005                         if (mview_ibuf[12 + i] != kUndefined)
2006                                 mp->mview->showPeriodicImage[i] = mview_ibuf[12 + i];
2007                 }
2008                 if (mview_dbuf[8] != kUndefined)
2009                         mp->mview->atomRadius = mview_dbuf[8];
2010                 if (mview_dbuf[9] != kUndefined)
2011                         mp->mview->bondRadius = mview_dbuf[9];          
2012                 if (mp->mview->track != NULL) {
2013                         if (mview_dbuf[0] != kUndefined)
2014                                 TrackballSetScale(mp->mview->track, mview_dbuf[0]);
2015                         if (mview_dbuf[1] != kUndefined)
2016                                 TrackballSetTranslate(mp->mview->track, mview_dbuf + 1);
2017                         if (mview_dbuf[4] != kUndefined)
2018                                 TrackballSetRotate(mp->mview->track, mview_dbuf + 4);
2019                 }
2020         }
2021 */
2022
2023         return 0;
2024
2025 err_exit:
2026         fclose(fp);
2027         /*  The content of mp may be broken, so make it empty  */
2028         MoleculeClear(mp);
2029         return -1;      
2030 }
2031
2032 int
2033 MoleculeLoadPsfFile(Molecule *mp, const char *fname, char **errbuf)
2034 {
2035         FILE *fp;
2036         char buf[1024];
2037         char *p;
2038         int section = -1;
2039         int i, j, err, fn;
2040         int lineNumber;
2041         Int ibuf[12];
2042         Vector *frames = NULL;
2043         Atom *ap;
2044         err = 0;
2045         *errbuf = NULL;
2046         if (mp == NULL)
2047                 mp = MoleculeNew();
2048         else MoleculeClear(mp);
2049         fp = fopen(fname, "rb");
2050         if (fp == NULL) {
2051                 s_append_asprintf(errbuf, "Cannot open file");
2052                 return 1;
2053         }
2054 /*      flockfile(fp); */
2055         lineNumber = 0;
2056         fn = 0;
2057         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2058                 if (strncmp(buf, "PSF", 3) == 0) {
2059                         section = 0;
2060                         continue;
2061                 } else {
2062                         for (p = buf; *p != 0 && isspace(*p); p++) {}
2063                         if (*p == 0) {
2064                                 section++;
2065                                 continue;
2066                         }
2067                 }
2068                 if (strstr(buf, "!COORD") != NULL) {
2069                         /*  Extended psf file with coordinates  */
2070                         if (fn > 0) {
2071                                 /*  Allocate a temporary storage for frames  */
2072                                 size_t size = sizeof(Vector) * mp->natoms * fn;
2073                                 if (frames == NULL)
2074                                         frames = (Vector *)malloc(size);
2075                                 else
2076                                         frames = (Vector *)realloc(frames, size);
2077                                 if (frames == NULL)
2078                                         goto panic;
2079                         }
2080                         /*  Read coordinates  */
2081                         for (i = 0; i < mp->natoms; i++) {
2082                                 double dval[3];
2083                                 Vector r;
2084                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2085                                         err = 1;
2086                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading coordinates (frame %d)", lineNumber, fn);
2087                                         goto exit;
2088                                 }
2089                                 if (sscanf(buf, "%lg %lg %lg", dval, dval + 1, dval + 2) != 3) {
2090                                         err = 1;
2091                                         s_append_asprintf(errbuf, "line %d: coordinates cannot be read for atom %d", lineNumber, i + 1);
2092                                         goto exit;
2093                                 }
2094                                 r.x = dval[0];
2095                                 r.y = dval[1];
2096                                 r.z = dval[2];
2097                                 if (fn == 0)
2098                                         ATOM_AT_INDEX(mp->atoms, i)->r = r;
2099                                 else
2100                                         frames[mp->natoms * (fn - 1) + i] = r;
2101                         }
2102                         fn++;
2103                         continue;
2104                 }
2105                 
2106                 if (section == 2) {
2107                         /*  Atoms  */
2108                         Int natoms;
2109                         ReadFormat(buf, "I8", &natoms);
2110                         if (natoms == 0)
2111                                 continue;
2112                         if (NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms) == NULL)
2113                                 goto panic;
2114                         mp->nresidues = 0;
2115                         for (i = 0; i < natoms; i++) {
2116                                 struct {
2117                                         char segName[5], resName[4], atomName[5], atomType[3], element[3];
2118                                         Int serial;
2119                                 } w;
2120                                 memset(&w, 0, sizeof(w));
2121                                 ap = ATOM_AT_INDEX(mp->atoms, i);
2122                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2123                                         err = 1;
2124                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading atoms", lineNumber);
2125                                         goto exit;
2126                                 }
2127                                 ReadFormat(buf, "I8 x1 S4 I5 x1 S3 x2 S4 x1 S4 F16 F10",
2128                                         &w.serial, w.segName, &ap->resSeq, w.resName, w.atomName, 
2129                                         w.atomType, &ap->charge, &ap->weight);
2130                                 strncpy(ap->segName, w.segName, 4);
2131                                 strncpy(ap->resName, w.resName, 3);
2132                                 strncpy(ap->aname, w.atomName, 4);
2133                                 ap->type = AtomTypeEncodeToUInt(w.atomType);
2134                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
2135                                 ap->atomicNumber = GuessAtomicNumber(w.atomName, ap->weight);
2136                                 ElementToString(ap->atomicNumber, w.element);
2137                                 strncpy(ap->element, w.element, 2);
2138                         /*      w.element[0] = 0;
2139                                 for (p = w.atomName; *p != 0; p++) {
2140                                         if (isalpha(*p) && *p != '_') {
2141                                                 w.element[0] = toupper(*p);
2142                                                 if (isalpha(p[1]) && p[1] != '_') {
2143                                                         w.element[1] = toupper(p[1]);
2144                                                         w.element[2] = 0;
2145                                                 } else {
2146                                                         w.element[1] = 0;
2147                                                 }
2148                                                 break;
2149                                         }
2150                                 }
2151                                 strncpy(ap->element, w.element, 2);
2152                                 ap->atomicNumber = ElementToInt(w.element); */
2153                                 if (w.resName[0] == 0)
2154                                         strncpy(ap->resName, "XXX", 3);
2155                                 if (ap->resSeq > mp->nresidues)
2156                                         mp->nresidues = ap->resSeq;
2157                         }
2158                         if (mp->residues != NULL)
2159                                 free(mp->residues);
2160                         if (NewArray(&mp->residues, &mp->nresidues, sizeof(char (*)[4]), mp->nresidues + 1) == 0)
2161                                 goto panic;
2162                         for (i = 0; i < mp->natoms; i++) {
2163                                 j = mp->atoms[i].resSeq;
2164                                 if (mp->residues[j][0] == 0)
2165                                         strncpy(mp->residues[j], mp->atoms[i].resName, 4);
2166                         }
2167                         continue;
2168                 } else if (section == 3) {
2169                         /*  Bonds  */
2170                         Int nbonds;
2171                         Int *bp;
2172                         ReadFormat(buf, "I8", &nbonds);
2173                         if (nbonds == 0)
2174                                 continue;
2175                         if (NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, nbonds) == NULL)
2176                                 goto panic;
2177                         bp = mp->bonds;
2178                         for (i = 0; i < nbonds; i += 4) {
2179                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2180                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading bonds", lineNumber);
2181                                         err = 1;
2182                                         goto exit;
2183                                 }
2184                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2185                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2186                                 for (j = 0; j < 4 && i + j < nbonds; j++) {
2187                                         Int b1, b2;
2188                                         Atom *ap;
2189                                         b1 = ibuf[j * 2] - 1;    /* Internal atom number is 0-based */
2190                                         b2 = ibuf[j * 2 + 1] - 1;
2191                                         if (b1 < 0 || b1 >= mp->natoms || b2 < 0 || b2 >= mp->natoms) {
2192                                                 s_append_asprintf(errbuf, "line %d: The bond %d-%d includes non-existent atom", lineNumber, b1+1, b2+1);
2193                                                 err = 1;
2194                                                 goto exit;
2195                                         }
2196                                         *bp++ = b1;
2197                                         *bp++ = b2;
2198                                         ap = ATOM_AT_INDEX(mp->atoms, b1);
2199                                         AtomConnectInsertEntry(&ap->connect, -1, b2);
2200                                         ap = ATOM_AT_INDEX(mp->atoms, b2);
2201                                         AtomConnectInsertEntry(&ap->connect, -1, b1);
2202                                 }
2203                         }
2204                         continue;
2205                 } else if (section == 4) {
2206                         /*  Angles  */
2207                         Int nangles;
2208                         Int *gp;
2209                         ReadFormat(buf, "I8", &nangles);
2210                         if (nangles == 0)
2211                                 continue;
2212                         if (NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, nangles) == NULL)
2213                                 goto panic;
2214                         gp = mp->angles;
2215                         for (i = 0; i < nangles; i += 3) {
2216                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2217                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading angles", lineNumber);
2218                                         err = 1;
2219                                         goto exit;
2220                                 }
2221                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
2222                                         ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7, ibuf + 8);
2223                                 for (j = 0; j < 3 && i + j < nangles; j++) {
2224                                         Int a1, a2, a3;
2225                                         a1 = ibuf[j * 3] - 1;   /* Internal atom number is 0-based */
2226                                         a2 = ibuf[j * 3 + 1] - 1;
2227                                         a3 = ibuf[j * 3 + 2] - 1;
2228                                         if (a1 < 0 || a1 >= mp->natoms || a2 < 0 || a2 >= mp->natoms || a3 < 0 || a3 >= mp->natoms) {
2229                                                 s_append_asprintf(errbuf, "line %d: The angle %d-%d-%d includes non-existent atom", lineNumber, a1+1, a2+1, a3+1);
2230                                                 err = 1;
2231                                                 goto exit;
2232                                         }
2233                                         *gp++ = a1;
2234                                         *gp++ = a2;
2235                                         *gp++ = a3;
2236                                 }
2237                         }
2238                         continue;
2239                 } else if (section == 5 || section == 6) {
2240                         /*  Dihedrals and Impropers  */
2241                         Int ndihedrals;
2242                         Int *dp;
2243                         ReadFormat(buf, "I8", &ndihedrals);
2244                         if (ndihedrals == 0)
2245                                 continue;
2246                         if (section == 5) {
2247                                 if (NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, ndihedrals) == NULL)
2248                                         goto panic;
2249                                 dp = mp->dihedrals;
2250                         } else {
2251                                 if (NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, ndihedrals) == NULL)
2252                                         goto panic;
2253                                 dp = mp->impropers;
2254                         }
2255                         for (i = 0; i < ndihedrals; i += 2) {
2256                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2257                                         fclose(fp);
2258                                         s_append_asprintf(errbuf, "line %d: premature end of file while reading %s", lineNumber, (section == 5 ? "dihedral" : "improper"));
2259                                         err = 1;
2260                                         goto exit;
2261                                 }
2262                                 ReadFormat(buf, "I8I8I8I8I8I8I8I8", ibuf, ibuf + 1, ibuf + 2, ibuf + 3, ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7);
2263                                 for (j = 0; j < 2 && i + j < ndihedrals; j++) {
2264                                         Int d1, d2, d3, d4;
2265                                         d1 = ibuf[j * 4] - 1;   /*  Internal atom number is 0-based  */
2266                                         d2 = ibuf[j * 4 + 1] - 1;
2267                                         d3 = ibuf[j * 4 + 2] - 1;
2268                                         d4 = ibuf[j * 4 + 3] - 1;
2269                                         if (d1 < 0 || d1 >= mp->natoms || d2 < 0 || d2 >= mp->natoms || d3 < 0 || d3 >= mp->natoms || d4 < 0 || d4 >= mp->natoms) {
2270                                                 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);
2271                                                 err = 1;
2272                                                 goto exit;
2273                                         }
2274                                         *dp++ = d1;
2275                                         *dp++ = d2;
2276                                         *dp++ = d3;
2277                                         *dp++ = d4;
2278                                 }
2279                         }
2280                         continue;
2281                 }
2282         }
2283         
2284         /*  Create frames for each atom if necessary  */
2285         if (fn > 1) {
2286                 for (i = 0; i < mp->natoms; i++) {
2287                         ap = ATOM_AT_INDEX(mp->atoms, i);
2288                         NewArray(&ap->frames, &ap->nframes, sizeof(Vector), fn);
2289                         if (ap->frames == NULL)
2290                                 goto panic;
2291                         for (j = 0; j < fn; j++)
2292                                 ap->frames[j] = frames[mp->natoms * j + i];
2293                 }
2294                 free(frames);
2295                 frames = NULL;
2296         }
2297
2298   exit:
2299 /*      funlockfile(fp); */
2300         fclose(fp);
2301         mp->nframes = -1;  /*  Should be recalculated later  */
2302         if (err)
2303                 return 1;
2304         else if (section == -1)
2305                 return -1;
2306         return 0;
2307   panic:
2308         Panic("low memory while reading structure file %s", fname);
2309         return 1; /* not reached */
2310 }
2311
2312 /* ("-x", "y", "-z+0.5") -> (-1,0,0,0,0,1,0,0,0,0,-1,0.5)  */
2313 static int
2314 sMoleculeSymopStringsToTransform(char **symops, Transform tr)
2315 {
2316         int i;
2317         char *symop;
2318         memset(tr, 0, sizeof(Transform));
2319         for (i = 0; i < 3; i++) {
2320                 symop = symops[i];
2321                 if (symop == NULL)
2322                         return 1;
2323                 while (*symop != 0) {
2324                         int sn = 1;
2325                         while (isspace(*symop))
2326                                 symop++;
2327                         if (*symop == 0 || *symop == '\r' || *symop == 'n')
2328                                 break;
2329                         if (*symop == '-') {
2330                                 sn = -1;
2331                                 symop++;
2332                         } else if (*symop == '+') {
2333                                 sn = 1;
2334                                 symop++;
2335                         }
2336                         while (isspace(*symop))
2337                                 symop++;
2338                         if (*symop == '.' || isdigit(*symop)) {
2339                                 /*  Numerical offset  */
2340                                 double d = strtod(symop, &symop);
2341                                 if (*symop == '/') {
2342                                         double dd = strtod(symop + 1, &symop);
2343                                         if (dd > 0)
2344                                                 d /= dd;
2345                                         else
2346                                                 return 1;  /*  Bad format  */
2347                                 }
2348                                 tr[9 + i] = d * sn;
2349                         } else if (*symop == 'x' || *symop == 'X') {
2350                                 tr[i] = sn;
2351                                 symop++;
2352                         } else if (*symop == 'y' || *symop == 'Y') {
2353                                 tr[i + 3] = sn;
2354                                 symop++;
2355                         } else if (*symop == 'z' || *symop == 'Z') {
2356                                 tr[i + 6] = sn;
2357                                 symop++;
2358                         } else return 1;  /*  Bad format  */
2359                 } /* end while (*symop != 0) */
2360         }
2361         return 0;
2362 }
2363
2364 static void
2365 sMoleculeGenerateSymopWithTransform(Molecule *mp, Transform gtr, int num)
2366 {
2367         int i, j;
2368         Transform tr;
2369         if (num <= 0)
2370                 num = mp->nsyms;
2371         for (i = 0; i < num; i++) {
2372                 memmove(tr, mp->syms[i], sizeof(Transform));
2373                 TransformMul(tr, gtr, tr);
2374                 for (j = 9; j < 12; j++) {
2375                         if (tr[j] >= 1.0)
2376                                 tr[j] -= 1.0;
2377                         else if (tr[j] <= 0.0)
2378                                 tr[j] += 1.0;
2379                 }
2380                 AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr);
2381         }
2382 }
2383
2384 static char *
2385 sChomp(char *buf)
2386 {
2387         char *p = buf + strlen(buf) - 1;
2388         if (p >= buf && (*p == '\n' || *p == '\r')) {
2389                 *p = 0;
2390                 if (--p >= buf && (*p == '\n' || *p == '\r')) {
2391                         *p = 0;
2392                 }
2393         }
2394         return buf;
2395 }
2396
2397 int
2398 MoleculeLoadTepFile(Molecule *mp, const char *fname, char **errbuf)
2399 {
2400         FILE *fp;
2401         char buf[1024];
2402         int section = -1;
2403         int lineNumber;
2404         int cellType;
2405         Int ibuf[12];
2406         Double fbuf[12];
2407         Int *bonds, nbonds;
2408         *errbuf = NULL;
2409         if (mp == NULL)
2410                 mp = MoleculeNew();
2411         fp = fopen(fname, "rb");
2412         if (fp == NULL) {
2413                 s_append_asprintf(errbuf, "Cannot open file");
2414                 return 1;
2415         }
2416         lineNumber = 0;
2417         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2418                 if (section == -1) {
2419                         /*  Title  */
2420                         section = 0;
2421                         continue;
2422                 }
2423                 if (section == 0) {
2424                         /*  XtalCell  */
2425                         ReadFormat(buf, "I1F8F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5);
2426                         cellType = ibuf[0];
2427                         MoleculeSetCell(mp, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], 0);
2428                         section = 1;
2429                         continue;
2430                 }
2431                 if (section == 1) {
2432                         /*  Symmetry  */
2433                         Transform tr;
2434                         if (cellType == 0) {
2435                                 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);
2436                                 tr[0] = fbuf[1];
2437                                 tr[3] = fbuf[2];
2438                                 tr[6] = fbuf[3];
2439                                 tr[1] = fbuf[5];
2440                                 tr[4] = fbuf[6];
2441                                 tr[7] = fbuf[7];
2442                                 tr[2] = fbuf[9];
2443                                 tr[5] = fbuf[10];
2444                                 tr[8] = fbuf[11];
2445                                 tr[9] = fbuf[0];
2446                                 tr[10] = fbuf[4];
2447                                 tr[11] = fbuf[8];
2448                         } else {
2449                                 char *symops[3], *brks;
2450                                 sChomp(buf);
2451                                 memset(tr, 0, sizeof(Transform));
2452                                 ReadFormat(buf, "I1", ibuf);
2453                                 symops[0] = strtok_r(buf + 1, ", ", &brks);
2454                                 symops[1] = strtok_r(NULL, ", ", &brks);
2455                                 symops[2] = strtok_r(NULL, ", ", &brks);
2456                                 if (sMoleculeSymopStringsToTransform(symops, tr)) {
2457                                         s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2458                                         return 1;
2459                                 }
2460                         }
2461                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2462                                 goto panic;
2463                         if (ibuf[0] != 0)
2464                                 section = 2;
2465                         continue;
2466                 }
2467                 if (section == 2) {      /*  Atoms  */
2468                         char name[8];
2469                         Atom *ap;
2470                         int atomType;
2471                         int atomIndex = mp->natoms;
2472                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2473                         memset(ap, 0, gSizeOfAtomRecord);
2474                         ReadFormat(buf, "S6x3x9x9F9F9F9F9", name, fbuf, fbuf+1, fbuf+2, fbuf+3);
2475                         strncpy(ap->aname, name, 4);
2476                         ap->r.x = fbuf[0];
2477                         ap->r.y = fbuf[1];
2478                         ap->r.z = fbuf[2];
2479                         MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2480                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2481                         ElementToString(ap->atomicNumber, ap->element); */
2482                 /*      sAtomSetElement(ap, -1, ap->name); */
2483                         guessElement(ap);
2484                         atomType = fbuf[3];
2485                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2486                                 s_append_asprintf(errbuf, "unexpected end of file");
2487                                 return 1;
2488                         }
2489                         ReadFormat(buf, "I1F8F9F9F9F9F9F9", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2490                         atomType = fbuf[6];
2491                         if ((atomType >= 0 && atomType <= 5) || (atomType >= 8 && atomType <= 10)) { 
2492                                 /*  Anisotropic thermal parameters  */
2493                                 MoleculeSetAniso(mp, atomIndex, atomType, fbuf[0], fbuf[1], fbuf[2], fbuf[3], fbuf[5], fbuf[4], NULL);
2494                         }
2495                         if (ibuf[0] != 0)
2496                                 section = 3;
2497                         continue;
2498                 }
2499         }
2500         fclose(fp);
2501         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2502         if (nbonds > 0) {
2503                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2504                 free(bonds);
2505         }
2506         mp->nframes = -1;  /*  Should be recalculated later  */
2507         return 0;
2508   panic:
2509         Panic("low memory while reading structure file %s", fname);
2510         return -1; /* not reached */
2511 }
2512
2513 int
2514 MoleculeLoadShelxFile(Molecule *mp, const char *fname, char **errbuf)
2515 {
2516         FILE *fp;
2517         char buf[1024];
2518         char *p1, *p2;
2519         int n;
2520         int lineNumber;
2521         int latticeType;
2522         int currentResSeq = 0;
2523         char currentResName[6];
2524         Transform tr;
2525         int ibuf[12];
2526         float fbuf[12];
2527         Double dbuf[12];
2528         Int nsfacs = 0;
2529         Int nbonds, *bonds;
2530         char (*sfacs)[4] = NULL;
2531
2532         *errbuf = NULL;
2533         if (mp == NULL)
2534                 mp = MoleculeNew();
2535         currentResName[0] = 0;
2536         fp = fopen(fname, "rb");
2537         if (fp == NULL) {
2538                 s_append_asprintf(errbuf, "Cannot open file");
2539                 return 1;
2540         }
2541         lineNumber = 0;
2542         tr[0] = tr[4] = tr[8] = 1;
2543         tr[1] = tr[2] = tr[3] = tr[5] = tr[6] = tr[7] = tr[9] = tr[10] = tr[11] = 0;
2544         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), 0, tr) == 0)
2545                 goto panic;
2546         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
2547                 if (strncmp(buf, "CELL", 4) == 0) {
2548                         /*  XtalCell  */
2549                         sscanf(buf + 4, " %f %f %f %f %f %f %f", fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, fbuf+6);
2550                         MoleculeSetCell(mp, fbuf[1], fbuf[2], fbuf[3], fbuf[4], fbuf[5], fbuf[6], 0);
2551                         continue;
2552                 } else if (strncmp(buf, "SFAC", 4) == 0) {
2553                         sChomp(buf);
2554                         for (p1 = strtok_r(buf + 4, " ", &p2); p1 != NULL; p1 = strtok_r(NULL, " ", &p2)) {
2555                                 char *pp = (char *)AssignArray(&sfacs, &nsfacs, 4, nsfacs, NULL);
2556                                 if (pp == NULL)
2557                                         goto panic;
2558                                 strncpy(pp, p1, 3);
2559                                 pp[3] = 0;
2560                         }
2561                         continue;
2562                 } else if (strncmp(buf, "LATT", 4) == 0) {
2563                         sscanf(buf + 4, " %d", &latticeType);
2564                         continue;
2565                 } else if (strncmp(buf, "SYMM", 4) == 0) {
2566                         char *symops[3], *brks;
2567                         memset(tr, 0, sizeof(Transform));
2568                 //      ReadFormat(buf + 4, "I1", ibuf);
2569                         sChomp(buf);
2570                         symops[0] = strtok_r(buf + 4, ",", &brks);
2571                         symops[1] = strtok_r(NULL, ",", &brks);
2572                         symops[2] = strtok_r(NULL, ",", &brks);
2573                         if (sMoleculeSymopStringsToTransform(symops, tr)) {
2574                                 s_append_asprintf(errbuf, "line %d: bad symmetry specification", lineNumber);
2575                                 return 1;
2576                         }
2577                         if (AssignArray(&mp->syms, &mp->nsyms, sizeof(Transform), mp->nsyms, tr) == 0)
2578                                 goto panic;
2579                         continue;
2580                 } else if (strncmp(buf, "RESI", 4) == 0) {
2581                         for (p1 = buf + 4; isspace(*p1); p1++);
2582                         if (isalpha(*p1)) {
2583                                 for (p2 = p1 + 1; isalnum(*p2); p2++);
2584                                 *p2 = 0;
2585                                 strncpy(currentResName, p1, 4);
2586                                 currentResName[4] = 0;
2587                                 p1 = p2 + 1;
2588                         } else currentResName[0] = 0;
2589                         sscanf(buf + 4, " %d", &currentResSeq);
2590                         continue;
2591                 } else {
2592                         /* Atom name: [A-Za-z]{1,2}[0-9]*  */
2593                         for (p1 = buf; p1 < buf + 2 && (isalpha(*p1) && *p1 != '_'); p1++);
2594                         if (p1 > buf) {
2595                                 while (isdigit(*p1))
2596                                         p1++;
2597                         }
2598                         if (p1 > buf && p1 <= buf + 4 && isspace(*p1)) {
2599                                 /*  Atom  */
2600                                 Atom *ap;
2601                                 char cont[4];
2602                                 int atomIndex = mp->natoms;
2603                                 ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, atomIndex, NULL);
2604                                 memset(ap, 0, gSizeOfAtomRecord);
2605                                 strncpy(ap->aname, buf, 4);
2606                                 n = sscanf(p1, " %d %f %f %f %f %f %f %2s", ibuf, fbuf, fbuf+1, fbuf+2, fbuf+3, fbuf+4, fbuf+5, cont);
2607                                 if (n == 8 && strcmp(cont, "=") == 0) {
2608                                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
2609                                                 s_append_asprintf(errbuf, "line %d: unexpected end of file within the atom cards", lineNumber);
2610                                                 return 1;
2611                                         }
2612                                         sscanf(buf, " %f %f %f %f", fbuf+6, fbuf+7, fbuf+8, fbuf+9);
2613                                         n = 10;   /*  Aniso  */
2614                                 } else n = 5; /*  Iso  */
2615                                 ap->r.x = fbuf[0];
2616                                 ap->r.y = fbuf[1];
2617                                 ap->r.z = fbuf[2];
2618                                 MoleculeXtalToCartesian(mp, &(ap->r), &(ap->r));
2619                                 ap->occupancy = fbuf[3];
2620                                 if (ap->aname[0] != 'Q' && ibuf[0] >= 1 && ibuf[0] <= nsfacs) {
2621                                         strncpy(ap->element, sfacs[ibuf[0] - 1], 2);
2622                                         ap->element[2] = 0;
2623                                 /*      sAtomSetElement(ap, -1, sfacs[ibuf[0] - 1]); */
2624                                 /*      strncpy(ap->element, sfacs[ibuf[0] - 1], 4);
2625                                         ap->atomicNumber = ElementToInt(ap->element); */
2626                         /*      } else {
2627                                         sAtomSetElement(ap, -1, ap->name); */
2628                                 /*      ap->atomicNumber = AtomNameToElement(ap->name);
2629                                         ElementToString(ap->atomicNumber, ap->element); */
2630                                 }
2631                                 guessElement(ap);
2632                                 if (n == 10 || fbuf[4] >= 0.0) {
2633                                         int i, c, j;
2634                                         /*  Read in the standard deviations  */
2635                                         ReadLine(buf, sizeof buf, fp, &lineNumber);
2636                                         for (i = 0; i < 9; i++) {
2637                                                 j = 3 + i * 8;
2638                                                 c = buf[j + 8];
2639                                                 buf[j + 8] = 0;
2640                                                 dbuf[i] = strtod(buf + j, NULL);
2641                                                 buf[j + 8] = c;
2642                                         }
2643                                         ap->sigma.x = dbuf[0];
2644                                         ap->sigma.y = dbuf[1];
2645                                         ap->sigma.z = dbuf[2];
2646                                 }
2647                                 if (n == 5)
2648                                         ap->tempFactor = fbuf[4] * 78.9568352087147; /* 8*pi*pi */
2649                                 else
2650                                         MoleculeSetAniso(mp, atomIndex, 8, fbuf[4], fbuf[5], fbuf[6], fbuf[9], fbuf[7], fbuf[8], dbuf);
2651                                 ap->resSeq = currentResSeq;
2652                                 strncpy(ap->resName, currentResName, 4);
2653                         }
2654                         continue;
2655                 }
2656         }
2657         fclose(fp);
2658
2659         /*  Add symmetry operations according to the lattice type  */
2660         switch (latticeType < 0 ? -latticeType : latticeType) {
2661                 static Transform tr_i = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0.5};
2662                 static Transform tr_c = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0.5, 0};
2663                 static Transform tr_a = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0.5};
2664                 static Transform tr_b = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5};
2665                 static Transform tr_r1 = {0, 1, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0};
2666                 static Transform tr_r2 = {-1, -1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0};
2667                 case 1:  /* P */
2668                         break;
2669                 case 2:  /* I */
2670                         sMoleculeGenerateSymopWithTransform(mp, tr_i, 0);
2671                         break;
2672                 case 3:  /* Rhombohedral obverse on hexagonal axes  */
2673                         n = mp->nsyms;
2674                         sMoleculeGenerateSymopWithTransform(mp, tr_r1, n);
2675                         sMoleculeGenerateSymopWithTransform(mp, tr_r2, n);
2676                         break;
2677                 case 4:  /* F */
2678                         n = mp->nsyms;
2679                         sMoleculeGenerateSymopWithTransform(mp, tr_a, n);
2680                         sMoleculeGenerateSymopWithTransform(mp, tr_b, n);
2681                         sMoleculeGenerateSymopWithTransform(mp, tr_c, n);
2682                         break;
2683                 case 5:  /* A */
2684                         sMoleculeGenerateSymopWithTransform(mp, tr_a, 0);
2685                         break;
2686                 case 6:  /* B */
2687                         sMoleculeGenerateSymopWithTransform(mp, tr_b, 0);
2688                         break;
2689                 case 7:  /* C */
2690                         sMoleculeGenerateSymopWithTransform(mp, tr_c, 0);
2691                         break;
2692         }
2693                 
2694         if (latticeType > 0) {
2695                 static Transform tr_inv = {-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0};
2696                 sMoleculeGenerateSymopWithTransform(mp, tr_inv, 0);
2697         }
2698         
2699         MoleculeGuessBonds(mp, 0.0, &nbonds, &bonds);
2700         if (nbonds > 0) {
2701                 MoleculeAddBonds(mp, nbonds, bonds, NULL, 1);
2702                 free(bonds);
2703         }
2704         mp->nframes = -1;  /*  Should be recalculated later  */
2705         return 0;
2706   panic:
2707         Panic("low memory while reading structure file %s", fname);
2708         return -1; /* not reached */
2709 }
2710
2711 /*  Add one gaussian orbital shell information (not undoable)  */
2712 int
2713 MoleculeAddGaussianOrbitalShell(Molecule *mol, Int a_idx, Int sym, Int nprims)
2714 {
2715         BasisSet *bset;
2716         ShellInfo *shellp;
2717         if (mol == NULL)
2718                 return -1;  /*  Molecule is empty  */
2719         bset = mol->bset;
2720         if (bset == NULL) {
2721                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2722                 if (bset == NULL)
2723                         return -2;  /*  Low memory  */
2724         }
2725         shellp = AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), bset->nshells, NULL);
2726         if (shellp == NULL)
2727                 return -2;  /*  Low memory  */
2728         switch (sym) {
2729                 case 0:  shellp->sym = kGTOType_S;  shellp->ncomp = 1; break;
2730                 case 1:  shellp->sym = kGTOType_P;  shellp->ncomp = 3; break;
2731                 case -1: shellp->sym = kGTOType_SP; shellp->ncomp = 4; break;
2732                 case 2:  shellp->sym = kGTOType_D;  shellp->ncomp = 6; break;
2733                 case -2: shellp->sym = kGTOType_D5; shellp->ncomp = 5; break;
2734                 case 3:  shellp->sym = kGTOType_F;  shellp->ncomp = 10; break;
2735                 case -3: shellp->sym = kGTOType_F7; shellp->ncomp = 7; break;
2736                 case 4:  shellp->sym = kGTOType_G;  shellp->ncomp = 15; break;
2737                 case -4: shellp->sym = kGTOType_G9; shellp->ncomp = 9; break;
2738                 default:
2739                         return -3;  /* Unsupported shell type  */
2740         }
2741         shellp->nprim = nprims;
2742         shellp->a_idx = a_idx;
2743         if (bset->shells < shellp) {
2744                 shellp->m_idx = shellp[-1].m_idx + shellp[-1].ncomp;
2745                 shellp->p_idx = shellp[-1].p_idx + shellp[-1].nprim;
2746         } else {
2747                 shellp->m_idx = 0;
2748                 shellp->p_idx = 0;
2749         }
2750         /*  Update the number of components (if not yet determined)  */
2751         if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2752                 bset->ncomps = shellp->m_idx + shellp->ncomp;
2753         return 0;
2754 }
2755
2756 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2757 int
2758 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2759 {
2760         BasisSet *bset;
2761         PrimInfo *primp;
2762         if (mol == NULL)
2763                 return -1;  /*  Molecule is empty  */
2764         bset = mol->bset;
2765         if (bset == NULL) {
2766                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2767                 if (bset == NULL)
2768                         return -2;  /*  Low memory  */
2769         }
2770         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2771         if (primp == NULL)
2772                 return -2;  /*  Low memory  */
2773         primp->A = exponent;
2774         primp->C = contraction;
2775         primp->Csp = contraction_sp;
2776         return 0;
2777 }
2778
2779 /*  Get the shell information from the component index  */
2780 /*  The outLabel must have space for at least 23 non-Null characters  */
2781 int
2782 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char *outLabel, Int *outShellIdx)
2783 {
2784         BasisSet *bset;
2785         ShellInfo *shellp;
2786         int si;
2787         if (mol == NULL || (bset = mol->bset) == NULL)
2788                 return -1;  /*  No basis set info  */
2789         if (comp_idx < 0 || comp_idx >= bset->ncomps)
2790                 return -2;  /*  Component index out of range  */
2791         for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2792                 if (comp_idx >= shellp->ncomp) {
2793                         comp_idx -= shellp->ncomp;
2794                         continue;
2795                 } else {
2796                         static const char *type_p = "xyz";
2797                         static const char *type_d = "xxyyzzxyxzyz";
2798                         static const char *type_d5[] = {"xy","yz","zz", "xz", "xx-yy"};
2799                         static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2800                         static const char *type_f7[] = {"x3-3xy2", "x2z-y2z", "x(5z2-r2)", "z(5z2-3r2)", "y(5z2-r2)", "xyz", "3x2y-y3"};
2801                         static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "x2yz", "xyz2"};
2802                         static const char *type_g9[] = {"x4+y4-6x2y2", "xz(x2-3y2)", "(x2-y2)(7z2-r2)", "xz(7z2-3r2)", "35z4-30z2r2+3r4", "yz(7z2-3r2)", "xy(7z2-r2)", "yz(3x2-y2)", "xy(x2-y2)"};
2803                         *outAtomIdx = shellp->a_idx;
2804                         *outShellIdx = si;
2805                         switch (shellp->sym) {
2806                                 case kGTOType_S:
2807                                         strcpy(outLabel, "S");
2808                                         break;
2809                                 case kGTOType_P:
2810                                         outLabel[0] = 'P';
2811                                         outLabel[1] = type_p[comp_idx];
2812                                         outLabel[2] = 0;
2813                                         break;
2814                                 case kGTOType_SP:
2815                                         if (comp_idx == 0)
2816                                                 strcpy(outLabel, "S");
2817                                         else {
2818                                                 outLabel[0] = 'P';
2819                                                 outLabel[1] = type_p[comp_idx - 1];
2820                                                 outLabel[2] = 0;
2821                                         }
2822                                         break;
2823                                 case kGTOType_D:
2824                                         outLabel[0] = 'D';
2825                                         strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2826                                         outLabel[3] = 0;
2827                                         break;
2828                                 case kGTOType_D5:
2829                                         outLabel[0] = 'D';
2830                                         strcpy(outLabel + 1, type_d5[comp_idx]);
2831                                         break;
2832                                 case kGTOType_F:
2833                                         outLabel[0] = 'F';
2834                                         strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2835                                         outLabel[4] = 0;
2836                                         break;
2837                                 case kGTOType_F7:
2838                                         outLabel[0] = 'F';
2839                                         strcpy(outLabel + 1, type_f7[comp_idx]);
2840                                         break;
2841                                 case kGTOType_G:
2842                                         outLabel[0] = 'G';
2843                                         strcpy(outLabel + 1, type_g[comp_idx]);
2844                                         break;
2845                                 case kGTOType_G9:
2846                                         outLabel[0] = 'G';
2847                                         strcpy(outLabel + 1, type_g9[comp_idx]);
2848                                         break;
2849                                 default:
2850                                         return -3;  /*  Unsupported orbital type (internal error) */
2851                         }
2852                         return 0;
2853                 }
2854         }
2855         return -4;  /*  comp_idx out of range? (internal error)  */
2856 }
2857
2858 /*  Set MO coefficients for idx-th MO (1-based)  */
2859 int
2860 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2861 {
2862         BasisSet *bset;
2863         int i, n;
2864         if (mol == NULL)
2865                 return -1;  /*  Molecule is empty  */
2866         bset = mol->bset;
2867         if (bset == NULL) {
2868                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2869                 if (bset == NULL)
2870                         return -2;  /*  Low memory  */
2871         }
2872         if (bset->nmos == 0) {
2873                 if (bset->nshells > 0) {
2874                         /*  Shell info is already set: calculate the number of MOs from there  */
2875                         for (i = n = 0; i < bset->nshells; i++)
2876                                 n += bset->shells[i].ncomp;
2877                         bset->ncomps = n;
2878                 } else if (ncomps > 0) {
2879                         bset->ncomps = ncomps;
2880                 }
2881                 if (bset->rflag == 0)
2882                         bset->nmos = bset->ncomps * 2;
2883                 else
2884                         bset->nmos = bset->ncomps;
2885                 if (bset->nmos <= 0)
2886                         return -3;  /*  Bad or inconsistent number of MOs  */
2887                 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2888                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2889                 if (bset->mo == NULL || bset->moenergies == NULL) {
2890                         if (bset->mo != NULL)
2891                                 free(bset->mo);
2892                         if (bset->moenergies != NULL)
2893                                 free(bset->moenergies);
2894                         bset->mo = NULL;
2895                         bset->moenergies = NULL;
2896                         bset->nmos = 0;
2897                         return -2;  /*  Low memory  */
2898                 }
2899         }
2900         if (idx < 0)
2901                 idx = -idx + bset->ncomps;
2902         if (idx < 0 || idx > bset->nmos)
2903                 return -4;  /*  Bad MO index  */
2904         if (idx == 0)
2905                 idx = bset->nmos;  /*  Arbitrary vector  */
2906         else
2907                 idx--;
2908         if (energy != -1000000)
2909                 bset->moenergies[idx] = energy;
2910         if (ncomps < bset->ncomps)
2911                 return -5;  /*  Insufficient number of data provided  */
2912         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2913         if (bset->cns != NULL) {
2914                 /*  Clear the cached values  */
2915                 free(bset->cns);
2916                 bset->cns = NULL;
2917                 bset->ncns = 0;
2918         }
2919         return 0;
2920 }
2921
2922 /*  Get MO coefficients for idx-th MO (1-based)  */
2923 /*  Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e.  */
2924 /*  *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs  */
2925 /*  properly designates the memory size as an array of Doubles.  */
2926 int
2927 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2928 {
2929         BasisSet *bset;
2930         if (mol == NULL)
2931                 return -1;  /*  Molecule is empty  */
2932         bset = mol->bset;
2933         if (bset == NULL || bset->ncomps <= 0)
2934                 return -2;  /*  No basis set info  */
2935         if (idx < 0)
2936                 idx = -idx + bset->ncomps;
2937         if (idx < 0 || idx > bset->nmos)
2938                 return -3;  /*  MO index out of range  */
2939         if (idx == 0)
2940                 idx = bset->nmos;  /*  Arbitrary vector  */
2941         else
2942                 idx--;
2943         if (energy != NULL)
2944                 *energy = bset->moenergies[idx];
2945         if (ncoeffs != NULL && coeffs != NULL) {
2946                 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2947                         if (*coeffs != NULL)
2948                                 free(*coeffs);  /*  Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2949                         *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2950                         *ncoeffs = bset->ncomps;
2951                 }
2952                 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2953         }
2954         return 0;
2955 }
2956
2957 /*  Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
2958     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons   */
2959 int
2960 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2961 {
2962         BasisSet *bset;
2963         if (mol == NULL || mol->natoms == 0)
2964                 return -1;  /*  Molecule is empty  */
2965         if (rflag < 0) {
2966                 if (mol->bset != NULL) {
2967                         BasisSetRelease(mol->bset);
2968                         mol->bset = NULL;
2969                 }
2970                 return 0;
2971         }
2972         bset = mol->bset;
2973         if (bset == NULL) {
2974                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2975                 if (bset == NULL)
2976                         return -2;  /*  Low memory  */
2977         }
2978         bset->natoms_bs = mol->natoms;
2979         bset->ne_alpha = ne_alpha;
2980         bset->ne_beta = ne_beta;
2981         bset->rflag = rflag;
2982         return 0;
2983 }
2984
2985 static void
2986 sSeparateTokens(char *inString, char **outPtr, int size)
2987 {
2988         char *p;
2989         int i;
2990         for (i = 0; i < size; i++) {
2991                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2992                 if (p == NULL)
2993                         break;
2994                 outPtr[i] = p;
2995         }
2996         while (i < size) {
2997                 outPtr[i++] = NULL;
2998         }
2999 }
3000
3001 static int
3002 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3003 {
3004         char buf[256];
3005         Int i, n;
3006         *((void **)basep) = NULL;
3007         *countp = 0;
3008         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3009                 return 4;  /*  Out of memory  */
3010         n = 0;
3011         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3012                 char *tokens[16], *p;
3013                 sSeparateTokens(buf, tokens, 16);
3014                 for (i = 0; i < 16; i++) {
3015                         if (tokens[i] == NULL)
3016                                 break;
3017                         if (size == sizeof(Int)) {
3018                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3019                         } else if (size == sizeof(Double)) {
3020                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3021                         } else return -1;  /*  Internal error  */
3022                         if (tokens[i] == p || *p != 0)
3023                                 return 1;  /*  Non-digit character  */
3024                         if (++n == num) {
3025                                 if (i < 15 && tokens[i + 1] != NULL)
3026                                         return 2;  /*  Too many data  */
3027                                 return 0;  /*  All data are successfully read  */
3028                         }
3029                 }
3030         }
3031         return 3;  /*  Unexpected EOF  */                       
3032 }
3033
3034 static int
3035 sSetupGaussianCoefficients(BasisSet *bset)
3036 {
3037         ShellInfo *sp;
3038         PrimInfo *pp;
3039         int i, j, k;
3040         Double *dp, d;
3041         
3042         /*  Cache the contraction coefficients for efficient calculation  */
3043         /*  Sum up the number of components for all primitives  */
3044         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3045                 sp->cn_idx = k;
3046                 k += sp->nprim * sp->ncomp;
3047         }
3048         /*  Allocate memory for the cached values  */
3049         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3050                 return 1;
3051         /*  Iterate over all primitives  */
3052         dp = bset->cns;
3053         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3054                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3055                         switch (sp->sym) {
3056                                 case kGTOType_S:
3057                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3058                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3059                                         break;
3060                                 case kGTOType_P:
3061                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3062                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3063                                         *dp++ = d;
3064                                         *dp++ = d;
3065                                         *dp++ = d;
3066                                         break;
3067                                 case kGTOType_SP:
3068                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3069                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3070                                         *dp++ = d;
3071                                         *dp++ = d;
3072                                         *dp++ = d;
3073                                         break;
3074                                 case kGTOType_D:
3075                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3076                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3077                                         d = pp->C * pow(pp->A, 1.75);
3078                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
3079                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
3080                                         dp += 6;
3081                                         break;
3082                                 case kGTOType_D5:
3083                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3084                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3085                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3086                                         d = pp->C * pow(pp->A, 1.75);
3087                                         dp[0] = d * 0.822961390;
3088                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
3089                                         dp[3] = d * 1.425410941;
3090                                         dp += 5;
3091                                         break;
3092                                 /*  TODO: Support F/F7 and G/G9 type orbitals  */
3093                         }
3094                 }
3095         }
3096         return 0;
3097 }
3098
3099 int
3100 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3101 {
3102         FILE *fp;
3103         char buf[1024];
3104         int lineNumber;
3105         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3106         BasisSet *bset;
3107         ShellInfo *sp;
3108         PrimInfo *pp;
3109         Int nary;
3110         Int *iary;
3111         Double *dary;
3112         Atom *ap;
3113 /*      Vector *vp; */
3114         Double w;
3115
3116         *errbuf = NULL;
3117         if (mp == NULL)
3118                 mp = MoleculeNew();
3119         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3120         if (bset == NULL)
3121                 goto panic;
3122         mp->bset = bset;
3123         fp = fopen(fname, "rb");
3124         if (fp == NULL) {
3125                 s_append_asprintf(errbuf, "Cannot open file");
3126                 return 1;
3127         }
3128         lineNumber = 0;
3129         natoms = nbasis = -1;
3130         mxbond = 0;
3131         ncomps = 0;
3132         nelec = 0;
3133         nprims = 0;
3134         nary = 0;
3135         iary = NULL;
3136         dary = NULL;
3137         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3138                 char *tokens[16];
3139                 char *p = buf + 41;
3140                 if (lineNumber == 2) {
3141                         /*  job info line  */
3142                         if (buf[10] == 'U')
3143                                 bset->rflag = 0;  /*  UHF  */
3144                         else if (buf[11] == 'O')
3145                                 bset->rflag = 2;  /*  ROHF  */
3146                         else bset->rflag = 1; /*  RHF  */
3147                         continue;
3148                 }
3149                 while (p > buf && *p == ' ')
3150                         p--;
3151                 p[1] = 0;
3152                 sSeparateTokens(buf + 42, tokens, 16);
3153                 if (strcmp(buf, "Number of atoms") == 0) {
3154                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3155                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3156                                 retval = 2;
3157                                 goto cleanup;
3158                         }
3159                         bset->natoms_bs = natoms;
3160                         /*  Allocate atom records (all are empty for now)  */
3161                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3162                         /*  Also allocate atom position array for MO calculations  */
3163                 /*      AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3164                         /*  Also allocate nuclear charge array  */
3165                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3166                 } else if (strcmp(buf, "Number of electrons") == 0) {
3167                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3168                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3169                                 retval = 2;
3170                                 goto cleanup;
3171                         }
3172                         nelec = i;
3173                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3174                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3175                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3176                                 retval = 2;
3177                                 goto cleanup;
3178                         }
3179                         bset->ne_alpha = i;
3180                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3181                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3182                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3183                                 retval = 2;
3184                                 goto cleanup;
3185                         }
3186                         bset->ne_beta = i;
3187                         if (bset->ne_alpha + bset->ne_beta != nelec) {
3188                                 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);
3189                                 retval = 2;
3190                                 goto cleanup;
3191                         }
3192                 } else if (strcmp(buf, "Number of basis functions") == 0) {
3193                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3194                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3195                                 retval = 2;
3196                                 goto cleanup;
3197                         }
3198                 } else if (strcmp(buf, "Atomic numbers") == 0) {
3199                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3200                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3201                                 retval = 2;
3202                                 goto cleanup;
3203                         }
3204                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3205                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3206                                 retval = 2;
3207                                 goto cleanup;
3208                         }
3209                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3210                                 ap->atomicNumber = iary[i];
3211                                 bset->nuccharges[i] = iary[i];
3212                                 ElementToString(ap->atomicNumber, ap->element);
3213                                 memmove(ap->aname, ap->element, 4);
3214                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3215                                         ap->weight = w;
3216                         }
3217                         free(iary);
3218                         iary = NULL;
3219                 } else if (strcmp(buf, "Nuclear charges") == 0) {
3220                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3221                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3222                                 retval = 2;
3223                                 goto cleanup;
3224                         }
3225                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3226                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3227                                 retval = 2;
3228                                 goto cleanup;
3229                         }
3230                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3231                                 bset->nuccharges[i] = dary[i];
3232                         }
3233                         free(iary);
3234                         iary = NULL;
3235                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3236                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3237                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3238                                 retval = 2;
3239                                 goto cleanup;
3240                         }
3241                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3242                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3243                                 retval = 2;
3244                                 goto cleanup;
3245                         }
3246                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3247                                 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3248                                 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3249                                 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3250                         }
3251                         free(dary);
3252                         dary = NULL;
3253                 } else if (strcmp(buf, "MxBond") == 0) {
3254                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3255                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3256                                 retval = 2;
3257                                 goto cleanup;
3258                         }
3259                 } else if (strcmp(buf, "IBond") == 0) {
3260                         Int *bonds;
3261                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3262                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3263                                 retval = 2;
3264                                 goto cleanup;
3265                         }
3266                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3267                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3268                                 retval = 2;
3269                                 goto cleanup;
3270                         }
3271                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3272                         for (i = 0; i < natoms; i++) {
3273                                 for (j = k = 0; j < mxbond; j++) {
3274                                         n = iary[i * mxbond + j] - 1;
3275                                         if (n > i) {
3276                                                 /*  Connect atom i and atom n  */
3277                                                 bonds[k++] = i;
3278                                                 bonds[k++] = n;
3279                                         }
3280                                 }
3281                                 if (k > 0) {
3282                                         bonds[k] = kInvalidIndex;
3283                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3284                                 }
3285                         }
3286                         free(iary);
3287                         free(bonds);
3288                         iary = NULL;
3289                 } else if (strcmp(buf, "Shell types") == 0) {
3290                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3291                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3292                                 retval = 2;
3293                                 goto cleanup;
3294                         }
3295                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3296                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3297                                 retval = 2;
3298                                 goto cleanup;
3299                         }
3300                         /*  Allocate ShellInfo table and store shell type information  */
3301                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3302                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3303                                 switch (iary[i]) {
3304                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
3305                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
3306                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3307                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
3308                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3309                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
3310                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3311                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
3312                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3313                                         default:
3314                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3315                                                 retval = 2;
3316                                                 goto cleanup;
3317                                 }
3318                                 sp->m_idx = n;
3319                                 n += sp->ncomp;
3320                         }
3321                         bset->ncomps = ncomps = n;
3322                         free(iary);
3323                         iary = NULL;
3324                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3325                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3326                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3327                                 retval = 2;
3328                                 goto cleanup;
3329                         }
3330                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3331                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3332                                 retval = 2;
3333                                 goto cleanup;
3334                         }
3335                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3336                                 sp->nprim = iary[i];
3337                                 sp->p_idx = n;
3338                                 n += sp->nprim;
3339                         }
3340                         nprims = n;
3341                         free(iary);
3342                         iary = NULL;
3343                 } else if (strcmp(buf, "Shell to atom map") == 0) {
3344                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3345                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3346                                 retval = 2;
3347                                 goto cleanup;
3348                         }
3349                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3350                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3351                                 retval = 2;
3352                                 goto cleanup;
3353                         }
3354                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3355                                 sp->a_idx = iary[i] - 1;
3356                         }
3357                         free(iary);
3358                         iary = NULL;
3359                 } else if (strcmp(buf, "Primitive exponents") == 0) {
3360                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3361                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3362                                 retval = 2;
3363                                 goto cleanup;
3364                         }
3365                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3366                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3367                                 retval = 2;
3368                                 goto cleanup;
3369                         }
3370                         /*  Allocate PrimInfo table  */
3371                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3372                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3373                                 pp->A = dary[i];
3374                         }
3375                         free(dary);
3376                         dary = NULL;
3377                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3378                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3379                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3380                                 retval = 2;
3381                                 goto cleanup;
3382                         }
3383                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3384                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3385                                 retval = 2;
3386                                 goto cleanup;
3387                         }
3388                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3389                                 pp->C = dary[i];
3390                         }
3391                         free(dary);
3392                         dary = NULL;
3393                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3394                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3395                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3396                                 retval = 2;
3397                                 goto cleanup;
3398                         }
3399                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3400                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3401                                 retval = 2;
3402                                 goto cleanup;
3403                         }
3404                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3405                                 pp->Csp = dary[i];
3406                         }
3407                         free(dary);
3408                         dary = NULL;
3409                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3410                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3411                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3412                                 retval = 2;
3413                                 goto cleanup;
3414                         }
3415                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3416                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3417                                 retval = 2;
3418                                 goto cleanup;
3419                         }
3420                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3421                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3422                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3423                                 retval = 2;
3424                                 goto cleanup;
3425                         }
3426                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3427                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3428                                 retval = 2;
3429                                 goto cleanup;
3430                         }
3431                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3432                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3433                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3434                                 retval = 2;
3435                                 goto cleanup;
3436                         }
3437                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3438                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3439                                 retval = 2;
3440                                 goto cleanup;
3441                         }
3442                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3443                         bset->nmos = ncomps * 2;
3444                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3445                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3446                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3447                         free(dary);
3448                         dary = NULL;
3449                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3450                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3451                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3452                                 retval = 2;
3453                                 goto cleanup;
3454                         }
3455                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3456                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3457                                 retval = 2;
3458                                 goto cleanup;
3459                         }
3460                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
3461                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3462                         free(dary);
3463                         dary = NULL;
3464                 } else if (strcmp(buf, "Total SCF Density") == 0) {
3465                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3466                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3467                                 retval = 2;
3468                                 goto cleanup;
3469                         }
3470                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3471                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3472                                 retval = 2;
3473                                 goto cleanup;
3474                         }
3475                 }
3476         }
3477         if (mp->natoms == 0) {
3478                 s_append_asprintf(errbuf, "Atom information is missing");
3479                 retval = 2;
3480                 goto cleanup;
3481         }
3482         if (bset->shells == NULL || bset->priminfos == NULL) {
3483                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3484                 retval = 2;
3485                 goto cleanup;
3486         }
3487         if (bset->mo == NULL) {
3488                 s_append_asprintf(errbuf, "MO coefficients were not found");
3489                 retval = 2;
3490                 goto cleanup;
3491         }
3492         if (sSetupGaussianCoefficients(bset) != 0) {
3493                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3494                 retval = 2;
3495                 goto cleanup;
3496         }
3497         mp->nframes = -1;
3498         retval = 0;
3499 cleanup:
3500         fclose(fp);
3501         if (iary != NULL)
3502                 free(iary);
3503         if (dary != NULL)
3504                 free(dary);
3505         if (retval != 0) {
3506                 if (mp->bset != NULL) {
3507                         BasisSetRelease(mp->bset);
3508                         mp->bset = NULL;
3509                 }
3510         }
3511         return retval;
3512 panic:
3513         Panic("low memory while reading fchk file %s", fname);
3514         return -1; /* not reached */    
3515 }
3516
3517 int
3518 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3519 {
3520         FILE *fp;
3521         int newmol = 0;
3522         char buf[1024];
3523         int lineNumber, i, j, k, len, natoms = 0;
3524         int nframes = 0;
3525         int n1;
3526         int retval = 0;
3527         int ival[8];
3528         double dval[8];
3529         char sval[16];
3530         Vector *vbuf = NULL;
3531         IntGroup *ig;
3532         int optimizing = 0, status = 0;
3533         
3534         *errbuf = NULL;
3535         if (mol == NULL) {
3536                 mol = MoleculeNew();
3537         }
3538         if (mol->natoms == 0)
3539                 newmol = 1;
3540
3541         fp = fopen(fname, "rb");
3542         if (fp == NULL) {
3543                 s_append_asprintf(errbuf, "Cannot open file");
3544                 return 1;
3545         }
3546         
3547         /*  ESP is cleared (not undoable!)  */
3548         if (mol->elpots != NULL) {
3549                 free(mol->elpots);
3550                 mol->elpots = NULL;
3551                 mol->nelpots = 0;
3552         }
3553         
3554         lineNumber = 0;
3555         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3556         redo:
3557                 n1 = 0;
3558                 if (strncmp(buf, " $DATA", 6) == 0) {
3559                         /*  Initial geometry  */
3560                         if (!newmol) {
3561                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3562                         }
3563                         i = 0;
3564                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3565                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3566                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3567                                 if (strncmp(buf, " $END", 5) == 0)
3568                                         break;
3569                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3570                                         s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3571                                         retval = 2;
3572                                         goto exit_loop;
3573                                 }
3574                                 if (newmol) {
3575                                         Atom a;
3576                                         memset(&a, 0, sizeof(a));
3577                                         strncpy(a.aname, sval, 4);
3578                                         a.r.x = dval[1];
3579                                         a.r.y = dval[2];
3580                                         a.r.z = dval[3];
3581                                         a.atomicNumber = (Int)dval[0];
3582                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3583                                         a.type = AtomTypeEncodeToUInt(a.element);
3584                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3585                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3586                                 } else {
3587                                         Atom *ap;
3588                                         if (i >= mol->natoms) {
3589                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3590                                                 retval = 3;
3591                                                 goto exit_loop;
3592                                         }
3593                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3594                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3595                                                 retval = 4;
3596                                                 goto exit_loop;
3597                                         }
3598                                         vbuf[i].x = dval[1];
3599                                         vbuf[i].y = dval[2];
3600                                         vbuf[i].z = dval[3];
3601                                 }
3602                                 /*  Skip until a blank line is found  */
3603                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3604                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3605                                         for (j = 0; buf[j] == ' '; j++);
3606                                         if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3607                                                 break;
3608                                 }
3609                                 i++;
3610                         }
3611                         natoms = i;
3612                         if (!newmol) {
3613                                 /*  Set atom positions  */
3614                                 IntGroup *ig;
3615                                 if (natoms < mol->natoms) {
3616                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3617                                         retval = 5;
3618                                         goto exit_loop;
3619                                 }
3620                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3621                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3622                                 IntGroupRelease(ig);
3623                         }
3624                         if (vbuf == NULL)
3625                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3626                         nframes = MoleculeGetNumberOfFrames(mol);
3627                         if (status < 0)
3628                                 break;
3629                         continue;
3630                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3631                         /*  Skip until the separator line is read (three or four lines)  */
3632                         i = 0;
3633                         do {
3634                                 if (i++ >= 4) {
3635                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3636                                         retval = 6;
3637                                         goto exit_loop;
3638                                 }
3639                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3640                         } while (strstr(buf, "----------------------------") == NULL);
3641                         for (i = 0; i < natoms; i++) {
3642                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3643                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3644                                         retval = 6;
3645                                         goto exit_loop;
3646                                 }
3647                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3648                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3649                                         retval = 6;
3650                                         goto exit_loop;
3651                                 }
3652                                 vbuf[i].x = dval[1];
3653                                 vbuf[i].y = dval[2];
3654                                 vbuf[i].z = dval[3];
3655                         }
3656                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3657                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3658                         IntGroupRelease(ig);
3659                         nframes++;
3660                         if (n1 == 0)
3661                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3662                         else
3663                                 optimizing = 0;
3664                         continue;
3665                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3666                         if (mol->bset == NULL) {
3667                                 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3668                                 if (i != 0) {
3669                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3670                                         retval = 8;
3671                                         goto exit_loop;
3672                                 }
3673                         }
3674                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3675                         Double *coeffs;
3676                         /*  Read the vec group  */
3677                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3678                                 continue;  /*  Just ignore  */
3679                         if (optimizing)
3680                                 continue;  /*  Ignore VEC group during optimization  */
3681                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3682                         if (coeffs == NULL) {
3683                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3684                                 retval = 9;
3685                                 goto exit_loop;
3686                         }
3687                         i = k = 0;
3688                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3689                                 len = strlen(buf);
3690                                 if (strncmp(buf, " $END", 5) == 0)
3691                                         break;
3692                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3693                                         strncpy(sval, buf + j, 15);
3694                                         sval[15] = 0;
3695                                         coeffs[k] = strtod(sval, NULL);
3696                                         k++;
3697                                         if ((k % 5) == 0)
3698                                                 break;
3699                                 }
3700                                 if (k < mol->bset->ncomps)
3701                                         continue;
3702                                 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3703                                 if (j != 0) {
3704                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3705                                         free(coeffs);
3706                                         retval = 10;
3707                                         goto exit_loop;
3708                                 }
3709                                 i++;
3710                                 k = 0;
3711                         }
3712                         if (status < 0)
3713                                 break;
3714                         continue;
3715                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3716                         i = 0;
3717                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3718                                 Elpot *ep;
3719                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3720                                         continue;
3721                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3722                                         break;
3723                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3724                                 ep->pos.x = dval[0];
3725                                 ep->pos.y = dval[1];
3726                                 ep->pos.z = dval[2];
3727                                 ep->esp = dval[3];
3728                                 i++;
3729                         }
3730                         if (status > 0)
3731                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3732                         else break;    /*  End of file encountered or interrupted */
3733                 }  /*  TODO: read MOLPLT info if present  */
3734         }
3735         if (status < 0) {
3736                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3737                 retval = 11;
3738         }
3739 exit_loop:
3740         if (vbuf != NULL)
3741                 free(vbuf);
3742         if (mol->natoms > 0)
3743                 retval = 0;  /*  Return the partially constructed molecule  */
3744         if (newmol && mol->nbonds == 0) {
3745                 /*  Guess bonds  */
3746                 Int nbonds, *bonds;
3747                 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3748                 if (nbonds > 0) {
3749                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3750                         free(bonds);
3751                 }
3752         }
3753         return 0;
3754 }
3755
3756 int
3757 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3758 {
3759         int retval;
3760         if (ftype == NULL || *ftype == 0) {
3761                 const char *cp;
3762                 cp = strrchr(fname, '.');
3763                 if (cp != NULL)
3764                         ftype = cp + 1;
3765                 else {
3766                         cp = guessMoleculeType(fname);
3767                         if (strcmp(cp, "???") != 0)
3768                                 ftype = cp;
3769                 }
3770         }
3771         if (strcasecmp(ftype, "pdb") == 0) {
3772                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3773         }
3774         if (retval != 0) {
3775                 /*  Try all formats once again  */
3776                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3777         }
3778         return retval;
3779 }
3780
3781 int
3782 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3783 {
3784         FILE *fp;
3785         char buf[1024];
3786         char *p;
3787         int lineNumber;
3788         int i, j, new_unit, retval;
3789         Atom *ap;
3790         IntGroup *ig;
3791         Vector *vp = NULL;
3792         Int ibuf[12];
3793         Int entries = 0;
3794         retval = 0;
3795         *errbuf = NULL;
3796         fp = fopen(fname, "rb");
3797         if (fp == NULL) {
3798                 s_append_asprintf(errbuf, "Cannot open file");
3799                 return -1;
3800         }
3801 /*      flockfile(fp); */
3802         if (mp->natoms == 0)
3803                 new_unit = 1;
3804         else {
3805                 /*  Allocate buffer for undo-capable modification  */
3806                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3807                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3808                         /*  Retain current position if the atom info is missing in the input file  */
3809                         vp[i] = ap->r;
3810                 }
3811                 new_unit = 0;
3812         }
3813         lineNumber = 0;
3814         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3815                 if (strncmp(buf, "END", 3) == 0)
3816                         break;
3817                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3818                         struct {
3819                                 Int serial, intCharge, resSeq;
3820                                 Vector r;
3821                                 Double occ, temp;
3822                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3823                         } w;
3824                         memset(&w, 0, sizeof(w));
3825                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3826                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3827                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3828                         if (w.atomName[0] == 0) {
3829                                 continue;  /*  Atom name is empty  */
3830                         }
3831                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3832                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3833                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3834                         } else {
3835                                 w.resSeq = atoi(w.resSeqStr);
3836                         }
3837                         if (w.element[0] == 0) {
3838                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3839                                 for (p = w.atomName; *p != 0; p++) {
3840                                         if (isalpha(*p) && *p != '_') {
3841                                                 w.element[0] = toupper(*p);
3842                                                 if (isalpha(p[1]) && p[1] != '_') {
3843                                                         w.element[1] = toupper(p[1]);
3844                                                         w.element[2] = 0;
3845                                                 } else {
3846                                                         w.element[1] = 0;
3847                                                 }
3848                                                 break;
3849                                         }
3850                                 }
3851                         }
3852                         if (w.occStr[0] == 0)
3853                                 w.occ = 1.0;
3854                         else
3855                                 w.occ = atof(w.occStr);
3856                         if (w.serial <= 0) {
3857                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3858                                 retval = 1;
3859                                 goto abort;
3860                         }
3861                         w.serial--;  /*  The internal atom number is 0-based  */
3862                         if (w.serial >= mp->natoms) {
3863                                 if (new_unit) {
3864                                         /*  Create a new atom entry  */
3865                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3866                                 } else {
3867                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3868                                         retval = 1;
3869                                         goto abort;
3870                                 }
3871                         }
3872                         if (new_unit) {
3873                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3874                                 ap->r = w.r;
3875                                 ap->occupancy = w.occ;
3876                                 ap->tempFactor = w.temp;
3877                                 if (w.segName[0] == 0)
3878                                         strncpy(w.segName, "MAIN", 4);
3879                                 strncpy(ap->segName, w.segName, 4);
3880                                 ap->resSeq = w.resSeq;
3881                                 strncpy(ap->resName, w.resName, 4);
3882                                 strncpy(ap->aname, w.atomName, 4);
3883                                 strncpy(ap->element, w.element, 2);
3884                                 ap->element[2] = 0;
3885                                 ap->atomicNumber = ElementToInt(ap->element);
3886                                 ap->type = AtomTypeEncodeToUInt(ap->element);
3887                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3888                                 ap->intCharge = w.intCharge;
3889                                 if (ap->resSeq > 0) {
3890                                         if (ap->resSeq < mp->nresidues) {
3891                                                 /*  Update the resName according to residues[]  */
3892                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3893                                         } else {
3894                                                 /*  Register the resName to residues[]  */
3895                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3896                                         }
3897                                 } else {
3898                                         ap->resSeq = 0;
3899                                         strcpy(ap->resName, "XXX");
3900                                         if (mp->nresidues == 0)
3901                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3902                                 }
3903                                 i = ElementToInt(ap->element);
3904                                 if (i >= 0)
3905                                         ap->weight = gElementParameters[i].weight;
3906                         } else {
3907                                 /*  Not a new unit: only the atom position is updated  */
3908                                 vp[w.serial] = w.r;
3909                         }
3910                         entries++;
3911                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3912                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3913                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3914                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3915                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3916                         if (i >= 2) {
3917                                 Int bbuf[25];
3918                                 int bi;
3919                                 for (j = 0; j < i; j++) {
3920                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3921                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3922                                                 retval = 1;
3923                                                 goto abort;
3924                                         } else if (ibuf[j] == 0)
3925                                                 break;
3926                                 }
3927                                 i = j;
3928                                 if (i < 2)
3929                                         continue;
3930                                 for (j = 1, bi = 0; j < i; j++) {
3931                                         if (ibuf[0] < ibuf[j]) {
3932                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3933                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3934                                                 } else {
3935                                                         bbuf[bi * 2] = ibuf[0] - 1;
3936                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
3937                                                         bi++;
3938                                                 }
3939                                         }
3940                                 }
3941                                 if (bi == 0)
3942                                         continue;
3943                                 bbuf[bi * 2] = -1;
3944                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3945                                 if (retval < 0) {
3946                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3947                                         retval = 1;
3948                                         goto abort;
3949                                 }
3950                         }
3951                 }
3952         }
3953 /*      funlockfile(fp); */
3954         fclose(fp);
3955         if (new_unit) {
3956                 /*  Renumber atoms if some atom number is unoccupied  */
3957                 int *old2new, oldidx, newidx;
3958                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3959                 if (old2new == NULL) {
3960                         s_append_asprintf(errbuf, "Out of memory");
3961                         retval = 1;
3962                         goto abort;
3963                 }
3964                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3965                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3966                         if (ap->aname[0] != 0) {
3967                                 old2new[oldidx] = newidx;
3968                                 if (oldidx > newidx)
3969                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3970                                 newidx++;
3971                         }
3972                 }
3973                 mp->natoms = newidx;
3974                 if (oldidx > newidx) {
3975                         /*  Renumber the connects and bonds  */
3976                         Int *cp;
3977                         for (i = 0; i < mp->natoms; i++) {
3978                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3979                                 cp = AtomConnectData(&ap->connect);
3980                                 for (j = 0; j < ap->connect.count; j++) {
3981                                         cp[j] = old2new[cp[j]];
3982                                 }
3983                         }
3984                         for (i = 0; i < mp->nbonds * 2; i++) {
3985                                 mp->bonds[i] = old2new[mp->bonds[i]];
3986                         }
3987                 }
3988                 retval = MoleculeRebuildTablesFromConnects(mp);
3989                 if (retval != 0) {
3990                         /*  This error may not happen  */
3991                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3992                         retval = 1;
3993                         goto abort;
3994                 }
3995                 /*  Undo action: delete all atoms  */
3996                 {
3997                         MolAction *act;
3998                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
3999                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
4000                         act->frame = mp->cframe;
4001                         MolActionCallback_registerUndo(mp, act);
4002                         MolActionRelease(act);
4003                         IntGroupRelease(ig);
4004                 }
4005         } else {
4006                 /*  Set the new atom positions  */
4007                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4008                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4009                 IntGroupRelease(ig);
4010                 free(vp);
4011                 vp = NULL;
4012         }
4013         mp->nframes = -1;  /*  Should be recalculated later  */
4014         if (entries == 0)
4015                 return 1;  /*  No atoms  */
4016         return 0;
4017         abort:
4018         if (fp != NULL) {
4019         /*      funlockfile(fp); */
4020                 fclose(fp);
4021         }
4022         if (vp != NULL)
4023                 free(vp);
4024         if (entries == 0)
4025                 return 1;  /*  Maybe different format?  */
4026         return retval;
4027 }
4028
4029 int
4030 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4031 {
4032         DcdRecord dcd;
4033         SFloat32 *xp, *yp, *zp;
4034         Vector *vp, *cp;
4035         IntGroup *ig;
4036         int n, errcount = 0;
4037         *errbuf = NULL;
4038         if (mp == NULL || mp->natoms == 0) {
4039                 s_append_asprintf(errbuf, "Molecule is empty");
4040                 return 1;
4041         }
4042         n = DcdOpen(fname, &dcd);
4043         if (n != 0) {
4044                 switch (n) {
4045                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4046                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4047                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4048                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4049                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4050                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
4051                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4052                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4053                 }
4054                 errcount++;
4055         } else {
4056                 if (dcd.natoms == 0) {
4057                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4058                         errcount++;
4059                 } else if (dcd.nframes == 0) {
4060                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
4061                         errcount++;
4062                 }
4063         }
4064         if (errcount > 0) {
4065                 if (n == 0)
4066                         DcdClose(&dcd);
4067                 return 1;
4068         }
4069
4070         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4071         if (dcd.nextra)
4072                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4073         else cp = NULL;
4074         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4075         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4076         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4077         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4078         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4079                 s_append_asprintf(errbuf, "Cannot allocate memory");
4080                 if (vp) free(vp);
4081                 if (cp) free(cp);
4082                 if (xp) free(xp);
4083                 if (yp) free(yp);
4084                 if (zp) free(zp);
4085                 if (ig) IntGroupRelease(ig);
4086                 return 1;
4087         }
4088         for (n = 0; n < dcd.nframes; n++) {
4089                 int i;
4090                 Vector *vpp;
4091                 SFloat32 dcdcell[6];
4092                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4093                         s_append_asprintf(errbuf, "Read error in dcd file");
4094                         goto exit;
4095                 }
4096                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4097                         vpp->x = xp[i];
4098                         vpp->y = yp[i];
4099                         vpp->z = zp[i];
4100                 }
4101                 if (cp != NULL) {
4102                         Double sing;
4103                         vpp = &cp[n * 4];
4104                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
4105                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
4106                         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) {
4107                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
4108                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
4109                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
4110                         }
4111                         /*  a axis lies along the cartesian x axis  */
4112                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4113                         vpp[0].x = dcdcell[0];
4114                         vpp[0].y = 0;
4115                         vpp[0].z = 0;
4116                         vpp[1].x = dcdcell[2] * dcdcell[1];
4117                         vpp[1].y = dcdcell[2] * sing;
4118                         vpp[1].z = 0;
4119                         vpp[2].x = dcdcell[5] * dcdcell[3];
4120                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4121                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4122                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4123                         if (mp->cell == NULL) {
4124                                 /*  Create periodicity if not present  */
4125                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4126                         }
4127                 }
4128         }
4129         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4130                 s_append_asprintf(errbuf, "Cannot insert frames");
4131         mp->startStep = dcd.nstart;
4132         mp->stepsPerFrame = dcd.ninterval;
4133         mp->psPerStep = dcd.delta;
4134 exit:
4135         DcdClose(&dcd);
4136         if (cp != NULL)
4137                 free(cp);
4138         free(vp);
4139         free(xp);
4140         free(yp);
4141         free(zp);
4142         IntGroupRelease(ig);
4143         if (errcount == 0)
4144                 return 0;
4145         else return 1;
4146 }
4147
4148 int
4149 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4150 {
4151         FILE *fp;
4152         char buf[1024];
4153         int lineNumber;
4154         int i, retval;
4155         Vector v[3], vv;
4156         double d[3];
4157         int n, flag;
4158         char flags[3];
4159         *errbuf = NULL;
4160         fp = fopen(fname, "rb");
4161         if (fp == NULL) {
4162                 s_append_asprintf(errbuf, "Cannot open file");
4163                 return -1;
4164         }
4165         errbuf[0] = 0;
4166         lineNumber = 0;
4167         retval = 0;
4168         flags[0] = flags[1] = flags[2] = 0;
4169         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4170                 if (strncmp(buf, "Bounding box:", 13) == 0) {
4171                         for (i = 0; i < 3; i++) {
4172                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4173                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4174                                         retval = 1;
4175                                         goto abort;
4176                                 }
4177                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4178                                 if (n < 3) {
4179                                         vv.x = vv.y = vv.z = 0.0;
4180                                         switch (i) {
4181                                                 case 0: vv.x = d[0]; break;
4182                                                 case 1: vv.y = d[0]; break;
4183                                                 case 2: vv.z = d[0]; break;
4184                                         }
4185                                         if (n == 1 || (n == 2 && d[1] != 0.0))
4186                                                 flags[i] = 1;
4187                                 } else {
4188                                         vv.x = d[0];
4189                                         vv.y = d[1];
4190                                         vv.z = d[2];
4191                                         if (n == 4)
4192                                                 flags[i] = (flag != 0);
4193                                         else
4194                                                 flags[i] = (VecLength2(vv) != 0);
4195                                 }
4196                                 v[i] = vv;
4197                         }
4198                         if (mp->cell != NULL)
4199                                 vv = mp->cell->origin;
4200                         else
4201                                 vv.x = vv.y = vv.z = 0.0;
4202                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4203                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4204                         if (mp->cell != NULL) {
4205                                 v[0] = mp->cell->axes[0];
4206                                 v[1] = mp->cell->axes[1];
4207                                 v[2] = mp->cell->axes[2];
4208                                 memmove(flags, mp->cell->flags, 3);
4209                         } else {
4210                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4211                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4212                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4213                                 flags[0] = flags[1] = flags[2] = 1.0;
4214                         }
4215                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4216                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4217                                 retval = 1;
4218                                 goto abort;
4219                         }
4220                         vv.x = d[0];
4221                         vv.y = d[1];
4222                         vv.z = d[2];
4223                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4224                 }
4225         }
4226         fclose(fp);
4227         return 0;
4228 abort:
4229         if (fp != NULL)
4230                 fclose(fp);
4231         return retval;
4232 }
4233                         
4234 int
4235 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4236 {
4237         int retval;
4238         *errbuf = NULL;
4239         if (ftype == NULL || *ftype == 0) {
4240                 const char *cp;
4241                 cp = strrchr(fname, '.');
4242                 if (cp != NULL)
4243                         ftype = cp + 1;
4244                 else {
4245                         cp = guessMoleculeType(fname);
4246                         if (strcmp(cp, "???") != 0)
4247                                 ftype = cp;
4248                 }
4249         }
4250         if (strcasecmp(ftype, "psf") == 0) {
4251                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4252         } else if (strcasecmp(ftype, "pdb") == 0) {
4253                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4254         } else if (strcasecmp(ftype, "tep") == 0) {
4255                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4256         } else {
4257                 s_append_asprintf(errbuf, "The file format should be specified");
4258                 retval = 1;
4259         }
4260         if (retval == 0)
4261                 MoleculeSetPath(mp, fname);
4262         return retval;
4263 }
4264
4265 int
4266 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4267 {
4268         FILE *fp;
4269         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4270         Atom *ap;
4271         char *p;
4272         char bufs[6][8];
4273
4274         *errbuf = NULL;
4275         fp = fopen(fname, "wb");
4276         if (fp == NULL) {
4277                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4278                 return 1;
4279         }
4280         errbuf[0] = 0;
4281
4282         nframes = MoleculeFlushFrames(mp);
4283
4284         fprintf(fp, "!:atoms\n");
4285         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4286         n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4287         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4288                 strncpy(bufs[0], ap->segName, 4);
4289                 bufs[0][4] = 0;
4290                 strncpy(bufs[1], ap->resName, 4);
4291                 bufs[1][4] = 0;
4292                 strncpy(bufs[2], ap->aname, 4);
4293                 bufs[2][4] = 0;
4294                 AtomTypeDecodeToString(ap->type, bufs[3]);
4295                 bufs[3][6] = 0;
4296                 strncpy(bufs[4], ap->element, 4);
4297                 bufs[4][2] = 0;
4298                 for (j = 0; j < 5; j++) {
4299                         if (bufs[j][0] == 0) {
4300                                 bufs[j][0] = '_';
4301                                 bufs[j][1] = 0;
4302                         }
4303                         for (k = 0; k < 6; k++) {
4304                                 if (bufs[j][k] == 0)
4305                                         break;
4306                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4307                                         bufs[j][k] = '_';
4308                         }
4309                 }
4310                 if (SYMOP_ALIVE(ap->symop))
4311                         n1++;
4312                 if (ap->fix_force != 0)
4313                         n2++;
4314                 if (ap->mm_exclude || ap->periodic_exclude)
4315                         n3++;
4316                 if (ap->aniso != NULL)
4317                         n_aniso++;
4318                 if (ap->anchor != NULL)
4319                         nanchors++;
4320                 if (ap->uff_type[0] != 0)
4321                         n_uff++;
4322                 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);
4323         }
4324         fprintf(fp, "\n");
4325         
4326         if (n_uff > 0) {
4327                 fprintf(fp, "!:uff_type\n");
4328                 fprintf(fp, "! idx uff_type\n");
4329                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4330                         fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4331                 }
4332                 fprintf(fp, "\n");
4333         }
4334         
4335         if (n1 > 0) {
4336                 fprintf(fp, "!:atoms_symop\n");
4337                 fprintf(fp, "! idx symop symbase\n");
4338                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4339                         int n;
4340                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4341                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4342                 }
4343                 fprintf(fp, "\n");
4344         }
4345         
4346         if (n2 > 0) {
4347                 fprintf(fp, "!:atoms_fix\n");
4348                 fprintf(fp, "! idx fix_force fix_pos\n");
4349                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4350                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4351                 }
4352                 fprintf(fp, "\n");
4353         }
4354         
4355         if (n3 > 0) {
4356                 fprintf(fp, "!:mm_exclude\n");
4357                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4358                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4359                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4360                 }
4361                 fprintf(fp, "\n");
4362         }
4363         
4364         if (nanchors > 0) {
4365                 fprintf(fp, "!:pi_anchor\n");
4366                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4367                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4368                         Int *ip;
4369                         if (ap->anchor == NULL)
4370                                 continue;
4371                         k = ap->anchor->connect.count;
4372                         ip = AtomConnectData(&ap->anchor->connect);
4373                         fprintf(fp, "%d %d\n", i, k);
4374                         for (j = 0; j < k; j++) {
4375                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4376                         }
4377                 }
4378                 fprintf(fp, "\n");
4379         }
4380                                 
4381         n1 = nframes;
4382         if (n1 > 0)
4383                 n2 = mp->cframe;
4384         else
4385                 n2 = 0;
4386         for (i = 0; (i == n2 || i < n1); i++) {
4387                 fprintf(fp, "!:positions ; frame %d\n", i);
4388                 fprintf(fp, "! idx x y z [sx sy sz]\n");
4389                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4390                         Vector *vp;
4391                         Byte sig_flag = 0;
4392                         if (i != n2 && i < ap->nframes)
4393                                 vp = ap->frames + i;
4394                         else {
4395                                 vp = &(ap->r);
4396                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4397                                         sig_flag = 1;
4398                         }
4399                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4400                         if (sig_flag) {
4401                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4402                         }
4403                         fprintf(fp, "\n");
4404                 }
4405                 fprintf(fp, "\n");
4406         }
4407         
4408         if (mp->nbonds > 0) {
4409                 fprintf(fp, "!:bonds\n");
4410                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4411                 for (i = 0; i < mp->nbonds; i++) {
4412                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4413                 }
4414                 fprintf(fp, "\n");
4415         }
4416
4417         if (mp->nbondOrders > 0) {
4418                 fprintf(fp, "!:bond_orders\n");
4419                 fprintf(fp, "! order1 order2 order3 order4\n");
4420                 for (i = 0; i < mp->nbondOrders; i++) {
4421                         fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4422                 }
4423                 fprintf(fp, "\n");
4424         }
4425         
4426         if (mp->nangles > 0) {
4427                 fprintf(fp, "!:angles\n");
4428                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4429                 for (i = 0; i < mp->nangles; i++) {
4430                         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' : ' '));
4431                 }
4432                 fprintf(fp, "\n");
4433         }
4434         
4435         if (mp->ndihedrals > 0) {
4436                 fprintf(fp, "!:dihedrals\n");
4437                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4438                 for (i = 0; i < mp->ndihedrals; i++) {
4439                         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' : ' '));
4440                 }
4441                 fprintf(fp, "\n");
4442         }
4443         
4444         if (mp->nimpropers > 0) {
4445                 fprintf(fp, "!:impropers\n");
4446                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4447                 for (i = 0; i < mp->nimpropers; i++) {
4448                         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' : ' '));
4449                 }
4450                 fprintf(fp, "\n");
4451         }
4452         
4453         if (mp->cell != NULL) {
4454                 fprintf(fp, "!:xtalcell\n");
4455                 fprintf(fp, "! a b c alpha beta gamma\n");
4456                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4457                 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]);
4458                 fprintf(fp, "\n");
4459
4460                 fprintf(fp, "!:periodic_box\n");
4461                 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");
4462                 for (i = 0; i < 3; i++)
4463                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4464                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4465                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4466                 if (mp->cell->has_sigma) {
4467                         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]);
4468                 }
4469                 fprintf(fp, "\n");
4470         }
4471         
4472         if (mp->nframe_cells > 0) {
4473                 fprintf(fp, "!:frame_periodic_boxes\n");
4474                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4475                 for (i = 0; i < mp->nframe_cells * 4; i++) {
4476                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4477                 }
4478                 fprintf(fp, "\n");
4479         }
4480         
4481         if (mp->nsyms > 0) {
4482                 fprintf(fp, "!:symmetry_operations\n");
4483                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4484                 for (i = 0; i < mp->nsyms; i++) {
4485                         Transform *tp = mp->syms + i;
4486                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4487                         for (j = 0; j < 12; j++)
4488                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4489                 }
4490                 fprintf(fp, "\n");
4491         }
4492         
4493         if (n_aniso > 0) {
4494                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4495                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4496                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4497                         if (ap->aniso != NULL) {
4498                                 Double *bp = ap->aniso->bij;
4499                                 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" : ""));
4500                                 if (ap->aniso->has_bsig) {
4501                                         bp = ap->aniso->bsig;
4502                                         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]);
4503                                 }
4504                         } else {
4505                                 fprintf(fp, "0 0 0 0 0 0\n");
4506                         }
4507                 }
4508                 fprintf(fp, "\n");              
4509         }
4510         
4511         if (mp->arena != NULL) {
4512                 MDArena *arena = mp->arena;
4513                 fprintf(fp, "!:md_parameters\n");
4514                 fprintf(fp, "log_file %s\n", arena->log_result_name);
4515                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4516                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4517                 fprintf(fp, "force_file %s\n", arena->force_result_name);
4518                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4519                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4520                 fprintf(fp, "step %d\n", arena->step);
4521                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4522                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4523                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4524                 fprintf(fp, "timestep %g\n", arena->timestep);
4525                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4526                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4527                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4528                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4529                 fprintf(fp, "temperature %g\n", arena->temperature);
4530                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4531                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4532                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4533                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4534                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4535                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4536                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4537                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4538                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4539                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4540                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4541                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4542                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4543                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4544                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4545                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4546                 if (arena->nalchem_flags > 0) {
4547                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4548                         for (i = 0; i < arena->nalchem_flags; i++) {
4549                                 if (i % 60 == 0)
4550                                         fputc('\n', fp);
4551                                 else if (i % 10 == 0)
4552                                         fputc(' ', fp);
4553                                 fputc('0' + arena->alchem_flags[i], fp);
4554                         }
4555                         fputc('\n', fp);
4556                 }
4557                 if (arena->pressure != NULL) {
4558                         Double *dp;
4559                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4560                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4561                         dp = arena->pressure->apply;
4562                         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]);
4563                         dp = arena->pressure->cell_flexibility;
4564                         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]);
4565                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4566                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4567                 }
4568                 fprintf(fp, "\n");
4569
4570                 if (mp->par != NULL) {
4571                         Parameter *par = mp->par;
4572                         fprintf(fp, "!:parameters\n");
4573                         ParameterAppendToFile(par, fp);
4574                         fprintf(fp, "\n");
4575                 }
4576                 
4577                 fprintf(fp, "!:velocity\n");
4578                 fprintf(fp, "! idx vx vy vz\n");
4579                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4580                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4581                 }
4582                 fprintf(fp, "\n");
4583
4584                 fprintf(fp, "!:force\n");
4585                 fprintf(fp, "! idx fx fy fz\n");
4586                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4587                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4588                 }
4589                 fprintf(fp, "\n");
4590         }
4591         
4592         if (mp->mview != NULL) {
4593                 double f[4];
4594                 if (mp->mview->track != NULL) {
4595                         fprintf(fp, "!:trackball\n");
4596                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4597                         f[0] = TrackballGetScale(mp->mview->track);
4598                         fprintf(fp, "%f\n", f[0]);
4599                         TrackballGetTranslate(mp->mview->track, f);
4600                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4601                         TrackballGetRotate(mp->mview->track, f);
4602                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4603                         fprintf(fp, "\n");
4604                 }
4605                 fprintf(fp, "!:view\n");
4606                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4607                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4608                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4609                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4610                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4611                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4612                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4613                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4614                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4615                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4616                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4617                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4618                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4619                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4620                 if (mp->mview->atomRadius != 0.2)
4621                         fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4622                 if (mp->mview->bondRadius != 0.1)
4623                         fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4624                 if (mp->mview->atomResolution != 12)
4625                         fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4626                 if (mp->mview->bondResolution != 8)
4627                         fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4628                 fprintf(fp, "\n");
4629         }
4630
4631         if (mp->nmolprops > 0) {
4632                 MolProp *prp;
4633                 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4634                         /*  Encode the property name if necessary  */
4635                         char enc[1024];
4636                         n1 = n2 = 0;
4637                         for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4638                                 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4639                                         enc[n1++] = *p;
4640                                         n2 = n1;
4641                                 } else {
4642                                         sprintf(enc + n1, "%%%02x", *p);
4643                                         n1 += 3;
4644                                 }
4645                         }
4646                         if (*p == 0)
4647                                 enc[n1] = 0;
4648                         else {
4649                                 enc[n2] = 0; /* Truncate after last ASCII character */
4650                                 n1 = n2;
4651                         }
4652                         if (n1 == 0) {
4653                                 sprintf(enc, "prop_%d", i + 1);
4654                                 n1 = strlen(enc);
4655                         }
4656                         fprintf(fp, "!:property ; %s\n", enc);
4657                         for (j = 0; j < nframes; j++) {
4658                                 fprintf(fp, "%.18g\n", prp->propvals[j]);
4659                         }
4660                         fprintf(fp, "\n");
4661                 }
4662         }
4663         
4664         if (mp->bset != NULL) {
4665                 /*  Gaussian primitive info  */
4666                 ShellInfo *sp;
4667                 PrimInfo *pp;
4668                 fprintf(fp, "!:gaussian_primitives\n");
4669                 fprintf(fp, "! sym nprims a_idx; A C Csp\n");
4670                 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4671                         switch (sp->sym) {
4672                                 case kGTOType_S:  p = "S";  break;
4673                                 case kGTOType_P:  p = "P";  break;
4674                                 case kGTOType_SP: p = "SP"; break;
4675                                 case kGTOType_D:  p = "D";  break;
4676                                 case kGTOType_D5: p = "D5"; break;
4677                                 case kGTOType_F:  p = "F";  break;
4678                                 case kGTOType_F7: p = "F7"; break;
4679                                 case kGTOType_G:  p = "G";  break;
4680                                 case kGTOType_G9: p = "G9"; break;
4681                                 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4682                         }
4683                         fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4684                         pp = mp->bset->priminfos + sp->p_idx;
4685                         for (j = 0; j < sp->nprim; j++, pp++) {
4686                                 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4687                         }
4688                 }
4689                 fprintf(fp, "\n");
4690                 
4691                 /*  MO info  */
4692                 fprintf(fp, "!:mo_info\n");
4693                 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4694                 switch (mp->bset->rflag) {
4695                         case 0: p = "UHF"; break;
4696                         case 1: p = "RHF"; break;
4697                         case 2: p = "ROHF"; break;
4698                         default: p = "(unknown)"; break;
4699                 }
4700                 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4701                 fprintf(fp, "\n");
4702
4703                 /*  MO coefficients  */
4704                 fprintf(fp, "!:mo_coefficients\n");
4705                 for (i = 0; i < mp->bset->nmos; i++) {
4706                         fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4707                         for (j = 0; j < mp->bset->ncomps; j++) {
4708                                 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4709                         }
4710                 }
4711                 fprintf(fp, "\n");
4712         }
4713
4714         if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4715                 MainViewGraphic *gp;
4716                 fprintf(fp, "!:graphics\n");
4717                 for (i = 0; i < mp->mview->ngraphics; i++) {
4718                         gp = mp->mview->graphics + i;
4719                         switch (gp->kind) {
4720                                 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4721                                 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4722                                 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4723                                 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4724                                 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4725                                 default: fprintf(fp, "unknown\n"); break;
4726                         }
4727                         fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4728                         fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4729                         fprintf(fp, "%d\n", gp->npoints);
4730                         for (j = 0; j < gp->npoints; j++)
4731                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4732                         fprintf(fp, "%d\n", gp->nnormals);
4733                         for (j = 0; j < gp->nnormals; j++)
4734                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4735                 }
4736                 fprintf(fp, "\n");
4737         }
4738         
4739         /*  Plug-in in the Ruby world  */
4740         {
4741                 char *outMessage;
4742                 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4743                                                                           "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4744                         if (outMessage[0] != 0) {
4745                                 if (strncmp(outMessage, "Plug-in", 7) == 0) {
4746                                         s_append_asprintf(errbuf, "%s", outMessage);
4747                                 } else {
4748                                         fprintf(fp, "%s\n", outMessage);
4749                                 }
4750                         }
4751                         free(outMessage);
4752                 }
4753         }
4754         
4755         fclose(fp);
4756         return 0;
4757 }
4758
4759 int
4760 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4761 {
4762         FILE *fp;
4763         int i;
4764         Atom *ap;
4765         *errbuf = NULL;
4766         fp = fopen(fname, "wb");
4767         if (fp == NULL) {
4768                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4769                 return 1;
4770         }
4771         fprintf(fp, "PSF\n\n");
4772         fprintf(fp, "       1 !NTITLE\n");
4773         fprintf(fp, " REMARKS FILENAME=\n");
4774         fprintf(fp, "\n");
4775         
4776         /*  Atoms  */
4777         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4778         for (i = 0; i < mp->natoms; i++) {
4779                 const char *fmt;
4780                 ap = ATOM_AT_INDEX(mp->atoms, i);
4781                 fprintf(fp, "%8d ", i + 1);
4782                 if (ap->resSeq >= 10000) {
4783                         fmt = "%-3.3s %-5d ";
4784                 } else {
4785                         fmt = "%-4.4s %-4d ";
4786                 }
4787                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4788                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4789                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4790         }
4791         fprintf(fp, "\n");
4792         
4793         /*  Bonds  */
4794         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4795         for (i = 0; i < mp->nbonds * 2; i++) {
4796                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4797                 if (i % 8 == 7)
4798                         fprintf(fp, "\n");
4799         }
4800         if (i % 8 != 0)
4801                 fprintf(fp, "\n");
4802         fprintf(fp, "\n");
4803         
4804         /*  Angles  */
4805         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4806         for (i = 0; i < mp->nangles * 3; i++) {
4807                 fprintf(fp, "%8d", mp->angles[i] + 1);
4808                 if (i % 9 == 8)
4809                         fprintf(fp, "\n");
4810         }
4811         if (i % 9 != 0)
4812                 fprintf(fp, "\n");
4813         fprintf(fp, "\n");
4814         
4815         /*  Dihedrals  */
4816         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4817         for (i = 0; i < mp->ndihedrals * 4; i++) {
4818                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4819                 if (i % 8 == 7)
4820                         fprintf(fp, "\n");
4821         }
4822         if (i % 8 != 0)
4823                 fprintf(fp, "\n");
4824         fprintf(fp, "\n");
4825         
4826         /*  Dihedrals  */
4827         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4828         for (i = 0; i < mp->nimpropers * 4; i++) {
4829                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4830                 if (i % 8 == 7)
4831                         fprintf(fp, "\n");
4832         }
4833         if (i % 8 != 0)
4834                 fprintf(fp, "\n");
4835         fprintf(fp, "\n");
4836         
4837         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4838         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4839         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4840         for (i = 0; i < mp->natoms; i++) {
4841                 fprintf(fp, "%8d", 0);
4842                 if (i % 8 == 7)
4843                         fprintf(fp, "\n");
4844         }
4845         if (i % 8 != 0)
4846                 fprintf(fp, "\n");
4847         fprintf(fp, "\n");
4848         fprintf(fp, "%8d !NGRP: groups\n", 1);
4849         fprintf(fp, "       0       0       0\n");
4850         fprintf(fp, "\n");
4851         
4852         i = strlen(fname);
4853         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4854                 /*  Extended psf (with coordinates and other info)  */
4855                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4856                 for (i = 0; i < mp->natoms; i++) {
4857                         Vector r;
4858                         ap = ATOM_AT_INDEX(mp->atoms, i);
4859                         r = ap->r;
4860                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4861                 }
4862                 fprintf(fp, "\n");
4863         }
4864                 
4865         fclose(fp);
4866         return 0;
4867 }
4868
4869 int
4870 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4871 {
4872         FILE *fp;
4873         int i, j;
4874         Atom *ap;
4875         *errbuf = NULL;
4876         fp = fopen(fname, "wb");
4877         if (fp == NULL) {
4878                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4879                 return 1;
4880         }
4881         for (i = 0; i < mp->natoms; i++) {
4882                 char buf[6];
4883                 ap = ATOM_AT_INDEX(mp->atoms, i);
4884                 if (ap->resSeq >= 10000) {
4885                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4886                 } else {
4887                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4888                 }
4889                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4890                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4891                                         "%-4.4s%-2.2s%-2d\n",
4892                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4893                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4894                         ap->segName, ap->element, ap->intCharge);
4895         }
4896         for (i = 0; i < mp->natoms; i++) {
4897                 Int *cp;
4898                 ap = ATOM_AT_INDEX(mp->atoms, i);
4899                 cp = AtomConnectData(&ap->connect);
4900                 for (j = 0; j < ap->connect.count; j++) {
4901                         if (j % 4 == 0) {
4902                                 if (j > 0)
4903                                         fprintf(fp, "\n");
4904                                 fprintf(fp, "CONECT%5d", i + 1);
4905                         }
4906                         fprintf(fp, "%5d", cp[j] + 1);
4907                 }
4908                 if (j > 0)
4909                         fprintf(fp, "\n");
4910         }
4911         fprintf(fp, "END\n");
4912         fclose(fp);
4913         return 0;
4914 }
4915
4916 int
4917 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4918 {
4919         DcdRecord dcd;
4920         SFloat32 *xp, *yp, *zp;
4921         int n;
4922         *errbuf = NULL;
4923         if (mp == NULL || mp->natoms == 0) {
4924                 s_append_asprintf(errbuf, "Molecule is empty");
4925                 return 1;
4926         }
4927         memset(&dcd, 0, sizeof(dcd));
4928         dcd.natoms = mp->natoms;
4929         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4930         if (dcd.nframes == 0) {
4931                 s_append_asprintf(errbuf, "no frame is present");
4932                 return 1;
4933         }
4934         dcd.nstart = mp->startStep;
4935         dcd.ninterval = mp->stepsPerFrame;
4936         if (dcd.ninterval == 0)
4937                 dcd.ninterval = 1;
4938         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4939         if (mp->cell != NULL)
4940                 dcd.nextra = 1;
4941         dcd.delta = mp->psPerStep;
4942         if (dcd.delta == 0.0)
4943                 dcd.delta = 1.0;
4944         dcd.ncharmver = 24;
4945         n = DcdCreate(fname, &dcd);
4946         if (n != 0) {
4947                 if (n < 0)
4948                         s_append_asprintf(errbuf, "Cannot create dcd file");
4949                 else
4950                         s_append_asprintf(errbuf, "Cannot write dcd header");
4951                 DcdClose(&dcd);
4952                 return 1;
4953         }
4954         
4955         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4956         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4957         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4958         if (xp == NULL || yp == NULL || zp == NULL) {
4959                 s_append_asprintf(errbuf, "Cannot allocate memory");
4960                 if (xp) free(xp);
4961                 if (yp) free(yp);
4962                 if (zp) free(zp);
4963                 DcdClose(&dcd);
4964                 return 1;
4965         }
4966         for (n = 0; n < dcd.nframes; n++) {
4967                 int i;
4968                 Atom *ap;
4969                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4970                         Vector r;
4971                         if (ap->frames == NULL || n >= ap->nframes)
4972                                 r = ap->r;
4973                         else
4974                                 r = ap->frames[n];
4975                         xp[i] = r.x;
4976                         yp[i] = r.y;
4977                         zp[i] = r.z;
4978                 }
4979                 if (i < dcd.natoms) {
4980                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4981                         memset(xp + i, 0, sz);
4982                         memset(yp + i, 0, sz);
4983                         memset(zp + i, 0, sz);
4984                 }
4985                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4986                         Vector *cp = &(mp->frame_cells[n * 4]);
4987                         dcd.globalcell[0] = VecLength(cp[0]);
4988                         dcd.globalcell[2] = VecLength(cp[1]);
4989                         dcd.globalcell[5] = VecLength(cp[2]);
4990                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4991                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4992                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4993                 }                       
4994                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4995                         s_append_asprintf(errbuf, "Write error in dcd file");
4996                         goto exit;
4997                 }
4998         }
4999         
5000 exit:
5001         DcdClose(&dcd);
5002         free(xp);
5003         free(yp);
5004         free(zp);
5005         if (errbuf[0] == 0)
5006                 return 0;
5007         else return 1;
5008 }
5009
5010 int
5011 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5012 {
5013         FILE *fp;
5014         int i;
5015         Vector v;
5016         *errbuf = NULL;
5017         fp = fopen(fname, "wb");
5018         if (fp == NULL) {
5019                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5020                 return 1;
5021         }
5022         if (mp->cell != NULL) {
5023                 fprintf(fp, "Bounding box:\n");
5024                 for (i = 0; i < 3; i++) {
5025                         v = mp->cell->axes[i];
5026                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5027                 }
5028                 fprintf(fp, "Bounding box origin:\n");
5029                 v = mp->cell->origin;
5030                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5031         }
5032         fclose(fp);
5033         return 0;
5034 }
5035                 
5036  static int
5037 sCompareByElement(const void *ap, const void *bp)
5038 {
5039         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5040 }
5041
5042 static int
5043 sMakeAdc(int n, int base, Symop symop)
5044 {
5045         int an, sym;
5046         if (SYMOP_ALIVE(symop)) {
5047                 an = base;
5048                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5049         } else {
5050                 an = n;
5051                 sym = 55501;
5052         }
5053         return (an + 1) * 100000 + sym;
5054 }
5055
5056 static int
5057 sCompareAdc(const void *ap, const void *bp)
5058 {
5059         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5060         if (n == 0)
5061                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5062         return n;
5063 }
5064
5065 static void
5066 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5067 {
5068         int i, j, k, an, sym;
5069         Atom *ap;
5070         Int *adc;
5071         adc = (Int *)malloc(sizeof(Int) * natoms);
5072         if (adc == NULL)
5073                 return;
5074         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5075                 if (ap->exflags & kAtomHiddenFlag)
5076                         continue;
5077                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5078         }
5079         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5080         
5081         /*  Create the atom list  */
5082         an = sym = -1;
5083         for (i = j = k = 0; i < natoms; i++) {
5084                 int an1 = adc[i] / 100000;
5085                 int sym1 = adc[i] % 100000;
5086                 if (sym == sym1 && an1 == an + 1) {
5087                         /*  Continuous  */
5088                         an = an1;
5089                         k++;
5090                         continue;
5091                 }
5092                 if (k > 0)
5093                         /*  Output the last atom with a minus sign  */
5094                         adc[j++] = -(an * 100000 + sym);
5095                 /*  Output this atom  */
5096                 adc[j++] = adc[i];
5097                 an = an1;
5098                 sym = sym1;
5099                 k = 0;
5100         }
5101         if (k > 0)
5102                 adc[j++] = -(an * 100000 + sym);
5103         
5104         /*  Create the instruction cards  */
5105         for (i = k = 0; i < j; i++) {
5106                 if (k == 0)
5107                         fprintf(fp, "      401");
5108                 fprintf(fp, "%9d", adc[i]);
5109                 k++;
5110                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5111                         fprintf(fp, "\n");
5112                         k = 0;
5113                 }
5114         }
5115         free(adc);
5116 }
5117
5118 static int
5119 sEllipsoidType(int an)
5120 {
5121         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5122 }
5123
5124 static void
5125 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5126 {
5127         int i;
5128         Atom *ap;
5129         int etype, elast, istart, ilast, n1, n2;
5130         elast = istart = ilast = -1;
5131         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5132                 if (i < natoms) {
5133                         if (SYMOP_ALIVE(ap->symop))
5134                                 continue;
5135                         if (ap->exflags & kAtomHiddenFlag)
5136                                 continue;
5137                         etype = sEllipsoidType(ap->atomicNumber);
5138                         if (elast < 0) {
5139                                 istart = ilast = i;
5140                                 elast = etype;
5141                                 continue;
5142                         } else if (elast == etype && ilast == i - 1) {
5143                                 ilast++;
5144                                 continue;
5145                         }
5146                 }
5147                 /*  Output the instruction card for the 'last' block of atoms  */
5148                 switch (etype) {
5149                         case 2:
5150                                 n1 = 4; n2 = 0; break;
5151                         case 3:
5152                                 n1 = 4; n2 = 5; break;
5153                         default:
5154                                 n1 = 1; n2 = 0; break;
5155                 }
5156                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
5157                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
5158                 elast = etype;
5159                 ilast = istart = i;
5160         }
5161 }
5162
5163 static int
5164 sCompareBondType(const void *ap, const void *bp)
5165 {
5166         /*  Descending order  */
5167         return *((int *)bp) - *((int *)ap);
5168 }
5169
5170 static void
5171 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5172 {
5173         Atom *ap, *ap2;
5174         char buf[96];
5175         int i, j, n[5], an, count, n1, n2, k;
5176         Int *cp;
5177         Int nexbonds;
5178         Int *exbonds;
5179         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5180         static const int sBondShade[4] = {5, 3, 1, 1};
5181
5182         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5183         n[4] = natoms;
5184         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5185                 an = ap->atomicNumber;
5186                 if (an < 2)
5187                         n[3] = i;
5188                 if (an < 10)
5189                         n[2] = i;
5190                 if (an < 18)
5191                         n[1] = i;
5192         }
5193         nexbonds = 0;
5194         exbonds = NULL;
5195         count = 0;
5196
5197         if (overlap_correction)
5198                 strcpy(buf, "  2  1001    0.000\n");
5199         else
5200                 strcpy(buf, "  2   812\n");
5201         
5202         for (i = 0; i < 4; i++) {
5203                 for (j = i; j < 4; j++) {
5204                         /*  Examine bonds between "group i" and "group j"  */
5205                         Vector dr;
5206                         double d;
5207                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
5208                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
5209                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
5210                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
5211                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5212                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5213                                         if (n1 == n2)
5214                                                 continue;
5215                                         VecSub(dr, ap->r, ap2->r);
5216                                         d = VecLength(dr);
5217                                         cp = AtomConnectData(&ap->connect);
5218                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5219                                                 if (cp[k] == n2)
5220                                                         break;
5221                                         }
5222                                         if (k >= 0) {
5223                                                 /*  n1 and n2 are bound  */
5224                                                 if (d < min_bond)
5225                                                         min_bond = d;
5226                                                 if (d > max_bond)
5227                                                         max_bond = d;
5228                                         } else {
5229                                                 /*  n1 and n2 are not bound  */
5230                                                 if (d < min_nonbond)
5231                                                         min_nonbond = d;
5232                                         }
5233                                 }
5234                         }
5235                         if (min_bond == 10000.0)
5236                                 continue;  /*  No bonds between these groups  */
5237                         min_bond *= 0.9;
5238                         if (max_bond + 0.002 < min_nonbond)
5239                                 max_bond += 0.002;
5240                         else {
5241                                 max_bond = min_nonbond - 0.002;
5242                                 /*  Some bonds may be omitted, so scan all bonds again  */
5243                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5244                                         cp = AtomConnectData(&ap->connect);
5245                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5246                                                 n2 = cp[k];
5247                                                 if (n2 < n[j] || n2 >= n[j + 1])
5248                                                         continue;
5249                                                 ap2 = atoms + n2;
5250                                                 VecSub(dr, ap->r, ap2->r);
5251                                                 d = VecLength(dr);
5252                                                 if (d > max_bond) {
5253                                                         /*  This bond should be explicitly defined  */
5254                                                         Int adc1, adc2;
5255                                                         if (count_exbond == 0) {
5256                                                                 adc1 = -(i + 1);  /*  Bond type  */
5257                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5258                                                         }
5259                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5260                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5261                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5262                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5263                                                         count_exbond++;
5264                                                 }
5265                                         }
5266                                 }
5267                         }
5268                         /*  Output the last instruction card  */
5269                         fputs(buf, fp);
5270                         /*  Make a new trailer card  */
5271                         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]);
5272                         count++;
5273                 }
5274         }
5275         if (count > 0) {
5276                 /*  Output the last trailer card  */
5277                 buf[2] = ' ';
5278                 fputs(buf, fp);
5279         }
5280         if (nexbonds > 0) {
5281                 if (count == 0 && overlap_correction) {
5282                         /*  1001 card is not yet written, so write it  */
5283                         buf[2] = ' ';
5284                         fputs(buf, fp);
5285                 }
5286                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
5287                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
5288                 i = 1;  /*  Index for exbonds[]  */
5289                 j = 0;  /*  Count in this block  */
5290                 while (i <= nexbonds) {
5291                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5292                                 /*  End of block  */
5293                                 buf[2] = '2';
5294                                 fputs(buf, fp);
5295                                 /*  The trailer card  */
5296                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
5297                                 if (i == nexbonds)
5298                                         break;
5299                                 if (exbonds[i] < 0)
5300                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
5301                                 j = 0;
5302                         } else if (j > 0 && j % 3 == 0) {
5303                                 buf[2] = '1';
5304                                 fputs(buf, fp);
5305                         }
5306                         n1 = exbonds[i++];
5307                         n2 = exbonds[i++];
5308                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5309                         j++;
5310                 }
5311                 free(exbonds);
5312         }
5313 }
5314
5315 int
5316 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5317 {
5318         FILE *fp;
5319         int i, j, natoms, *ip;
5320         Int *cp;
5321         Atom *ap, *atoms, **app;
5322         Double *dp;
5323         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5324         
5325         *errbuf = NULL;
5326
5327         /*  Create sorted array of atoms  */
5328         natoms = mp->natoms;
5329         atoms = (Atom *)calloc(sizeof(Atom), natoms);
5330         app = (Atom **)calloc(sizeof(Atom *), natoms);
5331         ip = (int *)calloc(sizeof(int), natoms);
5332         if (atoms == NULL || app == NULL || ip == NULL) {
5333                 s_append_asprintf(errbuf, "Cannot allocate memory");
5334                 return 1;
5335         }
5336         /*  Sort the atom pointer by atomic number  */
5337         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5338                 app[i] = ap;
5339         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5340         for (i = 0; i < natoms; i++) {
5341                 /*  ip[old_index] is new_index  */
5342                 ip[app[i] - mp->atoms] = i;
5343         }
5344         /*  Copy the atom record to atoms[]  */
5345         /*  The 'v' member contains crystallographic coordinates  */
5346         /*  The connection table and symbase are renumbered  */
5347         /*  Hidden flags are modified to reflect the visibility in the MainView  */
5348         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5349                 AtomDuplicateNoFrame(ap, app[i]);
5350         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
5351                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5352                 cp = AtomConnectData(&ap->connect);
5353                 for (j = ap->connect.count - 1; j >= 0; j--) {
5354                         cp[j] = ip[cp[j]];
5355                 }
5356                 if (SYMOP_ALIVE(ap->symop))
5357                         ap->symbase = ip[ap->symbase];
5358                 if (MainView_isAtomHidden(mp->mview, i)) {
5359                         ap->exflags |= kAtomHiddenFlag;
5360                 } else {
5361                         ap->exflags &= ~kAtomHiddenFlag;
5362                 }
5363         }
5364         free(ip);
5365         free(app);
5366         
5367         fp = fopen(fname, "wb");
5368         if (fp == NULL) {
5369                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5370                 return 1;
5371         }
5372
5373         /*  Title line  */
5374         fprintf(fp, "Generated by Molby\n");
5375         
5376         /*  XtalCell  */
5377         if (mp->cell != NULL) {
5378                 dp = mp->cell->cell;
5379         } else {
5380                 dp = sUnit;
5381         }
5382         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]);
5383         
5384         /*  Symmetry operations  */
5385         if (mp->nsyms > 0) {
5386                 for (i = 0; i < mp->nsyms; i++) {
5387                         dp = mp->syms[i];
5388                         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]);
5389                 }
5390         } else {
5391                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
5392         }
5393         
5394         /*  Atoms  */
5395         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5396                 /*  The 'v' field contains crystallographic coordinates  */
5397                 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);
5398                 if (ap->aniso != NULL) {
5399                         dp = ap->aniso->bij;
5400                         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);
5401                 } else {
5402                         Double temp = ap->tempFactor;
5403                         if (temp <= 0)
5404                                 temp = 1.2;
5405                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5406                 }
5407         }
5408         /*  Special points  */
5409         {
5410                 Vector camera, lookat, up, xvec, yvec, zvec;
5411                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5412                 VecSub(zvec, lookat, camera);
5413                 VecCross(xvec, zvec, up);
5414                 NormalizeVec(&xvec, &xvec);
5415                 NormalizeVec(&yvec, &up);
5416                 VecInc(xvec, lookat);
5417                 VecInc(yvec, lookat);
5418                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5419                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5420                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5421                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
5422                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5423                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
5424                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5425                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
5426                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5427                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
5428                 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);
5429         }
5430         
5431         /*  Instructions  */
5432         fprintf(fp, "      201\n");
5433         fprintf(fp, "      205       12\n");
5434         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
5435         sOutputAtomListInstructions(fp, natoms, atoms);
5436         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5437         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
5438         fprintf(fp, "      604                               1.538\n");
5439
5440         sOutputBondInstructions(fp, natoms, atoms, 1);
5441         sOutputAtomTypeInstructions(fp, natoms, atoms);
5442         sOutputBondInstructions(fp, natoms, atoms, 0);
5443
5444         for (i = 0; i < natoms; i++) {
5445                 AtomClean(atoms + i);
5446         }
5447         free(atoms);
5448
5449         fprintf(fp, "      202\n");
5450         fprintf(fp, "  0    -1\n");
5451         fclose(fp);
5452         return 0;
5453 }
5454
5455 void
5456 MoleculeDump(Molecule *mol)
5457 {
5458         int i, j;
5459         Int *cp;
5460         Atom *ap;
5461         for (i = 0; i < mol->natoms; i++) {
5462                 char buf1[8];
5463                 ap = ATOM_AT_INDEX(mol->atoms, i);
5464                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5465                 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);
5466                 cp = AtomConnectData(&ap->connect);
5467                 for (j = 0; j < ap->connect.count; j++) {
5468                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5469                 }
5470                 fprintf(stderr, "]\n");
5471         }
5472 }
5473
5474 #pragma mark ====== MD support (including modification of Molecule) ======
5475
5476 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5477         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5478     If retmsg is not NULL, a message describing the problem is returned there. This message
5479     must be free'd by the caller.  */
5480 int
5481 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5482 {
5483         const char *msg;
5484         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5485         Int missing = 0;
5486         IntGroup *ig1, *ig2, *ig3;
5487         MDArena *arena = mol->arena;
5488
5489         if (arena == NULL) {
5490                 md_arena_new(mol);
5491                 arena = mol->arena;
5492         } else if (arena->xmol != mol)
5493                 md_arena_set_molecule(arena, mol);
5494
5495         arena->is_initialized = 0;
5496         
5497         /*  Rebuild the tables  */
5498         ig1 = ig2 = ig3 = NULL;
5499         nangles = MoleculeFindMissingAngles(mol, &angles);
5500         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5501         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5502         if (nangles > 0) {
5503                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5504                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5505                 free(angles);
5506                 IntGroupRelease(ig1);
5507         }
5508         if (ndihedrals > 0) {
5509                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5510                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5511                 free(dihedrals);
5512                 IntGroupRelease(ig2);
5513         }
5514         if (nimpropers > 0) {
5515                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5516                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5517                 free(impropers);
5518                 IntGroupRelease(ig3);
5519         }
5520         
5521         {
5522                 /*  Update the path information of the molecule before MD setup  */
5523                 char *buf = (char *)malloc(4096);
5524                 MoleculeCallback_pathName(mol, buf, sizeof buf);
5525                 MoleculeSetPath(mol, buf);
5526                 free(buf);
5527         }
5528                 
5529         /*  Prepare parameters and internal information  */
5530         msg = md_prepare(arena, check_only);
5531         
5532         /*  Some parameters are missing?  */
5533         if (msg != NULL) {
5534                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5535                         missing = 1;
5536                 else {
5537                         if (retmsg != NULL)
5538                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5539                         return -1;
5540                 }
5541         }
5542         
5543         /*  The local parameter list is updated  */
5544         {
5545                 Int parType, idx;
5546                 if (mol->par == NULL)
5547                         mol->par = ParameterNew();
5548                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5549                         /*  Delete global and undefined parameters  */
5550                         UnionPar *up, *upbuf;
5551                         Int nparams, count;
5552                         ig1 = IntGroupNew();
5553                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5554                                 if (up->bond.src != 0)
5555                                         IntGroupAdd(ig1, idx, 1);
5556                         }
5557                         if (IntGroupGetCount(ig1) > 0)
5558                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5559                         IntGroupRelease(ig1);
5560                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
5561                         nparams = ParameterGetCountForType(arena->par, parType);
5562                         if (nparams == 0)
5563                                 continue;
5564                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5565                         ig1 = IntGroupNew();
5566                         ig2 = IntGroupNew();
5567                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5568                                 if (up->bond.src > 0)
5569                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
5570                                 else if (up->bond.src < 0)
5571                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5572                         }
5573                         if ((count = IntGroupGetCount(ig1)) > 0) {
5574                                 /*  Insert global parameters (at the top)  */
5575                                 ParameterCopy(arena->par, parType, upbuf, ig1);
5576                                 ig3 = IntGroupNewWithPoints(0, count, -1);
5577                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5578                                 IntGroupRelease(ig3);
5579                         }
5580                         if ((count = IntGroupGetCount(ig2)) > 0) {
5581                                 /*  Insert undefined parameters (at the bottom)  */
5582                                 ParameterCopy(arena->par, parType, upbuf, ig2);
5583                                 idx = ParameterGetCountForType(mol->par, parType);
5584                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
5585                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5586                                 IntGroupRelease(ig3);
5587                         }
5588                         IntGroupRelease(ig2);
5589                         IntGroupRelease(ig1);
5590                         free(upbuf);
5591                 }
5592                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5593         }
5594         
5595         if (missing) {
5596                 if (retmsg != NULL)
5597                         *retmsg = strdup(msg);
5598                 return 1;
5599         } else return 0;
5600 }
5601
5602 #pragma mark ====== Serialize ======
5603
5604 Molecule *
5605 MoleculeDeserialize(const char *data, Int length, Int *timep)
5606 {
5607         Molecule *mp;
5608         Parameter *par;
5609         Atom *ap;
5610 /*      int result; */
5611
5612         mp = MoleculeNew();
5613         if (mp == NULL)
5614                 goto out_of_memory;
5615         par = ParameterNew();
5616         if (par == NULL)
5617                 goto out_of_memory;
5618
5619         while (length >= 12) {
5620                 const char *ptr = data + 8 + sizeof(Int);
5621                 int len = *((const Int *)(data + 8));
5622                 int i, j, n;
5623                 if (strcmp(data, "ATOM") == 0) {
5624                         n = len / gSizeOfAtomRecord;
5625                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5626                         memmove(mp->atoms, ptr, len);
5627                 } else if (strcmp(data, "ANISO") == 0) {
5628                         n = len / (sizeof(Int) + sizeof(Aniso));
5629                         for (i = 0; i < n; i++) {
5630                                 j = *((const Int *)ptr);
5631                                 if (j < 0 || j >= mp->natoms)
5632                                         goto bad_format;
5633                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5634                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5635                                 if (ap->aniso == NULL)
5636                                         goto out_of_memory;
5637                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5638                                 ptr += sizeof(Int) + sizeof(Aniso);
5639                         }
5640                 } else if (strcmp(data, "FRAME") == 0) {
5641                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5642                                 if (ap->nframes == 0)
5643                                         continue;
5644                                 n = ap->nframes;
5645                                 ap->frames = NULL;
5646                                 ap->nframes = 0;
5647                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5648                                 if (ap->frames == NULL)
5649                                         goto out_of_memory;
5650                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5651                                 ptr += sizeof(Vector) * ap->nframes;
5652                         }
5653                 } else if (strcmp(data, "EXTCON") == 0) {
5654                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5655                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5656                                         continue;
5657                                 n = ap->connect.count;
5658                                 ap->connect.count = 0;
5659                                 ap->connect.u.ptr = NULL;
5660                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5661                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5662                                 ptr += sizeof(Int) * n;
5663                         }
5664                 } else if (strcmp(data, "BOND") == 0) {
5665                         n = len / (sizeof(Int) * 2);
5666                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5667                         memmove(mp->bonds, ptr, len);
5668                 } else if (strcmp(data, "ANGLE") == 0) {
5669                         n = len / (sizeof(Int) * 3);
5670                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5671                         memmove(mp->angles, ptr, len);
5672                 } else if (strcmp(data, "DIHED") == 0) {
5673                         n = len / (sizeof(Int) * 4);
5674                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5675                         memmove(mp->dihedrals, ptr, len);
5676                 } else if (strcmp(data, "IMPROP") == 0) {
5677                         n = len / (sizeof(Int) * 4);
5678                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5679                         memmove(mp->impropers, ptr, len);
5680                 } else if (strcmp(data, "RESIDUE") == 0) {
5681                         n = len / 4;
5682                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5683                         memmove(mp->residues, ptr, len);
5684                 } else if (strcmp(data, "CELL") == 0) {
5685                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5686                         if (mp->cell == NULL)
5687                                 goto out_of_memory;
5688                         memmove(mp->cell, ptr, sizeof(XtalCell));
5689                 } else if (strcmp(data, "SYMOP") == 0) {
5690                         n = len / sizeof(Transform);
5691                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5692                         memmove(mp->syms, ptr, len);
5693                 } else if (strcmp(data, "ANCHOR") == 0) {
5694                         const char *ptr2 = ptr + len;
5695                         while (ptr < ptr2) {
5696                                 PiAnchor an;
5697                                 memset(&an, 0, sizeof(an));
5698                                 i = *((Int *)ptr);
5699                                 if (i >= 0 && i < mp->natoms) {
5700                                         n = *((Int *)(ptr + sizeof(Int)));
5701                                         AtomConnectResize(&(an.connect), n);
5702                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5703                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5704                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5705                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5706                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5707                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5708                                 }
5709                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5710                         }
5711                 } else if (strcmp(data, "TIME") == 0) {
5712                         if (timep != NULL)
5713                                 *timep = *((Int *)ptr);
5714                 } else if (strcmp(data, "BONDPAR") == 0) {
5715                         mp->par = par;
5716                         n = len / sizeof(BondPar);
5717                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5718                         memmove(par->bondPars, ptr, len);
5719                 } else if (strcmp(data, "ANGPAR") == 0) {
5720                         mp->par = par;
5721                         n = len / sizeof(AnglePar);
5722                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5723                         memmove(par->anglePars, ptr, len);
5724                 } else if (strcmp(data, "DIHEPAR") == 0) {
5725                         mp->par = par;
5726                         n = len / sizeof(TorsionPar);
5727                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5728                         memmove(par->dihedralPars, ptr, len);
5729                 } else if (strcmp(data, "IMPRPAR") == 0) {
5730                         mp->par = par;
5731                         n = len / sizeof(TorsionPar);
5732                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5733                         memmove(par->improperPars, ptr, len);
5734                 } else if (strcmp(data, "VDWPAR") == 0) {
5735                         mp->par = par;
5736                         n = len / sizeof(VdwPar);
5737                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5738                         memmove(par->vdwPars, ptr, len);
5739                 } else if (strcmp(data, "VDWPPAR") == 0) {
5740                         mp->par = par;
5741                         n = len / sizeof(VdwPairPar);
5742                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5743                         memmove(par->vdwpPars, ptr, len);
5744                 } else if (strcmp(data, "VCUTPAR") == 0) {
5745                         mp->par = par;
5746                         n = len / sizeof(VdwCutoffPar);
5747                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5748                         memmove(par->vdwCutoffPars, ptr, len);
5749                 }
5750                 len += 8 + sizeof(Int);
5751                 data += len;
5752                 length -= len;
5753         }
5754         if (mp->par == NULL)
5755                 ParameterRelease(par);
5756 /*      result = MoleculeRebuildTablesFromConnects(mp);
5757         if (result != 0)
5758                 goto bad_format; */
5759         return mp;
5760         
5761   out_of_memory:
5762         Panic("Low memory while deserializing molecule data");
5763         return NULL; /* Not reached */
5764
5765   bad_format:
5766         Panic("internal error: bad format during deserializing molecule data");
5767         return NULL; /* Not reached */
5768 }
5769
5770 char *
5771 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5772 {
5773         char *ptr, *p;
5774         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5775         Atom *ap;
5776
5777         /*  Array of atoms  */
5778         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5779         ptr = (char *)malloc(len);
5780         if (ptr == NULL)
5781                 goto out_of_memory;
5782         memmove(ptr, "ATOM\0\0\0\0", 8);
5783         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5784         p = ptr + 8 + sizeof(Int);
5785         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5786         naniso = nframes = nconnects = nanchors = 0;
5787         for (i = 0; i < mp->natoms; i++) {
5788                 ap = ATOM_AT_INDEX(p, i);
5789                 if (ap->aniso != NULL) {
5790                         naniso++;
5791                         ap->aniso = NULL;
5792                 }
5793                 if (ap->frames != NULL) {
5794                         nframes += ap->nframes;
5795                         ap->frames = NULL;
5796                 }
5797                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5798                         nconnects += ap->connect.count;
5799                         ap->connect.u.ptr = NULL;
5800                 }
5801                 if (ap->anchor != NULL) {
5802                         nanchors++;
5803                         ap->anchor = NULL;
5804                 }
5805         }
5806         len_all = len;
5807
5808         /*  Array of aniso  */
5809         if (naniso > 0) {
5810                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5811                 ptr = (char *)realloc(ptr, len_all + len);
5812                 if (ptr == NULL)
5813                         goto out_of_memory;
5814                 p = ptr + len_all;
5815                 memmove(p, "ANISO\0\0\0", 8);
5816                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5817                 p += 8 + sizeof(Int);
5818                 for (i = 0; i < mp->natoms; i++) {
5819                         ap = ATOM_AT_INDEX(mp->atoms, i);
5820                         if (ap->aniso != NULL) {
5821                                 *((Int *)p) = i;
5822                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5823                                 p += sizeof(Int) + sizeof(Aniso);
5824                         }
5825                 }
5826                 len_all += len;
5827         }
5828         
5829         /*  Array of frames  */
5830         if (nframes > 0) {
5831                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5832                 ptr = (char *)realloc(ptr, len_all + len);
5833                 if (ptr == NULL)
5834                         goto out_of_memory;
5835                 p = ptr + len_all;
5836                 memmove(p, "FRAME\0\0\0", 8);
5837                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5838                 p += 8 + sizeof(Int);
5839                 for (i = 0; i < mp->natoms; i++) {
5840                         ap = ATOM_AT_INDEX(mp->atoms, i);
5841                         if (ap->frames != NULL) {
5842                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5843                                 p += sizeof(Vector) * ap->nframes;
5844                         }
5845                 }
5846                 len_all += len;
5847         }
5848         
5849         /*  Array of connects  */
5850         if (nconnects > 0) {
5851                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5852                 ptr = (char *)realloc(ptr, len_all + len);
5853                 if (ptr == NULL)
5854                         goto out_of_memory;
5855                 p = ptr + len_all;
5856                 memmove(p, "EXTCON\0\0", 8);
5857                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5858                 p += 8 + sizeof(Int);
5859                 for (i = 0; i < mp->natoms; i++) {
5860                         ap = ATOM_AT_INDEX(mp->atoms, i);
5861                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5862                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5863                                 p += sizeof(Int) * ap->connect.count;
5864                         }
5865                 }
5866                 len_all += len;
5867         }
5868         
5869         /*  Bonds, angles, dihedrals, impropers  */
5870         if (mp->nbonds > 0) {
5871                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5872                 ptr = (char *)realloc(ptr, len_all + len);
5873                 if (ptr == NULL)
5874                         goto out_of_memory;
5875                 p = ptr + len_all;
5876                 memmove(p, "BOND\0\0\0\0", 8);
5877                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5878                 p += 8 + sizeof(Int);
5879                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5880                 len_all += len;
5881         }
5882         if (mp->nangles > 0) {
5883                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5884                 ptr = (char *)realloc(ptr, len_all + len);
5885                 if (ptr == NULL)
5886                         goto out_of_memory;
5887                 p = ptr + len_all;
5888                 memmove(p, "ANGLE\0\0\0", 8);
5889                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5890                 p += 8 + sizeof(Int);
5891                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5892                 len_all += len;
5893         }
5894         if (mp->ndihedrals > 0) {
5895                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5896                 ptr = (char *)realloc(ptr, len_all + len);
5897                 if (ptr == NULL)
5898                         goto out_of_memory;
5899                 p = ptr + len_all;
5900                 memmove(p, "DIHED\0\0\0", 8);
5901                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5902                 p += 8 + sizeof(Int);
5903                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5904                 len_all += len;
5905         }
5906         if (mp->nimpropers > 0) {
5907                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5908                 ptr = (char *)realloc(ptr, len_all + len);
5909                 if (ptr == NULL)
5910                         goto out_of_memory;
5911                 p = ptr + len_all;
5912                 memmove(p, "IMPROP\0\0", 8);
5913                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5914                 p += 8 + sizeof(Int);
5915                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5916                 len_all += len;
5917         }
5918         
5919         /*  Array of residues  */
5920         if (mp->nresidues > 0) {
5921                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5922                 ptr = (char *)realloc(ptr, len_all + len);
5923                 if (ptr == NULL)
5924                         goto out_of_memory;
5925                 p = ptr + len_all;
5926                 memmove(p, "RESIDUE\0", 8);
5927                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5928                 p += 8 + sizeof(Int);
5929                 memmove(p, mp->residues, 4 * mp->nresidues);
5930                 len_all += len;
5931         }
5932
5933         /*  Unit cell  */
5934         if (mp->cell != NULL) {
5935                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5936                 ptr = (char *)realloc(ptr, len_all + len);
5937                 if (ptr == NULL)
5938                         goto out_of_memory;
5939                 p = ptr + len_all;
5940                 memmove(p, "CELL\0\0\0\0", 8);
5941                 *((Int *)(p + 8)) = sizeof(XtalCell);
5942                 p += 8 + sizeof(Int);
5943                 memmove(p, mp->cell, sizeof(XtalCell));
5944                 len_all += len;
5945         }
5946         
5947         /*  Symmetry operations  */
5948         if (mp->nsyms > 0) {
5949                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5950                 ptr = (char *)realloc(ptr, len_all + len);
5951                 if (ptr == NULL)
5952                         goto out_of_memory;
5953                 p = ptr + len_all;
5954                 memmove(p, "SYMOP\0\0\0", 8);
5955                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5956                 p += 8 + sizeof(Int);
5957                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5958                 len_all += len;
5959         }
5960         
5961         /*  Pi-anchors  */
5962         if (nanchors > 0) {
5963                 /*  Estimate the necessary storage first  */
5964                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
5965                 len = 8 + sizeof(Int);
5966                 for (i = 0; i < mp->natoms; i++) {
5967                         ap = ATOM_AT_INDEX(mp->atoms, i);
5968                         if (ap->anchor != NULL)
5969                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5970                 }
5971                 ptr = (char *)realloc(ptr, len_all + len);
5972                 if (ptr == NULL)
5973                         goto out_of_memory;
5974                 p = ptr + len_all;
5975                 memmove(p, "ANCHOR\0\0", 8);
5976                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5977                 p += 8 + sizeof(Int);
5978                 for (i = 0; i < mp->natoms; i++) {
5979                         Int count, *ip;
5980                         ap = ATOM_AT_INDEX(mp->atoms, i);
5981                         if (ap->anchor != NULL) {
5982                                 count = ap->anchor->connect.count;
5983                                 *((Int *)p) = i;
5984                                 *((Int *)(p + sizeof(Int))) = count;
5985                                 p += sizeof(Int) * 2;
5986                                 ip = AtomConnectData(&(ap->anchor->connect));
5987                                 memmove(p, ip, sizeof(Int) * count);
5988                                 p += sizeof(Int) * count;
5989                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5990                                 p += sizeof(Double) * count;
5991                         }
5992                 }
5993                 len_all += len;
5994         }
5995         
5996         /*  Parameters  */
5997         if (mp->par != NULL) {
5998                 int type;
5999                 for (type = kFirstParType; type <= kLastParType; type++) {
6000                         const char *parname;
6001                         Int parsize, parcount;
6002                         void *parptr;
6003                         switch (type) {
6004                                 case kBondParType:
6005                                         parname = "BONDPAR\0";
6006                                         parsize = sizeof(BondPar);
6007                                         parcount = mp->par->nbondPars;
6008                                         parptr = mp->par->bondPars;
6009                                         break;
6010                                 case kAngleParType:
6011                                         parname = "ANGPAR\0\0";
6012                                         parsize = sizeof(AnglePar);
6013                                         parcount = mp->par->nanglePars;
6014                                         parptr = mp->par->anglePars;
6015                                         break;
6016                                 case kDihedralParType:
6017                                         parname = "DIHEPAR\0";
6018                                         parsize = sizeof(TorsionPar);
6019                                         parcount = mp->par->ndihedralPars;
6020                                         parptr = mp->par->dihedralPars;
6021                                         break;
6022                                 case kImproperParType:
6023                                         parname = "IMPRPAR\0";
6024                                         parsize = sizeof(TorsionPar);
6025                                         parcount = mp->par->nimproperPars;
6026                                         parptr = mp->par->improperPars;
6027                                         break;
6028                                 case kVdwParType:
6029                                         parname = "VDWPAR\0\0";
6030                                         parsize = sizeof(VdwPar);
6031                                         parcount = mp->par->nvdwPars;
6032                                         parptr = mp->par->vdwPars;
6033                                         break;
6034                                 case kVdwPairParType:
6035                                         parname = "VDWPPAR\0";
6036                                         parsize = sizeof(VdwPairPar);
6037                                         parcount = mp->par->nvdwpPars;
6038                                         parptr = mp->par->vdwpPars;
6039                                         break;
6040                                 case kVdwCutoffParType:
6041                                         parname = "VCUTPAR\0";
6042                                         parsize = sizeof(VdwCutoffPar);
6043                                         parcount = mp->par->nvdwCutoffPars;
6044                                         parptr = mp->par->vdwCutoffPars;
6045                                         break;
6046                                 default:
6047                                         continue;
6048                         }
6049                         if (parcount > 0) {
6050                                 len = 8 + sizeof(Int) + parsize * parcount;
6051                                 ptr = (char *)realloc(ptr, len_all + len);
6052                                 if (ptr == NULL)
6053                                         goto out_of_memory;
6054                                 p = ptr + len_all;
6055                                 memmove(p, parname, 8);
6056                                 *((Int *)(p + 8)) = parsize * parcount;
6057                                 p += 8 + sizeof(Int);
6058                                 memmove(p, parptr, parsize * parcount);
6059                                 len_all += len;
6060                         }
6061                 }
6062         }
6063         
6064         /*  Time stamp  */
6065         {
6066                 time_t tm = time(NULL);
6067                 len = 8 + sizeof(Int) + sizeof(Int);
6068                 ptr = (char *)realloc(ptr, len_all + len);
6069                 if (ptr == NULL)
6070                         goto out_of_memory;
6071                 p = ptr + len_all;
6072                 memmove(p, "TIME\0\0\0\0", 8);
6073                 *((Int *)(p + 8)) = sizeof(Int);
6074                 p += 8 + sizeof(Int);
6075                 *((Int *)p) = (Int)tm;
6076                 len_all += len;
6077                 if (timep != NULL)
6078                         *timep = (Int)tm;
6079         }
6080         
6081         if (outLength != NULL)
6082                 *outLength = len_all;
6083         return ptr;
6084
6085   out_of_memory:
6086     Panic("Low memory while serializing a molecule data");
6087         return NULL; /* Not reached */  
6088 }
6089
6090 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6091
6092 static IntGroup *
6093 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6094 {
6095         int i, j;
6096         Int *ip;
6097         IntGroup *gp = NULL;
6098         if (atomgroup == NULL)
6099                 return NULL;
6100         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6101                 for (j = 0; j < nsize; j++) {
6102                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6103                                 if (gp == NULL)
6104                                         gp = IntGroupNew();
6105                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6106                                         Panic("Low memory while searching %s", msg);
6107                                 break;
6108                         }
6109                 }
6110         }
6111         return gp;
6112 }
6113
6114 IntGroup *
6115 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6116 {
6117         if (mp == NULL)
6118                 return NULL;
6119         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6120 }
6121
6122 IntGroup *
6123 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6124 {
6125         if (mp == NULL)
6126                 return NULL;
6127         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6128 }
6129
6130 IntGroup *
6131 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6132 {
6133         if (mp == NULL)
6134                 return NULL;
6135         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6136 }
6137
6138 IntGroup *
6139 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6140 {
6141         if (mp == NULL)
6142                 return NULL;
6143         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6144 }
6145
6146 static IntGroup *
6147 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6148 {
6149         int i, j;
6150         Int *ip;
6151         IntGroup *gp = NULL;
6152         if (atomgroup == NULL)
6153                 return NULL;
6154         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6155                 int k = -1;
6156                 for (j = 0; j < nsize; j++) {
6157                         int kk;
6158                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6159                         if (k < 0)
6160                                 k = kk;
6161                         else if (k != kk) {
6162                                 /*  This bond etc. crosses the atom group border  */
6163                                 if (gp == NULL)
6164                                         gp = IntGroupNew();
6165                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6166                                         Panic("Low memory while searching %s", msg);
6167                                 break;
6168                         }
6169                 }
6170         }
6171         return gp;
6172 }
6173
6174 IntGroup *
6175 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6176 {
6177         if (mp == NULL)
6178                 return NULL;
6179         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6180 }
6181
6182 IntGroup *
6183 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6184 {
6185         if (mp == NULL)
6186                 return NULL;
6187         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6188 }
6189
6190 IntGroup *
6191 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6192 {
6193         if (mp == NULL)
6194                 return NULL;
6195         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6196 }
6197
6198 IntGroup *
6199 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6200 {
6201         if (mp == NULL)
6202                 return NULL;
6203         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6204 }
6205
6206 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6207     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
6208 /*  Find atoms within the given "distance" from the given position.  */
6209 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6210  the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6211  the van der Waals radius of the atom at the given position. */
6212 /*  Index is the atom index of the given atom; it is only used in returning the "bond" array
6213  to the caller. If index is negative, then (-index) is the real atom index, and
6214  only atoms with lower indices than (-index) are looked for.  */
6215 int
6216 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6217 {
6218         Int n2, j, nlim, newbond[2];
6219         Double a2, alim;
6220         Vector dr, r2;
6221         if (index < 0) {
6222                 nlim = index = -index;
6223         } else {
6224                 nlim = mp->natoms;
6225         }
6226         for (j = 0; j < nlim; j++) {
6227                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6228                 if (index == j)
6229                         continue;
6230                 n2 = bp->atomicNumber;
6231                 if (n2 >= 0 && n2 < gCountElementParameters)
6232                         a2 = gElementParameters[n2].radius;
6233                 else a2 = gElementParameters[6].radius;
6234                 r2 = bp->r;
6235                 VecSub(dr, *vp, r2);
6236                 if (limit < 0)
6237                         alim = -limit;
6238                 else
6239                         alim = limit * (radius + a2);
6240                 if (VecLength2(dr) < alim * alim) {
6241                         newbond[0] = index;
6242                         newbond[1] = j;
6243                         /*      MoleculeAddBonds(mp, 1, newbonds); */
6244                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6245                 }
6246         }
6247         return 0;
6248 }
6249
6250 /*  Guess the bonds from the coordinates  */
6251 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6252     the threshold distance is given by the sum of van der Waals radii times limit.  */
6253 int
6254 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6255 {
6256         Int nbonds, *bonds, i, newbond[2];
6257         Atom *ap;
6258         nbonds = 0;
6259         bonds = NULL;
6260         if (limit == 0.0)
6261                 limit = 1.2;
6262         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6263                 Vector r = ap->r;
6264                 Int an = ap->atomicNumber;
6265                 Double rad;
6266                 if (an >= 0 && an < gCountElementParameters)
6267                         rad = gElementParameters[an].radius;
6268                 else rad = gElementParameters[6].radius;
6269                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6270         }
6271         if (nbonds > 0) {
6272                 newbond[0] = kInvalidIndex;
6273                 newbond[1] = 0;
6274                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6275                 nbonds--;
6276         }
6277         if (outNbonds != NULL)
6278                 *outNbonds = nbonds;
6279         if (outBonds != NULL)
6280                 *outBonds = bonds;
6281         return 0;
6282 }
6283
6284 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
6285 int
6286 MoleculeRebuildTablesFromConnects(Molecule *mp)
6287 {
6288         int i, j, k, retval;
6289         Atom *ap;
6290         Int ibuf[6], *cp;
6291         
6292         __MoleculeLock(mp);
6293
6294         /*  Find bonds   */
6295         if (mp->nbonds == 0) {
6296                 for (i = 0; i < mp->natoms; i++) {
6297                         ap = ATOM_AT_INDEX(mp->atoms, i);
6298                         cp = AtomConnectData(&ap->connect);
6299                         for (j = 0; j < ap->connect.count; j++) {
6300                                 k = cp[j];
6301                                 if (i >= k)
6302                                         continue;
6303                                 ibuf[0] = i;
6304                                 ibuf[1] = k;
6305                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
6306                                     bonds are already in sync  */
6307                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6308                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
6309                                 if (retval != 0)
6310                                         goto abort; */
6311                         }
6312                 }
6313         }
6314         
6315         /*  Find angles  */
6316         if (mp->nangles == 0) {
6317                 for (i = 0; i < mp->natoms; i++) {
6318                         ap = ATOM_AT_INDEX(mp->atoms, i);
6319                         cp = AtomConnectData(&ap->connect);
6320                         for (j = 0; j < ap->connect.count; j++) {
6321                                 for (k = j + 1; k < ap->connect.count; k++) {
6322                                         ibuf[0] = cp[j];
6323                                         ibuf[1] = i;
6324                                         ibuf[2] = cp[k];
6325                                         ibuf[3] = -1;
6326                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
6327                                         if (retval < 0)
6328                                                 goto abort;
6329                                 }
6330                         }
6331                 }
6332         }
6333         
6334         /*  Find dihedrals  */
6335         if (mp->ndihedrals == 0) {
6336                 for (i = 0; i < mp->natoms; i++) {
6337                         ap = ATOM_AT_INDEX(mp->atoms, i);
6338                         cp = AtomConnectData(&ap->connect);
6339                         for (j = 0; j < ap->connect.count; j++) {
6340                                 int jj, kk, mm, m;
6341                                 Atom *apjj;
6342                                 Int *cpjj;
6343                                 jj = cp[j];
6344                                 if (i >= jj)
6345                                         continue;
6346                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6347                                 cpjj = AtomConnectData(&apjj->connect);
6348                                 for (k = 0; k < ap->connect.count; k++) {
6349                                         if (k == j)
6350                                                 continue;
6351                                         kk = cp[k];
6352                                         for (m = 0; m < apjj->connect.count; m++) {
6353                                                 mm = cpjj[m];
6354                                                 if (mm == i || mm == kk)
6355                                                         continue;
6356                                                 ibuf[0] = kk;
6357                                                 ibuf[1] = i;
6358                                                 ibuf[2] = jj;
6359                                                 ibuf[3] = mm;
6360                                                 ibuf[4] = -1;
6361                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6362                                                 if (retval < 0)
6363                                                         goto abort;
6364                                         }
6365                                 }
6366                         }
6367                 }
6368         }
6369         
6370         /*  Find impropers  */
6371         if (mp->nimpropers == 0) {
6372                 for (i = 0; i < mp->natoms; i++) {
6373                         int i1, i2, i4, n1, n2, n4;
6374                         ap = ATOM_AT_INDEX(mp->atoms, i);
6375                         cp = AtomConnectData(&ap->connect);
6376                         for (i1 = 0; i1 < ap->connect.count; i1++) {
6377                                 n1 = cp[i1];
6378                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6379                                         n2 = cp[i2];
6380                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6381                                                 n4 = cp[i4];
6382                                                 ibuf[0] = n1;
6383                                                 ibuf[1] = n2;
6384                                                 ibuf[2] = i;
6385                                                 ibuf[3] = n4;
6386                                                 ibuf[4] = -1;
6387                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6388                                                 if (retval < 0)
6389                                                         goto abort;
6390                                         }
6391                                 }
6392                         }
6393                 }
6394         }
6395
6396         mp->needsMDRebuild = 1;
6397         __MoleculeUnlock(mp);
6398         return 0;
6399
6400   abort:
6401         __MoleculeUnlock(mp);
6402         return retval;
6403 }
6404
6405 int
6406 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6407 {
6408         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6409         if (AtomConnectHasEntry(&ap1->connect, idx2))
6410                 return 1;
6411         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6412                 return 2;
6413         else return 0;
6414 }
6415
6416 #pragma mark ====== Atom names ======
6417
6418 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
6419 int
6420 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6421 {
6422         int i, j, lasti;
6423         Atom *ap;
6424         if (mp == NULL || mp->natoms == 0)
6425                 return -1;
6426         lasti = -1;
6427         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6428                 if (ap->resSeq == resno) {
6429                         lasti = i;
6430                         if (j++ == n1)
6431                                 return i;
6432                 }
6433         }
6434         if (n1 == -1)
6435                 return lasti; /* max */
6436         return -1;
6437 }
6438
6439 int
6440 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6441 {
6442     int n;
6443     char *p;
6444         n = strtol(s, &p, 0);
6445         if (p > s) {
6446                 while (isspace(*p))
6447                         p++;
6448                 if (*p == 0) {
6449                   resName[0] = 0;
6450                   *resSeq = -1;
6451                   atomName[0] = 0;
6452                   return n;
6453                 }
6454         }
6455
6456         if ((p = strchr(s, ':')) != NULL) {
6457                 /*  Residue is specified  */
6458                 char *pp;
6459                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6460                         /*  Residue number is also specified  */
6461                         char *ppp;
6462                         n = pp - s;
6463                         *resSeq = strtol(pp + 1, &ppp, 0);
6464                         if (ppp == pp + 1)
6465                                 return -2;  /*  Bad format  */
6466                         while (isspace(*ppp))
6467                                 ppp++;
6468                         if (ppp != p)
6469                                 return -2;  /*  Bad format  */
6470                 } else {
6471                         *resSeq = -1;
6472                         /*  Check whether the "residue name" is an integer  */
6473                         n = strtol(s, &pp, 0);
6474                         if (pp > s) {
6475                                 while (isspace(*pp))
6476                                         pp++;
6477                                 if (*pp == 0 || *pp == ':') {
6478                                         *resSeq = n;
6479                                         if (*resSeq < 0)
6480                                                 return -2;  /*  Bad format  */
6481                                 }
6482                         }
6483                         if (*resSeq >= 0)
6484                                 n = 0;
6485                         else
6486                                 n = p - s;
6487                 }
6488                 if (n >= sizeof(resName))
6489                         n = sizeof(resName) - 1;
6490                 strncpy(resName, s, n);
6491                 resName[n] = 0;
6492                 p++;
6493         } else {
6494                 resName[0] = 0;
6495                 *resSeq = -1;
6496                 p = (char *)s;
6497         }
6498         strncpy(atomName, p, 4);
6499         atomName[4] = 0;
6500         return 0;
6501 }
6502
6503 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
6504 int
6505 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6506 {
6507         char resName[6];
6508         int resSeq, n;
6509         char atomName[6];
6510         /*      char *p; */
6511
6512         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6513         if (atomName[0] == 0) {
6514           if (n >= mp->natoms)
6515             n = -1;  /* Out of range */
6516           return n;
6517         }
6518         for (n = 0; n < mp->natoms; n++) {
6519                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6520                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6521                         && (resSeq < 0 || ap->resSeq == resSeq)
6522                         && strncmp(atomName, ap->aname, 4) == 0) {
6523                         return n;
6524                 }
6525         }
6526         return -1;  /*  Not found  */
6527 }
6528
6529 void
6530 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6531 {
6532         Atom *ap;
6533         int n;
6534         if (mp == NULL || index < 0 || index >= mp->natoms) {
6535                 buf[0] = 0;
6536                 return;
6537         }
6538         ap = mp->atoms + index;
6539         if (ap->resSeq != 0) {
6540                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6541                 buf += n;
6542                 bufsize -= n;
6543         }
6544         snprintf(buf, bufsize, "%.4s", ap->aname);
6545 }
6546
6547 #pragma mark ====== Selection ======
6548
6549 static void
6550 sMoleculeNotifyChangeSelection(Molecule *mp)
6551 {
6552         /*  TODO: Finer control of notification types may be necessary  */
6553         MoleculeCallback_notifyModification(mp, 0);
6554 }
6555
6556 void
6557 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6558 {
6559         if (mp == NULL)
6560                 return;
6561         if (select != NULL)
6562                 IntGroupRetain(select);
6563         if (mp->selection != NULL)
6564                 IntGroupRelease(mp->selection);
6565         mp->selection = select;
6566         sMoleculeNotifyChangeSelection(mp);
6567 }
6568
6569 IntGroup *
6570 MoleculeGetSelection(Molecule *mp)
6571 {
6572         if (mp == NULL)
6573                 return NULL;
6574         else return mp->selection;
6575 }
6576
6577 void
6578 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6579 {
6580         if (mp->selection == NULL)
6581                 mp->selection = IntGroupNew();
6582         if (!extending)
6583                 IntGroupClear(mp->selection);
6584         IntGroupAdd(mp->selection, n1, 1);
6585         sMoleculeNotifyChangeSelection(mp);
6586 }
6587
6588 void
6589 MoleculeUnselectAtom(Molecule *mp, int n1)
6590 {
6591         if (mp->selection != NULL)
6592                 IntGroupRemove(mp->selection, n1, 1);
6593         sMoleculeNotifyChangeSelection(mp);
6594 }
6595
6596 void
6597 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6598 {
6599         if (mp->selection == NULL)
6600                 mp->selection = IntGroupNew();
6601         IntGroupReverse(mp->selection, n1, 1);
6602         sMoleculeNotifyChangeSelection(mp);
6603 }
6604
6605 int
6606 MoleculeIsAtomSelected(Molecule *mp, int n1)
6607 {
6608         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6609                 return 1;
6610         else return 0;
6611 }
6612
6613 int
6614 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6615 {
6616         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6617                 return 1;
6618         else return 0;
6619 }
6620
6621 IntGroup *
6622 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6623 {
6624         int status;
6625         IntGroup *remain, *ig1, *ig2;
6626         ig1 = ig2 = NULL;
6627         remain = IntGroupNewFromIntGroup(remove);
6628         if (remain == NULL)
6629                 status = -1;
6630         else
6631                 status = IntGroupReverse(remain, 0, mp->natoms);
6632         if (status == 0) {
6633                 ig1 = IntGroupNew();
6634                 if (ig1 == NULL)
6635                         status = -1;
6636                 else
6637                         status = IntGroupDifference(selection, remove, ig1);
6638         }
6639         if (status == 0) {
6640                 ig2 = IntGroupNew();
6641                 if (ig2 == NULL)
6642                         status = -1;
6643                 else
6644                         status = IntGroupDeconvolute(ig1, remain, ig2);
6645         }
6646         if (remain != NULL)
6647                 IntGroupRelease(remain);
6648         if (ig1 != NULL)
6649                 IntGroupRelease(ig1);
6650         if (status == 0)
6651                 return ig2;
6652         else {
6653                 if (ig2 != NULL)
6654                         IntGroupRelease(ig2);
6655                 return NULL;
6656         }
6657 }
6658
6659 #pragma mark ====== Atom Equivalence ======
6660
6661 struct sEqList {
6662         int i[2];
6663         struct sEqList *next;
6664         struct sEqList *link;
6665 };
6666
6667 static struct sEqList *sListBase = NULL;
6668 static struct sEqList *sListFree = NULL;
6669
6670 static struct sEqList *
6671 sAllocEqList(void)
6672 {
6673         struct sEqList *lp;
6674         if (sListFree != NULL) {
6675                 lp = sListFree;
6676                 sListFree = lp->next;
6677                 lp->i[0] = lp->i[1] = 0;
6678                 lp->next = NULL;
6679                 return lp;
6680         }
6681         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6682         lp->link = sListBase;
6683         sListBase = lp;
6684         return lp;
6685 }
6686
6687 static void
6688 sFreeEqList(struct sEqList *list)
6689 {
6690         list->next = sListFree;
6691         sListFree = list;
6692 }
6693
6694 static void
6695 sDeallocateEqLists(void)
6696 {
6697         struct sEqList *lp, *lp_link;
6698         for (lp = sListBase; lp != NULL; lp = lp_link) {
6699                 lp_link = lp->link;
6700                 free(lp);
6701         }
6702         sListBase = NULL;
6703         sListFree = NULL;
6704 }
6705
6706 static int
6707 sExistInEqList(int i, int idx, struct sEqList *list)
6708 {
6709         while (list != NULL) {
6710                 if (list->i[idx] == i)
6711                         return 1;
6712                 list = list->next;
6713         }
6714         return 0;
6715 }
6716
6717 static struct sEqList *
6718 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6719 {
6720         Atom *api, *apj;
6721         struct sEqList *list1, *list2;
6722         Int ii, jj, ni, nj, *cpi, *cpj;
6723         api = ATOM_AT_INDEX(mol->atoms, i);
6724         apj = ATOM_AT_INDEX(mol->atoms, j);
6725         if (api->atomicNumber != apj->atomicNumber)
6726                 return NULL;
6727         list1 = sAllocEqList();
6728         if (list1 == NULL)
6729                 return NULL;
6730         list1->i[0] = i;
6731         list1->i[1] = j;
6732         list1->next = list;
6733         if (i == j || (db[i] != NULL && db[i] == db[j]))
6734                 return list1;
6735         cpi = AtomConnectData(&api->connect);
6736         cpj = AtomConnectData(&apj->connect);
6737         for (ni = 0; ni < api->connect.count; ni++) {
6738                 ii = cpi[ni];
6739                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6740                         continue;
6741                 if (sExistInEqList(ii, 0, list1))
6742                         continue;
6743                 list2 = NULL;
6744                 for (nj = 0; nj < apj->connect.count; nj++) {
6745                         jj = cpj[nj];
6746                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6747                                 continue;
6748                         if (sExistInEqList(jj, 1, list1))
6749                                 continue;
6750                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6751                         if (list2 != NULL)
6752                                 break;
6753                 }
6754                 if (list2 == NULL) {
6755                         sFreeEqList(list1);
6756                         return NULL;    /*  No equivalent to ii  */
6757                 }
6758                 list1 = list2;      /*  ii is OK, try next  */
6759         }
6760         return list1;
6761 }
6762
6763 int
6764 sDBInclude(Int *ip, int i)
6765 {
6766         int j;
6767         if (ip == NULL)
6768                 return -1;
6769         for (j = ip[0] - 1; j >= 0; j--) {
6770                 if (ip[j] == i)
6771                         return j;
6772         }
6773         return -1;
6774 }
6775
6776 Int *
6777 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6778 {
6779         Int **db;  /*  List of equivalents for each atom  */
6780         Int *ip, *result;
6781         Atom *api, *apj, *apk;
6782         Int *cpi, *cpj, *ibuf, nibuf;
6783         int i, j, k, ii, jj, kk;
6784         if (mol == NULL || mol->natoms == 0)
6785                 return NULL;
6786         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6787         ibuf = NULL;
6788         nibuf = 0;
6789
6790         /*  Find the equivalent univalent atoms  */
6791         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6792                 if (api->connect.count < 2)
6793                         continue;
6794                 cpi = AtomConnectData(&api->connect);
6795                 for (j = 0; j < api->connect.count; j++) {
6796                         Int n;
6797                         n = 0;
6798                         jj = cpi[j];
6799                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6800                                 continue;
6801                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6802                         n++;
6803                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6804                         if (apj->connect.count != 1 || db[jj] != NULL)
6805                                 continue;
6806                         cpj = AtomConnectData(&apj->connect);
6807                         for (k = j + 1; k < api->connect.count; k++) {
6808                                 kk = cpj[k];
6809                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6810                                         continue;
6811                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6812                                 if (apk->connect.count != 1 || db[kk] != NULL)
6813                                         continue;
6814                                 if (apj->atomicNumber == apk->atomicNumber) {
6815                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6816                                         n++;
6817                                 }
6818                         }
6819                         if (n > 1) {
6820                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6821                                 if (ip == NULL)
6822                                         return NULL;
6823                                 ip[0] = n;
6824                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6825                                 for (k = 0; k < n; k++)
6826                                         db[ip[k + 1]] = ip;
6827                         }
6828                 }
6829         }
6830         if (ibuf != NULL) {
6831                 free(ibuf);
6832                 ibuf = NULL;
6833         }
6834         
6835         /*  Try matching (i,j) pair  */
6836         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6837                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6838                         continue;
6839                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6840                         struct sEqList *list;
6841                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6842                                 continue;
6843                         if (api->atomicNumber != apj->atomicNumber)
6844                                 continue;  /*  Different elements do not match  */
6845                         if (db[i] != NULL && db[i] == db[j])
6846                                 continue;  /*  Already equivalent  */
6847                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6848                         if (list == NULL)
6849                                 continue;  /*  (i,j) do not match  */
6850                         while (list != NULL) {
6851                                 ii = list->i[0];
6852                                 jj = list->i[1];
6853                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6854                                         /*  Merge db[ii] and db[jj]  */
6855                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6856                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6857                                         if (ip == NULL)
6858                                                 return NULL;  /*  Out of memory  */
6859                                         if (db[ii] == NULL) {
6860                                                 ip[1] = ii;
6861                                                 k = 2;
6862                                         } else {
6863                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6864                                                 k = db[ii][0] + 1;
6865                                         }
6866                                         if (db[jj] == NULL) {
6867                                                 ip[k++] = jj;
6868                                         } else {
6869                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6870                                                 k += db[jj][0];
6871                                         }
6872                                         ip[0] = k - 1;
6873                                         /*  Free old ones  */
6874                                         if (db[ii] != NULL)
6875                                                 free(db[ii]);
6876                                         if (db[jj] != NULL)
6877                                                 free(db[jj]);
6878                                         for (k = 0; k < ip[0]; k++)
6879                                                 db[ip[k + 1]] = ip;
6880                                         if (0) {
6881                                                 /*  For debug  */
6882                                                 printf("(%d,%d) matched: ", ii, jj);
6883                                                 for (k = 0; k < ip[0]; k++) {
6884                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6885                                                 }
6886                                                 printf("]\n");
6887                                         }
6888                                 }
6889                                 list = list->next;
6890                         }
6891                 }
6892         }
6893         
6894         /*  Record the equivalent atoms with the lowest index for each atom  */
6895         result = (Int *)calloc(sizeof(Int), mol->natoms);
6896         for (i = 0; i < mol->natoms; i++)
6897                 result[i] = -1;
6898         for (i = 0; i < mol->natoms; i++) {
6899                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6900                         continue;
6901                 k = mol->natoms;
6902                 for (j = 0; j < ip[0]; j++) {
6903                         kk = ip[j + 1];
6904                         if (kk < k)
6905                                 k = kk;
6906                 }
6907                 for (j = 0; j < ip[0]; j++) {
6908                         result[ip[j + 1]] = k;
6909                         db[ip[j + 1]] = NULL;
6910                 }
6911                 free(ip);
6912         }
6913         sDeallocateEqLists();
6914         return result;
6915 }
6916
6917 #pragma mark ====== Symmetry expansion ======
6918
6919 int
6920 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6921 {
6922         Transform t;
6923         if (mp == NULL || mp->cell == NULL)
6924                 return -1;
6925         if (symop.sym >= mp->nsyms && symop.sym != 0)
6926                 return -2;
6927         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6928         (*tf)[9] += symop.dx;
6929         (*tf)[10] += symop.dy;
6930         (*tf)[11] += symop.dz;
6931         if (is_cartesian) {
6932                 TransformMul(t, *tf, mp->cell->rtr);
6933                 TransformMul(*tf, mp->cell->tr, t);
6934         }
6935         return 0;
6936 }
6937
6938 int
6939 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6940 {
6941         Transform t;
6942         int i, j, n[3];
6943         if (mp == NULL || mp->cell == NULL)
6944                 return -1;
6945         if (is_cartesian) {
6946                 TransformMul(t, tf, mp->cell->tr);
6947                 TransformMul(t, mp->cell->rtr, t);
6948         } else {
6949                 memmove(t, tf, sizeof(Transform));
6950         }
6951         for (i = 0; i < mp->nsyms || i == 0; i++) {
6952                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6953                 for (j = 0; j < 9; j++) {
6954                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6955                                 break;
6956                 }
6957                 if (j == 9) {
6958                         for (j = 9; j < 12; j++) {
6959                                 double f1 = t[j] - (*tp)[j];
6960                                 double f2 = floor(f1 + 0.5);
6961                                 if (fabs(f1 - f2) > 1e-4)
6962                                         break;
6963                                 n[j - 9] = f2;
6964                         }
6965                         if (j == 12) {
6966                                 /*  Found  */
6967                                 symop->sym = i;
6968                                 symop->dx = n[0];
6969                                 symop->dy = n[1];
6970                                 symop->dz = n[2];
6971                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6972                                 return 0;
6973                         }
6974                 }
6975         }
6976         return -3;  /*  Not found  */
6977 }
6978
6979 int
6980 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6981 {
6982         if (mp == NULL)
6983                 return 1;
6984         if (symop.sym >= mp->nsyms && symop.sym != 0)
6985                 return 2;
6986         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6987                 TransformVec(vpout, mp->cell->rtr, vpin);
6988                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6989                 vpout->x += symop.dx;
6990                 vpout->y += symop.dy;
6991                 vpout->z += symop.dz;
6992                 TransformVec(vpout, mp->cell->tr, vpout);
6993         } else {
6994                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6995                 vpout->x += symop.dx;
6996                 vpout->y += symop.dy;
6997                 vpout->z += symop.dz;
6998         }
6999         return 0;
7000 }
7001
7002 /*  Add expanded atoms. Returns the number of newly created atoms.
7003         If indices is non-NULL, it should be an array of Int with at least 
7004         IntGroupGetCount(group) entries, and on return it contains the
7005     indices of the expanded atoms (may be existing atoms if the expanded
7006     atoms are already present)
7007     If allowOverlap is non-zero, then the new atom is created even when the
7008     coordinates coincide with the some other atom (special position) of the
7009     same element; otherwise, such atom will not be created and the existing
7010     atom is returned in indices[].  */
7011 int
7012 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7013 {
7014         int i, n, n0, n1, n2, base, count, *table;
7015         Atom *ap;
7016         IntGroupIterator iter;
7017         Transform tr, t1;
7018         Symop symop1;
7019         Atom *ap2;
7020         Vector nr, dr;
7021         
7022         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7023                 return -1;
7024         if (symop.sym != 0 && symop.sym >= mp->nsyms)
7025                 return -2;
7026
7027         /*  Create atoms, with avoiding duplicates  */
7028         n0 = n1 = mp->natoms;
7029         table = (int *)malloc(sizeof(int) * n0);
7030         if (table == NULL)
7031                 return -3;
7032         for (i = 0; i < n0; i++)
7033                 table[i] = -1;
7034         IntGroupIteratorInit(group, &iter);
7035         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7036         __MoleculeLock(mp);
7037         for (i = 0; i < count; i++) {
7038                 n = IntGroupIteratorNext(&iter);
7039                 ap = ATOM_AT_INDEX(mp->atoms, n);
7040                 if (SYMOP_ALIVE(ap->symop)) {
7041                         /*  Calculate the cumulative symop  */
7042                         Transform tr2;
7043                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7044                         TransformMul(tr2, tr, t1);
7045                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7046                                 if (indices != NULL)
7047                                         indices[i] = -1;
7048                                 continue;  /*  Skip this atom  */
7049                         }
7050                         base = ap->symbase;
7051                 } else {
7052                         symop1 = symop;
7053                         base = n;
7054                 }
7055
7056                 /*  Calculate the expande position  */
7057                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7058                 
7059                 /*  Is this expansion already present?  */
7060                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7061                         /*  Symmetry operation and the base atom are the same  */
7062                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7063                                 break;
7064                         /*  Atomic number and the position are the same  */
7065                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7066                                 VecSub(dr, ap2->r, nr);
7067                                 if (VecLength2(dr) < 1e-6)
7068                                         break;
7069                         }
7070                 }
7071                 if (n2 < n0) {
7072                         /*  If yes, then skip it  */
7073                         if (indices != NULL)
7074                                 indices[i] = n2;
7075                         continue;
7076                 } else {
7077                         /*  Create a new atom  */
7078                         Atom newAtom;
7079                         AtomDuplicate(&newAtom, ap);
7080                         MoleculeCreateAnAtom(mp, &newAtom, -1);
7081                         AtomClean(&newAtom);
7082                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7083                         ap2->r = nr;
7084                         ap2->symbase = base;
7085                         ap2->symop = symop1;
7086                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7087                         table[n] = n1;  /*  The index of the new atom  */
7088                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
7089                         if (indices != NULL)
7090                                 indices[i] = n1;
7091                         n1++;
7092                 }
7093         }
7094         IntGroupIteratorRelease(&iter);
7095
7096         /*  Create bonds  */
7097         for (i = n0; i < n1; i++) {
7098                 Int b[2], j;
7099                 ap = ATOM_AT_INDEX(mp->atoms, i);
7100                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7101                         /*  For each connected atom, look for the transformed atom  */
7102                         Int *cp;
7103                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7104                         cp = AtomConnectData(&ap2->connect);
7105                         n2 = ap2->connect.count;
7106                         for (n = 0; n < n2; n++) {
7107                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7108                                 nr = apn->r;
7109                                 TransformVec(&nr, tr, &nr);
7110                                 /*  Look for the bonded atom transformed by ap->symop  */
7111                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7112                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7113                                                 break;
7114                                         VecSub(dr, nr, ap2->r);
7115                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7116                                                 break;
7117                                 }
7118                                 if (j < mp->natoms) {
7119                                         /*  Bond i-j is created  */
7120                                         b[0] = i;
7121                                         b[1] = j;
7122                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7123                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
7124                                 }
7125                         }
7126                 }
7127         }
7128         mp->needsMDRebuild = 1;
7129         __MoleculeUnlock(mp);
7130         free(table);
7131         return n1 - n0;  /*  The number of added atoms  */
7132 }
7133
7134 /*  Recalculate the coordinates of symmetry expanded atoms.
7135     (Also recalculate the positions of pi-anchor atoms)
7136         Returns the number of affected atoms.
7137     If group is non-NULL, only the expanded atoms whose base atoms are in the
7138     given group are considered.
7139         If groupout and vpout are non-NULL, the indices of the affected atoms
7140         and the original positions are returned (for undo operation).
7141         The pointers returned in *groupout and *vpout must be released and 
7142         free()'ed by the caller  */
7143 int
7144 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7145 {
7146         int i, count;
7147         Atom *ap, *bp;
7148         Vector nr, dr;
7149         IntGroup *ig = NULL;
7150         Vector *vp = NULL;
7151         
7152         if (mp == NULL || mp->natoms == 0)
7153                 return 0;
7154
7155         __MoleculeLock(mp);
7156         count = 0;
7157         if (mp->nsyms != 0) {
7158                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7159                         if (!SYMOP_ALIVE(ap->symop))
7160                                 continue;
7161                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7162                                 continue;
7163                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7164                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7165                         VecSub(dr, nr, ap->r);
7166                         if (VecLength2(dr) < 1e-20)
7167                                 continue;
7168                         if (groupout != NULL) {
7169                                 if (ig == NULL) {
7170                                         ig = IntGroupNew();
7171                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7172                                 }
7173                                 vp[count] = ap->r;
7174                                 IntGroupAdd(ig, i, 1);
7175                         }
7176                         ap->r = nr;
7177                         count++;
7178                 }
7179         }
7180         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7181                 Int *ip, j, n;
7182                 if (ap->anchor == NULL)
7183                         continue;
7184                 if (group != NULL) {
7185                         if (IntGroupLookup(group, i, NULL) == 0) {
7186                                 n = ap->anchor->connect.count;
7187                                 ip = AtomConnectData(&(ap->anchor->connect));
7188                                 for (j = 0; j < n; j++) {
7189                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
7190                                                 break;
7191                                 }
7192                                 if (j == n)
7193                                         continue;  /*  This pi-anchor should not be modified  */
7194                         }
7195                 }
7196                 nr = ap->r;
7197                 MoleculeCalculatePiAnchorPosition(mp, i);
7198                 VecSub(dr, nr, ap->r);
7199                 if (VecLength2(dr) < 1e-20) {
7200                         ap->r = nr;  /*  No change  */
7201                         continue;
7202                 }
7203                 if (groupout != NULL) {
7204                         if (ig == NULL) {
7205                                 ig = IntGroupNew();
7206                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7207                         }
7208                         vp[count] = nr;
7209                         IntGroupAdd(ig, i, 1);
7210                 }
7211                 count++;
7212         }
7213         mp->needsMDCopyCoordinates = 1;
7214         __MoleculeUnlock(mp);
7215
7216         if (count > 0) {
7217                 if (groupout != NULL && vpout != NULL) {
7218                         *groupout = ig;
7219                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7220                 } else {
7221                         IntGroupRelease(ig);
7222                         free(vp);
7223                 }
7224         } else {
7225                 if (groupout != NULL && vpout != NULL) {
7226                         *groupout = NULL;
7227                         *vpout = NULL;
7228                 }
7229         }
7230         return count;
7231 }
7232
7233 #pragma mark ====== Show/hide atoms ======
7234
7235 static void
7236 sMoleculeNotifyChangeAppearance(Molecule *mp)
7237 {
7238         /*  TODO: Finer control of notification types may be necessary  */
7239         MoleculeCallback_notifyModification(mp, 0);
7240 }
7241
7242
7243 static void
7244 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7245 {
7246         int i;
7247         if (mp == NULL || mp->selection == NULL)
7248                 return;
7249         for (i = 0; i < mp->natoms; i++) {
7250                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7251                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7252                         IntGroupRemove(mp->selection, i, 1);
7253         }
7254         sMoleculeNotifyChangeAppearance(mp);
7255 }
7256
7257 int
7258 MoleculeShowAllAtoms(Molecule *mp)
7259 {
7260         int i;
7261         if (mp == NULL)
7262                 return 0;
7263         for (i = 0; i < mp->natoms; i++) {
7264                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7265                 ap->exflags &= ~kAtomHiddenFlag;
7266         }
7267         sMoleculeNotifyChangeAppearance(mp);
7268         return 1;
7269 }
7270
7271 int
7272 MoleculeShowReverse(Molecule *mp)
7273 {
7274         int i;
7275         if (mp == NULL)
7276                 return 0;
7277         for (i = 0; i < mp->natoms; i++) {
7278                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7279                 ap->exflags ^= kAtomHiddenFlag;
7280         }
7281         sMoleculeUnselectHiddenAtoms(mp);
7282         sMoleculeNotifyChangeAppearance(mp);
7283         return 1;
7284 }
7285
7286 int
7287 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7288 {
7289         int i;
7290         if (mp == NULL || ig == NULL)
7291                 return 0;
7292         for (i = 0; i < mp->natoms; i++) {
7293                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7294                 if (ap->exflags & kAtomHiddenFlag)
7295                         continue;  /*  Already hidden  */
7296                 if (IntGroupLookupPoint(ig, i) >= 0)
7297                         ap->exflags |= kAtomHiddenFlag;
7298         }
7299         sMoleculeUnselectHiddenAtoms(mp);
7300         sMoleculeNotifyChangeAppearance(mp);
7301         return 1;
7302 }
7303
7304 #pragma mark ====== Reversible Editing ======
7305
7306 /*
7307 static void
7308 sMoleculeNotifyModification(Molecule *mp)
7309 {
7310         **  TODO: Finer control of notification types may be necessary  **
7311         MoleculeCallback_notifyModification(mp, 0);
7312 }
7313 */
7314
7315 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7316 int
7317 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7318 {
7319         int n1, n2, n3, i;
7320         if (where == NULL) {
7321                 /*  Append the new objects at the end  */
7322                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7323                 return 0;
7324         }
7325         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
7326         n2 = nobjs;                    /*  Position to get old object  */
7327         n3 = n1 + n2;                  /*  Position to place new/old object  */
7328         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7329                 int start = IntGroupGetStartPoint(where, i);
7330                 int end = IntGroupGetEndPoint(where, i);
7331                 if (end < n3) {
7332                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
7333                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7334                         n2 = end - (n3 - n2);
7335                         n3 = end;
7336                 }
7337                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
7338                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7339                 n3 -= end - start;
7340                 n1 -= end - start;
7341         }
7342         return 0;
7343 }
7344
7345 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7346 int
7347 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7348 {
7349         int n1, n2, n3, start, end, i;
7350         if (where == NULL || IntGroupGetCount(where) == 0)
7351                 return 0;  /*  No operation  */
7352         if (objs == NULL || nobjs == 0)
7353                 return 1;  /*  Bad argument  */
7354         n1 = 0;  /*  Position to move remaining elements to */
7355         n2 = 0;  /*  Position to move remaining elements from  */
7356         n3 = 0;  /*  Position to move removed elements to  */
7357         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7358                 end = IntGroupGetEndPoint(where, i);
7359                 if (n2 < start) {
7360                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
7361                         if (n1 < n2)
7362                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7363                         n1 += start - n2;
7364                         n2 = start;
7365                 }
7366                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
7367                 if (clip != NULL)
7368                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7369                 n3 += (end - start);
7370                 n2 += (end - start);
7371         }
7372         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
7373         if (nobjs > n2)
7374                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7375         return 0;
7376 }
7377
7378 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7379 int
7380 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7381 {
7382         int n1, start, end, i;
7383         if (objs == NULL || where == NULL)
7384                 return 1;  /*  Bad argument  */
7385         n1 = 0;  /*  Position to move removed elements to  */
7386         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7387                 end = IntGroupGetEndPoint(where, i);
7388                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
7389                 if (clip != NULL)
7390                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7391                 n1 += (end - start);
7392         }
7393         return 0;
7394 }
7395
7396 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7397    (Use AtomDuplicate() first) */
7398 int
7399 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7400 {
7401     Atom *ap1, *api;
7402         int i;
7403         if (mp == NULL || ap == NULL || mp->noModifyTopology)
7404                 return -1;
7405         __MoleculeLock(mp);
7406         if (pos < 0 || pos >= mp->natoms)
7407                 pos = mp->natoms;
7408         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7409         if (ap1 == NULL)
7410                 goto error;  /*  Out of memory  */
7411         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7412         if (pos < mp->natoms - 1) {
7413                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7414         }
7415         if (AtomDuplicate(ap1, ap) == NULL) {
7416                 /*  Cannot duplicate: restore the original state  */
7417                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7418                 mp->natoms--;
7419                 goto error;
7420         }
7421         ap1->connect.count = 0;
7422         if (ap1->resSeq >= mp->nresidues)
7423                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7424         if (ap1->resName[0] == 0)
7425           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7426         if (ap1->segName[0] == 0)
7427           strncpy(ap1->segName, "MAIN", 4);
7428         if (pos < mp->natoms - 1) {
7429                 /*  Renumber the connect table, bonds, angles, etc. */
7430                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7431                         int j;
7432                         Int *cp;
7433                         cp = AtomConnectData(&api->connect);
7434                         for (j = 0; j < api->connect.count; j++) {
7435                                 if (cp[j] >= pos)
7436                                         cp[j]++;
7437                         }
7438                         if (api->anchor != NULL) {
7439                                 cp = AtomConnectData(&api->anchor->connect);
7440                                 for (j = 0; j < api->anchor->connect.count; j++) {
7441                                         if (cp[j] >= pos)
7442                                                 cp[j]++;
7443                                 }
7444                         }
7445                 }
7446                 for (i = 0; i < mp->nbonds * 2; i++) {
7447                         if (mp->bonds[i] >= pos)
7448                                 mp->bonds[i]++;
7449                 }
7450                 for (i = 0; i < mp->nangles * 3; i++) {
7451                         if (mp->angles[i] >= pos)
7452                                 mp->angles[i]++;
7453                 }
7454                 for (i = 0; i < mp->ndihedrals * 4; i++) {
7455                         if (mp->dihedrals[i] >= pos)
7456                                 mp->dihedrals[i]++;
7457                 }
7458                 for (i = 0; i < mp->nimpropers * 4; i++) {
7459                         if (mp->impropers[i] >= pos)
7460                                 mp->impropers[i]++;
7461                 }
7462         }
7463         mp->nframes = -1;  /*  Should be recalculated later  */
7464         MoleculeIncrementModifyCount(mp);
7465         mp->needsMDRebuild = 1;
7466         __MoleculeUnlock(mp);
7467         return pos;
7468 error:
7469         __MoleculeUnlock(mp);
7470         return -1;
7471 }
7472
7473 #if defined(DEBUG)
7474
7475 static int s_error_count;
7476
7477 static int
7478 s_fprintf(FILE *fp, const char *fmt, ...)
7479 {
7480         va_list va;
7481         va_start(va, fmt);
7482         s_error_count++;
7483         return vfprintf(fp, fmt, va);
7484 }
7485
7486 int
7487 MoleculeCheckSanity(Molecule *mol)
7488 {
7489         const char *fail = "Sanity check failure";
7490         Int i, j, *ip, c[4];
7491         Atom *ap;
7492         s_error_count = 0;
7493         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7494                 if (ap->resSeq >= mol->nresidues)
7495                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7496                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7497                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7498                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7499                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7500                 ip = AtomConnectData(&ap->connect);
7501                 for (j = 0; j < ap->connect.count; j++) {
7502                         if (ip[j] < 0 || ip[j] >= mol->natoms)
7503                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7504                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7505                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7506                 }
7507         }
7508         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7509                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7510                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7511                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7512                         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]);
7513         }
7514         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7515                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7516                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7517                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7518                 if (c[0] == 0)
7519                         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]);
7520                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7521                 if (c[1] == 0)
7522                         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]);
7523                 if (c[0] == 2 && c[1] == 2)
7524                         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]);
7525         }
7526         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7527                 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)
7528                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7529                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7530                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7531                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7532                 if (c[0] == 0)
7533                         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]);
7534                 if (c[1] == 0)
7535                         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]);
7536                 if (c[2] == 0)
7537                         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]);
7538         }
7539         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7540                 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)
7541                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7542                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7543                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7544                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7545                 if (c[0] == 0)
7546                         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]);
7547                 if (c[1] == 0)
7548                         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]);
7549                 if (c[2] == 0)
7550                         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]);
7551         }
7552         return s_error_count;
7553 }
7554 #endif
7555
7556 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7557 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7558 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7559 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7560     separately by other undo actions.  */
7561 int
7562 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7563 {
7564         Int nsrc, ndst;
7565         Int i, j, n1, n2, n3, n4, *cp;
7566         Int *new2old, *old2new;
7567         IntGroup *ig;
7568         Atom *ap;
7569         MolAction *act;
7570         
7571         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7572                 return 0;  /*  Do nothing  */
7573
7574         if (dst->noModifyTopology)
7575                 return 1;  /*  Prohibited operation  */
7576
7577         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7578                 return 1;  /*  Bad parameter  */
7579
7580         if (nactions != NULL)
7581                 *nactions = 0;
7582         if (actions != NULL)
7583                 *actions = NULL;
7584         act = NULL;
7585
7586         __MoleculeLock(dst);
7587
7588         nsrc = src->natoms;
7589         ndst = dst->natoms;
7590         if (resSeqOffset < 0)
7591                 resSeqOffset = 0;
7592
7593         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7594             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7595         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7596         if (new2old == NULL)
7597                 goto panic;
7598         old2new = new2old + ndst + nsrc;
7599         n1 = 0;  /*  dst index  */
7600         n2 = 0;  /*  src index  */
7601         n3 = 0;  /*  "merged" index  */
7602         i = 0;
7603         while (n1 < ndst || n2 < nsrc) {
7604                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7605                         n4 = ndst - n1;
7606                 else n4 -= n3;
7607                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7608                 for (j = 0; j < n4; j++) {
7609                         old2new[n1 + j] = n3 + j;
7610                         new2old[n3 + j] = n1 + j;
7611                 }
7612                 n3 += n4;
7613                 n1 += n4;
7614                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7615                         n4 = nsrc - n2;
7616                 /*  n4 elements from src[n2] will go to merged[n3]  */
7617                 for (j = 0; j < n4; j++) {
7618                         old2new[ndst + n2 + j] = n3 + j;
7619                         new2old[n3 + j] = ndst + n2 + j;
7620                 }
7621                 n3 += n4;
7622                 n2 += n4;
7623                 i++;
7624         }
7625
7626         /*  Expand the destination array  */
7627         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7628                 goto panic;
7629
7630         /*  Move the atoms  */
7631         if (where == NULL) {
7632                 /*  Duplicate atoms to the end of the destination array  */
7633                 for (i = 0; i < nsrc; i++) {
7634                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7635                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7636                                 goto panic;
7637                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7638                                 AtomConnectResize(&ap->connect, 0);
7639                 }
7640         } else {
7641                 /*  Duplicate to a temporary storage and then insert  */
7642                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7643                 if (tempatoms == NULL)
7644                         goto panic;
7645                 for (i = 0; i < nsrc; i++) {
7646                         ap = ATOM_AT_INDEX(tempatoms, i);
7647                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7648                                 goto panic;
7649                         if (forUndo)  /*  See above  */
7650                                 AtomConnectResize(&ap->connect, 0);                             
7651                 }
7652                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7653                         goto panic;
7654                 free(tempatoms);
7655         }
7656         dst->natoms = ndst + nsrc;
7657
7658         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7659         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7660                 if (new2old[i] < ndst) {
7661                         /*  This atom is from dst  */
7662                         n1 = 0;
7663                 } else {
7664                         /*  This atom is from src  */
7665                         n1 = ndst;  /*  Offset to the internal number  */
7666                         if (ap->resSeq != 0)
7667                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7668                 }
7669                 cp = AtomConnectData(&ap->connect);
7670                 for (j = 0; j < ap->connect.count; j++)
7671                         cp[j] = old2new[cp[j] + n1];
7672                 if (SYMOP_ALIVE(ap->symop))
7673                         ap->symbase = old2new[ap->symbase + n1];
7674                 if (ap->anchor != NULL) {
7675                         cp = AtomConnectData(&ap->anchor->connect);
7676                         for (j = 0; j < ap->anchor->connect.count; j++)
7677                                 cp[j] = old2new[cp[j] + n1];
7678                 }
7679         }
7680         
7681         /*  Move the bonds, angles, dihedrals, impropers  */
7682         for (i = 0; i < 4; i++) {
7683                 Int *nitems, *nitems_src;
7684                 Int **items, **items_src;
7685                 Int nsize;  /*  Number of Ints in one element  */
7686                 switch (i) {
7687                         case 0:
7688                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7689                         case 1:
7690                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7691                         case 2:
7692                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7693                         case 3:
7694                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7695                 }
7696                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7697                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7698                 if (forUndo) {
7699                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7700                             by undo actions  */
7701                         n1 = *nitems;
7702                         n2 = 0;
7703                 } else {
7704                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7705                         n1 = *nitems;
7706                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7707                         n2 = *nitems_src;
7708                         /*  Expand the array  */
7709                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7710                                 goto panic;
7711                         /*  Copy the items  */
7712                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7713                         if (i == 0) {
7714                                 /*  Copy the bond order info if present */
7715                                 Int nn1 = dst->nbondOrders;
7716                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7717                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7718                                                 goto panic;
7719                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7720                                         if (src->bondOrders != NULL)
7721                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7722                                 }
7723                         }
7724                 }
7725                 /*  Renumber  */
7726                 for (j = 0; j < n1 * nsize; j++)
7727                         (*items)[j] = old2new[(*items)[j]];
7728                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7729                         (*items)[j] = old2new[(*items)[j] + ndst];
7730                 if (forUndo == 0 && actions != NULL) {
7731                         ig = IntGroupNewWithPoints(n1, n2, -1);
7732                         switch (i) {
7733                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7734                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7735                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7736                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7737                         }
7738                         IntGroupRelease(ig);
7739                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7740                         act = NULL;
7741                 }
7742         }
7743         
7744         /*  Renumber existing parameters  */
7745         if (dst->par != NULL) {
7746                 int type;
7747                 for (type = kFirstParType; type <= kLastParType; type++) {
7748                         UnionPar *up1;
7749                         n1 = ParameterGetCountForType(dst->par, type);
7750                         for (i = 0; i < n1; i++) {
7751                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7752                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7753                         }
7754                 }
7755         }
7756
7757         /*  Merge parameters from src  */
7758         if (src->par != NULL && forUndo == 0) {
7759                 UnionPar *up1, *up2;
7760                 int type;
7761                 if (dst->par == NULL)
7762                         dst->par = ParameterNew();
7763                 else {
7764                         /*  Renumber existing parameters  */
7765                         for (type = kFirstParType; type <= kLastParType; type++) {
7766                                 n1 = ParameterGetCountForType(dst->par, type);
7767                                 for (i = 0; i < n1; i++) {
7768                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7769                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7770                                 }
7771                         }
7772                 }
7773                 ig = IntGroupNew();
7774                 for (type = kFirstParType; type <= kLastParType; type++) {
7775                         n1 = ParameterGetCountForType(src->par, type);
7776                         n2 = ParameterGetCountForType(dst->par, type);
7777                         if (n1 == 0)
7778                                 continue;
7779                         /*  Determine which parameter should be copied from src to dst  */
7780                         for (i = 0; i < n1; i++) {
7781                                 UInt types[4];
7782                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7783                                 n3 = ParameterGetAtomTypes(type, up1, types);
7784                                 for (j = 0; j < n3; j++) {
7785                                         /*  If it includes explicit atom index, then it should be copied  */
7786                                         if (types[j] < kAtomTypeMinimum) {
7787                                                 IntGroupAdd(ig, i, 1);
7788                                                 break;
7789                                         }
7790                                 }
7791                                 if (j == n3) {
7792                                         for (j = 0; j < n2; j++) {
7793                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7794                                                 if (ParameterCompare(up1, up2, type))
7795                                                         break;
7796                                         }
7797                                         if (j >= n2)
7798                                                 /*  This is an unknown parameter; should be copied  */
7799                                                 IntGroupAdd(ig, i, 1);
7800                                 }
7801                         }
7802                         n1 = IntGroupGetCount(ig);
7803                         if (n1 == 0)
7804                                 continue;
7805                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7806                         if (up1 == NULL)
7807                                 goto panic;
7808                         /*  Copy parameters and renumber indices if necessary  */
7809                         for (i = j = 0; i < n1; i++) {
7810                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7811                                 if (up2 == NULL)
7812                                         continue;
7813                                 up1[j] = *up2;
7814                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7815                                 j++;
7816                         }
7817                         /*  Merge parameters  */
7818                         IntGroupClear(ig);
7819                         IntGroupAdd(ig, n2, j);
7820                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7821                                 goto panic;
7822                         if (actions != NULL) {
7823                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7824                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7825                                 act = NULL;
7826                         }
7827                         IntGroupClear(ig);
7828                         free(up1);
7829                 }
7830                 IntGroupRelease(ig);
7831         }
7832         
7833         /*  Copy the residues if necessary  */
7834         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7835             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7836                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7837         if (forUndo == 0) {
7838                 n1 = dst->nresidues;
7839                 if (1 + resSeqOffset < n1) {
7840                         n2 = n1;
7841                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7842                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7843                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7844                                 goto panic;
7845                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7846                         if (nactions != NULL) {
7847                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7848                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7849                                 act = NULL;
7850                         }
7851                 }
7852         }
7853
7854         MoleculeCleanUpResidueTable(dst);
7855         
7856         free(new2old);
7857         dst->nframes = -1;  /*  Should be recalculated later  */
7858
7859         MoleculeIncrementModifyCount(dst);
7860         dst->needsMDRebuild = 1;
7861         __MoleculeUnlock(dst);
7862         return 0;
7863
7864   panic:
7865         __MoleculeUnlock(dst);
7866     Panic("Low memory while adding atoms");
7867         return 1;  /*  Not reached  */
7868 }
7869
7870 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7871     (The nactions/actions array must be initialized by the caller)  */
7872 static int
7873 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7874 {
7875         Int nsrc, ndst, nsrcnew;
7876         Int i, j, n1, n2, n3, n4, *cp;
7877         Int *new2old, *old2new;
7878         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7879         Molecule *dst;
7880         Atom *ap, *dst_ap;
7881         UnionPar *up;
7882         MolAction *act;
7883
7884         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7885                 /*  Do nothing  */
7886                 if (dstp != NULL)
7887                         *dstp = NULL;
7888                 return 0;
7889         }
7890         
7891         if (src->noModifyTopology && moveFlag)
7892                 return 1;  /*  Prohibit editing  */
7893
7894         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7895                 return 1;  /*  Bad parameter  */
7896
7897         __MoleculeLock(src);
7898         
7899         act = NULL;
7900         
7901         nsrc = src->natoms;
7902         nsrcnew = nsrc - ndst;
7903         if (resSeqOffset < 0)
7904                 resSeqOffset = 0;
7905
7906         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7907             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7908         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7909         if (new2old == NULL)
7910                 goto panic;
7911         old2new = new2old + nsrc;
7912         n1 = 0;  /*  src index  */
7913         n2 = 0;  /*  dst index  */
7914         n3 = 0;  /*  src index after "unmerge"  */
7915         i = 0;
7916         while (n1 < nsrc || n2 < ndst) {
7917                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7918                         n4 = nsrc - n1;
7919                 else n4 -= n1;
7920                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7921                 for (j = 0; j < n4; j++) {
7922                         old2new[n1 + j] = n3 + j;
7923                         new2old[n3 + j] = n1 + j;
7924                 }
7925                 n3 += n4;
7926                 n1 += n4;
7927                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7928                         n4 = nsrc - n1;
7929                 /*  n4 elements from src[n1] will go to dst[n2]  */
7930                 for (j = 0; j < n4; j++) {
7931                         old2new[n1 + j] = nsrcnew + n2 + j;
7932                         new2old[nsrcnew + n2 + j] = n1 + j;
7933                 }
7934                 n1 += n4;
7935                 n2 += n4;
7936                 i++;
7937         }
7938
7939         /*  Atoms to remain in the source group  */
7940         if (moveFlag) {
7941                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7942                 IntGroupRemoveIntGroup(remain_g, where);
7943         } else remain_g = NULL;
7944         
7945         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7946         if (src->par != NULL) {
7947                 dst_par_g = IntGroupNew();
7948                 if (moveFlag)
7949                         remove_par_g = IntGroupNew();
7950                 else remove_par_g = NULL;
7951                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7952                         n2 = ParameterGetCountForType(src->par, n1);
7953                         if (n2 == 0)
7954                                 continue;
7955                         for (i = 0; i < n2; i++) {
7956                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7957                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7958                                         /*  This parameter is to be copied to dst  */
7959                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7960                                 }
7961                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7962                                         /*  This parameter is to be removed  */
7963                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7964                                 }
7965                         }
7966                 }
7967         } else dst_par_g = remove_par_g = NULL;
7968         
7969         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7970             src anc dst  */
7971         if (moveFlag) {
7972                 Int ibufsize, *ibuf, flag_i, flag_j;
7973                 ibufsize = 8;
7974                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7975                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7976                         if (ap->anchor == NULL)
7977                                 continue;
7978                         flag_i = (old2new[i] < nsrcnew);
7979                         cp = AtomConnectData(&ap->anchor->connect);
7980                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7981                                 flag_j = (old2new[cp[j]] < nsrcnew);
7982                                 if (flag_i == flag_j) {
7983                                         if (n1 >= ibufsize) {
7984                                                 ibufsize += 8;
7985                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7986                                         }
7987                                         ibuf[n1++] = cp[j];
7988                                 }
7989                         }
7990                         if (n1 < j) {
7991                                 /*  Need to modify the pi anchor list  */
7992                                 if (n1 <= 1)
7993                                         n1 = 0;
7994                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7995                         }
7996                 }
7997         }
7998         
7999         /*  Make a new molecule  */
8000         if (dstp != NULL) {
8001                 dst = MoleculeNew();
8002                 if (dst == NULL)
8003                         goto panic;
8004                 /*  Expand the destination array  */
8005                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8006                         goto panic;
8007                 dst_ap = dst->atoms;
8008         } else {
8009                 dst = NULL;
8010                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8011                 if (dst_ap == NULL)
8012                         goto panic;
8013         }
8014         
8015         /*  Move the atoms  */
8016         if (moveFlag) {
8017                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8018                         goto panic;
8019                 src->natoms = nsrcnew;
8020                 if (dst == NULL) {
8021                         /*  The atom record must be deallocated correctly  */
8022                         for (i = 0; i < ndst; i++)
8023                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8024                 }
8025         } else {
8026                 if (dst != NULL) {
8027                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8028                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8029                 }
8030         }
8031         
8032         if (dst == NULL) {
8033                 /*  The dummy destination array is no longer needed  */
8034                 free(dst_ap);
8035                 dst_ap = NULL;
8036         }
8037         
8038         /*  Renumber the atom indices in connect[] (src) */
8039         if (moveFlag) {
8040                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8041                         cp = AtomConnectData(&ap->connect);
8042                         for (j = n1 = 0; j < ap->connect.count; j++) {
8043                                 n2 = old2new[cp[j]];
8044                                 if (n2 < nsrcnew)
8045                                         cp[n1++] = n2;
8046                         }
8047                         AtomConnectResize(&ap->connect, n1);
8048                         if (ap->anchor != NULL) {
8049                                 cp = AtomConnectData(&ap->anchor->connect);
8050                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8051                                         n2 = old2new[cp[j]];
8052                                         if (n2 < nsrcnew)
8053                                                 cp[n1++] = n2;
8054                                 }
8055                                 if (n1 != ap->anchor->connect.count) {
8056                                         /*  This should not happen!!  */
8057                                         AtomConnectResize(&ap->anchor->connect, n1);
8058                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8059                                         if (n1 == 0) {
8060                                                 free(ap->anchor->coeffs);
8061                                                 free(ap->anchor);
8062                                                 ap->anchor = NULL;
8063                                         }
8064                                 }
8065                         }
8066                 }
8067         }
8068         
8069         /*  Renumber the atom indices in connect[] (dst)  */
8070         if (dst != NULL) {
8071                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8072                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8073                                 ap->resSeq -= resSeqOffset;
8074                         else ap->resSeq = 0;
8075                         cp = AtomConnectData(&ap->connect);
8076                         for (j = n1 = 0; j < ap->connect.count; j++) {
8077                                 n2 = old2new[cp[j]] - nsrcnew;
8078                                 if (n2 >= 0)
8079                                         cp[n1++] = n2;
8080                         }
8081                         AtomConnectResize(&ap->connect, n1);
8082                         if (ap->anchor != NULL) {
8083                                 cp = AtomConnectData(&ap->anchor->connect);
8084                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8085                                         n2 = old2new[cp[j]] - nsrcnew;
8086                                         if (n2 >= 0)
8087                                                 cp[n1++] = n2;
8088                                 }
8089                                 if (n1 != ap->anchor->connect.count) {
8090                                         /*  This can happen, and the anchor info is silently modified  */
8091                                         if (n1 <= 1) {
8092                                                 AtomConnectResize(&ap->anchor->connect, 0);
8093                                                 free(ap->anchor->coeffs);
8094                                                 free(ap->anchor);
8095                                                 ap->anchor = NULL;
8096                                         } else {
8097                                                 Double d;
8098                                                 AtomConnectResize(&ap->anchor->connect, n1);
8099                                                 d = 0.0;
8100                                                 for (j = 0; j < n1; j++)
8101                                                         d += ap->anchor->coeffs[j];
8102                                                 for (j = 0; j < n1; j++)
8103                                                         ap->anchor->coeffs[j] /= d;
8104                                                 MoleculeCalculatePiAnchorPosition(dst, i);
8105                                         }
8106                                 }
8107                         }
8108                 }
8109         }
8110
8111         /*  Separate the bonds, angles, dihedrals, impropers  */
8112         /*  TODO: Improper torsions should also be copied!  */
8113         move_g = IntGroupNew();
8114         if (move_g == NULL)
8115                 goto panic;
8116         for (i = 3; i >= 0; i--) {
8117                 Int *nitems, *nitems_dst;
8118                 Int **items, **items_dst;
8119                 Int nsize;  /*  Number of Ints in one element  */
8120                 unsigned char *counts;
8121                 del_g = IntGroupNew();
8122                 switch (i) {
8123                         case 0:
8124                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8125                         case 1:
8126                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8127                         case 2:
8128                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8129                         case 3:
8130                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8131                         default:
8132                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
8133                 }
8134                 if (dst != NULL) {
8135                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8136                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8137                 } else {
8138                         nitems_dst = NULL;
8139                         items_dst = NULL;
8140                 }
8141                 counts = (unsigned char *)calloc(1, *nitems);
8142                 /*  Find the entries that should be moved to dst  */
8143                 n2 = 0;
8144                 for (j = 0; j < *nitems * nsize; j++) {
8145                         n1 = old2new[(*items)[j]];
8146                         if (n1 >= nsrcnew)
8147                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
8148                 }
8149                 for (j = n2 = n3 = 0; j < *nitems; j++) {
8150                         if (counts[j] > 0) {
8151                                 /*  Remove from src  */
8152                                 n2++;
8153                                 if (IntGroupAdd(del_g, j, 1) != 0)
8154                                         goto panic;
8155                                 if (counts[j] == nsize) {
8156                                         /*  Move to dst  */
8157                                         n3++;
8158                                         if (IntGroupAdd(move_g, j, 1) != 0)
8159                                                 goto panic;
8160                                 }
8161                         }
8162                 }
8163                 if (n2 > 0) {
8164                         /*  Expand the destination array  */
8165                         if (items_dst != NULL && n3 > 0) {
8166                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8167                                         goto panic;
8168                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8169                                         goto panic;
8170                                 if (i == 0 && src->bondOrders != NULL) {
8171                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8172                                                 goto panic;
8173                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8174                                                 goto panic;
8175                                 }
8176                         }
8177                         /*  Remove from src  */
8178                         if (moveFlag && forUndo == 0) {
8179                                 if (nactions != NULL) {
8180                                         Int k, *ip;
8181                                         Double *dp;
8182                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8183                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8184                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8185                                         if (i == 0 && src->bondOrders != NULL) {
8186                                                 dp = (Double *)malloc(sizeof(Double) * n2);
8187                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8188                                                         dp[j] = src->bondOrders[k];
8189                                         } else dp = NULL;
8190                                         switch (i) {
8191                                                 case 0:
8192                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8193                                                 case 1:
8194                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8195                                                 case 2:
8196                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8197                                                 case 3:
8198                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8199                                         }
8200                                         if (act != NULL) {
8201                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8202                                                 act = NULL;
8203                                         }
8204                                         free(ip);
8205                                         if (dp != NULL) {
8206                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8207                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8208                                                 act = NULL;
8209                                                 free(dp);
8210                                         }
8211                                 }
8212                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8213                                         goto panic;
8214                                 (*nitems) -= n2;
8215                         }
8216                 }
8217                 /*  Renumber the entries  */
8218                 if (moveFlag) {
8219                         for (j = 0; j < *nitems * nsize; j++) {
8220                                 (*items)[j] = old2new[(*items)[j]];
8221                         }
8222                 }
8223                 if (items_dst != NULL) {
8224                         for (j = 0; j < *nitems_dst * nsize; j++) {
8225                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8226                         }
8227                 }
8228                 free(counts);
8229                 IntGroupClear(move_g);
8230                 IntGroupRelease(del_g);
8231         }
8232         IntGroupRelease(move_g);
8233         
8234         /*  Copy the residues  */
8235         if (dst != NULL) {
8236                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
8237                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
8238                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8239                         goto panic;
8240                 if (n1 > 1) {
8241                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8242                 }
8243         }
8244
8245         /*  Copy the parameters to dst */
8246         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8247                 IntGroup *dst_new_g = IntGroupNew();
8248                 Int dst_par_count[kLastParType - kFirstParType + 1];
8249                 if (dst_new_g == NULL)
8250                         goto panic;
8251                 for (i = 0; i <= kLastParType - kFirstParType; i++)
8252                         dst_par_count[i] = 0;
8253                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8254                 if (up == NULL)
8255                         goto panic;
8256                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8257                         goto panic;
8258                 /*  Renumber the explicit atom indices  */
8259                 for (i = 0; i < nsrc; i++)
8260                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
8261                 for (i = 0; i < n2; i++) {
8262                         /*  Renumber the indices, and count the number of parameters for each type  */
8263                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8264                         dst_par_count[n1 - kFirstParType]++;
8265                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8266                 }
8267                 for (i = 0; i < nsrc; i++)
8268                         old2new[i] += nsrcnew;
8269                 if (dst->par == NULL)
8270                         dst->par = ParameterNew();
8271                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8272                         if (dst_par_count[i] > 0)
8273                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8274                 }
8275                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8276                         goto panic;
8277                 free(up);
8278                 IntGroupRelease(dst_new_g);
8279         }
8280         IntGroupRelease(dst_par_g);
8281
8282         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
8283             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
8284         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8285                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8286                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8287                 if (nactions != NULL) {
8288                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8289                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8290                         act = NULL;
8291                 }
8292                 free(up);
8293         }
8294         IntGroupRelease(remove_par_g);
8295         
8296         /*  Renumber the parameter records remaining in the src  */
8297         if (moveFlag) {
8298                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8299                         n2 = ParameterGetCountForType(src->par, n1);
8300                         for (i = 0; i < n2; i++) {
8301                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8302                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8303                         }
8304                 }
8305         }
8306
8307         /*  Clean up  */
8308         IntGroupRelease(remain_g);
8309         MoleculeCleanUpResidueTable(src);
8310         if (dst != NULL)
8311                 MoleculeCleanUpResidueTable(dst);
8312         free(new2old);
8313
8314         src->nframes = -1;  /*  Should be recalculated later  */
8315         if (dst != NULL)
8316                 dst->nframes = -1;  /*  Should be recalculated later  */
8317
8318         
8319         if (dstp != NULL)
8320                 *dstp = dst;
8321
8322         MoleculeIncrementModifyCount(src);
8323         src->needsMDRebuild = 1;
8324         __MoleculeUnlock(src);
8325         
8326         return 0;
8327
8328   panic:
8329         __MoleculeUnlock(src);
8330 /*    Panic("Low memory while removing atoms"); */
8331         return -1;
8332 }
8333
8334 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
8335     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
8336         in which case the moved atoms are discarded.  */
8337 int
8338 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8339 {
8340         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8341 }
8342
8343 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
8344         'where' are copied from src to a new molecule, which is returned as *dstp.
8345     If dummyFlag is non-zero, then the atoms that are not included in the group 
8346         but are connected to any atoms in the group are converted to "dummy" atoms 
8347         (i.e. with element "Du" and names beginning with an underscore) and included 
8348         in the new molecule object.  */
8349 int
8350 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8351 {
8352         int retval;
8353
8354         /*  Extract the fragment  */
8355         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8356         if (retval != 0)
8357                 return retval;
8358
8359         if (dummyFlag) {
8360
8361                 /*  Search bonds crossing the molecule border  */
8362                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8363                 if (ig != NULL) {
8364                         IntGroupIterator iter;
8365                         Int i, idx;
8366                         idx = 1;
8367                         IntGroupIteratorInit(ig, &iter);
8368                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8369                                 /*  The atoms at the border  */
8370                                 Int n1, n2, nn[3];
8371                                 Atom a, *ap;
8372                                 n1 = src->bonds[i*2];
8373                                 n2 = src->bonds[i*2+1];
8374                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8375                                         int w = n1;
8376                                         n1 = n2;
8377                                         n2 = w;
8378                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8379                                                 continue;  /*  Actually this is an internal error  */
8380                                 }
8381                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
8382                                 /*  Create a new dummy atom with the same segment/residue info with n1
8383                                     and the same position as n2  */
8384                                 ap = ATOM_AT_INDEX(src->atoms, n1);
8385                                 memset(&a, 0, gSizeOfAtomRecord);
8386                                 a.segSeq = ap->segSeq;
8387                                 memmove(a.segName, ap->segName, 4);
8388                                 a.resSeq = ap->resSeq;
8389                                 memmove(a.resName, ap->resName, 4);
8390                                 ElementToString(0, a.element);  /*  "Du"  */
8391                                 snprintf(a.aname, 4, "_%d", idx++);
8392                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8393                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
8394                                     of the new dummy atom in the new molecule  */
8395                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8396                                 /*  Connect nn1 and nn2  */
8397                                 nn[2] = kInvalidIndex;
8398                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8399                         }
8400                         IntGroupIteratorRelease(&iter);
8401                         IntGroupRelease(ig);
8402                 }
8403         }
8404         
8405         return 0;
8406 }
8407
8408 int
8409 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8410 {
8411         Int nangles, ndihedrals;
8412         Int *angles, *dihedrals;
8413         Int i, j, k, kk, n1, n2, cn1, cn2;
8414         Int *cp1, *cp2;
8415         Int temp[4];
8416         Atom *ap1, *ap2, *ap3;
8417         
8418         if (mp == NULL || bonds == NULL || nbonds <= 0)
8419                 return 0;
8420         if (mp->noModifyTopology)
8421                 return -4;  /*  Prohibited operation  */
8422
8423         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
8424
8425         __MoleculeLock(mp);
8426
8427         n1 = mp->nbonds;
8428         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8429                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8430                 __MoleculeUnlock(mp);
8431                 return -4;  /*  Out of memory  */
8432         }
8433         if (mp->bondOrders != NULL) {
8434                 /*  Expand the bond order info (all new entries are zero)  */
8435                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8436                 if (dp == NULL)
8437                         return -4;
8438                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8439                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8440                         __MoleculeUnlock(mp);
8441                         free(dp);
8442                         return -4;
8443                 }
8444                 free(dp);
8445         }
8446         
8447         angles = dihedrals = NULL;
8448         nangles = ndihedrals = 0;
8449         
8450         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
8451         for (i = 0; i < nbonds; i++) {
8452                 
8453                 /*  One entry at time  */
8454                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
8455                 n1 = bonds[i * 2];
8456                 n2 = bonds[i * 2 + 1];
8457                 
8458                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8459                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8460                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8461                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8462         
8463                 /*  Add angles and dihedrals  */
8464                 if (autoGenerate) {
8465                         AtomConnect *ac1, *ac2;
8466                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
8467                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
8468                                 for (j = 0; j < 4; j++) {
8469                                         switch (j) {
8470                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
8471                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8472                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
8473                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8474                                         }
8475                                         cp1 = AtomConnectData(ac1);
8476                                         cn1 = ac1->count;
8477                                         for (k = 0; k < cn1; k++) {
8478                                                 temp[2] = cp1[k];
8479                                                 if (temp[2] == temp[0])
8480                                                         continue;
8481                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8482                                                 if (ap3->anchor != NULL) {
8483                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
8484                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8485                                                                 continue;
8486                                                 }
8487                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8488                                                         goto panic;
8489                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
8490                                                 if (j == 1 || j == 3)
8491                                                         continue;
8492                                                 cp2 = AtomConnectData(&ap3->connect);
8493                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
8494                                                         temp[3] = cp2[kk];
8495                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8496                                                                 continue;
8497                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8498                                                                 goto panic;
8499                                                 }
8500                                                 if (ap3->anchor != NULL) {
8501                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
8502                                                         /*  for Y, only the first constitute atom is considered  */
8503                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
8504                                                         temp[3] = cp2[0];
8505                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8506                                                                 continue;
8507                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8508                                                                 goto panic;
8509                                                 }
8510                                         }
8511                                 }
8512                         }
8513                         /*  X-N1-N2-X dihedrals  */
8514                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8515                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
8516                         if (ap1->anchor == NULL) {
8517                                 ac1 = &ap1->connect;
8518                                 cn1 = ac1->count;
8519                         } else {
8520                                 ac1 = &ap1->anchor->connect;
8521                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8522                         }
8523                         if (ap2->anchor == NULL) {
8524                                 ac2 = &ap2->connect;
8525                                 cn2 = ac2->count;
8526                         } else {
8527                                 ac2 = &ap2->anchor->connect;
8528                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8529                         }
8530                         temp[1] = n1;
8531                         temp[2] = n2;
8532                         cp1 = AtomConnectData(ac1);
8533                         cp2 = AtomConnectData(ac2);
8534                         for (j = 0; j < cn1; j++) {
8535                                 temp[0] = cp1[j];
8536                                 if (temp[0] == temp[2])
8537                                         continue;
8538                                 for (k = 0; k < cn2; k++) {
8539                                         temp[3] = cp2[k];
8540                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8541                                                 continue;
8542                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8543                                                 goto panic;
8544                                 }
8545                         }
8546                 }
8547         }
8548         
8549         if (angles != NULL) {
8550                 temp[0] = kInvalidIndex;
8551                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8552                         goto panic;
8553                 MoleculeAddAngles(mp, angles, NULL);
8554                 free(angles);
8555         }
8556         if (dihedrals != NULL) {
8557                 temp[0] = kInvalidIndex;
8558                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8559                         goto panic;
8560                 MoleculeAddDihedrals(mp, dihedrals, NULL);
8561                 free(dihedrals);
8562         }
8563
8564         MoleculeIncrementModifyCount(mp);
8565         mp->needsMDRebuild = 1;
8566         __MoleculeUnlock(mp);
8567
8568         return nbonds;
8569
8570   panic:
8571         __MoleculeUnlock(mp);
8572         Panic("Low memory while adding bonds");
8573         return -1;  /*  Not reached  */
8574 }
8575
8576 /*  Delete bonds  */
8577 /*  The deleted angles and dihedrals are stored in outRemoval.  */
8578 /*  (*outRemoval) is an array of integers, containing:
8579       [0..na*3-1]: the angle indices
8580       [na*3..na*3+nd*4-1]: the dihedral indices
8581           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8582     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8583           the angle indices are included as they are,
8584       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8585       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8586     Note: the removed bond indices are not returned, because the caller should already know them.  */
8587 int
8588 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8589 {
8590         Int i, j, n1, n2, nw;
8591         Int *ip, *jp, na, nd, ni;
8592         IntGroup *ag, *dg, *ig;
8593         Atom *ap;
8594         IntGroupIterator iter;
8595
8596         if (mp == NULL)
8597                 return 0;
8598         if (mp->noModifyTopology)
8599                 return -4;  /*  Prohibited operation  */
8600
8601         __MoleculeLock(mp);
8602
8603         /*  Update connects[]  */
8604         IntGroupIteratorInit(where, &iter);
8605         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8606                 n1 = mp->bonds[i * 2];
8607                 n2 = mp->bonds[i * 2 + 1];
8608                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8609                 ip = AtomConnectData(&ap->connect);
8610                 for (j = 0; j < ap->connect.count; j++) {
8611                         if (ip[j] == n2) {
8612                                 AtomConnectDeleteEntry(&ap->connect, j);
8613                                 break;
8614                         }
8615                 }
8616                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8617                 ip = AtomConnectData(&ap->connect);
8618                 for (j = 0; j < ap->connect.count; j++) {
8619                         if (ip[j] == n1) {
8620                                 AtomConnectDeleteEntry(&ap->connect, j);
8621                                 break;
8622                         }
8623                 }
8624         }
8625         
8626         /*  Remove bonds, angles, dihedrals, impropers  */
8627         ag = IntGroupNew();
8628         dg = ig = NULL;
8629         na = nd = ni = 0;
8630         
8631         nw = IntGroupGetCount(where);
8632         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8633         j = 0;
8634         IntGroupIteratorReset(&iter);
8635         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8636                 jp[j++] = mp->bonds[i * 2];
8637                 jp[j++] = mp->bonds[i * 2 + 1];
8638         }
8639         IntGroupIteratorRelease(&iter);
8640
8641         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8642                 for (j = 0; j < nw; j++) {
8643                         n1 = jp[j * 2];
8644                         n2 = jp[j * 2 + 1];
8645                         if ((ip[0] == n1 && ip[1] == n2)
8646                                 || (ip[1] == n1 && ip[0] == n2)
8647                                 || (ip[1] == n1 && ip[2] == n2)
8648                                 || (ip[2] == n1 && ip[1] == n2)) {
8649                                 if (IntGroupAdd(ag, i, 1) != 0)
8650                                         goto panic;
8651                                 na++;
8652                                 break;
8653                         }
8654                 }
8655         }
8656         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8657                 for (j = 0; j < nw; j++) {
8658                         n1 = jp[j * 2];
8659                         n2 = jp[j * 2 + 1];
8660                         if ((ip[0] == n1 && ip[1] == n2)
8661                          || (ip[1] == n1 && ip[0] == n2)
8662                          || (ip[1] == n1 && ip[2] == n2)
8663                          || (ip[2] == n1 && ip[1] == n2)
8664                          || (ip[2] == n1 && ip[3] == n2)
8665                          || (ip[3] == n1 && ip[2] == n2)) {
8666                                 if (dg == NULL)
8667                                         dg = IntGroupNew();
8668                                 if (IntGroupAdd(dg, i, 1) != 0)
8669                                         goto panic;
8670                                 nd++;
8671                                 break;
8672                         }
8673                 }
8674         }
8675         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8676                 for (j = 0; j < nw; j++) {
8677                         n1 = jp[j * 2];
8678                         n2 = jp[j * 2 + 1];
8679                         if ((ip[0] == n1 && ip[2] == n2)
8680                          || (ip[1] == n1 && ip[2] == n2)
8681                          || (ip[3] == n1 && ip[2] == n2)
8682                          || (ip[0] == n2 && ip[2] == n1)
8683                          || (ip[1] == n2 && ip[2] == n1)
8684                          || (ip[3] == n2 && ip[2] == n1)) {
8685                                 if (ig == NULL)
8686                                         ig = IntGroupNew();
8687                                 if (IntGroupAdd(ig, i, 1) != 0)
8688                                         goto panic;
8689                                 ni++;
8690                                 break;
8691                         }
8692                 }
8693         }
8694         free(jp);
8695         
8696         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8697                 goto panic;
8698         mp->nbonds -= IntGroupGetCount(where);
8699         if (mp->nbonds == 0) {
8700                 free(mp->bonds);
8701                 mp->bonds = NULL;
8702         }
8703         if (mp->bondOrders != NULL) {
8704                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8705                         goto panic;
8706                 mp->nbondOrders -= IntGroupGetCount(where);
8707                 if (mp->nbondOrders == 0) {
8708                         free(mp->bondOrders);
8709                         mp->bondOrders = NULL;
8710                 }
8711         }
8712         if (na == 0 && nd == 0 && ni == 0)
8713                 ip = NULL;
8714         else
8715                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8716         if (na > 0)
8717                 MoleculeDeleteAngles(mp, ip, ag);
8718         if (nd > 0)
8719                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8720         if (ni > 0)
8721                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8722         if (ip != NULL) {
8723                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8724                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8725                 IntGroupAddIntGroup(ag, dg);
8726                 IntGroupAddIntGroup(ag, ig);
8727                 IntGroupRelease(dg);
8728                 IntGroupRelease(ig);
8729         }
8730
8731         if (IntGroupGetCount(ag) == 0) {
8732                 IntGroupRelease(ag);
8733                 ag = NULL;
8734         }
8735         
8736         *outRemoved = ip;
8737         *outRemovedPos = ag;
8738
8739         MoleculeIncrementModifyCount(mp);
8740         mp->needsMDRebuild = 1;
8741         __MoleculeUnlock(mp);
8742
8743         return na * 3 + nd * 4 + ni * 4;
8744
8745   panic:
8746         __MoleculeUnlock(mp);
8747         Panic("Low memory while removing bonds");
8748         return -1;  /*  Not reached  */
8749 }
8750
8751 int
8752 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8753 {
8754         Int i, j;
8755         IntGroupIterator iter;
8756         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8757                 return 0;
8758         if (mp->noModifyTopology)
8759                 return -4;  /*  Prohibited operation  */
8760         if (mp->bondOrders == NULL) {
8761                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8762                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8763         }
8764         IntGroupIteratorInit(where, &iter);
8765         j = 0;
8766         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8767                 if (i >= mp->nbondOrders)
8768                         break;
8769                 mp->bondOrders[i] = orders[j++];
8770         }
8771         IntGroupIteratorRelease(&iter);
8772         return 0;
8773 }
8774
8775 int
8776 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8777 {
8778         Int i, j;
8779         IntGroupIterator iter;
8780         if (mp == NULL || mp->nbonds == 0)
8781                 return 0;
8782         if (mp->bondOrders == NULL) {
8783                 /*  Returns all zero  */
8784                 i = IntGroupGetCount(where);
8785                 for (j = 0; j < i; j++)
8786                         outOrders[j] = 0.0;
8787         } else {
8788                 IntGroupIteratorInit(where, &iter);
8789                 j = 0;
8790                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8791                         if (i < mp->nbondOrders)
8792                                 outOrders[j] = mp->bondOrders[i];
8793                         else outOrders[j] = 0.0;
8794                         j++;
8795                 }
8796         }
8797         return 0;
8798 }
8799
8800 int
8801 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8802 {
8803         int n1, nc;
8804         if (mp == NULL || angles == NULL)
8805                 return 0;
8806         if (mp->noModifyTopology)
8807                 return -4;  /*  Prohibited operation  */
8808
8809         __MoleculeLock(mp);
8810         if (where != NULL)
8811                 nc = IntGroupGetCount(where);
8812         else {
8813                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8814                         ;
8815                 nc = n1;
8816         }
8817         if (nc > 0) {
8818                 n1 = mp->nangles;
8819                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8820                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8821                         __MoleculeUnlock(mp);
8822                         Panic("Low memory while adding angles");
8823                 }
8824         }
8825         mp->needsMDRebuild = 1;
8826         __MoleculeUnlock(mp);
8827         return nc;
8828 }
8829
8830 int
8831 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8832 {
8833         int nc;
8834         if (mp == NULL || where == NULL)
8835                 return 0;
8836         if (mp->noModifyTopology)
8837                 return -4;  /*  Prohibited operation  */
8838         __MoleculeLock(mp);
8839         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8840                 __MoleculeUnlock(mp);
8841                 Panic("Bad argument while deleting angles");
8842         }
8843         mp->nangles -= (nc = IntGroupGetCount(where));
8844         if (mp->nangles == 0) {
8845                 free(mp->angles);
8846                 mp->angles = NULL;
8847         }
8848         mp->needsMDRebuild = 1;
8849         __MoleculeUnlock(mp);
8850         return nc;
8851 }
8852
8853 int
8854 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8855 {
8856         int n1, nc;
8857         if (mp == NULL || dihedrals == NULL)
8858                 return 0;
8859         if (mp->noModifyTopology)
8860                 return -4;  /*  Prohibited operation  */
8861         if (where != NULL)
8862                 nc = IntGroupGetCount(where);
8863         else {
8864                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8865                         ;
8866                 nc = n1;
8867         }
8868         if (nc <= 0)
8869                 return 0;
8870         n1 = mp->ndihedrals;
8871         __MoleculeLock(mp);
8872         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8873         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8874                 __MoleculeUnlock(mp);
8875                 Panic("Low memory while adding dihedrals");
8876         }
8877         mp->needsMDRebuild = 1;
8878         __MoleculeUnlock(mp);
8879         return nc;
8880 }
8881
8882 int
8883 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8884 {       
8885         int nc;
8886         if (mp == NULL || where == NULL)
8887                 return 0;
8888         if (mp->noModifyTopology)
8889                 return -4;  /*  Prohibited operation  */
8890         __MoleculeLock(mp);
8891         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8892                 __MoleculeUnlock(mp);
8893                 Panic("Internal error: bad argument while deleting dihedrals");
8894         }
8895         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8896         if (mp->ndihedrals == 0) {
8897                 free(mp->dihedrals);
8898                 mp->dihedrals = NULL;
8899         }
8900         mp->needsMDRebuild = 1;
8901         __MoleculeUnlock(mp);
8902         return nc;
8903 }
8904
8905 int
8906 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8907 {
8908         int n1, nc;
8909         if (mp == NULL || impropers == NULL)
8910                 return 0;
8911         if (mp->noModifyTopology)
8912                 return -4;  /*  Prohibited operation  */
8913         if (where != NULL)
8914                 nc = IntGroupGetCount(where);
8915         else {
8916                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8917                         ;
8918                 nc = n1;
8919         }
8920         if (nc <= 0)
8921                 return 0;
8922         n1 = mp->nimpropers;
8923         __MoleculeLock(mp);
8924         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8925         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8926                 __MoleculeUnlock(mp);
8927                 Panic("Low memory while adding impropers");
8928         }
8929         mp->needsMDRebuild = 1;
8930         __MoleculeUnlock(mp);
8931         return nc;
8932 }
8933
8934 int
8935 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8936 {
8937         int nc;
8938         if (mp == NULL || where == NULL)
8939                 return 0;
8940         if (mp->noModifyTopology)
8941                 return -4;  /*  Prohibited operation  */
8942         __MoleculeLock(mp);
8943         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8944                 __MoleculeUnlock(mp);
8945                 Panic("Internal error: bad argument while deleting impropers");
8946         }
8947         mp->nimpropers -= (nc = IntGroupGetCount(where));
8948         if (mp->impropers == NULL) {
8949                 free(mp->impropers);
8950                 mp->impropers = NULL;
8951         }
8952         __MoleculeUnlock(mp);
8953         return nc;
8954 }
8955
8956 int
8957 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8958 {
8959         Int i, *ip;
8960         if (mp == NULL || mp->bonds == NULL)
8961                 return -1;
8962         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8963                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8964                         return i;
8965         }
8966         return -1;
8967 }
8968
8969 int
8970 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8971 {
8972         Int i, *ip;
8973         if (mp == NULL || mp->angles == NULL)
8974                 return -1;
8975         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8976                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8977                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8978                         return i;
8979         }
8980         return -1;
8981 }
8982
8983 int
8984 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8985 {
8986         Int i, *ip;
8987         if (mp == NULL || mp->dihedrals == NULL)
8988                 return -1;
8989         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8990                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8991                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8992                         return i;
8993         }
8994         return -1;
8995 }
8996
8997 int
8998 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8999 {
9000         Int i, *ip;
9001         if (mp == NULL || mp->impropers == NULL)
9002                 return -1;
9003         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9004                 if (n3 != ip[2])
9005                         continue;
9006                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9007                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9008                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9009                         return i;
9010         }
9011         return -1;
9012 }
9013
9014 /*  Remove the bond at bondIndex and create two dummy atoms instead.
9015     The dummy atoms are placed at the end of atoms[], and the residue
9016         numbers are the same as the root atoms (i.e. the atoms to which
9017         the dummy atoms are connected). The indices are returned in
9018         dummyIndices[0,1].  */
9019 int
9020 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9021 {
9022         Int roots[3], newBonds[5];
9023         Vector dr;
9024         Atom *rootp[2];
9025         Atom na[2], *nap;
9026         int i, natoms;
9027         IntGroup *ig;
9028         if (mp == NULL || mp->noModifyTopology)
9029                 return 0;
9030         if (bondIndex < 0 || bondIndex >= mp->nbonds)
9031                 return -1;
9032         roots[0] = mp->bonds[bondIndex * 2];
9033         roots[1] = mp->bonds[bondIndex * 2 + 1];
9034         roots[2] = kInvalidIndex;
9035         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9036         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9037         VecSub(dr, rootp[0]->r, rootp[1]->r);
9038         for (i = 0; i < 2; i++) {
9039                 float w;
9040                 nap = &na[i];
9041                 memmove(nap, rootp[i], sizeof(na));
9042                 nap->aname[0] = '*';
9043                 strcpy(nap->element, "Du");
9044                 nap->type = 0;
9045                 nap->charge = nap->weight = 0.0;
9046                 nap->atomicNumber = 0;
9047                 nap->connect.count = 0;
9048                 w = (i == 0 ? 0.4 : -0.4);
9049                 VecScaleInc(nap->r, dr, w);
9050                 VecZero(nap->v);
9051                 VecZero(nap->f);
9052                 nap->intCharge = 0;
9053                 nap->exflags = 0;
9054         }
9055
9056         /*  Expand atoms array and append the dummy atoms at the end  */
9057         __MoleculeLock(mp);
9058         natoms = mp->natoms;
9059         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9060                 goto panic;
9061         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9062         dummyIndices[0] = natoms;
9063         dummyIndices[1] = natoms + 1;
9064
9065         /*  Remove the old bond and create new bonds  */
9066         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9067         if (ig == NULL)
9068                 goto panic;
9069         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9070         IntGroupRelease(ig);
9071         newBonds[0] = roots[0];
9072         newBonds[1] = dummyIndices[0];
9073         newBonds[2] = roots[1];
9074         newBonds[3] = dummyIndices[1];
9075         newBonds[4] = kInvalidIndex;
9076         
9077         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9078         mp->needsMDRebuild = 1;
9079         __MoleculeUnlock(mp);
9080         return i;
9081
9082 panic:
9083         __MoleculeUnlock(mp);
9084         Panic("Low memory during creating dummy atoms");
9085         return 1;
9086 }
9087
9088 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9089     a bond between the two root atoms. The value bondIndex is used as a
9090         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9091         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9092         is ignored and the new bond is stored at the end of bonds[].  */
9093 int
9094 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9095 {
9096         return 0;
9097 }
9098
9099 /*
9100 Int
9101 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9102 {
9103         Int n1, *np1;
9104         if (mol == NULL || mol->noModifyTopology)
9105                 return -1;
9106         n1 = mol->nangles;
9107         np1 = mol->angles;
9108         mol->nangles = 0;
9109         mol->angles = NULL;
9110         if (nangles > 0) {
9111                 __MoleculeLock(mol);
9112                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9113                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9114                 mol->needsMDRebuild = 1;
9115                 __MoleculeUnlock(mol);
9116         }
9117         *outAngles = np1;
9118         return n1;
9119 }
9120                                                 
9121 Int
9122 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9123 {
9124         Int n1, *np1;
9125         if (mol == NULL || mol->noModifyTopology)
9126                 return -1;
9127         n1 = mol->ndihedrals;
9128         np1 = mol->dihedrals;
9129         mol->ndihedrals = 0;
9130         mol->dihedrals = NULL;
9131         if (ndihedrals > 0) {
9132                 __MoleculeLock(mol);
9133                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9134                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9135                 mol->needsMDRebuild = 1;
9136                 __MoleculeUnlock(mol);
9137         }
9138         *outDihedrals = np1;
9139         return n1;
9140 }
9141
9142 Int
9143 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9144 {
9145         Int n1, *np1;
9146         if (mol == NULL || mol->noModifyTopology)
9147                 return -1;
9148         n1 = mol->nimpropers;
9149         np1 = mol->impropers;
9150         mol->nimpropers = 0;
9151         mol->impropers = NULL;
9152         if (nimpropers > 0) {
9153                 __MoleculeLock(mol);
9154                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9155                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9156                 mol->needsMDRebuild = 1;
9157                 __MoleculeUnlock(mol);
9158         }
9159         *outImpropers = np1;
9160         return n1;
9161 }
9162 */
9163
9164 Int
9165 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9166 {
9167         Int i, j, k, *ip;
9168         Atom *ap;
9169         Int nangles;
9170         Int *angles;
9171         
9172         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9173                 return 0;  /*  molecule is empty  */
9174         if (mol->noModifyTopology)
9175                 return -1;
9176         nangles = 0;
9177         angles = NULL;
9178         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9179                 Int *cp = AtomConnectData(&ap->connect);
9180                 if (ap->anchor != NULL)
9181                         continue;
9182                 for (j = 0; j < ap->connect.count; j++) {
9183                         Int j0 = cp[j];
9184                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9185                                 continue;
9186                         for (k = j + 1; k < ap->connect.count; k++) {
9187                                 Int k0 = cp[k];
9188                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9189                                         continue;
9190                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9191                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9192                                         ip[0] = j0;
9193                                         ip[1] = i;
9194                                         ip[2] = k0;
9195                                 }
9196                         }
9197                 }
9198         }
9199         if (nangles > 0) {
9200                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9201                 ip[0] = -1;
9202                 nangles--;
9203         }
9204         if (outAngles != NULL)
9205                 *outAngles = angles;
9206         return nangles;
9207 }
9208
9209 Int
9210 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9211 {
9212         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9213         Atom *ap2, *ap3;
9214         Int ndihedrals;
9215         Int *dihedrals;
9216         
9217         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9218                 return 0;  /*  molecule is empty  */
9219         ndihedrals = 0;
9220         dihedrals = NULL;
9221         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9222                 Int i1, i3, i4, *ip;
9223                 if (ap2->anchor != NULL)
9224                         continue;
9225                 cp2 = AtomConnectData(&ap2->connect);
9226                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9227                         n3 = cp2[i3];
9228                         if (n2 > n3)
9229                                 continue;
9230                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9231                         if (ap3->anchor != NULL)
9232                                 continue;
9233                         cp3 = AtomConnectData(&ap3->connect);
9234                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
9235                                 n1 = cp2[i1];
9236                                 if (n1 == n3)
9237                                         continue;
9238                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9239                                         continue;
9240                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9241                                         n4 = cp3[i4];
9242                                         if (n2 == n4 || n1 == n4)
9243                                                 continue;
9244                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9245                                                 continue;
9246                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9247                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9248                                                 ip[0] = n1;
9249                                                 ip[1] = n2;
9250                                                 ip[2] = n3;
9251                                                 ip[3] = n4;
9252                                         }
9253                                 }
9254                         }
9255                 }
9256         }
9257         if (ndihedrals > 0) {
9258                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9259                 ip[0] = -1;
9260                 ndihedrals--;
9261         }
9262         if (outDihedrals != NULL)
9263                 *outDihedrals = dihedrals;
9264         return ndihedrals;
9265 }
9266
9267 Int
9268 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9269 {
9270         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9271         Parameter *par = mol->par;
9272         Atom *ap, *ap3;
9273         Int nimpropers;
9274         Int *impropers;
9275         
9276         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9277                 return 0;  /*  molecule is empty  */
9278         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9279                 return 0;  /*  No improper parameters are defined  */
9280         nimpropers = 0;
9281         impropers = NULL;
9282         ap = mol->atoms;
9283         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9284                 Int i1, i2, i4, found, *ip;
9285                 t3 = ap3->type;
9286                 cp = AtomConnectData(&ap3->connect);
9287                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9288                         n1 = cp[i1];
9289                         t1 = ATOM_AT_INDEX(ap, n1)->type;
9290                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9291                                 n2 = cp[i2];
9292                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
9293                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9294                                         n4 = cp[i4];
9295                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
9296                                         found = 0;
9297                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9298                                                 found = 1;
9299                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9300                                                 found = 1;
9301                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9302                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9303                                                 ip[0] = n1;
9304                                                 ip[1] = n2;
9305                                                 ip[2] = n3;
9306                                                 ip[3] = n4;
9307                                         }
9308                                 }
9309                         }
9310                 }
9311         }
9312         if (nimpropers > 0) {
9313                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9314                 ip[0] = -1;
9315                 nimpropers--;
9316         }
9317         if (outImpropers != NULL)
9318                 *outImpropers = impropers;
9319         return nimpropers;
9320 }
9321
9322 #pragma mark ====== Residues ======
9323
9324 void
9325 MoleculeCleanUpResidueTable(Molecule *mp)
9326 {
9327         int i, maxres;
9328         Atom *ap;
9329         if (mp == NULL || mp->natoms == 0)
9330                 return;
9331         maxres = 0;
9332         __MoleculeLock(mp);
9333         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9334                 if (ap->resSeq >= maxres)
9335                         maxres = ap->resSeq + 1;
9336                 if (ap->resSeq < mp->nresidues) {
9337                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9338                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9339                 } else {
9340                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9341                 }
9342         }
9343         if (maxres < mp->nresidues)
9344                 mp->nresidues = maxres;
9345         __MoleculeUnlock(mp);
9346 }
9347
9348 /*  Change the number of residues. If nresidues is greater than the current value,
9349     then the array mp->residues is expanded with null names. If nresidues is smaller
9350         than the current value, mp->nresidues is set to the smallest possible value
9351         that is no smaller than nresidues and larger than any of the resSeq values.  */
9352 int
9353 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9354 {
9355         int n;
9356         if (mp == NULL)
9357                 return 0;
9358         if (mp->nresidues == nresidues)
9359                 return nresidues;
9360         else if (mp->nresidues < nresidues) {
9361                 __MoleculeLock(mp);
9362                 n = mp->nresidues;
9363                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9364                 while (n < nresidues)
9365                         mp->residues[n++][0] = 0;
9366                 __MoleculeUnlock(mp);
9367                 return nresidues;
9368         } else {
9369                 int i;
9370                 Atom *ap;
9371                 n = nresidues;
9372                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9373                         if (ap->resSeq >= n)
9374                                 n = ap->resSeq + 1;
9375                 }
9376                 mp->nresidues = n;
9377                 return n;
9378         }
9379 }
9380
9381 int
9382 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9383 {
9384         IntGroupIterator iter;
9385         int withArray, resSeq, maxSeq;
9386         int i, j;
9387         Atom *ap;
9388         
9389         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
9390         if (((uintptr_t)resSeqs & 1) == 0) {
9391                 withArray = 1;
9392                 resSeq = 0;
9393         } else {
9394                 withArray = 0;
9395                 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9396         }
9397         
9398         IntGroupIteratorInit(group, &iter);
9399
9400         /*  Change resSeqs  */
9401         maxSeq = 0;
9402         j = 0;
9403         __MoleculeLock(mp);
9404         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9405                 ap = ATOM_AT_INDEX(mp->atoms, i);
9406                 if (withArray)
9407                         resSeq = resSeqs[j++];
9408                 if (resSeq > maxSeq)
9409                         maxSeq = resSeq;
9410                 ap->resSeq = resSeq;
9411         }
9412         __MoleculeUnlock(mp);
9413
9414         /*  Expand array if necessary  */
9415         if (maxSeq >= mp->nresidues)
9416                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9417
9418         /*  Synchronize resName and residues[]  */
9419         __MoleculeLock(mp);
9420         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9421                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9422                         continue;
9423                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9424         }
9425         IntGroupIteratorRelease(&iter);
9426         __MoleculeUnlock(mp);
9427         
9428         MoleculeIncrementModifyCount(mp);
9429         
9430         return 0;
9431 }
9432
9433 int
9434 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9435 {
9436         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9437 }
9438
9439 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9440     specifies the mp->nresidues after modifying the residue numbers.
9441         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9442         the table of residue names is not touched. */
9443 int
9444 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9445 {
9446         int i, maxSeq, nmodatoms;
9447         Atom *ap;
9448         IntGroupIterator iter;
9449         IntGroupIteratorInit(group, &iter);
9450         maxSeq = 0;
9451         if (nresidues < 0)
9452                 nresidues = mp->nresidues;
9453         nmodatoms = 0;
9454         __MoleculeLock(mp);
9455         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9456                 ap = ATOM_AT_INDEX(mp->atoms, i);
9457                 ap->resSeq += offset;
9458                 if (ap->resSeq < 0) {
9459                         /*  Bad argument; undo change and returns this index + 1  */
9460                         int bad_index = i;
9461                         ap->resSeq -= offset;
9462                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9463                                 ap = ATOM_AT_INDEX(mp->atoms, i);
9464                                 ap->resSeq -= offset;
9465                         }
9466                         IntGroupIteratorRelease(&iter);
9467                         return bad_index + 1;
9468                 }
9469                 if (ap->resSeq > maxSeq)
9470                         maxSeq = ap->resSeq;
9471                 nmodatoms++;
9472         }
9473         if (maxSeq >= nresidues)
9474                 nresidues = maxSeq + 1;
9475         if (offset < 0 && nmodatoms == mp->natoms) {
9476                 /*  Shift the residue names downward  */
9477                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9478         }
9479         __MoleculeUnlock(mp);
9480         MoleculeChangeNumberOfResidues(mp, nresidues);
9481         if (offset > 0 && nmodatoms == mp->natoms) {
9482                 /*  Shift the residue names upward  */
9483                 __MoleculeLock(mp);
9484                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9485                 __MoleculeUnlock(mp);
9486         }
9487         IntGroupIteratorRelease(&iter);
9488
9489         MoleculeIncrementModifyCount(mp);
9490         
9491         return 0;
9492 }
9493
9494 /*  Change residue names for the specified residue numbers. Names is an array of
9495     chars containing argc*4 characters, and every 4 characters represent a
9496         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
9497         names to be handled as a C string.  */
9498 int
9499 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9500 {
9501         int i, maxSeq;
9502         Atom *ap;
9503         maxSeq = 0;
9504         for (i = 0; i < argc; i++) {
9505                 if (maxSeq < resSeqs[i])
9506                         maxSeq = resSeqs[i];
9507         }
9508         if (maxSeq >= mp->nresidues)
9509                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9510         __MoleculeLock(mp);
9511         for (i = 0; i < argc; i++) {
9512                 char *p = mp->residues[resSeqs[i]];
9513                 int j;
9514                 strncpy(p, names + i * 4, 4);
9515                 for (j = 0; j < 4; j++) {
9516                         if (p[j] >= 0 && p[j] < 0x20)
9517                                 p[j] = 0;
9518                 }
9519         }
9520         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9521                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9522         }
9523         __MoleculeUnlock(mp);
9524
9525         MoleculeIncrementModifyCount(mp);
9526         
9527         return 0;
9528 }
9529
9530 /*  Returns the maximum residue number actually used  */
9531 int
9532 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9533 {
9534         int i, maxSeq;
9535         Atom *ap;
9536         maxSeq = -1;
9537         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9538                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9539                         continue;
9540                 if (ap->resSeq > maxSeq)
9541                         maxSeq = ap->resSeq;
9542         }
9543         return maxSeq;
9544 }
9545
9546 /*  Returns the minimum residue number actually used  */
9547 int
9548 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9549 {
9550         int i, minSeq;
9551         Atom *ap;
9552         minSeq = ATOMS_MAX_NUMBER;
9553         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9554                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9555                         continue;
9556                 if (ap->resSeq < minSeq)
9557                         minSeq = ap->resSeq;
9558         }
9559         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9560 }
9561
9562 #pragma mark ====== Sort by Residues ======
9563
9564 static int
9565 sAtomSortComparator(const void *a, const void *b)
9566 {
9567         const Atom *ap, *bp;
9568         ap = *((const Atom **)a);
9569         bp = *((const Atom **)b);
9570         if (ap->resSeq == bp->resSeq) {
9571                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
9572                 if (ap < bp)
9573                         return -1;
9574                 else if (ap > bp)
9575                         return 1;
9576                 else return 0;
9577         } else {
9578                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
9579                 if (ap->resSeq == 0)
9580                         return 1;
9581                 else if (bp->resSeq == 0)
9582                         return -1;
9583                 else if (ap->resSeq < bp->resSeq)
9584                         return -1;
9585                 else if (ap->resSeq > bp->resSeq)
9586                         return 1;
9587                 else return 0;
9588         }
9589 }
9590
9591 static void
9592 sMoleculeReorder(Molecule *mp)
9593 {
9594         int i, res, prevRes;
9595         Atom **apArray;
9596         Int *old2new;
9597         Atom *newAtoms;
9598         if (mp == NULL || mp->natoms <= 1)
9599                 return;
9600
9601         /*  Sort the atoms, bonds, etc. */
9602         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9603         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9604         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9605         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9606                 Panic("Low memory during reordering atoms");
9607         for (i = 0; i < mp->natoms; i++)
9608                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9609
9610         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9611         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9612         
9613         /*  Make a table of 'which atom becomes which'  */
9614         for (i = 0; i < mp->natoms; i++) {
9615                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9616                 old2new[j] = i;
9617         }
9618         
9619         /*  Renumber the bonds, etc.  */
9620         for (i = 0; i < mp->nbonds * 2; i++) {
9621                 mp->bonds[i] = old2new[mp->bonds[i]];
9622         }
9623         for (i = 0; i < mp->nangles * 3; i++) {
9624                 mp->angles[i] = old2new[mp->angles[i]];
9625         }
9626         for (i = 0; i < mp->ndihedrals * 4; i++) {
9627                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9628         }
9629         for (i = 0; i < mp->nimpropers * 4; i++) {
9630                 mp->impropers[i] = old2new[mp->impropers[i]];
9631         }
9632         for (i = 0; i < mp->natoms; i++) {
9633                 Int *ip, j;
9634                 ip = AtomConnectData(&(apArray[i]->connect));
9635                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9636                         *ip = old2new[*ip];
9637         }
9638         
9639         /*  Renumber the residues so that the residue numbers are contiguous  */
9640         res = prevRes = 0;
9641         for (i = 0; i < mp->natoms; i++) {
9642                 if (apArray[i]->resSeq == 0)
9643                         break;
9644                 if (apArray[i]->resSeq != prevRes) {
9645                         res++;
9646                         prevRes = apArray[i]->resSeq;
9647                         if (prevRes != res) {
9648                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9649                         }
9650                 }
9651                 apArray[i]->resSeq = res;
9652         }
9653         mp->nresidues = res + 1;
9654
9655         /*  Sort the atoms and copy back to atoms[] */
9656         for (i = 0; i < mp->natoms; i++) {
9657                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9658         }
9659         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9660         
9661         /*  Free the locally allocated storage  */
9662         free(newAtoms);
9663         free(old2new);
9664         free(apArray);
9665 }
9666
9667 /*  Renumber atoms  */
9668 int
9669 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9670 {
9671         Int *old2new, i, j, retval;
9672         Atom *saveAtoms;
9673         if (mp == NULL)
9674                 return 0;
9675         if (mp->noModifyTopology)
9676                 return -1;
9677         if (old2new_out != NULL)
9678                 old2new = old2new_out;
9679         else
9680                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9681         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9682         if (old2new == NULL || saveAtoms == NULL)
9683                 Panic("Low memory during reordering atoms");
9684         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9685         __MoleculeLock(mp);
9686         for (i = 0; i < mp->natoms; i++)
9687                 old2new[i] = -1;
9688         for (i = 0; i < isize && i < mp->natoms; i++) {
9689                 j = new2old[i];
9690                 if (j < 0 || j >= mp->natoms) {
9691                         retval = 1; /* Out of range */
9692                         goto end;
9693                 }
9694                 if (old2new[j] != -1) {
9695                         retval = 2;  /*  Duplicate entry  */
9696                         goto end;
9697                 }
9698                 old2new[j] = i;
9699         }
9700         if (i < mp->natoms) {
9701                 for (j = 0; j < mp->natoms; j++) {
9702                         if (old2new[j] != -1)
9703                                 continue;
9704                         old2new[j] = i++;
9705                 }
9706         }
9707         if (i != mp->natoms) {
9708                 retval = 3;  /*  Internal inconsistency  */
9709                 goto end;
9710         }
9711
9712         /*  Renumber the bonds, etc.  */
9713         for (i = 0; i < mp->nbonds * 2; i++) {
9714                 mp->bonds[i] = old2new[mp->bonds[i]];
9715         }
9716         for (i = 0; i < mp->nangles * 3; i++) {
9717                 mp->angles[i] = old2new[mp->angles[i]];
9718         }
9719         for (i = 0; i < mp->ndihedrals * 4; i++) {
9720                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9721         }
9722         for (i = 0; i < mp->nimpropers * 4; i++) {
9723                 mp->impropers[i] = old2new[mp->impropers[i]];
9724         }
9725         /*  Renumber the connection table and pi anchor table  */
9726         for (i = 0; i < mp->natoms; i++) {
9727                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9728                 Int *ip = AtomConnectData(&ap->connect);
9729                 for (j = 0; j < ap->connect.count; j++, ip++)
9730                         *ip = old2new[*ip];
9731                 if (ap->anchor != NULL) {
9732                         ip = AtomConnectData(&ap->anchor->connect);
9733                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9734                                 *ip = old2new[*ip];
9735                 }
9736         }
9737         
9738         if (mp->par != NULL) {
9739                 /*  Renumber the parameters  */
9740                 int n;
9741                 for (j = kFirstParType; j <= kLastParType; j++) {
9742                         n = ParameterGetCountForType(mp->par, j);
9743                         for (i = 0; i < n; i++) {
9744                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9745                                 if (up != NULL)
9746                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9747                         }
9748                 }
9749         }
9750         
9751         /*  Renumber the atoms  */
9752         for (i = 0; i < mp->natoms; i++)
9753                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9754         retval = 0;
9755         
9756         MoleculeIncrementModifyCount(mp);
9757         mp->needsMDRebuild = 1;
9758
9759   end:
9760         __MoleculeUnlock(mp);
9761         free(saveAtoms);
9762         if (old2new_out == NULL)
9763                 free(old2new);
9764         return retval;
9765 }
9766
9767 #pragma mark ====== Coordinate Transform ======
9768
9769 void
9770 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9771 {
9772         int i;
9773         Atom *ap;
9774         Symop new_symop;
9775         Transform rtr, symtr;
9776         if (mp == NULL || tr == NULL)
9777                 return;
9778         TransformInvert(rtr, tr);
9779         __MoleculeLock(mp);
9780         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9781                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9782                         TransformVec(&ap->r, tr, &ap->r);
9783                         if (!SYMOP_ALIVE(ap->symop))
9784                                 continue;
9785                         /*  Transform symop  */
9786                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9787                                 continue;
9788                         TransformMul(symtr, tr, symtr);
9789                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9790                                 TransformMul(symtr, symtr, rtr);
9791                 } else {
9792                         if (!SYMOP_ALIVE(ap->symop))
9793                                 continue;
9794                         /*  Transform symop if the base atom is transformed  */
9795                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9796                                 continue;
9797                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9798                                 continue;
9799                         TransformMul(symtr, symtr, rtr);
9800                 }
9801                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9802                         continue;
9803                 ap->symop = new_symop;
9804         }
9805         mp->needsMDCopyCoordinates = 1;
9806         __MoleculeUnlock(mp);
9807         sMoleculeNotifyChangeAppearance(mp);
9808 }
9809
9810 /*
9811 void
9812 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9813 {
9814         int i;
9815         Atom *ap;
9816         if (mp == NULL || tr == NULL)
9817                 return;
9818         __MoleculeLock(mp);
9819         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9820                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9821                         continue;
9822                 TransformVec(&ap->r, tr, &ap->r);
9823         }
9824         mp->needsMDCopyCoordinates = 1;
9825         __MoleculeUnlock(mp);
9826         sMoleculeNotifyChangeAppearance(mp);
9827 }
9828 */
9829
9830 void
9831 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9832 {
9833         Transform tr;
9834         if (mp == NULL || vp == NULL)
9835                 return;
9836         memset(tr, 0, sizeof(tr));
9837         tr[0] = tr[4] = tr[8] = 1.0;
9838         tr[9] = vp->x;
9839         tr[10] = vp->y;
9840         tr[11] = vp->z;
9841         MoleculeTransform(mp, tr, group);
9842 }
9843
9844 void
9845 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9846 {
9847         Transform tr;
9848         TransformForRotation(tr, axis, angle, center);
9849         MoleculeTransform(mp, tr, group);
9850 }
9851
9852 int
9853 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9854 {
9855         int i;
9856         Atom *ap;
9857         Double w;
9858         if (mp == NULL || center == NULL)
9859                 return 1;
9860         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9861                 return 2;   /*  Empty molecule  */
9862         w = 0.0;
9863         center->x = center->y = center->z = 0.0;
9864         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9865                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9866                         continue;
9867                 VecScaleInc(*center, ap->r, ap->weight);
9868                 w += ap->weight;
9869         }
9870         if (w < 1e-7)
9871                 return 3;  /*  Atomic weights are not defined?  */
9872         w = 1.0 / w;
9873         VecScaleSelf(*center, w);
9874         return 0;
9875 }
9876
9877 int
9878 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9879 {
9880         Vector vmin, vmax;
9881         int i;
9882         Atom *ap;
9883         if (mp == NULL)
9884                 return 1;
9885         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9886                 return 2;   /*  Empty molecule  */
9887         vmin.x = vmin.y = vmin.z = 1e50;
9888         vmax.x = vmax.y = vmax.z = -1e50;
9889         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9890                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9891                         continue;
9892                 if (vmin.x > ap->r.x)
9893                         vmin.x = ap->r.x;
9894                 if (vmin.y > ap->r.y)
9895                         vmin.y = ap->r.y;
9896                 if (vmin.z > ap->r.z)
9897                         vmin.z = ap->r.z;
9898                 if (vmax.x < ap->r.x)
9899                         vmax.x = ap->r.x;
9900                 if (vmax.y < ap->r.y)
9901                         vmax.y = ap->r.y;
9902                 if (vmax.z < ap->r.z)
9903                         vmax.z = ap->r.z;
9904         }
9905         if (min != NULL)
9906                 *min = vmin;
9907         if (max != NULL)
9908                 *max = vmax;
9909         return 0;       
9910 }
9911
9912 #pragma mark ====== Measurements ======
9913
9914 Double
9915 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9916 {
9917         Vector r1, r2;
9918 /*      if (mp->is_xtal_coord) {
9919                 TransformVec(&r1, mp->cell->tr, vp1);
9920                 TransformVec(&r2, mp->cell->tr, vp2);
9921         } else */ {
9922                 r1 = *vp1;
9923                 r2 = *vp2;
9924         }
9925         VecDec(r1, r2);
9926         return VecLength(r1);
9927 }
9928
9929 Double
9930 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9931 {
9932         Vector r1, r2, r3;
9933         double w;
9934 /*      if (mp->is_xtal_coord) {
9935                 TransformVec(&r1, mp->cell->tr, vp1);
9936                 TransformVec(&r2, mp->cell->tr, vp2);
9937                 TransformVec(&r3, mp->cell->tr, vp3);
9938         } else */ {
9939                 r1 = *vp1;
9940                 r2 = *vp2;
9941                 r3 = *vp3;
9942         }
9943         VecDec(r1, r2);
9944         VecDec(r3, r2);
9945         w = VecLength(r1) * VecLength(r3);
9946         if (w < 1e-20)
9947                 return NAN;
9948         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9949 }
9950
9951 Double
9952 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9953 {
9954         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9955         double w1, w2, w3;
9956 /*      if (mp->is_xtal_coord) {
9957                 TransformVec(&r1, mp->cell->tr, vp1);
9958                 TransformVec(&r2, mp->cell->tr, vp2);
9959                 TransformVec(&r3, mp->cell->tr, vp3);
9960                 TransformVec(&r4, mp->cell->tr, vp4);
9961         } else */ {
9962                 r1 = *vp1;
9963                 r2 = *vp2;
9964                 r3 = *vp3;
9965                 r4 = *vp4;
9966         }
9967         VecSub(r21, r1, r2);
9968         VecSub(r32, r2, r3);
9969         VecSub(r43, r3, r4);
9970         VecCross(v1, r21, r32);
9971         VecCross(v2, r32, r43);
9972         VecCross(v3, r32, v1);
9973         w1 = VecLength(v1);
9974         w2 = VecLength(v2);
9975         w3 = VecLength(v3);
9976         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9977                 return NAN;
9978         } else {
9979                 w1 = 1.0 / w1;
9980                 w2 = 1.0 / w2;
9981                 w3 = 1.0 / w3;
9982                 VecScaleSelf(v1, w1);
9983                 VecScaleSelf(v2, w2);
9984                 VecScaleSelf(v3, w3);
9985                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9986         }
9987 }
9988
9989 #pragma mark ====== XtalCell Parameters ======
9990
9991 void
9992 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9993 {
9994         if (mp->cell != NULL) {
9995                 TransformVec(dst, mp->cell->tr, src);
9996         } else *dst = *src;
9997 }
9998
9999 void
10000 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10001 {
10002         if (mp->cell != NULL) {
10003                 TransformVec(dst, mp->cell->rtr, src);
10004         } else *dst = *src;
10005 }
10006
10007 int
10008 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10009 {
10010         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10011         int n1, n2, n3;
10012         Vector *vp1, *vp2, *vp3;
10013         Vector v1, v2;
10014
10015         if (cp == NULL)
10016                 return 0;
10017         for (n1 = 0; n1 < 3; n1++) {
10018                 if (cp->flags[n1] != 0)
10019                         break;
10020         }
10021         if (n1 == 3) {
10022                 /*  All directions are non-periodic  */
10023                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10024                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10025         } else {
10026                 n2 = (n1 + 1) % 3;
10027                 n3 = (n1 + 2) % 3;
10028                 vp1 = &(cp->axes[n1]);
10029                 vp2 = &(cp->axes[n2]);
10030                 vp3 = &(cp->axes[n3]);
10031                 cp->tr[n1*3] = vp1->x;
10032                 cp->tr[n1*3+1] = vp1->y;
10033                 cp->tr[n1*3+2] = vp1->z;
10034                 cp->tr[9] = cp->origin.x;
10035                 cp->tr[10] = cp->origin.y;
10036                 cp->tr[11] = cp->origin.z;
10037                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10038                         /*  1-dimensional or 2-dimensional system  */
10039                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10040                          possible with a single matrix  */
10041                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10042                                 /*  1-dimensional  */
10043                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10044                                 VecCross(v1, *vp1, xvec);
10045                                 VecCross(v2, *vp1, yvec);
10046                                 if (VecLength2(v1) < VecLength2(v2))
10047                                         v1 = v2;
10048                                 VecCross(v2, *vp1, v1);
10049                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10050                                         return -1;   /*  Non-regular transform  */
10051                         } else if (cp->flags[n2] == 0) {
10052                                 v2 = *vp3;
10053                                 VecCross(v1, v2, *vp1);
10054                                 if (NormalizeVec(&v1, &v1))
10055                                         return -1;  /*  Non-regular transform  */
10056                         } else {
10057                                 v1 = *vp2;
10058                                 VecCross(v2, *vp1, v1);
10059                                 if (NormalizeVec(&v2, &v2))
10060                                         return -1;  /*  Non-regular transform  */
10061                         }
10062                         cp->tr[n2*3] = v1.x;
10063                         cp->tr[n2*3+1] = v1.y;
10064                         cp->tr[n2*3+2] = v1.z;
10065                         cp->tr[n3*3] = v2.x;
10066                         cp->tr[n3*3+1] = v2.y;
10067                         cp->tr[n3*3+2] = v2.z;
10068                 } else {
10069                         VecCross(v1, *vp1, *vp2);
10070                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
10071                                 return -1;  /*  Non-regular transform  */
10072                         cp->tr[n2*3] = vp2->x;
10073                         cp->tr[n2*3+1] = vp2->y;
10074                         cp->tr[n2*3+2] = vp2->z;
10075                         cp->tr[n3*3] = vp3->x;
10076                         cp->tr[n3*3+1] = vp3->y;
10077                         cp->tr[n3*3+2] = vp3->z;
10078                 }
10079         }
10080         if (TransformInvert(cp->rtr, cp->tr))
10081                 return -1;  /*  Non-regular transform  */
10082
10083         /*  Calculate the reciprocal cell parameters  */
10084         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10085         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10086         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10087         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;
10088         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;
10089         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;
10090         
10091         if (calc_abc) {
10092                 /*  Calculate a, b, c, alpha, beta, gamma  */
10093                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10094                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10095                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10096                 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;
10097                 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;
10098                 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;
10099         }
10100         return 0;
10101 }
10102
10103 void
10104 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10105 {
10106         XtalCell *cp;
10107         int i;
10108         Atom *ap;
10109         Transform cmat;
10110         if (mp == NULL)
10111                 return;
10112         __MoleculeLock(mp);
10113         memset(&cmat, 0, sizeof(Transform));
10114         if (mp->cell != NULL)
10115                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10116         else
10117                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10118         if (a == 0.0) {
10119                 if (mp->cell != NULL) {
10120                         free(mp->cell);
10121                         mp->needsMDRebuild = 1;
10122                 }
10123                 mp->cell = NULL;
10124         } else {
10125                 cp = mp->cell;
10126                 if (cp == NULL) {
10127                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10128                         if (cp == NULL)
10129                                 Panic("Low memory during setting cell parameters");
10130                         mp->cell = cp;
10131                         mp->needsMDRebuild = 1;
10132                 }
10133                 /*  alpha, beta, gamma are in degree  */
10134                 cp->cell[0] = a;
10135                 cp->cell[1] = b;
10136                 cp->cell[2] = c;
10137                 cp->cell[3] = alpha;
10138                 cp->cell[4] = beta;
10139                 cp->cell[5] = gamma;
10140                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10141                         /*  c unique (hexagonal etc.)  */
10142                         Double cosa, cosb, sinb, cosg;
10143                         cosa = cos(alpha * kDeg2Rad);
10144                         cosb = cos(beta * kDeg2Rad);
10145                         sinb = sin(beta * kDeg2Rad);
10146                         cosg = cos(gamma * kDeg2Rad);
10147                         cp->axes[0].x = a * sinb;
10148                         cp->axes[0].y = 0;
10149                         cp->axes[0].z = a * cosb;
10150                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10151                         cp->axes[1].z = b * cosa;
10152                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10153                         cp->axes[2].x = 0;
10154                         cp->axes[2].y = 0;
10155                         cp->axes[2].z = c;
10156                 } else {
10157                         /*  b unique  */
10158                         Double cosg, sing, cosa, cosb;
10159                         cosa = cos(alpha * kDeg2Rad);
10160                         cosb = cos(beta * kDeg2Rad);
10161                         cosg = cos(gamma * kDeg2Rad);
10162                         sing = sin(gamma * kDeg2Rad);
10163                         cp->axes[0].x = a * sing;
10164                         cp->axes[0].y = a * cosg;
10165                         cp->axes[0].z = 0;
10166                         cp->axes[1].x = 0;
10167                         cp->axes[1].y = b;
10168                         cp->axes[1].z = 0;
10169                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10170                         cp->axes[2].y = c * cosa;
10171                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10172                 }
10173                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10174                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10175                 MoleculeCalculateCellFromAxes(cp, 0);
10176                 TransformMul(cmat, cp->tr, cmat);
10177         }
10178         
10179         /*  Update the coordinates (if requested)  */
10180         if (convertCoordinates) {
10181                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10182                         TransformVec(&(ap->r), cmat, &(ap->r));
10183                 }
10184         }
10185         
10186         /*  Update the anisotropic parameters  */
10187         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10188                 Aniso *anp = ap->aniso;
10189                 if (anp != NULL) {
10190                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10191                 }
10192         }
10193         __MoleculeUnlock(mp);
10194         sMoleculeNotifyChangeAppearance(mp);
10195 }
10196
10197 void
10198 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10199 {
10200         Double d, dx;
10201         int u = 0;
10202         const Double log2 = 0.693147180559945;
10203         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
10204         Transform m1, m2;
10205         Aniso *anp;
10206         XtalCell *cp;
10207         Vector axis[3];
10208         Double val[3];
10209         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10210                 return;
10211         anp = mp->atoms[n1].aniso;
10212         __MoleculeLock(mp);
10213         if (anp == NULL) {
10214                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10215                 if (anp == NULL) {
10216                         __MoleculeUnlock(mp);
10217                         Panic("Low memory during setting anisotropic atom parameters");
10218                 }
10219                 mp->atoms[n1].aniso = anp;
10220         }
10221         switch (type) {
10222                 case 1: d = 1; dx = 0.5; break;
10223                 case 2: d = log2; dx = log2; break;
10224                 case 3: d = log2; dx = log2 * 0.5; break;
10225                 case 4: u = 1; d = 0.25; dx = 0.25; break;
10226                 case 5: u = 1; d = 0.25; dx = 0.125; break;
10227                 case 8: u = 1; d = pi22; dx = pi22; break;
10228                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10229                 case 10: d = pi22; dx = pi22; break;
10230                 default: d = dx = 1; break;
10231         }
10232         anp->bij[0] = x11 * d;
10233         anp->bij[1] = x22 * d;
10234         anp->bij[2] = x33 * d;
10235         anp->bij[3] = x12 * dx;
10236         anp->bij[4] = x13 * dx;
10237         anp->bij[5] = x23 * dx;
10238         if (sigmaptr != NULL) {
10239                 anp->has_bsig = 1;
10240                 anp->bsig[0] = sigmaptr[0] * d;
10241                 anp->bsig[1] = sigmaptr[1] * d;
10242                 anp->bsig[2] = sigmaptr[2] * d;
10243                 anp->bsig[3] = sigmaptr[3] * dx;
10244                 anp->bsig[4] = sigmaptr[4] * dx;
10245                 anp->bsig[5] = sigmaptr[5] * dx;
10246         } else {
10247                 anp->has_bsig = 0;
10248                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10249         }
10250         cp = mp->cell;
10251         if (cp != NULL && u == 1) {
10252                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10253                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10254                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10255                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10256                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10257                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10258                 if (sigmaptr != NULL) {
10259                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10260                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10261                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10262                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10263                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10264                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10265                 }
10266         }
10267         
10268         /*  Calculate the principal axes (in Cartesian coordinates)  */
10269         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
10270                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10271                 in which x and z are the crystal-space and cartesian coordinates. */
10272         m1[0] = anp->bij[0] / pi22;
10273         m1[4] = anp->bij[1] / pi22;
10274         m1[8] = anp->bij[2] / pi22;
10275         m1[1] = m1[3] = anp->bij[3] / pi22;
10276         m1[2] = m1[6] = anp->bij[4] / pi22;
10277         m1[5] = m1[7] = anp->bij[5] / pi22;
10278         MatrixInvert(m1, m1);
10279         if (cp != NULL) {
10280                 memmove(m2, cp->rtr, sizeof(Mat33));
10281                 MatrixMul(m1, m1, m2);
10282                 MatrixTranspose(m2, m2);
10283                 MatrixMul(m1, m2, m1);
10284         }
10285         MatrixSymDiagonalize(m1, val, axis);
10286         for (u = 0; u < 3; u++) {
10287         anp->eigval[u] = val[u];
10288                 if (val[u] < 0) {
10289                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10290                         val[u] = 0.001;
10291                 } else {
10292                         val[u] = 1 / sqrt(val[u]);
10293                 }
10294                 anp->pmat[u*3] = axis[u].x * val[u];
10295                 anp->pmat[u*3+1] = axis[u].y * val[u];
10296                 anp->pmat[u*3+2] = axis[u].z * val[u];
10297         }
10298         __MoleculeUnlock(mp);
10299 }
10300
10301 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10302 void
10303 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10304 {
10305         Atom *ap, *ap2;
10306         Transform t1, t2;
10307         if (mp == NULL || idx < 0 || idx >= mp->natoms)
10308                 return;
10309         ap = ATOM_AT_INDEX(mp->atoms, idx);
10310         if (!SYMOP_ALIVE(ap->symop))
10311                 return;
10312         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10313         if (ap2->aniso == NULL) {
10314                 if (ap->aniso != NULL) {
10315                         free(ap->aniso);
10316                         ap->aniso = NULL;
10317                 }
10318                 return;
10319         }
10320         if (ap->aniso == NULL)
10321                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10322         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10323                 /*  Just copy the aniso parameters  */
10324                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10325                 return;
10326         }
10327         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10328         t1[9] = t1[10] = t1[11] = 0.0;
10329         memset(t2, 0, sizeof(Transform));
10330         t2[0] = ap2->aniso->bij[0];
10331         t2[4] = ap2->aniso->bij[1];
10332         t2[8] = ap2->aniso->bij[2];
10333         t2[1] = t2[3] = ap2->aniso->bij[3];
10334         t2[2] = t2[6] = ap2->aniso->bij[4];
10335         t2[5] = t2[7] = ap2->aniso->bij[5];
10336         TransformMul(t2, t1, t2);
10337         TransformInvert(t1, t1);
10338         TransformMul(t2, t2, t1);
10339         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10340 }
10341
10342 int
10343 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10344 {
10345         static Vector zeroVec = {0, 0, 0};
10346         XtalCell b;
10347         Transform cmat;
10348         int i, n;
10349         Atom *ap;
10350         if (mp == NULL)
10351                 return 0;
10352         if (mp->cell != NULL)
10353                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10354         else
10355                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10356         if (ax == NULL) {
10357                 if (mp->cell != NULL) {
10358                         free(mp->cell);
10359                         mp->needsMDRebuild = 1;
10360                 }
10361                 mp->cell = NULL;
10362                 return 0;
10363         }       
10364         memset(&b, 0, sizeof(b));
10365         b.axes[0] = (ax != NULL ? *ax : zeroVec);
10366         b.axes[1] = (ay != NULL ? *ay : zeroVec);
10367         b.axes[2] = (az != NULL ? *az : zeroVec);
10368         b.origin = *ao;
10369         memmove(b.flags, periodic, 3);
10370         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10371                 return -1;
10372         __MoleculeLock(mp);
10373         if (mp->cell == NULL) {
10374                 mp->needsMDRebuild = 1;
10375         } else {
10376                 if (mp->cell->has_sigma) {
10377                         /*  Keep the sigma  */
10378                         b.has_sigma = 1;
10379                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10380                 }
10381                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10382                         mp->needsMDRebuild = 1;
10383                 }
10384                 free(mp->cell);
10385         }
10386         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10387         if (mp->cell != NULL) {
10388                 memmove(mp->cell, &b, sizeof(XtalCell));
10389                 TransformMul(cmat, b.tr, cmat);
10390                 /*  Update the coordinates (if requested)  */
10391                 if (convertCoordinates) {
10392                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10393                                 TransformVec(&(ap->r), cmat, &(ap->r));
10394                         }
10395                 }
10396                 
10397                 /*  Update the anisotropic parameters  */
10398                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10399                         Aniso *anp = ap->aniso;
10400                         if (anp != NULL) {
10401                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10402                         }
10403                 }
10404                 n = 0;
10405         } else n = -2;  /*  Out of memory  */
10406         __MoleculeUnlock(mp);
10407         sMoleculeNotifyChangeAppearance(mp);
10408         return n;
10409 }
10410
10411 #pragma mark ====== Fragment manipulation ======
10412
10413 static void
10414 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10415 {
10416         Atom *ap;
10417         Int i, *cp, idx2;
10418         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10419                 return;
10420         IntGroupAdd(result, idx, 1);
10421         ap = ATOM_AT_INDEX(mp->atoms, idx);
10422         cp = AtomConnectData(&ap->connect);
10423         for (i = 0; i < ap->connect.count; i++) {
10424                 idx2 = cp[i];
10425                 if (IntGroupLookup(result, idx2, NULL))
10426                         continue;
10427                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10428                         continue;  /*  bond between two pi_anchors is ignored  */
10429                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10430         }
10431         if (ap->anchor != NULL) {
10432                 cp = AtomConnectData(&ap->anchor->connect);
10433                 for (i = 0; i < ap->anchor->connect.count; i++) {
10434                         idx2 = cp[i];
10435                         if (IntGroupLookup(result, idx2, NULL))
10436                                 continue;
10437                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
10438                 }
10439         }
10440 }
10441
10442 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10443     not containing the atoms in exatoms  */
10444 IntGroup *
10445 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10446 {
10447         IntGroup *result;
10448         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10449                 return NULL;
10450         result = IntGroupNew();
10451         sMoleculeFragmentSub(mp, n1, result, exatoms);
10452         return result;
10453 }
10454
10455 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10456     not containing the atoms n2, n3, ... (terminated by -1)  */
10457 IntGroup *
10458 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10459 {
10460         int i;
10461         IntGroup *exatoms, *result;
10462         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10463                 return NULL;
10464         exatoms = IntGroupNew();
10465         for (i = 0; i < argc; i++)
10466                 IntGroupAdd(exatoms, argv[i], 1);
10467         result = IntGroupNew();
10468         sMoleculeFragmentSub(mp, n1, result, exatoms);
10469         IntGroupRelease(exatoms);
10470         return result;
10471 }
10472
10473 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10474     not containing the atoms in exatoms  */
10475 IntGroup *
10476 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10477 {
10478         IntGroupIterator iter;
10479         IntGroup *result;
10480         int i;
10481         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10482                 return NULL;
10483         IntGroupIteratorInit(inatoms, &iter);
10484         result = IntGroupNew();
10485         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10486                 sMoleculeFragmentSub(mp, i, result, exatoms);
10487         }
10488         IntGroupIteratorRelease(&iter);
10489         return result;
10490 }
10491
10492 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10493     group is bound to the rest of the molecule via only one bond.
10494         If the result is true, then the atoms belonging to the (only) bond are returned
10495         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10496         and n2 can be NULL, if those informations are not needed.  */
10497 int
10498 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10499 {
10500         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10501         Atom *ap;
10502         if (mp == NULL || mp->natoms == 0 || group == NULL)
10503                 return 0;  /*  Invalid arguments  */
10504         bond_count = 0;
10505         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10506                 i2 = IntGroupGetEndPoint(group, i);
10507                 for (j = i1; j < i2; j++) {
10508                         if (j < 0 || j >= mp->natoms)
10509                                 return 0;  /*  Invalid atom group  */
10510                         ap = ATOM_AT_INDEX(mp->atoms, j);
10511                         cp = AtomConnectData(&ap->connect);
10512                         for (k = 0; k < ap->connect.count; k++) {
10513                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10514                                         continue;  /*  Ignore bond between two pi_anchors  */
10515                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10516                                         bond_count++;
10517                                         nval1 = j;
10518                                         nval2 = cp[k];
10519                                         if (bond_count > 1)
10520                                                 return 0;  /*  Too many bonds  */
10521                                 }
10522                         }
10523                         if (ap->anchor != NULL) {
10524                                 cp = AtomConnectData(&ap->anchor->connect);
10525                                 for (k = 0; k < ap->anchor->connect.count; k++) {
10526                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
10527                                                 bond_count++;
10528                                                 nval1 = j;
10529                                                 nval2 = cp[k];
10530                                                 if (bond_count > 1)
10531                                                         return 0;  /*  Too many bonds  */
10532                                         }
10533                                 }                                       
10534                         }
10535                 }
10536         }
10537         if (bond_count == 1) {
10538                 if (n1 != NULL)
10539                         *n1 = nval1;
10540                 if (n2 != NULL)
10541                         *n2 = nval2;
10542                 return 1;
10543         } else {
10544                 return 0;
10545         }       
10546 }
10547
10548 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
10549     is said to be 'rotatable' when either of the following conditions are met; (1)
10550         the group is detachable, or (2) the group consists of two bonded atoms that define
10551         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10552         (either a new IntGroup or 'group' with incremented reference count; thus the caller
10553         is responsible for releasing the returned value).  */
10554 int
10555 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10556 {
10557         int i1, i2;
10558         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10559                 if (rotGroup != NULL) {
10560                         IntGroupRetain(group);
10561                         *rotGroup = group;
10562                 }
10563                 return 1;
10564         }
10565         if (group != NULL && IntGroupGetCount(group) == 2) {
10566                 i1 = IntGroupGetNthPoint(group, 0);
10567                 i2 = IntGroupGetNthPoint(group, 1);
10568                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10569                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10570                         if (frag == NULL)
10571                                 return 0;
10572                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10573                         if (i1 == 0) {
10574                                 IntGroupRelease(frag);
10575                                 if (rotGroup != NULL)
10576                                         *rotGroup = NULL;
10577                                 return 0;
10578                         }
10579                         if (rotGroup != NULL)
10580                                 *rotGroup = frag;
10581                         else if (frag != NULL)
10582                                 IntGroupRelease(frag);
10583                         return i1;
10584                 }
10585         }
10586         return 0;
10587 }
10588
10589 #pragma mark ====== Multiple frame ======
10590
10591 int
10592 MoleculeGetNumberOfFrames(Molecule *mp)
10593 {
10594         if (mp == NULL)
10595                 return 0;
10596         if (mp->nframes <= 0) {
10597                 /*  Recalculate  */
10598                 int i, n;
10599                 Atom *ap;
10600                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10601                         if (ap->nframes > n)
10602                                 n = ap->nframes;
10603                 }
10604                 if (n == 0)
10605                         n = 1;
10606                 mp->nframes = n;
10607         }
10608         return mp->nframes;
10609 }
10610
10611 int
10612 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10613 {
10614         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10615         Vector *tempv, *vp;
10616         Atom *ap;
10617         MolProp *prp;
10618         Double *dp;
10619         
10620         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10621                 return -1;
10622
10623         n_old = MoleculeGetNumberOfFrames(mp);
10624         n_new = n_old + count;
10625         last_inserted = IntGroupGetNthPoint(group, count - 1);
10626         if (n_new <= last_inserted) {
10627                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10628                 n_new += exframes;
10629         } else exframes = 0;
10630
10631         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10632         if (tempv == NULL)
10633                 return -1;
10634
10635         __MoleculeLock(mp);
10636
10637         /*  Copy back the current coordinates  */
10638         /*  No change in the current coordinates, but the frame buffer is updated  */
10639         MoleculeSelectFrame(mp, mp->cframe, 1); 
10640         
10641         /*  Expand ap->frames for all atoms  */
10642         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10643                 Int n = ap->nframes;
10644                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10645                 if (ap->frames == NULL) {
10646                         __MoleculeUnlock(mp);
10647                         return -1;
10648                 }
10649                 for (j = n; j < n_new; j++)
10650                         ap->frames[j] = ap->r;
10651         }
10652         if (mp->cell != NULL) {
10653                 j = mp->nframe_cells;
10654                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10655                 for (i = j; i < n_new; i++) {
10656                         /*  Set the current cell parameters to the expanded frames  */
10657                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10658                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10659                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10660                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10661                 }
10662         }
10663         
10664         /*  Expand propvals for all properties  */
10665         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10666                 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10667                 if (dp == NULL) {
10668                         __MoleculeUnlock(mp);
10669                         return -1;
10670                 }
10671                 for (j = n_old; j < n_new; j++)
10672                         dp[j] = 0.0;
10673                 prp->propvals = dp;
10674         }
10675         
10676         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10677         /*  s = t = 0,  */
10678         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10679             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10680                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10681                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10682                 ...
10683                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10684                 At last, s will become n_old and t will become count.  */
10685         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10686                 int s, t, ns, ne, mult;
10687                 Vector cr;
10688                 ne = s = t = 0;
10689                 if (i == mp->natoms) {
10690                         if (mp->cell == NULL || mp->frame_cells == NULL)
10691                                 continue;
10692                         vp = mp->frame_cells;
10693                         mult = 4;
10694                 } else if (i < mp->natoms) {
10695                         cr = ap->r;
10696                         vp = ap->frames;
10697                         mult = 1;
10698                 } else {
10699                         dp = prp->propvals;
10700                 }
10701                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10702                         if (ns > ne) {
10703                                 if (i <= mp->natoms)
10704                                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10705                                 else
10706                                         memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne)); 
10707                                 s += ns - ne;
10708                         }
10709                         ne = IntGroupGetEndPoint(group, j);
10710                         while (ns < ne) {
10711                                 if (i == mp->natoms) {
10712                                         if (inFrameCell != NULL) {
10713                                                 tempv[ns * 4] = inFrameCell[t * 4];
10714                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10715                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10716                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10717                                         } else {
10718                                                 tempv[ns * 4] = mp->cell->axes[0];
10719                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10720                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10721                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10722                                         }
10723                                 } else if (i < mp->natoms) {
10724                                         if (inFrame != NULL)
10725                                                 tempv[ns] = inFrame[natoms * t + i];
10726                                         else
10727                                                 tempv[ns] = cr;
10728                                 } else {
10729                                         ((Double *)tempv)[ns] = 0.0;
10730                                 }
10731                                 t++;
10732                                 ns++;
10733                         }
10734                 }
10735                 if (n_new > ne) {
10736                         if (i <= mp->natoms)
10737                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10738                         else
10739                                 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10740                         s += n_new - ne;
10741                 }
10742                 if (i < mp->natoms)
10743                         ap->nframes = n_new;
10744                 if (i <= mp->natoms) {
10745                         memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10746                         if (i < mp->natoms) {
10747                                 ap->nframes = n_new;
10748                                 ap = ATOM_NEXT(ap);
10749                         }
10750                 } else {
10751                         memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10752                         prp++;
10753                 }
10754         }
10755         free(tempv);
10756         mp->nframes = n_new;
10757         MoleculeSelectFrame(mp, last_inserted, 0);
10758         MoleculeIncrementModifyCount(mp);
10759         __MoleculeUnlock(mp);
10760         return count;
10761 }
10762
10763 int
10764 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10765 {
10766         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10767         Vector *tempv, *vp;
10768         Atom *ap;
10769         MolProp *prp;
10770         IntGroup *group, *group2;
10771
10772         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10773                 return -1;
10774
10775         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10776         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10777         if (mp->cell != NULL && mp->frame_cells != NULL)
10778                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10779
10780         n_old = MoleculeGetNumberOfFrames(mp);
10781         if (n_old == 1)
10782                 return -2;  /*  Cannot delete last frame  */
10783
10784         group = IntGroupNew();
10785         group2 = IntGroupNewWithPoints(0, n_old, -1);
10786         IntGroupIntersect(inGroup, group2, group);
10787         IntGroupRelease(group2);
10788         count = IntGroupGetCount(group);
10789         n_new = n_old - count;
10790         if (n_new < 1) {
10791                 IntGroupRelease(group);
10792                 return -2;  /*  Trying to delete too many frames  */
10793         }
10794         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10795         if (tempv == NULL) {
10796                 IntGroupRelease(group);
10797                 return -1;
10798         }
10799
10800         __MoleculeLock(mp);
10801
10802         /*  Copy back the current coordinates  */
10803         /*  No change in the current coordinates, but the frame buffer is updated  */
10804         MoleculeSelectFrame(mp, mp->cframe, 1); 
10805
10806         /*  Determine which frame should be selected after removal is completed  */
10807         {
10808                 int n1;
10809                 if (IntGroupLookup(group, mp->cframe, &i)) {
10810                         /*  cframe will be removed  */
10811                         n1 = IntGroupGetStartPoint(group, i) - 1;
10812                         if (n1 < 0)
10813                                 n1 = IntGroupGetEndPoint(group, i);
10814                 } else n1 = mp->cframe;
10815                 /*  Change to that frame  */
10816                 MoleculeSelectFrame(mp, n1, 0);
10817                 group2 = IntGroupNewFromIntGroup(group);
10818                 IntGroupReverse(group2, 0, n_old);
10819                 new_cframe = IntGroupLookupPoint(group2, n1);
10820                 if (new_cframe < 0)
10821                         return -3;  /*  This cannot happen  */
10822                 IntGroupRelease(group2);
10823         }
10824
10825         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10826         /*  s = t = 0, */
10827         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10828             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10829                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10830                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10831                 ...
10832                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10833                 At last, s will become n_new and t will become count.  */
10834         nframes = 0;
10835         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10836                 int s, t, j, ns, ne;
10837                 int mult;
10838                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10839                 if (i == mp->natoms) {
10840                         if (mp->cell == NULL || mp->frame_cells == NULL)
10841                                 continue;
10842                         mult = 4 * sizeof(Vector);
10843                         vp = mp->frame_cells;
10844                         old_count = n_old;
10845                 } else if (i < mp->natoms) {
10846                         mult = sizeof(Vector);
10847                         vp = ap->frames;
10848                         if (vp == NULL) {
10849                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
10850                                 if (ap->frames == NULL) {
10851                                         __MoleculeUnlock(mp);
10852                                         return -1;
10853                                 }
10854                                 vp = ap->frames;
10855                         }
10856                         old_count = ap->nframes;
10857                 } else {
10858                         mult = sizeof(Double);
10859                         vp = (Vector *)prp->propvals;
10860                         old_count = n_old;
10861                 }
10862
10863                 /*  Copy vp to tempv  */
10864                 memset(tempv, 0, mult * n_old);
10865                 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
10866                 ne = ns = s = t = 0;
10867                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10868                         if (ns > n_old)
10869                                 ns = n_old;
10870                         if (ns > ne) {
10871                                 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
10872                                 s += ns - ne;
10873                         }
10874                         ne = IntGroupGetEndPoint(group, j);
10875                         if (ne > n_old)
10876                                 ne = n_old;
10877                         while (ns < ne) {
10878                                 if (i < mp->natoms)
10879                                         outFrame[natoms * t + i] = tempv[ns];
10880                                 else if (i == mp->natoms) {
10881                                         if (outFrameCell != NULL) {
10882                                                 outFrameCell[t * 4] = tempv[ns * 4];
10883                                                 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10884                                                 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10885                                                 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10886                                         }
10887                                 }
10888                                 t++;
10889                                 ns++;
10890                         }
10891                 }
10892                 if (n_old > ne) {
10893                         memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
10894                         s += n_old - ne;
10895                 }
10896                 if (i < mp->natoms)
10897                         ap->nframes = s;
10898                 if (nframes < s)
10899                         nframes = s;
10900                 if (s <= 1) {
10901                         if (i < mp->natoms) {
10902                                 free(ap->frames);
10903                                 ap->frames = NULL;
10904                                 ap->nframes = 0;
10905                         } else if (i == mp->natoms) {
10906                                 free(mp->frame_cells);
10907                                 mp->frame_cells = NULL;
10908                                 mp->nframe_cells = 0;
10909                         } else {
10910                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
10911                         }
10912                 } else {
10913                         if (i < mp->natoms) {
10914                                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
10915                                 ap->nframes = s;
10916                         } else if (i == mp->natoms) {
10917                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10918                                 mp->nframe_cells = s;
10919                         } else {
10920                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
10921                         }
10922                 }
10923                 if (i < mp->natoms) {
10924                         ap = ATOM_NEXT(ap);
10925                 } else if (i > mp->natoms) {
10926                         prp++;
10927                 }
10928         }
10929         free(tempv);
10930         mp->nframes = nframes;
10931         
10932         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10933 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10934         MoleculeSelectFrame(mp, new_cframe, 0);
10935
10936         IntGroupRelease(group);
10937
10938         MoleculeIncrementModifyCount(mp);
10939         __MoleculeUnlock(mp);
10940         return count;
10941 }
10942
10943 int
10944 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10945 {
10946         int i, cframe, nframes, modified;
10947         Atom *ap;
10948         cframe = mp->cframe;
10949         nframes = MoleculeGetNumberOfFrames(mp);
10950         if (frame == -1)
10951                 frame = mp->cframe;
10952         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10953                 return -1;
10954         modified = 0;
10955         __MoleculeLock(mp);
10956         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10957                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10958                         /*  Write the current coordinate back to the frame array  */
10959                         ap->frames[cframe] = ap->r;
10960                 }
10961                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
10962                         /*  Read the coordinate from the frame array  */
10963                         ap->r = ap->frames[frame];
10964                         modified = 1;
10965                 }
10966         }
10967
10968         if (mp->cell != NULL && mp->frame_cells != NULL) {
10969                 /*  Write the current cell back to the frame_cells array  */
10970                 if (copyback && cframe >= 0) {
10971                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10972                         vp[0] = mp->cell->axes[0];
10973                         vp[1] = mp->cell->axes[1];
10974                         vp[2] = mp->cell->axes[2];
10975                         vp[3] = mp->cell->origin;
10976                 }
10977                 /*  Set the cell from the frame array  */
10978                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
10979                         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);
10980                         modified = 1;
10981                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10982                 }
10983         }
10984         mp->cframe = frame;
10985         if (modified)
10986                 mp->needsMDCopyCoordinates = 1;
10987         __MoleculeUnlock(mp);
10988         sMoleculeNotifyChangeAppearance(mp);
10989         return frame;
10990 }
10991
10992 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10993     Returns the number of frames.  */
10994 int
10995 MoleculeFlushFrames(Molecule *mp)
10996 {
10997         int nframes = MoleculeGetNumberOfFrames(mp);
10998         if (nframes > 1)
10999                 MoleculeSelectFrame(mp, mp->cframe, 1);
11000         return nframes;
11001 }
11002
11003 int
11004 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11005 {
11006         Int *ip, i, j, n, nframes;
11007         Double *dp;
11008         Atom *ap;
11009         MolProp *prp;
11010         if (mp == NULL || old_idx == NULL)
11011                 return 0;
11012         nframes = MoleculeGetNumberOfFrames(mp);
11013         MoleculeFlushFrames(mp);
11014         ip = (Int *)malloc(sizeof(Int) * nframes);
11015         if (ip == NULL)
11016                 return -1;  /*  Out of memory  */
11017         memset(ip, 0, sizeof(Int) * nframes);
11018         /*  Check the argument  */
11019         for (i = 0; i < nframes; i++) {
11020                 j = old_idx[i];
11021                 if (j < 0 || j >= nframes || ip[j] != 0) {
11022                         free(ip);
11023                         return -2;  /*  Bad argument  */
11024                 }
11025                 ip[j] = 1;
11026         }
11027         free(ip);
11028         dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11029         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11030                 for (j = 0; j < nframes; j++) {
11031                         n = old_idx[j];
11032                         if (i < mp->natoms) {
11033                                 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11034                         } else if (i == mp->natoms) {
11035                                 if (mp->cell != NULL) {
11036                                         if (n < mp->nframe_cells && mp->frame_cells != NULL)
11037                                                 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11038                                         else {
11039                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11040                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11041                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11042                                                 ((Vector *)dp)[j * 4] = mp->cell->origin;
11043                                         }
11044                                 }
11045                         } else {
11046                                 dp[j] = prp->propvals[n];
11047                         }
11048                 }
11049                 for (j = 0; j < nframes; j++) {
11050                         if (i < mp->natoms) {
11051                                 if (ap->nframes <= j)
11052                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11053                                 ap->frames[j] = ((Vector *)dp)[j];
11054                         } else if (i == mp->natoms) {
11055                                 if (mp->cell != NULL) {
11056                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11057                                         memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11058                                 }
11059                         } else {
11060                                 prp->propvals[j] = dp[j];
11061                         }
11062                 }
11063                 if (i < mp->natoms)
11064                         ap = ATOM_NEXT(ap);
11065                 else if (i > mp->natoms)
11066                         prp++;
11067         }
11068         free(dp);
11069         MoleculeSelectFrame(mp, mp->cframe, 0);
11070         return 0;
11071 }
11072
11073 #pragma mark ====== Molecule Propeties ======
11074
11075 int
11076 MoleculeCreateProperty(Molecule *mp, const char *name)
11077 {
11078         int i;
11079         MolProp *prp;
11080         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11081                 if (strcmp(prp->propname, name) == 0)
11082                         return -(i + 1);
11083         }
11084         prp = (MolProp *)calloc(sizeof(MolProp), 1);
11085         if (prp == NULL)
11086                 return -10000;
11087         prp->propname = strdup(name);
11088         if (prp->propname == NULL)
11089                 return -10000;
11090         i = MoleculeGetNumberOfFrames(mp);
11091         prp->propvals = (Double *)calloc(sizeof(Double), i);
11092         if (prp->propvals == NULL)
11093                 return -10000;
11094         AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11095         free(prp);
11096         return mp->nmolprops - 1;
11097 }
11098
11099 int
11100 MoleculeLookUpProperty(Molecule *mp, const char *name)
11101 {
11102         int i;
11103         MolProp *prp;
11104         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11105                 if (strcmp(prp->propname, name) == 0)
11106                         return i;
11107         }
11108         return -1;
11109 }
11110
11111 int
11112 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11113 {
11114         if (idx >= 0 && idx < mp->nmolprops) {
11115                 free(mp->molprops[idx].propname);
11116                 free(mp->molprops[idx].propvals);
11117                 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11118                 return idx;
11119         }
11120         return -1;
11121 }
11122
11123 int
11124 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11125 {
11126         IntGroupIterator iter;
11127         int i, n, nframes;
11128         if (idx < 0 || idx >= mp->nmolprops)
11129                 return -1;
11130         IntGroupIteratorInit(ig, &iter);
11131         nframes = MoleculeGetNumberOfFrames(mp);
11132         n = 0;
11133         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11134                 if (i >= nframes)
11135                         break;
11136                 mp->molprops[idx].propvals[i] = values[n];
11137                 n++;
11138         }
11139         IntGroupIteratorRelease(&iter);
11140         return n;
11141 }
11142
11143 int
11144 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11145 {
11146         IntGroupIterator iter;
11147         int i, n, nframes;
11148         if (idx < 0 || idx >= mp->nmolprops)
11149                 return -1;
11150         IntGroupIteratorInit(ig, &iter);
11151         nframes = MoleculeGetNumberOfFrames(mp);
11152         n = 0;
11153         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11154                 if (i >= nframes)
11155                         break;
11156                 outValues[n] = mp->molprops[idx].propvals[i];
11157                 n++;
11158         }
11159         IntGroupIteratorRelease(&iter);
11160         return n;
11161 }
11162
11163 #pragma mark ====== Pi Atoms ======
11164
11165 static inline void
11166 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11167 {
11168         Int *cp, j, n;
11169         Atom *ap2;
11170         cp = AtomConnectData(&ap->anchor->connect);
11171         n = ap->anchor->connect.count;
11172         VecZero(ap->r);
11173         for (j = 0; j < n; j++) {
11174                 Double w = ap->anchor->coeffs[j];
11175                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11176                 VecScaleInc(ap->r, ap2->r, w);
11177         }       
11178 }
11179
11180 void
11181 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11182 {
11183         Int i;
11184         Atom *ap;
11185         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11186                 if (ap->anchor == NULL)
11187                         continue;
11188                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11189         }
11190 }
11191
11192 void
11193 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11194 {
11195         Atom *ap;
11196         if (mol == NULL || idx < 0 || idx >= mol->natoms)
11197                 return;
11198         ap = ATOM_AT_INDEX(mol->atoms, idx);
11199         if (ap->anchor == NULL)
11200                 return;
11201         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11202 }
11203
11204 int
11205 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11206 {
11207         Atom *ap;
11208         Int *ip, i, j, n, *np;
11209         Double d;
11210         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11211                 return -1;  /*  Invalid argument  */
11212         if (weights != NULL) {
11213                 d = 0.0;
11214                 for (i = 0; i < nentries; i++) {
11215                         if (weights[i] <= 0.0) {
11216                                 return 10;  /*  Weights must be positive  */
11217                         }
11218                         d += weights[i];
11219                 }
11220                 d = 1.0 / d;
11221         } else d = 1.0 / nentries;
11222         ap = ATOM_AT_INDEX(mol->atoms, idx);
11223         if (ap->anchor != NULL) {
11224                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
11225                 IntGroup *bg, *ag, *dg, *ig;
11226                 Int *ibuf, ibufsize;
11227                 MolAction *act;
11228                 bg = ag = dg = ig = NULL;
11229                 ip = AtomConnectData(&ap->anchor->connect);
11230                 for (i = 0; i < ap->anchor->connect.count; i++) {
11231                         n = ip[i];
11232                         for (j = 0; j < nentries; j++) {
11233                                 if (n == entries[j])
11234                                         break;
11235                         }
11236                         if (j == nentries) {
11237                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
11238                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11239                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11240                                                 if (bg == NULL)
11241                                                         bg = IntGroupNew();
11242                                                 IntGroupAdd(bg, j, 1);
11243                                         }
11244                                 }
11245                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11246                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11247                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11248                                                 if (ag == NULL)
11249                                                         ag = IntGroupNew();
11250                                                 IntGroupAdd(ag, j, 1);
11251                                         }
11252                                 }
11253                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11254                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11255                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11256                                                 if (dg == NULL)
11257                                                         dg = IntGroupNew();
11258                                                 IntGroupAdd(dg, j, 1);
11259                                         }
11260                                 }
11261                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11262                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11263                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11264                                                 if (ig == NULL)
11265                                                         ig = IntGroupNew();
11266                                                 IntGroupAdd(ig, j, 1);
11267                                         }
11268                                 }
11269                         }
11270                 }
11271                 ibuf = NULL;
11272                 ibufsize = 0;
11273                 if (ig != NULL) {
11274                         /*  Delete impropers (with undo info) */
11275                         i = IntGroupGetCount(ig);
11276                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11277                         MoleculeDeleteImpropers(mol, ibuf, ig);
11278                         if (nUndoActions != NULL && undoActions != NULL) {
11279                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11280                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11281                         }
11282                         IntGroupRelease(ig);
11283                 }
11284                 if (dg != NULL) {
11285                         /*  Delete dihedrals (with undo info)  */
11286                         i = IntGroupGetCount(dg);
11287                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11288                         MoleculeDeleteDihedrals(mol, ibuf, dg);
11289                         if (nUndoActions != NULL && undoActions != NULL) {
11290                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11291                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11292                         }
11293                         IntGroupRelease(dg);
11294                 }
11295                 if (ag != NULL) {
11296                         /*  Delete angles (with undo info) */
11297                         i = IntGroupGetCount(ag);
11298                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11299                         MoleculeDeleteAngles(mol, ibuf, ag);
11300                         if (nUndoActions != NULL && undoActions != NULL) {
11301                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11302                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11303                         }
11304                         IntGroupRelease(ag);
11305                 }
11306                 if (bg != NULL) {
11307                         /*  Delete bonds (with undo info) */
11308                         i = IntGroupGetCount(bg);
11309                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11310                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11311                         if (nUndoActions != NULL && undoActions != NULL) {
11312                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11313                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11314                         }
11315                         IntGroupRelease(bg);
11316                 }
11317         } else {
11318                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11319         }
11320         AtomConnectResize(&ap->anchor->connect, nentries);
11321         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11322         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11323         if (weights != NULL) {
11324                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11325                 for (i = 0; i < nentries; i++)
11326                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
11327         } else {
11328                 for (i = 0; i < nentries; i++)
11329                         ap->anchor->coeffs[i] = d;
11330         }
11331         MoleculeCalculatePiAnchorPosition(mol, idx);
11332         return 0;
11333 }
11334
11335 #pragma mark ====== MO calculation ======
11336
11337 /*  Calculate an MO value for a single point.  */
11338 /*  Index is the MO number (1-based); 0 denotes "arbitrary vector"  */
11339 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
11340 static Double
11341 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11342 {
11343         ShellInfo *sp;
11344         PrimInfo *pp;
11345         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11346         Int i, j;
11347         /*  Cache dr and |dr|^2  */
11348         if (index == 0)
11349                 index = bset->nmos + 1;
11350         for (i = 0; i < mp->natoms; i++) {
11351                 Vector r;
11352                 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11353                 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11354                 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11355                 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11356                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11357         }
11358         /*  Iterate over all shells  */
11359         val = 0.0;
11360         mobasep = bset->mo + (index - 1) * bset->ncomps;
11361         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11362                 pp = bset->priminfos + sp->p_idx;
11363                 cnp = bset->cns + sp->cn_idx;
11364                 if (sp->a_idx >= mp->natoms)
11365                         return 0.0; /*  This may happen when molecule is edited after setting up MO info  */
11366                 tmpp = tmp + sp->a_idx * 4;
11367                 mop = mobasep + sp->m_idx;
11368                 switch (sp->sym) {
11369                         case kGTOType_S: {
11370                                 tval = 0;
11371                                 for (j = 0; j < sp->nprim; j++) {
11372                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
11373                                         pp++;
11374                                 }
11375                                 val += mop[0] * tval;
11376                                 break;
11377                         }
11378                         case kGTOType_P: {
11379                                 Double x, y, z;
11380                                 x = y = z = 0;
11381                                 for (j = 0; j < sp->nprim; j++) {
11382                                         tval = exp(-pp->A * tmpp[3]);
11383                                         x += *cnp++ * tval;
11384                                         y += *cnp++ * tval;
11385                                         z += *cnp++ * tval;
11386                                         pp++;
11387                                 }
11388                                 x *= mop[0] * tmpp[0];
11389                                 y *= mop[1] * tmpp[1];
11390                                 z *= mop[2] * tmpp[2];
11391                                 val += x + y + z;
11392                                 break;
11393                         }
11394                         case kGTOType_SP: {
11395                                 Double t, x, y, z;
11396                                 t = x = y = z = 0;
11397                                 for (j = 0; j < sp->nprim; j++) {
11398                                         tval = exp(-pp->A * tmpp[3]);
11399                                         t += *cnp++ * tval;
11400                                         x += *cnp++ * tval;
11401                                         y += *cnp++ * tval;
11402                                         z += *cnp++ * tval;
11403                                         pp++;
11404                                 }
11405                                 t *= mop[0];
11406                                 x *= mop[1] * tmpp[0];
11407                                 y *= mop[2] * tmpp[1];
11408                                 z *= mop[3] * tmpp[2];
11409                                 val += t + x + y + z;
11410                                 break;
11411                         }
11412                         case kGTOType_D: {
11413                                 Double xx, yy, zz, xy, xz, yz;
11414                                 xx = yy = zz = xy = xz = yz = 0;
11415                                 for (j = 0; j < sp->nprim; j++) {
11416                                         tval = exp(-pp->A * tmpp[3]);
11417                                         xx += *cnp++ * tval;
11418                                         yy += *cnp++ * tval;
11419                                         zz += *cnp++ * tval;
11420                                         xy += *cnp++ * tval;
11421                                         xz += *cnp++ * tval;
11422                                         yz += *cnp++ * tval;
11423                                         pp++;
11424                                 }
11425                                 xx *= mop[0] * tmpp[0] * tmpp[0];
11426                                 yy *= mop[1] * tmpp[1] * tmpp[1];
11427                                 zz *= mop[2] * tmpp[2] * tmpp[2];
11428                                 xy *= mop[3] * tmpp[0] * tmpp[1];
11429                                 xz *= mop[4] * tmpp[0] * tmpp[2];
11430                                 yz *= mop[5] * tmpp[1] * tmpp[2];
11431                                 val += xx + yy + zz + xy + xz + yz;
11432                                 break;
11433                         }
11434                         case kGTOType_D5: {
11435                                 Double d0, d1p, d1n, d2p, d2n;
11436                                 d0 = d1p = d1n = d2p = d2n = 0;
11437                                 for (j = 0; j < sp->nprim; j++) {
11438                                         tval = exp(-pp->A * tmpp[3]);
11439                                         d0 += *cnp++ * tval;
11440                                         d1p += *cnp++ * tval;
11441                                         d1n += *cnp++ * tval;
11442                                         d2p += *cnp++ * tval;
11443                                         d2n += *cnp++ * tval;
11444                                         pp++;
11445                                 }
11446                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11447                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
11448                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
11449                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11450                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
11451                                 val += d0 + d1p + d1n + d2p + d2n;
11452                                 break;
11453                         }
11454                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
11455                 }
11456         }
11457         return val;
11458 }
11459
11460 /*  Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520)  */
11461 /*  mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11462 int
11463 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)
11464 {
11465         int ix, iy, iz, n, nn;
11466         Cube *cp;
11467         Double *tmp;
11468         if (mp == NULL || mp->bset == NULL)
11469                 return -1;
11470         if (mp->bset->cns == NULL) {
11471                 if (sSetupGaussianCoefficients(mp->bset) != 0)
11472                         return -1;
11473         }
11474         if (mp->bset->natoms_bs > mp->natoms)
11475                 return -3;  /*  Number of atoms is smaller than expected (internal error)  */
11476         
11477         cp = (Cube *)calloc(sizeof(Cube), 1);
11478         if (cp == NULL) {
11479                 return -1;
11480         }
11481         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11482         if (cp->dp == NULL) {
11483                 free(cp);
11484                 return -1;
11485         }
11486         cp->idn = mono;
11487         cp->origin = *op;
11488         cp->dx = *dxp;
11489         cp->dy = *dyp;
11490         cp->dz = *dzp;
11491         cp->nx = nx;
11492         cp->ny = ny;
11493         cp->nz = nz;
11494         
11495         /*  TODO: use multithread  */
11496         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11497         if (tmp == NULL) {
11498                 free(cp->dp);
11499                 free(cp);
11500                 return -1;
11501         }
11502         n = nn = 0;
11503         for (ix = 0; ix < nx; ix++) {
11504                 Vector p;
11505                 for (iy = 0; iy < ny; iy++) {
11506                         for (iz = 0; iz < nz; iz++) {
11507                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11508                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11509                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11510                                 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11511                         }
11512                         if (callback != NULL && n - nn > 100) {
11513                                 nn = n;
11514                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11515                                         free(cp->dp);
11516                                         free(cp);
11517                                         free(tmp);
11518                                         return -2;  /*  User interrupt  */
11519                                 }
11520                         }
11521                 }
11522         }
11523         free(tmp);
11524
11525         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11526         return mp->bset->ncubes - 1;
11527 }
11528
11529 /*  Output values are in angstrom unit (changed from bohr unit: 20140520)  */
11530 int
11531 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11532 {
11533         int i;
11534         Vector rmin, rmax, r;
11535         Double dr, dx, dy, dz;
11536         Atom *ap;
11537         if (mp == NULL || mp->bset == NULL)
11538                 return -1;
11539         if (npoints <= 0)
11540                 npoints = 1000000;
11541         rmin.x = rmin.y = rmin.z = 1e10;
11542         rmax.x = rmax.y = rmax.z = -1e10;
11543         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11544                 dr = RadiusForAtomicNumber(ap->atomicNumber);
11545                 r = ap->r;
11546                 if (dr == 0.0)
11547                         dr = 1.0;
11548                 dr = dr * 3.0 + 2.0;
11549                 if (rmin.x > r.x - dr)
11550                         rmin.x = r.x - dr;
11551                 if (rmin.y > r.y - dr)
11552                         rmin.y = r.y - dr;
11553                 if (rmin.z > r.z - dr)
11554                         rmin.z = r.z - dr;
11555                 if (rmax.x < r.x + dr)
11556                         rmax.x = r.x + dr;
11557                 if (rmax.y < r.y + dr)
11558                         rmax.y = r.y + dr;
11559                 if (rmax.z < r.z + dr)
11560                         rmax.z = r.z + dr;
11561         }
11562         dx = rmax.x - rmin.x;
11563         dy = rmax.y - rmin.y;
11564         dz = rmax.z - rmin.z;
11565         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11566         *nx = floor(dx / dr + 0.5);
11567         *ny = floor(dy / dr + 0.5);
11568         *nz = floor(dz / dr + 0.5);
11569         if (*nx == 0)
11570                 *nx = 1;
11571         if (*ny == 0)
11572                 *ny = 1;
11573         if (*nz == 0)
11574                 *nz = 1;
11575         *op = rmin;
11576         xp->x = yp->y = zp->z = dr;
11577         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11578         return 0;
11579 }
11580
11581 const Cube *
11582 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11583 {
11584         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11585                 return NULL;
11586         return mp->bset->cubes[index];
11587 }
11588
11589 int
11590 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11591 {
11592         int i;
11593         if (mp == NULL || mp->bset == NULL)
11594                 return -1;
11595         for (i = 0; i < mp->bset->ncubes; i++) {
11596                 if (mp->bset->cubes[i]->idn == mono)
11597                         return i;
11598         }
11599         return -1;
11600 }
11601
11602 int
11603 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11604 {
11605         int n;
11606         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11607                 return -1;
11608         CubeRelease(mp->bset->cubes[index]);
11609         if (index < n - 1)
11610                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11611         if (--(mp->bset->ncubes) == 0) {
11612                 free(mp->bset->cubes);
11613                 mp->bset->cubes = NULL;
11614         }
11615         return mp->bset->ncubes;
11616 }
11617
11618 int
11619 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11620 {
11621         const Cube *cp;
11622         int i, j, k, n;
11623         FILE *fp;
11624         if (mp == NULL || mp->bset == NULL)
11625                 return -1;  /*  Molecule or the basis set information is empty  */
11626         cp = MoleculeGetCubeAtIndex(mp, index);
11627         if (cp == NULL)
11628                 return -2;  /*  MO not yet calculated  */
11629         fp = fopen(fname, "wb");
11630         if (fp == NULL)
11631                 return -3;  /*  Cannot create file  */
11632
11633         /*  Comment lines  */
11634         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11635         fprintf(fp, " MO coefficients\n");
11636         
11637         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11638                         cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11639         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11640                         cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11641         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11642                         cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11643         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11644                         cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11645         
11646         /*  Atomic information  */
11647         for (i = 0; i < mp->natoms; i++) {
11648                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11649                 /*  The second number should actually be the effective charge  */
11650                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11651                                 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11652         }
11653         fprintf(fp, "%5d%5d\n", 1, 1);
11654         
11655         /*  3D data  */
11656         for (i = n = 0; i < cp->nx; i++) {
11657                 for (j = 0; j < cp->ny; j++) {
11658                         for (k = 0; k < cp->nz; k++) {
11659                                 /*  On Windows, the "%e" format writes the exponent in 3 digits, but
11660                                     this is not standard. So we avoid using %e  */
11661                                 Double d = cp->dp[n++];
11662                                 int exponent;
11663                                 Double base;
11664                                 if (d >= -1.0e-90 && d <= 1.0e-90) {
11665                                         exponent = 0;
11666                                         base = 0.0;
11667                                 } else {
11668                                         exponent = (int)floor(log10(fabs(d)));
11669                                         base = d * pow(10, -1.0 * exponent);
11670                                 }
11671                                 fprintf(fp, " %8.5fe%+03d", base, exponent);
11672                         /*      fprintf(fp, " %12.5e", d); */
11673                                 if (k == cp->nz - 1 || k % 6 == 5)
11674                                         fprintf(fp, "\n");
11675                         }
11676                 }
11677         }
11678         fclose(fp);
11679         return 0;
11680 }
11681
11682 #pragma mark ====== Marching Cube (for isosurface) ======
11683
11684 MCube *
11685 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
11686 {
11687         MCube *mc = mol->mcube;
11688         int i;
11689         float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
11690         if (mc != NULL) {
11691                 free(mc->dp);
11692                 free(mc->radii);
11693                 free(mc->c[0].fp);
11694                 free(mc->c[0].cubepoints);
11695                 free(mc->c[0].triangles);
11696                 free(mc->c[1].fp);
11697                 free(mc->c[1].cubepoints);
11698                 free(mc->c[1].triangles);
11699                 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
11700                 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
11701                 free(mc);
11702                 mol->mcube = NULL;
11703         }
11704         if (nx > 0 && ny > 0 && nz > 0) {
11705                 mc = (MCube *)calloc(sizeof(MCube), 1);
11706                 mc->idn = -1;
11707                 /*  round up to nearest 4N+1 integer  */
11708                 dx *= nx;
11709                 dy *= ny;
11710                 dz *= nz;
11711                 mc->nx = (nx + 2) / 4 * 4 + 1;
11712                 mc->ny = (ny + 2) / 4 * 4 + 1;
11713                 mc->nz = (nz + 2) / 4 * 4 + 1;
11714                 mc->dx = dx / mc->nx;
11715                 mc->dy = dy / mc->ny;
11716                 mc->dz = dz / mc->nz;
11717                 mc->origin = *origin;
11718                 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
11719                 if (mc->dp == NULL) {
11720                         free(mc);
11721                         return NULL;
11722                 }
11723                 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
11724                 if (mc->radii == NULL) {
11725                         free(mc->dp);
11726                         free(mc);
11727                         return NULL;
11728                 }
11729                 mc->nradii = mol->natoms;
11730                 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11731                 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11732                 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
11733                         free(mc->c[0].fp);
11734                         free(mc->c[1].fp);
11735                         free(mc->dp);
11736                         free(mc->radii);
11737                         free(mc);
11738                         return NULL;
11739                 }
11740                 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
11741                         mc->dp[i] = DBL_MAX;
11742                 }
11743                 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
11744                 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
11745                 mol->mcube = mc;
11746         }
11747         MoleculeCallback_notifyModification(mol, 0);
11748         return mol->mcube;
11749 }
11750
11751 static int sMarchingCubeTable[256][16] = {
11752         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11753         {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11754         {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11755         {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11756         {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11757         {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11758         {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11759         {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11760         {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11761         {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11762         {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11763         {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11764         {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11765         {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11766         {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11767         {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11768         {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11769         {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11770         {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11771         {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11772         {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11773         {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
11774         {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11775         {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11776         {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11777         {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11778         {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11779         {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
11780         {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11781         {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11782         {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11783         {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11784         {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11785         {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11786         {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11787         {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11788         {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11789         {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11790         {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11791         {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11792         {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11793         {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11794         {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11795         {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11796         {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11797         {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11798         {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11799         {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11800         {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11801         {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11802         {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11803         {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11804         {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11805         {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11806         {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11807         {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11808         {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11809         {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11810         {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11811         {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11812         {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11813         {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11814         {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11815         {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11816         {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11817         {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11818         {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11819         {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11820         {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11821         {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11822         {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11823         {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11824         {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11825         {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11826         {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11827         {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11828         {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11829         {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11830         {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11831         {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11832         {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11833         {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11834         {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11835         {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11836         {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11837         {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11838         {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11839         {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11840         {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11841         {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11842         {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11843         {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11844         {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11845         {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11846         {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11847         {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11848         {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11849         {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11850         {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11851         {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11852         {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11853         {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11854         {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11855         {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11856         {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11857         {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11858         {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11859         {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11860         {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11861         {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11862         {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11863         {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11864         {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11865         {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11866         {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11867         {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11868         {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11869         {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11870         {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11871         {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11872         {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11873         {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11874         {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11875         {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11876         {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11877         {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11878         {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11879         {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11880         {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11881         {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11882         {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11883         {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11884         {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11885         {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11886         {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11887         {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11888         {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11889         {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11890         {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11891         {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11892         {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11893         {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11894         {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11895         {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11896         {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11897         {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11898         {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11899         {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11900         {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11901         {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11902         {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11903         {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11904         {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11905         {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11906         {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11907         {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11908         {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11909         {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11910         {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11911         {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11912         {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11913         {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11914         {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11915         {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11916         {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11917         {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11918         {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11919         {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11920         {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11921         {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11922         {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11923         {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11924         {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11925         {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11926         {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11927         {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11928         {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11929         {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11930         {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11931         {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11932         {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11933         {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11934         {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11935         {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11936         {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11937         {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11938         {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11939         {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11940         {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11941         {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11942         {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11943         {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11944         {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11945         {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11946         {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11947         {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11948         {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11949         {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11950         {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11951         {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11952         {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11953         {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11954         {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11955         {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11956         {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11957         {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11958         {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11959         {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11960         {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11961         {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11962         {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11963         {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11964         {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11965         {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11966         {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11967         {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11968         {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11969         {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11970         {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11971         {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11972         {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11973         {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11974         {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11975         {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11976         {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11977         {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11978         {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11979         {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11980         {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11981         {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11982         {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11983         {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11984         {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11985         {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11986         {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11987         {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11988         {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11989         {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11990         {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11991         {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11992         {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11993         {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11994         {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11995         {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11996         {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11997         {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11998         {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11999         {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12000         {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12001         {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12002         {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12003         {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12004         {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12005         {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12006         {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12007         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12008 };
12009
12010 /*  Recalculate the MCube  */
12011 /*  If idn < 0, then the current grid settings and values are unchanged, and */
12012 /*  only the marching cubes are regenerated.  */
12013 int
12014 MoleculeUpdateMCube(Molecule *mol, int idn)
12015 {
12016         Int retval, step, sn;
12017         Int n, ix, iy, iz, nx, ny, nz;
12018         Int nn, iix, iiy, iiz;
12019         Int ncubepoints, c1, c2, c3;
12020         Int *ip;
12021         Double thres, *tmp, dd;
12022         Vector p;
12023         MCube *mc;
12024         MCubePoint *mcp;
12025         Atom *ap;
12026
12027         if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12028                 return -1;
12029         if (mol->bset->cns == NULL) {
12030                 if (sSetupGaussianCoefficients(mol->bset) != 0)
12031                         return -1;
12032         }
12033         if (mol->bset->natoms_bs > mol->natoms)
12034                 return -1;  /*  Number of atoms is smaller than expected  */
12035
12036         mc = mol->mcube;
12037         if (idn >= 0) {
12038                 ShellInfo *sp;
12039                 Double *mobasep, *mop, mopmax;
12040                 Double xmin, xmax, ymin, ymax, zmin, zmax;
12041                 /*  Clear mcube values  */
12042                 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12043                         mc->dp[ix] = DBL_MAX;
12044                         mc->c[0].fp[ix] = 0;
12045                         mc->c[1].fp[ix] = 0;
12046                 }
12047                 mc->idn = idn;
12048                 /*  Estimate the orbital sizes  */
12049                 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12050                 if (mc->radii == NULL)
12051                         return -2;  /*  Out of memory  */
12052                 mc->nradii = mol->natoms;
12053                 if (mc->idn == mol->bset->nmos + 1) {
12054                         /*  Total electron density  */
12055                         for (ix = 0; ix < mol->natoms; ix++)
12056                                 mc->radii[ix] = 1.0;
12057                         mopmax = 1.0;
12058                 } else {
12059                         memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12060                         mobasep = mol->bset->mo + (mc->idn == 0 ? mol->bset->nmos : mc->idn - 1) * mol->bset->ncomps;
12061                         mopmax = 0.0;
12062                         for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12063                                 if (sp->a_idx >= mol->natoms)
12064                                         continue;  /*  This may happen when molecule is edited after setting up MO info  */
12065                                 mop = mobasep + sp->m_idx;
12066                                 for (iy = 0; iy < sp->ncomp; iy++) {
12067                                         dd = fabs(mop[iy]);
12068                                         if (dd > mc->radii[sp->a_idx])
12069                                                 mc->radii[sp->a_idx] = dd;
12070                                         if (dd > mopmax)
12071                                                 mopmax = dd;
12072                                 }
12073                         }
12074                 }
12075                 xmin = ymin = zmin = 1e10;
12076                 xmax = ymax = zmax = -1e10;
12077                 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12078                         dd = RadiusForAtomicNumber(ap->atomicNumber);
12079                         dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
12080                         mc->radii[ix] = dd;
12081                         p = ap->r;
12082                         dd += 0.1;
12083                         if (p.x - dd < xmin)
12084                                 xmin = p.x - dd;
12085                         if (p.y - dd < ymin)
12086                                 ymin = p.y - dd;
12087                         if (p.z - dd < zmin)
12088                                 zmin = p.z - dd;
12089                         if (p.x + dd > xmax)
12090                                 xmax = p.x + dd;
12091                         if (p.y + dd > ymax)
12092                                 ymax = p.y + dd;
12093                         if (p.z + dd > zmax)
12094                                 zmax = p.z + dd;
12095                 }
12096                 mc->origin.x = xmin;
12097                 mc->origin.y = ymin;
12098                 mc->origin.z = zmin;
12099                 mc->dx = (xmax - xmin) / mc->nx;
12100                 mc->dy = (ymax - ymin) / mc->ny;
12101                 mc->dz = (zmax - zmin) / mc->nz;
12102         }
12103         
12104         /*  Temporary work area  */
12105         tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12106         if (tmp == NULL)
12107                 return -2;
12108         
12109         /*  TODO: use multithread  */
12110         nx = mc->nx;
12111         ny = mc->ny;
12112         nz = mc->nz;
12113         step = 4;
12114         
12115 #if 1
12116         /*  Calculate points within certain distances from atoms  */
12117         for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12118         /*      dd = RadiusForAtomicNumber(ap->atomicNumber);
12119                 if (dd == 0.0)
12120                         dd = 1.0;
12121                 dd = dd * 1.5 + 1.0; */
12122                 dd = mc->radii[nn];
12123                 p.x = ap->r.x - dd - mc->origin.x;
12124                 p.y = ap->r.y - dd - mc->origin.y;
12125                 p.z = ap->r.z - dd - mc->origin.z;
12126                 c1 = p.x / mc->dx;
12127                 c2 = p.y / mc->dy;
12128                 c3 = p.z / mc->dz;
12129                 iix = c1 + ceil(dd * 2.0 / mc->dx);
12130                 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12131                 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12132                 if (c1 < 0)
12133                         c1 = 0;
12134                 if (c2 < 0)
12135                         c2 = 0;
12136                 if (c3 < 0)
12137                         c3 = 0;
12138                 if (iix >= nx)
12139                         iix = nx - 1;
12140                 if (iiy >= ny)
12141                         iiy = ny - 1;
12142                 if (iiz >= nz)
12143                         iiz = nz - 1;
12144                 for (ix = c1; ix <= iix; ix++) {
12145                         p.x = mc->origin.x + mc->dx * ix;
12146                         for (iy = c2; iy <= iiy; iy++) {
12147                                 p.y = mc->origin.y + mc->dy * iy;
12148                                 for (iz = c3; iz <= iiz; iz++) {
12149                                         n = (ix * ny + iy) * nz + iz;
12150                                         if (mc->dp[n] == DBL_MAX) {
12151                                                 p.z = mc->origin.z + mc->dz * iz;
12152                                                 if (mc->idn == mol->bset->nmos + 1) {
12153                                                         /*  Total electron density  */
12154                                                         Int ne_alpha, ne_beta;
12155                                                         mc->dp[n] = 0.0;
12156                                                         ne_alpha = mol->bset->ne_alpha;
12157                                                         ne_beta = mol->bset->ne_beta;
12158                                                         if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12159                                                                 /*  ROHF case: ensure ne_alpha >= ne_beta  */
12160                                                                 ne_beta = ne_alpha;
12161                                                                 ne_alpha = mol->bset->ne_beta;
12162                                                         }
12163                                                         for (sn = 1; sn <= ne_alpha; sn++) {
12164                                                                 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12165                                                                 dd = dd * dd;
12166                                                                 if (mol->bset->rflag != 0 && sn <= ne_beta)
12167                                                                         dd *= 2;
12168                                                                 mc->dp[n] += dd;
12169                                                         }
12170                                                         if (mol->bset->rflag == 0) {
12171                                                                 for (sn = 1; sn <= ne_beta; sn++) {
12172                                                                         dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12173                                                                         mc->dp[n] += dd * dd;
12174                                                                 }
12175                                                         }
12176                                                 } else {
12177                                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12178                                                 }
12179                                         }
12180                                 }
12181                         }
12182                 }
12183         }
12184         
12185 #else
12186         /*  (i * step, j * step, k * step)  */
12187         for (ix = 0; ix < nx; ix += step) {
12188                 for (iy = 0; iy < ny; iy += step) {
12189                         for (iz = 0; iz < nz; iz += step) {
12190                                 n = (ix * ny + iy) * nz + iz;
12191                                 if (mc->dp[n] == DBL_MAX) {
12192                                         p.x = mc->origin.x + mc->dx * ix;
12193                                         p.y = mc->origin.y + mc->dy * iy;
12194                                         p.z = mc->origin.z + mc->dz * iz;
12195                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12196                                 }
12197                                 n += step;
12198                         }
12199                 }
12200         }
12201         
12202         /*  Intermediate points  */
12203         for (step = 4; step > 1; step /= 2) {
12204                 hstep = step / 2;
12205                 for (sn = 0; sn <= 1; sn++) {
12206                         n = 0;
12207                         for (ix = 0; ix < nx - 1; ix += step) {
12208                                 for (iy = 0; iy < ny - 1; iy += step) {
12209                                         for (iz = 0; iz < nz - 1; iz += step) {
12210                                                 flags = 0;
12211                                                 thres = mc->thres * (sn == 0 ? 1 : -1);
12212                                                 n = (ix * ny + iy) * nz + iz;
12213                                                 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12214                                                         continue;
12215                                                 /*  (ix, iy, iz)  */
12216                                                 if (mc->dp[n] >= thres)
12217                                                         flags |= 1;
12218                                                 /*  (ix + step, iy, iz)  */
12219                                                 if (mc->dp[n + step * ny * nz] >= thres)
12220                                                         flags |= 2;
12221                                                 /*  (ix, iy + step, iz)  */
12222                                                 if (mc->dp[n + step * nz] >= thres)
12223                                                         flags |= 4;
12224                                                 /*  (ix + 4, iy + step, iz)  */
12225                                                 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12226                                                         flags |= 8;
12227                                                 /*  (ix, iy, iz + step)  */
12228                                                 if (mc->dp[n + step] >= thres)
12229                                                         flags |= 16;
12230                                                 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12231                                                         flags |= 32;
12232                                                 /*  (ix, iy + step, iz + step)  */
12233                                                 if (mc->dp[n + step * (nz + 1)] >= thres)
12234                                                         flags |= 64;
12235                                                 /*  (ix + step, iy + step, iz + step)  */
12236                                                 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12237                                                         flags |= 128;
12238                                                 if (flags != 0 && flags != 255) {
12239                                                         /*  Calc the intermediate points  */
12240                                                         for (iix = 0; iix <= step; iix += hstep) {
12241                                                                 for (iiy = 0; iiy <= step; iiy += hstep) {
12242                                                                         for (iiz = 0; iiz <= step; iiz += hstep) {
12243                                                                                 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12244                                                                                         continue;
12245                                                                                 nn = n + (iix * ny + iiy) * nz + iiz;
12246                                                                                 if (mc->dp[nn] == DBL_MAX) {
12247                                                                                         p.x = mc->origin.x + mc->dx * (ix + iix);
12248                                                                                         p.y = mc->origin.y + mc->dy * (iy + iiy);
12249                                                                                         p.z = mc->origin.z + mc->dz * (iz + iiz);
12250                                                                                         mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12251                                                                                 }
12252                                                                         }
12253                                                                 }
12254                                                         }
12255                                                 }
12256                                         }
12257                                 }
12258                         }
12259                 }
12260         }
12261         
12262 #endif
12263
12264         free(tmp);
12265         
12266         /*  Calculate vertex positions and normal vectors  */
12267         for (sn = 0; sn <= 1; sn++) {
12268                 n = 0;
12269                 thres = mc->thres * (sn == 0 ? 1 : -1);
12270                 VecZero(p);
12271                 for (ix = 0; ix < nx - 1; ix++) {
12272                         for (iy = 0; iy < ny - 1; iy++) {
12273                                 for (iz = 0; iz < nz - 1; iz++) {
12274                                         Double dd0, dd1;
12275                                         nn = (ix * ny + iy) * nz + iz;
12276                                         dd0 = mc->dp[nn];
12277                                         if (dd0 == DBL_MAX)
12278                                                 continue;
12279                                         if (0) {
12280                                                 dd1 = mc->dp[nn + ny * nz];
12281                                                 if (dd1 != DBL_MAX)
12282                                                         p.x = (dd1 - dd0) / mc->dx;
12283                                                 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12284                                                         p.x = (dd0 - dd1) / mc->dx;
12285                                                 else continue;  /*  Cannot define gradient  */
12286                                                 dd1 = mc->dp[nn + nz];
12287                                                 if (dd1 != DBL_MAX)
12288                                                         p.y = (dd1 - dd0) / mc->dy;
12289                                                 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12290                                                         p.y = (dd0 - dd1) / mc->dy;
12291                                                 else continue;
12292                                                 dd1 = mc->dp[nn + 1];
12293                                                 if (dd1 != DBL_MAX)
12294                                                         p.z = (dd1 - dd0) / mc->dz;
12295                                                 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12296                                                         p.z = (dd0 - dd1) / mc->dz;
12297                                                 else continue;
12298                                                 NormalizeVec(&p, &p);
12299                                         }
12300                                         if (n + 3 >= mc->c[sn].ncubepoints) {
12301                                                 /*  Expand cubepoints[] array  */
12302                                                 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12303                                                 if (mc->c[sn].cubepoints == NULL) {
12304                                                         mc->c[sn].ncubepoints = 0;
12305                                                         retval = -3;
12306                                                         goto end;
12307                                                 }
12308                                                 mc->c[sn].ncubepoints += 8192;
12309                                         }
12310                                         mcp = mc->c[sn].cubepoints + n;
12311                                         iix = (dd0 >= thres ? 1 : -1);
12312                                         /*  (x, y, z)->(x + 1, y, z)  */
12313                                         dd1 = mc->dp[nn + ny * nz];
12314                                         if (dd1 != DBL_MAX) {
12315                                                 iiy = (dd1 >= thres ? 1 : -1);
12316                                                 if (iix != iiy) {
12317                                                         /*  Register  */
12318                                                         mcp->key = nn * 3;
12319                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12320                                                         mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12321                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12322                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12323                                                         mcp->grad[0] = p.x;
12324                                                         mcp->grad[1] = p.y;
12325                                                         mcp->grad[2] = p.z;
12326                                                         mcp++;
12327                                                         n++;
12328                                                 }
12329                                         }
12330                                         /*  (x, y, z)->(x, y + 1, z)  */
12331                                         dd1 = mc->dp[nn + nz];
12332                                         if (dd1 != DBL_MAX) {
12333                                                 iiy = (dd1 >= thres ? 1 : -1);
12334                                                 if (iix != iiy) {
12335                                                         /*  Register  */
12336                                                         mcp->key = nn * 3 + 1;
12337                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12338                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12339                                                         mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12340                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12341                                                         mcp->grad[0] = p.x;
12342                                                         mcp->grad[1] = p.y;
12343                                                         mcp->grad[2] = p.z;
12344                                                         mcp++;
12345                                                         n++;
12346                                                 }
12347                                         }
12348                                         /*  (x, y, z)->(x, y, z + 1)  */
12349                                         dd1 = mc->dp[nn + 1];
12350                                         if (dd1 != DBL_MAX) {
12351                                                 iiy = (dd1 >= thres ? 1 : -1);
12352                                                 if (iix != iiy) {
12353                                                         /*  Register  */
12354                                                         mcp->key = nn * 3 + 2;
12355                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12356                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12357                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12358                                                         mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12359                                                         mcp->grad[0] = p.x;
12360                                                         mcp->grad[1] = p.y;
12361                                                         mcp->grad[2] = p.z;
12362                                                         mcp++;
12363                                                         n++;
12364                                                 }
12365                                         }
12366                                 }
12367                         }
12368                 }
12369                 if (n < mc->c[sn].ncubepoints)
12370                         mc->c[sn].cubepoints[n].key = -1;  /*  End mark  */
12371                 ncubepoints = n;
12372                 if (ncubepoints < 3) {
12373                         /*  Less than 3 points: no triangles  */
12374                         if (mc->c[sn].ntriangles > 0)
12375                                 mc->c[sn].triangles[0] = -1;  /*  End mark  */
12376                         continue;
12377                 }
12378                 
12379                 /*  Create triangle table  */
12380                 n = 0;
12381                 for (ix = 0; ix < nx - 1; ix++) {
12382                         for (iy = 0; iy < ny - 1; iy++) {
12383                                 for (iz = 0; iz < nz - 1; iz++) {
12384                                         nn = (ix * ny + iy) * nz + iz;
12385                                         iix = 0;
12386                                         if ((dd = mc->dp[nn]) == DBL_MAX)
12387                                                 continue;
12388                                         else if (dd >= thres)
12389                                                 iix |= 1;
12390                                         if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12391                                                 continue;
12392                                         else if (dd >= thres)
12393                                                 iix |= 2;
12394                                         if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12395                                                 continue;
12396                                         else if (dd >= thres)
12397                                                 iix |= 4;
12398                                         if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12399                                                 continue;
12400                                         else if (dd >= thres)
12401                                                 iix |= 8;
12402                                         if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12403                                                 continue;
12404                                         else if (dd >= thres)
12405                                                 iix |= 16;
12406                                         if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12407                                                 continue;
12408                                         else if (dd >= thres)
12409                                                 iix |= 32;
12410                                         if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12411                                                 continue;
12412                                         else if (dd >= thres)
12413                                                 iix |= 64;
12414                                         if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12415                                                 continue;
12416                                         else if (dd >= thres)
12417                                                 iix |= 128;
12418                                         for (iiy = 0; iiy < 15; iiy++) {
12419                                                 nn = sMarchingCubeTable[iix][iiy];
12420                                                 if (nn < 0)
12421                                                         break;
12422                                                 /*  key index for edges 0-11  */
12423                                                 switch (nn) {
12424                                                         case 0:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3;     break;
12425                                                         case 1:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12426                                                         case 2:  iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3;     break;
12427                                                         case 3:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12428                                                         case 4:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3;     break;
12429                                                         case 5:  iiz = (((ix + 1) * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12430                                                         case 6:  iiz = (( ix      * ny + iy + 1) * nz + iz + 1) * 3;     break;
12431                                                         case 7:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12432                                                         case 8:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12433                                                         case 9:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12434                                                         case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12435                                                         case 11: iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12436                                                         default:
12437                                                                 /*  Skip this triangle  */
12438                                                                 iiy = (iiy - iiy % 3) + 2;
12439                                                                 n = n - n % 3;
12440                                                                 continue;
12441                                                 }
12442                                                 /*  Look for the key index in cubepoints  */
12443                                                 c1 = 0;
12444                                                 c3 = ncubepoints - 1;
12445                                                 mcp = mc->c[sn].cubepoints;
12446                                                 while (1) {
12447                                                         int w;
12448                                                         /*  c1 is always less than c3  */
12449                                                         if (c1 + 1 == c3) {
12450                                                                 /*  end of search  */
12451                                                                 if (mcp[c1].key == iiz) {
12452                                                                         c2 = c1;
12453                                                                 } else if (mcp[c3].key == iiz) {
12454                                                                         c2 = c3;
12455                                                                 } else {
12456                                                                         c2 = -1;
12457                                                                 }
12458                                                                 break;
12459                                                         }
12460                                                         c2 = (c1 + c3) / 2;
12461                                                         w = mcp[c2].key - iiz;
12462                                                         if (w == 0)
12463                                                                 break;
12464                                                         if (w < 0) {
12465                                                                 c1 = c2;
12466                                                         } else {
12467                                                                 c3 = c2;
12468                                                         }
12469                                                 }
12470                                                 if (c2 < 0) {
12471                                                         /*  Not found: skip this triangle  */
12472                                                         iiy = (iiy - iiy % 3) + 2;
12473                                                         n = n - n % 3;
12474                                                         continue;
12475                                                 }
12476                                                 if (n + 1 >= mc->c[sn].ntriangles) {
12477                                                         /*  Expand triangles[] array  */
12478                                                         mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12479                                                         if (mc->c[sn].triangles == NULL) {
12480                                                                 mc->c[sn].ntriangles = 0;
12481                                                                 retval = -4;
12482                                                                 goto end;
12483                                                         }
12484                                                         mc->c[sn].ntriangles += 8192;
12485                                                 }
12486                                                 mc->c[sn].triangles[n] = c2;
12487                                                 n++;
12488                                         }
12489                                 }
12490                         }
12491                 }
12492                 if (n < mc->c[sn].ntriangles)
12493                         mc->c[sn].triangles[n] = -1;  /*  End mark  */
12494                 
12495                 /*  Estimate the normal vector  */
12496                 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12497                         Vector v[3];
12498                         for (ix = 0; ix < 3; ix++) {
12499                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12500                                 v[ix].x = mcp->pos[0];
12501                                 v[ix].y = mcp->pos[1];
12502                                 v[ix].z = mcp->pos[2];
12503                         }
12504                         VecDec(v[2], v[0]);
12505                         VecDec(v[1], v[0]);
12506                         VecCross(v[0], v[1], v[2]);
12507                         NormalizeVec(v, v);
12508                         for (ix = 0; ix < 3; ix++) {
12509                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12510                                 mcp->grad[0] += v[0].x;
12511                                 mcp->grad[1] += v[0].y;
12512                                 mcp->grad[2] += v[0].z;
12513                         }
12514                 }
12515                 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12516                         if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12517                                 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12518                                 if (mc->thres < 0.0)
12519                                         dd = -dd;
12520                                 mcp->grad[0] *= dd;
12521                                 mcp->grad[1] *= dd;
12522                                 mcp->grad[2] *= dd;
12523                         }
12524                 }
12525         }
12526         retval = 0;
12527         MoleculeCallback_notifyModification(mol, 0);
12528 end:
12529         /*  For debug  */
12530         if (0) {
12531                 char *MyAppCallback_getDocumentHomeDir(void);
12532                 FILE *fp;
12533                 char *s;
12534                 Double dmax, dmin;
12535                 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12536                 fp = fopen(s, "w");
12537                 dmax = -1e8;
12538                 dmin = 1e8;
12539                 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12540                         if (mc->dp[n] == DBL_MAX)
12541                                 continue;
12542                         if (dmax < mc->dp[n])
12543                                 dmax = mc->dp[n];
12544                         if (dmin > mc->dp[n])
12545                                 dmin = mc->dp[n];
12546                 }
12547                 dmax = fabs(dmax);
12548                 dmin = fabs(dmin);
12549                 if (dmax < dmin)
12550                         dmax = dmin;
12551                 dmax = 1.001 * dmax;
12552                 fprintf(fp, "thres = %g = 100\n", mc->thres);
12553                 for (iz = 0; iz < mc->nz; iz++) {
12554                         fprintf(fp, "z = %d\n", iz);
12555                         for (iy = 0; iy < mc->ny; iy++) {
12556                                 for (ix = 0; ix < mc->nx; ix++) {
12557                                         n = (ix * ny + iy) * nz + iz;
12558                                         dd = mc->dp[n];
12559                                         if (dd == DBL_MAX)
12560                                                 fprintf(fp, " XXX ");
12561                                         else {
12562                                                 dd = dd * 100 / mc->thres;
12563                                                 if (dd > 999.0)
12564                                                         dd = 999.0;
12565                                                 else if (dd < -999.0)
12566                                                         dd = -999.0;
12567                                                 fprintf(fp, "%4d ", (int)(dd));
12568                                         }
12569                                 }
12570                                 fprintf(fp, "\n");
12571                         }
12572                         fprintf(fp, "\n");
12573                 }
12574                 
12575                 for (sn = 0; sn <= 1; sn++) {
12576                         for (n = 0; n < mc->c[sn].ncubepoints; n++) {
12577                                 MCubePoint *mcp = mc->c[sn].cubepoints + n;
12578                                 nn = mcp->key;
12579                                 if (nn == -1)
12580                                         break;
12581                                 iix = nn % 3;
12582                                 iz = nn / 3 % mc->nz;
12583                                 iy = nn / (3 * mc->nz) % mc->ny;
12584                                 ix = nn / (3 * mc->nz * mc->ny);
12585                                 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
12586                                                 n, ix, iy, iz, iix,
12587                                                 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
12588                         }
12589                         for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
12590                                 if (mc->c[sn].triangles[n] < 0)
12591                                         break;
12592                                 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
12593                                                 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
12594                         }
12595                 }
12596                 fclose(fp);
12597         }
12598         
12599         return retval;
12600 }
12601
12602 void
12603 MoleculeDeallocateMCube(MCube *mcube)
12604 {
12605         free(mcube->dp);
12606         free(mcube->radii);
12607         free(mcube->c[0].cubepoints);
12608         free(mcube->c[0].triangles);
12609         free(mcube->c[1].cubepoints);
12610         free(mcube->c[1].triangles);
12611         free(mcube);
12612 }