OSDN Git Service

Start implementing 'additional exponent' for JANPA output
[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], 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, Int add_exp)
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     shellp->add_exp = add_exp;
2751         /*  Update the number of components (if not yet determined)  */
2752         if (bset->ncomps < shellp->m_idx + shellp->ncomp)
2753                 bset->ncomps = shellp->m_idx + shellp->ncomp;
2754         return 0;
2755 }
2756
2757 /*  Add a set of gaussian primitive coefficients (not undoable)  */
2758 int
2759 MoleculeAddGaussianPrimitiveCoefficients(Molecule *mol, Double exponent, Double contraction, Double contraction_sp)
2760 {
2761         BasisSet *bset;
2762         PrimInfo *primp;
2763         if (mol == NULL)
2764                 return -1;  /*  Molecule is empty  */
2765         bset = mol->bset;
2766         if (bset == NULL) {
2767                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2768                 if (bset == NULL)
2769                         return -2;  /*  Low memory  */
2770         }
2771         primp = AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), bset->npriminfos, NULL);
2772         if (primp == NULL)
2773                 return -2;  /*  Low memory  */
2774         primp->A = exponent;
2775         primp->C = contraction;
2776         primp->Csp = contraction_sp;
2777         return 0;
2778 }
2779
2780 /*  Get the shell information from the component index  */
2781 /*  The outLabel must have space for at least 23 non-Null characters  */
2782 int
2783 MoleculeGetGaussianComponentInfo(Molecule *mol, Int comp_idx, Int *outAtomIdx, char *outLabel, Int *outShellIdx)
2784 {
2785         BasisSet *bset;
2786         ShellInfo *shellp;
2787         int si;
2788         if (mol == NULL || (bset = mol->bset) == NULL)
2789                 return -1;  /*  No basis set info  */
2790         if (comp_idx < 0 || comp_idx >= bset->ncomps)
2791                 return -2;  /*  Component index out of range  */
2792         for (si = 0, shellp = bset->shells; si < bset->nshells; si++, shellp++) {
2793                 if (comp_idx >= shellp->ncomp) {
2794                         comp_idx -= shellp->ncomp;
2795                         continue;
2796                 } else {
2797                         static const char *type_p = "xyz";
2798                         static const char *type_d = "xxyyzzxyxzyz";
2799                         static const char *type_d5[] = {"xy","yz","zz", "xz", "xx-yy"};
2800                         static const char *type_f = "xxxyyyzzzxxyxxzxyyyyzxzzyzzxyz";
2801                         static const char *type_f7[] = {"x3-3xy2", "x2z-y2z", "x(5z2-r2)", "z(5z2-3r2)", "y(5z2-r2)", "xyz", "3x2y-y3"};
2802                         static const char *type_g[] = {"x4", "y4", "z4", "x3y", "x3z", "xy3", "y3z", "xz3", "yz3", "x2y2", "x2z2", "y2z2", "x2yz", "x2yz", "xyz2"};
2803                         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)"};
2804                         *outAtomIdx = shellp->a_idx;
2805                         *outShellIdx = si;
2806                         switch (shellp->sym) {
2807                                 case kGTOType_S:
2808                                         strcpy(outLabel, "S");
2809                                         break;
2810                                 case kGTOType_P:
2811                                         outLabel[0] = 'P';
2812                                         outLabel[1] = type_p[comp_idx];
2813                                         outLabel[2] = 0;
2814                                         break;
2815                                 case kGTOType_SP:
2816                                         if (comp_idx == 0)
2817                                                 strcpy(outLabel, "S");
2818                                         else {
2819                                                 outLabel[0] = 'P';
2820                                                 outLabel[1] = type_p[comp_idx - 1];
2821                                                 outLabel[2] = 0;
2822                                         }
2823                                         break;
2824                                 case kGTOType_D:
2825                                         outLabel[0] = 'D';
2826                                         strncpy(outLabel + 1, type_d + comp_idx * 2, 2);
2827                                         outLabel[3] = 0;
2828                                         break;
2829                                 case kGTOType_D5:
2830                                         outLabel[0] = 'D';
2831                                         strcpy(outLabel + 1, type_d5[comp_idx]);
2832                                         break;
2833                                 case kGTOType_F:
2834                                         outLabel[0] = 'F';
2835                                         strncpy(outLabel + 1, type_f + comp_idx * 3, 3);
2836                                         outLabel[4] = 0;
2837                                         break;
2838                                 case kGTOType_F7:
2839                                         outLabel[0] = 'F';
2840                                         strcpy(outLabel + 1, type_f7[comp_idx]);
2841                                         break;
2842                                 case kGTOType_G:
2843                                         outLabel[0] = 'G';
2844                                         strcpy(outLabel + 1, type_g[comp_idx]);
2845                                         break;
2846                                 case kGTOType_G9:
2847                                         outLabel[0] = 'G';
2848                                         strcpy(outLabel + 1, type_g9[comp_idx]);
2849                                         break;
2850                                 default:
2851                                         return -3;  /*  Unsupported orbital type (internal error) */
2852                         }
2853                         return 0;
2854                 }
2855         }
2856         return -4;  /*  comp_idx out of range? (internal error)  */
2857 }
2858
2859 /*  Set MO coefficients for idx-th MO (1-based)  */
2860 int
2861 MoleculeSetMOCoefficients(Molecule *mol, Int idx, Double energy, Int ncomps, Double *coeffs)
2862 {
2863         BasisSet *bset;
2864         int i, n;
2865         if (mol == NULL)
2866                 return -1;  /*  Molecule is empty  */
2867         bset = mol->bset;
2868         if (bset == NULL) {
2869                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2870                 if (bset == NULL)
2871                         return -2;  /*  Low memory  */
2872         }
2873         if (bset->nmos == 0) {
2874                 if (bset->nshells > 0) {
2875                         /*  Shell info is already set: calculate the number of MOs from there  */
2876                         for (i = n = 0; i < bset->nshells; i++)
2877                                 n += bset->shells[i].ncomp;
2878                         bset->ncomps = n;
2879                 } else if (ncomps > 0) {
2880                         bset->ncomps = ncomps;
2881                 }
2882                 if (bset->rflag == 0)
2883                         bset->nmos = bset->ncomps * 2;
2884                 else
2885                         bset->nmos = bset->ncomps;
2886                 if (bset->nmos <= 0)
2887                         return -3;  /*  Bad or inconsistent number of MOs  */
2888                 bset->mo = (Double *)calloc(sizeof(Double), (bset->nmos + 1) * bset->ncomps);
2889                 bset->moenergies = (Double *)calloc(sizeof(Double), bset->nmos + 1);
2890                 if (bset->mo == NULL || bset->moenergies == NULL) {
2891                         if (bset->mo != NULL)
2892                                 free(bset->mo);
2893                         if (bset->moenergies != NULL)
2894                                 free(bset->moenergies);
2895                         bset->mo = NULL;
2896                         bset->moenergies = NULL;
2897                         bset->nmos = 0;
2898                         return -2;  /*  Low memory  */
2899                 }
2900         }
2901         if (idx < 0)
2902                 idx = -idx + bset->ncomps;
2903         if (idx < 0 || idx > bset->nmos)
2904                 return -4;  /*  Bad MO index  */
2905         if (idx == 0)
2906                 idx = bset->nmos;  /*  Arbitrary vector  */
2907         else
2908                 idx--;
2909         if (energy != -1000000)
2910                 bset->moenergies[idx] = energy;
2911         if (ncomps < bset->ncomps)
2912                 return -5;  /*  Insufficient number of data provided  */
2913         memmove(bset->mo + (idx * bset->ncomps), coeffs, sizeof(Double) * bset->ncomps);
2914         if (bset->cns != NULL) {
2915                 /*  Clear the cached values  */
2916                 free(bset->cns);
2917                 bset->cns = NULL;
2918                 bset->ncns = 0;
2919         }
2920         return 0;
2921 }
2922
2923 /*  Get MO coefficients for idx-th MO (1-based)  */
2924 /*  Caution: *ncoeffs and *coeffs should be valid _before_ calling this function, i.e.  */
2925 /*  *ncoeffs = 0 && *coeffs = NULL or *coeffs is a valid memory pointer and *ncoeffs  */
2926 /*  properly designates the memory size as an array of Doubles.  */
2927 int
2928 MoleculeGetMOCoefficients(Molecule *mol, Int idx, Double *energy, Int *ncoeffs, Double **coeffs)
2929 {
2930         BasisSet *bset;
2931         if (mol == NULL)
2932                 return -1;  /*  Molecule is empty  */
2933         bset = mol->bset;
2934         if (bset == NULL || bset->ncomps <= 0)
2935                 return -2;  /*  No basis set info  */
2936         if (idx < 0)
2937                 idx = -idx + bset->ncomps;
2938         if (idx < 0 || idx > bset->nmos)
2939                 return -3;  /*  MO index out of range  */
2940         if (idx == 0)
2941                 idx = bset->nmos;  /*  Arbitrary vector  */
2942         else
2943                 idx--;
2944         if (energy != NULL)
2945                 *energy = bset->moenergies[idx];
2946         if (ncoeffs != NULL && coeffs != NULL) {
2947                 if (*ncoeffs < bset->ncomps || *coeffs == NULL) {
2948                         if (*coeffs != NULL)
2949                                 free(*coeffs);  /*  Caution: possible cause of SIGBUS if *coeff is not initialized properly */
2950                         *coeffs = (Double *)calloc(sizeof(Double), bset->ncomps);
2951                         *ncoeffs = bset->ncomps;
2952                 }
2953                 memmove(*coeffs, bset->mo + (idx * bset->ncomps), sizeof(Double) * bset->ncomps);
2954         }
2955         return 0;
2956 }
2957
2958 /*  Set Basic MO Info. rflag: 0, UHF; 1, RHF; 2, ROHF; -1, clear
2959     ne_alpha: number of alpha electrons, ne_beta: number of beta electrons   */
2960 int
2961 MoleculeSetMOInfo(Molecule *mol, Int rflag, Int ne_alpha, Int ne_beta)
2962 {
2963         BasisSet *bset;
2964         if (mol == NULL || mol->natoms == 0)
2965                 return -1;  /*  Molecule is empty  */
2966         if (rflag < 0) {
2967                 if (mol->bset != NULL) {
2968                         BasisSetRelease(mol->bset);
2969                         mol->bset = NULL;
2970                 }
2971                 return 0;
2972         }
2973         bset = mol->bset;
2974         if (bset == NULL) {
2975                 bset = mol->bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
2976                 if (bset == NULL)
2977                         return -2;  /*  Low memory  */
2978         }
2979         bset->natoms_bs = mol->natoms;
2980         bset->ne_alpha = ne_alpha;
2981         bset->ne_beta = ne_beta;
2982         bset->rflag = rflag;
2983         return 0;
2984 }
2985
2986 static void
2987 sSeparateTokens(char *inString, char **outPtr, int size)
2988 {
2989         char *p;
2990         int i;
2991         for (i = 0; i < size; i++) {
2992                 p = strtok((i == 0 ? inString : NULL), " \r\n");
2993                 if (p == NULL)
2994                         break;
2995                 outPtr[i] = p;
2996         }
2997         while (i < size) {
2998                 outPtr[i++] = NULL;
2999         }
3000 }
3001
3002 static int
3003 sReadNumberArray(void *basep, Int *countp, Int size, Int num, FILE *fp, int *lnp)
3004 {
3005         char buf[256];
3006         Int i, n;
3007         *((void **)basep) = NULL;
3008         *countp = 0;
3009         if (AssignArray(basep, countp, size, num - 1, NULL) == NULL)
3010                 return 4;  /*  Out of memory  */
3011         n = 0;
3012         while (ReadLine(buf, sizeof buf, fp, lnp) > 0) {
3013                 char *tokens[16], *p;
3014                 sSeparateTokens(buf, tokens, 16);
3015                 for (i = 0; i < 16; i++) {
3016                         if (tokens[i] == NULL)
3017                                 break;
3018                         if (size == sizeof(Int)) {
3019                                 (*((Int **)basep))[n] = strtol(tokens[i], &p, 0);
3020                         } else if (size == sizeof(Double)) {
3021                                 (*((Double **)basep))[n] = strtod(tokens[i], &p);
3022                         } else return -1;  /*  Internal error  */
3023                         if (tokens[i] == p || *p != 0)
3024                                 return 1;  /*  Non-digit character  */
3025                         if (++n == num) {
3026                                 if (i < 15 && tokens[i + 1] != NULL)
3027                                         return 2;  /*  Too many data  */
3028                                 return 0;  /*  All data are successfully read  */
3029                         }
3030                 }
3031         }
3032         return 3;  /*  Unexpected EOF  */                       
3033 }
3034
3035 static int
3036 sSetupGaussianCoefficients(BasisSet *bset)
3037 {
3038         ShellInfo *sp;
3039         PrimInfo *pp;
3040         int i, j, k;
3041         Double *dp, d;
3042         
3043         /*  Cache the contraction coefficients for efficient calculation  */
3044         /*  Sum up the number of components for all primitives  */
3045         for (i = k = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3046                 sp->cn_idx = k;
3047                 k += sp->nprim * sp->ncomp;
3048         }
3049         /*  Allocate memory for the cached values  */
3050         if (AssignArray(&bset->cns, &bset->ncns, sizeof(Double), k - 1, NULL) == NULL)
3051                 return 1;
3052         /*  Iterate over all primitives  */
3053         dp = bset->cns;
3054         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3055                 for (j = 0, pp = bset->priminfos + sp->p_idx; j < sp->nprim; j++, pp++) {
3056                         switch (sp->sym) {
3057                                 case kGTOType_S:
3058                                         // (8 alpha^3/pi^3)^0.25 exp(-alpha r^2)
3059                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3060                                         break;
3061                                 case kGTOType_P:
3062                                         // (128 alpha^5/pi^3)^0.25 [x|y|z]exp(-alpha r^2)
3063                                         d = pp->C * pow(pp->A, 1.25) * 1.425410941;
3064                                         *dp++ = d;
3065                                         *dp++ = d;
3066                                         *dp++ = d;
3067                                         break;
3068                                 case kGTOType_SP:
3069                                         *dp++ = pp->C * pow(pp->A, 0.75) * 0.71270547;
3070                                         d = pp->Csp * pow(pp->A, 1.25) * 1.425410941;
3071                                         *dp++ = d;
3072                                         *dp++ = d;
3073                                         *dp++ = d;
3074                                         break;
3075                                 case kGTOType_D:
3076                                         //  xx|yy|zz: (2048 alpha^7/9pi^3)^0.25 [xx|yy|zz]exp(-alpha r^2)
3077                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3078                                         d = pp->C * pow(pp->A, 1.75);
3079                                         dp[0] = dp[1] = dp[2] = d * 1.645922781;
3080                                         dp[3] = dp[4] = dp[5] = d * 2.850821881;
3081                                         dp += 6;
3082                                         break;
3083                                 case kGTOType_D5:
3084                                         //  3zz-rr:   (128 alpha^7/9pi^3)^0.25 (3zz-rr)exp(-alpha r^2)
3085                                         //  xy|yz|zx: (2048 alpha^7/pi^3)^0.25 [xy|xz|yz]exp(-alpha r^2)
3086                                         //  xx-yy:    (128 alpha^7/pi^3)^0.25 (xx-yy)exp(-alpha r^2)
3087                                         d = pp->C * pow(pp->A, 1.75);
3088                                         dp[0] = d * 0.822961390;
3089                                         dp[1] = dp[2] = dp[4] = d * 2.850821881;
3090                                         dp[3] = d * 1.425410941;
3091                                         dp += 5;
3092                                         break;
3093                                 /*  TODO: Support F/F7 and G/G9 type orbitals  */
3094                         }
3095                 }
3096         }
3097         return 0;
3098 }
3099
3100 int
3101 MoleculeLoadGaussianFchkFile(Molecule *mp, const char *fname, char **errbuf)
3102 {
3103         FILE *fp;
3104         char buf[1024];
3105         int lineNumber;
3106         int natoms, nbasis, i, j, k, n, mxbond, retval, ncomps, nprims, nelec;
3107         BasisSet *bset;
3108         ShellInfo *sp;
3109         PrimInfo *pp;
3110         Int nary;
3111         Int *iary;
3112         Double *dary;
3113         Atom *ap;
3114 /*      Vector *vp; */
3115         Double w;
3116
3117         *errbuf = NULL;
3118         if (mp == NULL)
3119                 mp = MoleculeNew();
3120         bset = (BasisSet *)calloc(sizeof(BasisSet), 1);
3121         if (bset == NULL)
3122                 goto panic;
3123         mp->bset = bset;
3124         fp = fopen(fname, "rb");
3125         if (fp == NULL) {
3126                 s_append_asprintf(errbuf, "Cannot open file");
3127                 return 1;
3128         }
3129         lineNumber = 0;
3130         natoms = nbasis = -1;
3131         mxbond = 0;
3132         ncomps = 0;
3133         nelec = 0;
3134         nprims = 0;
3135         nary = 0;
3136         iary = NULL;
3137         dary = NULL;
3138         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3139                 char *tokens[16];
3140                 char *p = buf + 41;
3141                 if (lineNumber == 2) {
3142                         /*  job info line  */
3143                         if (buf[10] == 'U')
3144                                 bset->rflag = 0;  /*  UHF  */
3145                         else if (buf[11] == 'O')
3146                                 bset->rflag = 2;  /*  ROHF  */
3147                         else bset->rflag = 1; /*  RHF  */
3148                         continue;
3149                 }
3150                 while (p > buf && *p == ' ')
3151                         p--;
3152                 p[1] = 0;
3153                 sSeparateTokens(buf + 42, tokens, 16);
3154                 if (strcmp(buf, "Number of atoms") == 0) {
3155                         if (tokens[1] == NULL || (natoms = atoi(tokens[1])) <= 0) {
3156                                 s_append_asprintf(errbuf, "Line %d: strange number of atoms: %s", lineNumber, tokens[1]);
3157                                 retval = 2;
3158                                 goto cleanup;
3159                         }
3160                         bset->natoms_bs = natoms;
3161                         /*  Allocate atom records (all are empty for now)  */
3162                         AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, natoms - 1, NULL);
3163                         /*  Also allocate atom position array for MO calculations  */
3164                 /*      AssignArray(&bset->pos, &bset->natoms, sizeof(Vector), natoms - 1, NULL); */
3165                         /*  Also allocate nuclear charge array  */
3166                         bset->nuccharges = (Double *)calloc(sizeof(Double), natoms);
3167                 } else if (strcmp(buf, "Number of electrons") == 0) {
3168                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3169                                 s_append_asprintf(errbuf, "Line %d: strange number of electrons: %s", lineNumber, tokens[1]);
3170                                 retval = 2;
3171                                 goto cleanup;
3172                         }
3173                         nelec = i;
3174                 } else if (strcmp(buf, "Number of alpha electrons") == 0) {
3175                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3176                                 s_append_asprintf(errbuf, "Line %d: strange number of alpha electrons: %s", lineNumber, tokens[1]);
3177                                 retval = 2;
3178                                 goto cleanup;
3179                         }
3180                         bset->ne_alpha = i;
3181                 } else if (strcmp(buf, "Number of beta electrons") == 0) {
3182                         if (tokens[1] == NULL || (i = atoi(tokens[1])) < 0) {
3183                                 s_append_asprintf(errbuf, "Line %d: strange number of beta electrons: %s", lineNumber, tokens[1]);
3184                                 retval = 2;
3185                                 goto cleanup;
3186                         }
3187                         bset->ne_beta = i;
3188                         if (bset->ne_alpha + bset->ne_beta != nelec) {
3189                                 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);
3190                                 retval = 2;
3191                                 goto cleanup;
3192                         }
3193                 } else if (strcmp(buf, "Number of basis functions") == 0) {
3194                         if (tokens[1] == NULL || (nbasis = atoi(tokens[1])) <= 0) {
3195                                 s_append_asprintf(errbuf, "Line %d: strange number of basis functions: %s", lineNumber, tokens[1]);
3196                                 retval = 2;
3197                                 goto cleanup;
3198                         }
3199                 } else if (strcmp(buf, "Atomic numbers") == 0) {
3200                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3201                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3202                                 retval = 2;
3203                                 goto cleanup;
3204                         }
3205                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms, fp, &lineNumber) != 0) {
3206                                 s_append_asprintf(errbuf, "Line %d: cannot read atomic numbers", lineNumber);
3207                                 retval = 2;
3208                                 goto cleanup;
3209                         }
3210                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3211                                 ap->atomicNumber = iary[i];
3212                                 bset->nuccharges[i] = iary[i];
3213                                 ElementToString(ap->atomicNumber, ap->element);
3214                                 memmove(ap->aname, ap->element, 4);
3215                                 if ((w = WeightForAtomicNumber(ap->atomicNumber)) > 0.0)
3216                                         ap->weight = w;
3217                         }
3218                         free(iary);
3219                         iary = NULL;
3220                 } else if (strcmp(buf, "Nuclear charges") == 0) {
3221                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms) {
3222                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of atoms: %s", lineNumber, tokens[2]);
3223                                 retval = 2;
3224                                 goto cleanup;
3225                         }
3226                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms, fp, &lineNumber) != 0) {
3227                                 s_append_asprintf(errbuf, "Line %d: cannot read nuclear charges", lineNumber);
3228                                 retval = 2;
3229                                 goto cleanup;
3230                         }
3231                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3232                                 bset->nuccharges[i] = dary[i];
3233                         }
3234                         free(iary);
3235                         iary = NULL;
3236                 } else if (strcmp(buf, "Current cartesian coordinates") == 0) {
3237                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * 3) {
3238                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of cartesian coordinates: %s", lineNumber, tokens[2]);
3239                                 retval = 2;
3240                                 goto cleanup;
3241                         }
3242                         if (sReadNumberArray(&dary, &nary, sizeof(Double), natoms * 3, fp, &lineNumber) != 0) {
3243                                 s_append_asprintf(errbuf, "Line %d: cannot read cartesian coordinates", lineNumber);
3244                                 retval = 2;
3245                                 goto cleanup;
3246                         }
3247                         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap)) {
3248                                 ap->r.x = dary[i * 3] * kBohr2Angstrom;
3249                                 ap->r.y = dary[i * 3 + 1] * kBohr2Angstrom;
3250                                 ap->r.z = dary[i * 3 + 2] * kBohr2Angstrom;
3251                         }
3252                         free(dary);
3253                         dary = NULL;
3254                 } else if (strcmp(buf, "MxBond") == 0) {
3255                         if (tokens[1] == NULL || (mxbond = atoi(tokens[1])) <= 0) {
3256                                 s_append_asprintf(errbuf, "Line %d: strange number of bonds per atom: %s", lineNumber, tokens[1]);
3257                                 retval = 2;
3258                                 goto cleanup;
3259                         }
3260                 } else if (strcmp(buf, "IBond") == 0) {
3261                         Int *bonds;
3262                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != natoms * mxbond) {
3263                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of bonds: %s", lineNumber, tokens[2]);
3264                                 retval = 2;
3265                                 goto cleanup;
3266                         }
3267                         if (sReadNumberArray(&iary, &nary, sizeof(Int), natoms * mxbond, fp, &lineNumber) != 0) {
3268                                 s_append_asprintf(errbuf, "Line %d: cannot read bond information", lineNumber);
3269                                 retval = 2;
3270                                 goto cleanup;
3271                         }
3272                         bonds = (Int *)malloc(sizeof(Int) * (mxbond * 2 + 1));
3273                         for (i = 0; i < natoms; i++) {
3274                                 for (j = k = 0; j < mxbond; j++) {
3275                                         n = iary[i * mxbond + j] - 1;
3276                                         if (n > i) {
3277                                                 /*  Connect atom i and atom n  */
3278                                                 bonds[k++] = i;
3279                                                 bonds[k++] = n;
3280                                         }
3281                                 }
3282                                 if (k > 0) {
3283                                         bonds[k] = kInvalidIndex;
3284                                         MoleculeAddBonds(mp, k / 2, bonds, NULL, 1);
3285                                 }
3286                         }
3287                         free(iary);
3288                         free(bonds);
3289                         iary = NULL;
3290                 } else if (strcmp(buf, "Shell types") == 0) {
3291                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0) {
3292                                 s_append_asprintf(errbuf, "Line %d: wrong number of shell types: %s", lineNumber, tokens[2]);
3293                                 retval = 2;
3294                                 goto cleanup;
3295                         }
3296                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3297                                 s_append_asprintf(errbuf, "Line %d: cannot read shell types", lineNumber);
3298                                 retval = 2;
3299                                 goto cleanup;
3300                         }
3301                         /*  Allocate ShellInfo table and store shell type information  */
3302                         AssignArray(&bset->shells, &bset->nshells, sizeof(ShellInfo), nary - 1, NULL);
3303                         for (i = n = 0, sp = bset->shells; i < nary; i++, sp++) {
3304                                 switch (iary[i]) {
3305                                         case 0:  sp->sym = kGTOType_S;  sp->ncomp = 1; break;
3306                                         case 1:  sp->sym = kGTOType_P;  sp->ncomp = 3; break;
3307                                         case -1: sp->sym = kGTOType_SP; sp->ncomp = 4; break;
3308                                         case 2:  sp->sym = kGTOType_D;  sp->ncomp = 6; break;
3309                                         case -2: sp->sym = kGTOType_D5; sp->ncomp = 5; break;
3310                                         case 3:  sp->sym = kGTOType_F;  sp->ncomp = 10; break;
3311                                         case -3: sp->sym = kGTOType_F7; sp->ncomp = 7; break;
3312                                         case 4:  sp->sym = kGTOType_G;  sp->ncomp = 15; break;
3313                                         case -4: sp->sym = kGTOType_G9; sp->ncomp = 9; break;
3314                                         default:
3315                                                 s_append_asprintf(errbuf, "Line %d: unsupported shell type %d", lineNumber, iary[i]);
3316                                                 retval = 2;
3317                                                 goto cleanup;
3318                                 }
3319                                 sp->m_idx = n;
3320                                 n += sp->ncomp;
3321                         }
3322                         bset->ncomps = ncomps = n;
3323                         free(iary);
3324                         iary = NULL;
3325                 } else if (strcmp(buf, "Number of primitives per shell") == 0) {
3326                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3327                                 s_append_asprintf(errbuf, "Line %d: wrong size of the primitive table: %s", lineNumber, tokens[2]);
3328                                 retval = 2;
3329                                 goto cleanup;
3330                         }
3331                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3332                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive table", lineNumber);
3333                                 retval = 2;
3334                                 goto cleanup;
3335                         }
3336                         for (i = n = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3337                                 sp->nprim = iary[i];
3338                                 sp->p_idx = n;
3339                                 n += sp->nprim;
3340                         }
3341                         nprims = n;
3342                         free(iary);
3343                         iary = NULL;
3344                 } else if (strcmp(buf, "Shell to atom map") == 0) {
3345                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->nshells) {
3346                                 s_append_asprintf(errbuf, "Line %d: wrong size of the shell-to-atom map: %s", lineNumber, tokens[2]);
3347                                 retval = 2;
3348                                 goto cleanup;
3349                         }
3350                         if (sReadNumberArray(&iary, &nary, sizeof(Int), i, fp, &lineNumber) != 0) {
3351                                 s_append_asprintf(errbuf, "Line %d: cannot read shell-to-atom table", lineNumber);
3352                                 retval = 2;
3353                                 goto cleanup;
3354                         }
3355                         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
3356                                 sp->a_idx = iary[i] - 1;
3357                         }
3358                         free(iary);
3359                         iary = NULL;
3360                 } else if (strcmp(buf, "Primitive exponents") == 0) {
3361                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != nprims) {
3362                                 s_append_asprintf(errbuf, "Line %d: wrong number of primitive exponents: %s", lineNumber, tokens[2]);
3363                                 retval = 2;
3364                                 goto cleanup;
3365                         }
3366                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3367                                 s_append_asprintf(errbuf, "Line %d: cannot read primitive exponents", lineNumber);
3368                                 retval = 2;
3369                                 goto cleanup;
3370                         }
3371                         /*  Allocate PrimInfo table  */
3372                         AssignArray(&bset->priminfos, &bset->npriminfos, sizeof(PrimInfo), nprims - 1, NULL);
3373                         for (i = 0, pp = bset->priminfos; i < nprims; i++, pp++) {
3374                                 pp->A = dary[i];
3375                         }
3376                         free(dary);
3377                         dary = NULL;
3378                 } else if (strcmp(buf, "Contraction coefficients") == 0) {
3379                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3380                                 s_append_asprintf(errbuf, "Line %d: wrong number of contraction coefficients: %s", lineNumber, tokens[2]);
3381                                 retval = 2;
3382                                 goto cleanup;
3383                         }
3384                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3385                                 s_append_asprintf(errbuf, "Line %d: cannot read contraction coefficients", lineNumber);
3386                                 retval = 2;
3387                                 goto cleanup;
3388                         }
3389                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3390                                 pp->C = dary[i];
3391                         }
3392                         free(dary);
3393                         dary = NULL;
3394                 } else if (strcmp(buf, "P(S=P) Contraction coefficients") == 0) {
3395                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != bset->npriminfos) {
3396                                 s_append_asprintf(errbuf, "Line %d: wrong number of P(S=P) contraction coefficients: %s", lineNumber, tokens[2]);
3397                                 retval = 2;
3398                                 goto cleanup;
3399                         }
3400                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3401                                 s_append_asprintf(errbuf, "Line %d: cannot read P(S=P) contraction coefficients", lineNumber);
3402                                 retval = 2;
3403                                 goto cleanup;
3404                         }
3405                         for (i = 0, pp = bset->priminfos; i < bset->npriminfos; i++, pp++) {
3406                                 pp->Csp = dary[i];
3407                         }
3408                         free(dary);
3409                         dary = NULL;
3410                 } else if (strcmp(buf, "Alpha Orbital Energies") == 0) {
3411                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3412                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha orbitals: %s", lineNumber, tokens[2]);
3413                                 retval = 2;
3414                                 goto cleanup;
3415                         }
3416                         if (sReadNumberArray(&bset->moenergies, &bset->nmos, sizeof(Double), i, fp, &lineNumber) != 0) {
3417                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha orbital energies", lineNumber);
3418                                 retval = 2;
3419                                 goto cleanup;
3420                         }
3421                 } else if (strcmp(buf, "Alpha MO coefficients") == 0) {
3422                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3423                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of alpha MO coefficients: %s", lineNumber, tokens[2]);
3424                                 retval = 2;
3425                                 goto cleanup;
3426                         }
3427                         if (sReadNumberArray(&bset->mo, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3428                                 s_append_asprintf(errbuf, "Line %d: cannot read MO coefficients", lineNumber);
3429                                 retval = 2;
3430                                 goto cleanup;
3431                         }
3432                 } else if (strcmp(buf, "Beta Orbital Energies") == 0) {
3433                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps) {
3434                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta orbitals: %s", lineNumber, tokens[2]);
3435                                 retval = 2;
3436                                 goto cleanup;
3437                         }
3438                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3439                                 s_append_asprintf(errbuf, "Line %d: cannot read beta orbital energies", lineNumber);
3440                                 retval = 2;
3441                                 goto cleanup;
3442                         }
3443                         bset->moenergies = (Double *)realloc(bset->moenergies, sizeof(Double) * 2 * ncomps);
3444                         bset->nmos = ncomps * 2;
3445                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);
3446                         memmove(bset->moenergies + ncomps, dary, sizeof(Double) * ncomps);
3447                         memset(bset->mo + ncomps * ncomps, 0, sizeof(Double) * ncomps * ncomps);
3448                         free(dary);
3449                         dary = NULL;
3450                 } else if (strcmp(buf, "Beta MO coefficients") == 0) {
3451                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * ncomps) {
3452                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of beta MO coefficients: %s", lineNumber, tokens[2]);
3453                                 retval = 2;
3454                                 goto cleanup;
3455                         }
3456                         if (sReadNumberArray(&dary, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3457                                 s_append_asprintf(errbuf, "Line %d: cannot read alpha MO coefficients", lineNumber);
3458                                 retval = 2;
3459                                 goto cleanup;
3460                         }
3461                         bset->mo = (Double *)realloc(bset->mo, sizeof(Double) * 2 * ncomps * ncomps);  /*  Should be unnecessary, just in case  */
3462                         memmove(bset->mo + ncomps * ncomps, dary, sizeof(Double) * ncomps * ncomps);
3463                         free(dary);
3464                         dary = NULL;
3465                 } else if (strcmp(buf, "Total SCF Density") == 0) {
3466                         if (tokens[2] == NULL || (i = atoi(tokens[2])) <= 0 || i != ncomps * (ncomps + 1) / 2) {
3467                                 s_append_asprintf(errbuf, "Line %d: wrong or inconsistent number of SCF densities: %s", lineNumber, tokens[2]);
3468                                 retval = 2;
3469                                 goto cleanup;
3470                         }
3471                         if (sReadNumberArray(&bset->scfdensities, &nary, sizeof(Double), i, fp, &lineNumber) != 0) {
3472                                 s_append_asprintf(errbuf, "Line %d: cannot read SCF densities", lineNumber);
3473                                 retval = 2;
3474                                 goto cleanup;
3475                         }
3476                 }
3477         }
3478         if (mp->natoms == 0) {
3479                 s_append_asprintf(errbuf, "Atom information is missing");
3480                 retval = 2;
3481                 goto cleanup;
3482         }
3483         if (bset->shells == NULL || bset->priminfos == NULL) {
3484                 s_append_asprintf(errbuf, "Gaussian primitive information is missing");
3485                 retval = 2;
3486                 goto cleanup;
3487         }
3488         if (bset->mo == NULL) {
3489                 s_append_asprintf(errbuf, "MO coefficients were not found");
3490                 retval = 2;
3491                 goto cleanup;
3492         }
3493         if (sSetupGaussianCoefficients(bset) != 0) {
3494                 s_append_asprintf(errbuf, "Internal error during setup MO calculation");
3495                 retval = 2;
3496                 goto cleanup;
3497         }
3498         mp->nframes = -1;
3499         retval = 0;
3500 cleanup:
3501         fclose(fp);
3502         if (iary != NULL)
3503                 free(iary);
3504         if (dary != NULL)
3505                 free(dary);
3506         if (retval != 0) {
3507                 if (mp->bset != NULL) {
3508                         BasisSetRelease(mp->bset);
3509                         mp->bset = NULL;
3510                 }
3511         }
3512         return retval;
3513 panic:
3514         Panic("low memory while reading fchk file %s", fname);
3515         return -1; /* not reached */    
3516 }
3517
3518 int
3519 MoleculeLoadGamessDatFile(Molecule *mol, const char *fname, char **errbuf)
3520 {
3521         FILE *fp;
3522         int newmol = 0;
3523         char buf[1024];
3524         int lineNumber, i, j, k, len, natoms = 0;
3525         int nframes = 0;
3526         int n1;
3527         int retval = 0;
3528         int ival[8];
3529         double dval[8];
3530         char sval[16];
3531         Vector *vbuf = NULL;
3532         IntGroup *ig;
3533         int optimizing = 0, status = 0;
3534         
3535         *errbuf = NULL;
3536         if (mol == NULL) {
3537                 mol = MoleculeNew();
3538         }
3539         if (mol->natoms == 0)
3540                 newmol = 1;
3541
3542         fp = fopen(fname, "rb");
3543         if (fp == NULL) {
3544                 s_append_asprintf(errbuf, "Cannot open file");
3545                 return 1;
3546         }
3547         
3548         /*  ESP is cleared (not undoable!)  */
3549         if (mol->elpots != NULL) {
3550                 free(mol->elpots);
3551                 mol->elpots = NULL;
3552                 mol->nelpots = 0;
3553         }
3554         
3555         lineNumber = 0;
3556         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3557         redo:
3558                 n1 = 0;
3559                 if (strncmp(buf, " $DATA", 6) == 0) {
3560                         /*  Initial geometry  */
3561                         if (!newmol) {
3562                                 vbuf = (Vector *)calloc(sizeof(Vector), mol->natoms);
3563                         }
3564                         i = 0;
3565                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Title  */
3566                         ReadLine(buf, sizeof buf, fp, &lineNumber);  /*  Symmetry  */
3567                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3568                                 if (strncmp(buf, " $END", 5) == 0)
3569                                         break;
3570                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3571                                         s_append_asprintf(errbuf, "Line %d: bad format in $DATA section", lineNumber);
3572                                         retval = 2;
3573                                         goto exit_loop;
3574                                 }
3575                                 if (newmol) {
3576                                         Atom a;
3577                                         memset(&a, 0, sizeof(a));
3578                                         strncpy(a.aname, sval, 4);
3579                                         a.r.x = dval[1];
3580                                         a.r.y = dval[2];
3581                                         a.r.z = dval[3];
3582                                         a.atomicNumber = (Int)dval[0];
3583                                         strncpy(a.element, ElementToString(a.atomicNumber, sval), 3);
3584                                         a.type = AtomTypeEncodeToUInt(a.element);
3585                                         a.weight = WeightForAtomicNumber(a.atomicNumber);
3586                                         MoleculeCreateAnAtom(mol, &a, mol->natoms);
3587                                 } else {
3588                                         Atom *ap;
3589                                         if (i >= mol->natoms) {
3590                                                 s_append_asprintf(errbuf, "Line %d: too many atoms", lineNumber);
3591                                                 retval = 3;
3592                                                 goto exit_loop;
3593                                         }
3594                                         if ((ap = ATOM_AT_INDEX(mol->atoms, i))->atomicNumber != dval[0]) {
3595                                                 s_append_asprintf(errbuf, "Line %d: atomic number does not match", lineNumber);
3596                                                 retval = 4;
3597                                                 goto exit_loop;
3598                                         }
3599                                         vbuf[i].x = dval[1];
3600                                         vbuf[i].y = dval[2];
3601                                         vbuf[i].z = dval[3];
3602                                 }
3603                                 /*  Skip until a blank line is found  */
3604                                 /*  2013.6.11. Line including "PM3" is also recognized as the end of atom  */
3605                                 while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3606                                         for (j = 0; buf[j] == ' '; j++);
3607                                         if (buf[j] == '\n' || strncmp(buf + j, "PM3", 3) == 0)
3608                                                 break;
3609                                 }
3610                                 i++;
3611                         }
3612                         natoms = i;
3613                         if (!newmol) {
3614                                 /*  Set atom positions  */
3615                                 IntGroup *ig;
3616                                 if (natoms < mol->natoms) {
3617                                         s_append_asprintf(errbuf, "Line %d: too few atoms", lineNumber);
3618                                         retval = 5;
3619                                         goto exit_loop;
3620                                 }
3621                                 ig = IntGroupNewWithPoints(0, natoms, -1);
3622                                 MolActionCreateAndPerform(mol, gMolActionSetAtomPositions, ig, natoms, vbuf);
3623                                 IntGroupRelease(ig);
3624                         }
3625                         if (vbuf == NULL)
3626                                 vbuf = (Vector *)calloc(sizeof(Vector), natoms);
3627                         nframes = MoleculeGetNumberOfFrames(mol);
3628                         if (status < 0)
3629                                 break;
3630                         continue;
3631                 } else if (strstr(buf, "DATA FROM NSERCH") != NULL || (strstr(buf, "RESULTS FROM SUCCESSFUL") != NULL && (n1 = 1))) {
3632                         /*  Skip until the separator line is read (three or four lines)  */
3633                         i = 0;
3634                         do {
3635                                 if (i++ >= 4) {
3636                                         s_append_asprintf(errbuf, "Line %d: the separator line at the top of the coordinates is not found: bad format?", lineNumber);
3637                                         retval = 6;
3638                                         goto exit_loop;
3639                                 }
3640                                 ReadLine(buf, sizeof buf, fp, &lineNumber);
3641                         } while (strstr(buf, "----------------------------") == NULL);
3642                         for (i = 0; i < natoms; i++) {
3643                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
3644                                         s_append_asprintf(errbuf, "Unexpected end of file in reading NSERCH data");
3645                                         retval = 6;
3646                                         goto exit_loop;
3647                                 }
3648                                 if (sscanf(buf, "%12s %lf %lf %lf %lf", sval, &dval[0], &dval[1], &dval[2], &dval[3]) < 5) {
3649                                         s_append_asprintf(errbuf, "Line %d: bad format in NSERCH coordinate data", lineNumber);
3650                                         retval = 6;
3651                                         goto exit_loop;
3652                                 }
3653                                 vbuf[i].x = dval[1];
3654                                 vbuf[i].y = dval[2];
3655                                 vbuf[i].z = dval[3];
3656                         }
3657                         ig = IntGroupNewWithPoints(nframes, 1, -1);
3658                         MolActionCreateAndPerform(mol, gMolActionInsertFrames, ig, natoms, vbuf, 0, NULL);
3659                         IntGroupRelease(ig);
3660                         nframes++;
3661                         if (n1 == 0)
3662                                 optimizing = 1;  /*  Flag to skip reading the VEC group  */
3663                         else
3664                                 optimizing = 0;
3665                         continue;
3666                 } else if (strstr(buf, "E(UHF)") != NULL || (strstr(buf, "E(RHF)") != NULL && (n1 = 1)) || (strstr(buf, "E(ROHF)") != NULL && (n1 = 2))) {
3667                         if (mol->bset == NULL) {
3668                                 i = MoleculeSetMOInfo(mol, n1, 0, 0);
3669                                 if (i != 0) {
3670                                         s_append_asprintf(errbuf, "Line %d: cannot allocate basis set internal buffer", lineNumber);
3671                                         retval = 8;
3672                                         goto exit_loop;
3673                                 }
3674                         }
3675                 } else if (strncmp(buf, " $VEC", 5) == 0) {
3676                         Double *coeffs;
3677                         /*  Read the vec group  */
3678                         if (mol->bset == NULL || mol->bset->ncomps == 0)
3679                                 continue;  /*  Just ignore  */
3680                         if (optimizing)
3681                                 continue;  /*  Ignore VEC group during optimization  */
3682                         coeffs = (Double *)calloc(sizeof(Double), mol->bset->ncomps);
3683                         if (coeffs == NULL) {
3684                                 s_append_asprintf(errbuf, "Line %d: low memory during $VEC", lineNumber);
3685                                 retval = 9;
3686                                 goto exit_loop;
3687                         }
3688                         i = k = 0;
3689                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3690                                 len = strlen(buf);
3691                                 if (strncmp(buf, " $END", 5) == 0)
3692                                         break;
3693                                 while ((j = 5 + (k % 5) * 15) <= len && buf[j] != 0 && buf[j] != '\n') {
3694                                         strncpy(sval, buf + j, 15);
3695                                         sval[15] = 0;
3696                                         coeffs[k] = strtod(sval, NULL);
3697                                         k++;
3698                                         if ((k % 5) == 0)
3699                                                 break;
3700                                 }
3701                                 if (k < mol->bset->ncomps)
3702                                         continue;
3703                                 j = MoleculeSetMOCoefficients(mol, i + 1, -1000000, k, coeffs);
3704                                 if (j != 0) {
3705                                         s_append_asprintf(errbuf, "Line %d: cannot set coefficients for MO %d", lineNumber, i + 1);
3706                                         free(coeffs);
3707                                         retval = 10;
3708                                         goto exit_loop;
3709                                 }
3710                                 i++;
3711                                 k = 0;
3712                         }
3713                         if (status < 0)
3714                                 break;
3715                         continue;
3716                 } else if ((strstr(buf, "ELECTRIC POTENTIAL") != NULL || strstr(buf, "ELECTROSTATIC POTENTIAL") != NULL) && strstr(buf, "ELPOTT") != NULL) {
3717                         i = 0;
3718                         while ((status = sReadLineWithInterrupt(buf, sizeof buf, fp, &lineNumber)) > 0) {
3719                                 Elpot *ep;
3720                                 if (strstr(buf, "TOTAL NUMBER OF GRID POINTS") != NULL)
3721                                         continue;
3722                                 if (sscanf(buf, "%d %lf %lf %lf %lf", &ival[0], &dval[0], &dval[1], &dval[2], &dval[3]) < 5)
3723                                         break;
3724                                 ep = AssignArray(&mol->elpots, &mol->nelpots, sizeof(Elpot), i, NULL);
3725                                 ep->pos.x = dval[0];
3726                                 ep->pos.y = dval[1];
3727                                 ep->pos.z = dval[2];
3728                                 ep->esp = dval[3];
3729                                 i++;
3730                         }
3731                         if (status > 0)
3732                                 goto redo;  /*  This section has no end line, so the last line should be processed again  */
3733                         else break;    /*  End of file encountered or interrupted */
3734                 }  /*  TODO: read MOLPLT info if present  */
3735         }
3736         if (status < 0) {
3737                 s_append_asprintf(errbuf, "User interrupt at line %d", lineNumber);
3738                 retval = 11;
3739         }
3740 exit_loop:
3741         if (vbuf != NULL)
3742                 free(vbuf);
3743         if (mol->natoms > 0)
3744                 retval = 0;  /*  Return the partially constructed molecule  */
3745         if (newmol && mol->nbonds == 0) {
3746                 /*  Guess bonds  */
3747                 Int nbonds, *bonds;
3748                 MoleculeGuessBonds(mol, 0.0, &nbonds, &bonds);
3749                 if (nbonds > 0) {
3750                         MolActionCreateAndPerform(mol, gMolActionAddBonds, nbonds * 2, bonds, NULL);
3751                         free(bonds);
3752                 }
3753         }
3754         return 0;
3755 }
3756
3757 int
3758 MoleculeReadCoordinatesFromFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
3759 {
3760         int retval;
3761         if (ftype == NULL || *ftype == 0) {
3762                 const char *cp;
3763                 cp = strrchr(fname, '.');
3764                 if (cp != NULL)
3765                         ftype = cp + 1;
3766                 else {
3767                         cp = guessMoleculeType(fname);
3768                         if (strcmp(cp, "???") != 0)
3769                                 ftype = cp;
3770                 }
3771         }
3772         if (strcasecmp(ftype, "pdb") == 0) {
3773                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3774         }
3775         if (retval != 0) {
3776                 /*  Try all formats once again  */
3777                 retval = MoleculeReadCoordinatesFromPdbFile(mp, fname, errbuf);
3778         }
3779         return retval;
3780 }
3781
3782 int
3783 MoleculeReadCoordinatesFromPdbFile(Molecule *mp, const char *fname, char **errbuf)
3784 {
3785         FILE *fp;
3786         char buf[1024];
3787         char *p;
3788         int lineNumber;
3789         int i, j, new_unit, retval;
3790         Atom *ap;
3791         IntGroup *ig;
3792         Vector *vp = NULL;
3793         Int ibuf[12];
3794         Int entries = 0;
3795         retval = 0;
3796         *errbuf = NULL;
3797         fp = fopen(fname, "rb");
3798         if (fp == NULL) {
3799                 s_append_asprintf(errbuf, "Cannot open file");
3800                 return -1;
3801         }
3802 /*      flockfile(fp); */
3803         if (mp->natoms == 0)
3804                 new_unit = 1;
3805         else {
3806                 /*  Allocate buffer for undo-capable modification  */
3807                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
3808                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
3809                         /*  Retain current position if the atom info is missing in the input file  */
3810                         vp[i] = ap->r;
3811                 }
3812                 new_unit = 0;
3813         }
3814         lineNumber = 0;
3815         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
3816                 if (strncmp(buf, "END", 3) == 0)
3817                         break;
3818                 if (strncmp(buf, "HETATM", 6) == 0 || strncmp(buf, "ATOM", 4) == 0) {
3819                         struct {
3820                                 Int serial, intCharge, resSeq;
3821                                 Vector r;
3822                                 Double occ, temp;
3823                                 char segName[5], resName[4], atomName[5], resSeqStr[5], atomType[3], element[3], occStr[6];
3824                         } w;
3825                         memset(&w, 0, sizeof(w));
3826                         ReadFormat(buf, "x6 I5 x1 S4 x1 S3 x1 x1 S4 x1 x3 F8 F8 F8 S6 F6 x6 S4 S2 I2",
3827                                 &w.serial, w.atomName, w.resName, w.resSeqStr, &w.r.x, &w.r.y, &w.r.z,
3828                                 w.occStr, &w.temp, w.segName, w.element, &w.intCharge);
3829                         if (w.atomName[0] == 0) {
3830                                 continue;  /*  Atom name is empty  */
3831                         }
3832                         /*  A workaround for residue number >= 10000 (XPLOR style)  */
3833                         if (w.resSeqStr[0] >= 'A' && w.resSeqStr[0] <= 'Z') {
3834                                 w.resSeq = (w.resSeqStr[0] - 'A' + 10) * 1000 + atoi(w.resSeqStr + 1);
3835                         } else {
3836                                 w.resSeq = atoi(w.resSeqStr);
3837                         }
3838                         if (w.element[0] == 0) {
3839                                 /*  $element = ($name =~ /([A-Za-z]{1,2})/); # in Perl  */
3840                                 for (p = w.atomName; *p != 0; p++) {
3841                                         if (isalpha(*p) && *p != '_') {
3842                                                 w.element[0] = toupper(*p);
3843                                                 if (isalpha(p[1]) && p[1] != '_') {
3844                                                         w.element[1] = toupper(p[1]);
3845                                                         w.element[2] = 0;
3846                                                 } else {
3847                                                         w.element[1] = 0;
3848                                                 }
3849                                                 break;
3850                                         }
3851                                 }
3852                         }
3853                         if (w.occStr[0] == 0)
3854                                 w.occ = 1.0;
3855                         else
3856                                 w.occ = atof(w.occStr);
3857                         if (w.serial <= 0) {
3858                                 s_append_asprintf(errbuf, "line %d: non-positive atom number %d", lineNumber, w.serial);
3859                                 retval = 1;
3860                                 goto abort;
3861                         }
3862                         w.serial--;  /*  The internal atom number is 0-based  */
3863                         if (w.serial >= mp->natoms) {
3864                                 if (new_unit) {
3865                                         /*  Create a new atom entry  */
3866                                         ap = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, w.serial, NULL);
3867                                 } else {
3868                                         s_append_asprintf(errbuf, "line %d: the atom number %d does not exist in the structure file", lineNumber, w.serial+1);
3869                                         retval = 1;
3870                                         goto abort;
3871                                 }
3872                         }
3873                         if (new_unit) {
3874                                 ap = ATOM_AT_INDEX(mp->atoms, w.serial);
3875                                 ap->r = w.r;
3876                                 ap->occupancy = w.occ;
3877                                 ap->tempFactor = w.temp;
3878                                 if (w.segName[0] == 0)
3879                                         strncpy(w.segName, "MAIN", 4);
3880                                 strncpy(ap->segName, w.segName, 4);
3881                                 ap->resSeq = w.resSeq;
3882                                 strncpy(ap->resName, w.resName, 4);
3883                                 strncpy(ap->aname, w.atomName, 4);
3884                                 strncpy(ap->element, w.element, 2);
3885                                 ap->element[2] = 0;
3886                                 ap->atomicNumber = ElementToInt(ap->element);
3887                                 ap->type = AtomTypeEncodeToUInt(ap->element);
3888                                 ap->weight = WeightForAtomicNumber(ap->atomicNumber);
3889                                 ap->intCharge = w.intCharge;
3890                                 if (ap->resSeq > 0) {
3891                                         if (ap->resSeq < mp->nresidues) {
3892                                                 /*  Update the resName according to residues[]  */
3893                                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
3894                                         } else {
3895                                                 /*  Register the resName to residues[]  */
3896                                                 AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, w.resName);
3897                                         }
3898                                 } else {
3899                                         ap->resSeq = 0;
3900                                         strcpy(ap->resName, "XXX");
3901                                         if (mp->nresidues == 0)
3902                                                 AssignArray(&mp->residues, &mp->nresidues, 4, 0, ap->resName);
3903                                 }
3904                                 i = ElementToInt(ap->element);
3905                                 if (i >= 0)
3906                                         ap->weight = gElementParameters[i].weight;
3907                         } else {
3908                                 /*  Not a new unit: only the atom position is updated  */
3909                                 vp[w.serial] = w.r;
3910                         }
3911                         entries++;
3912                 } else if (strncmp(buf, "CONECT", 6) == 0 && new_unit) {
3913                         i = ReadFormat(buf, "x6 I5I5I5I5I5I5I5I5I5I5I5I5",
3914                                 ibuf, ibuf + 1, ibuf + 2, ibuf + 3,
3915                                 ibuf + 4, ibuf + 5, ibuf + 6, ibuf + 7,
3916                                 ibuf + 8, ibuf + 9, ibuf + 10, ibuf + 11);
3917                         if (i >= 2) {
3918                                 Int bbuf[25];
3919                                 int bi;
3920                                 for (j = 0; j < i; j++) {
3921                                         if (ibuf[j] < 0 || ibuf[j] > mp->natoms) {
3922                                                 s_append_asprintf(errbuf, "line %d: The CONECT record contains non-existent atom %d", lineNumber, ibuf[j]);
3923                                                 retval = 1;
3924                                                 goto abort;
3925                                         } else if (ibuf[j] == 0)
3926                                                 break;
3927                                 }
3928                                 i = j;
3929                                 if (i < 2)
3930                                         continue;
3931                                 for (j = 1, bi = 0; j < i; j++) {
3932                                         if (ibuf[0] < ibuf[j]) {
3933                                                 if (MoleculeLookupBond(mp, ibuf[0], ibuf[j]) >= 0) {
3934                                                         s_append_asprintf(errbuf, "line %d: warning: duplicate bond %d-%d\n", lineNumber, ibuf[0], ibuf[j]);
3935                                                 } else {
3936                                                         bbuf[bi * 2] = ibuf[0] - 1;
3937                                                         bbuf[bi * 2 + 1] = ibuf[j] - 1;
3938                                                         bi++;
3939                                                 }
3940                                         }
3941                                 }
3942                                 if (bi == 0)
3943                                         continue;
3944                                 bbuf[bi * 2] = -1;
3945                                 retval = MoleculeAddBonds(mp, bi, bbuf, NULL, 1);
3946                                 if (retval < 0) {
3947                                         s_append_asprintf(errbuf, "line %d: bad bond specification", lineNumber);
3948                                         retval = 1;
3949                                         goto abort;
3950                                 }
3951                         }
3952                 }
3953         }
3954 /*      funlockfile(fp); */
3955         fclose(fp);
3956         if (new_unit) {
3957                 /*  Renumber atoms if some atom number is unoccupied  */
3958                 int *old2new, oldidx, newidx;
3959                 old2new = (int *)calloc(sizeof(int), mp->natoms);
3960                 if (old2new == NULL) {
3961                         s_append_asprintf(errbuf, "Out of memory");
3962                         retval = 1;
3963                         goto abort;
3964                 }
3965                 for (oldidx = newidx = 0; oldidx < mp->natoms; oldidx++) {
3966                         ap = ATOM_AT_INDEX(mp->atoms, oldidx);
3967                         if (ap->aname[0] != 0) {
3968                                 old2new[oldidx] = newidx;
3969                                 if (oldidx > newidx)
3970                                         memmove(ATOM_AT_INDEX(mp->atoms, newidx), ap, gSizeOfAtomRecord);
3971                                 newidx++;
3972                         }
3973                 }
3974                 mp->natoms = newidx;
3975                 if (oldidx > newidx) {
3976                         /*  Renumber the connects and bonds  */
3977                         Int *cp;
3978                         for (i = 0; i < mp->natoms; i++) {
3979                                 ap = ATOM_AT_INDEX(mp->atoms, i);
3980                                 cp = AtomConnectData(&ap->connect);
3981                                 for (j = 0; j < ap->connect.count; j++) {
3982                                         cp[j] = old2new[cp[j]];
3983                                 }
3984                         }
3985                         for (i = 0; i < mp->nbonds * 2; i++) {
3986                                 mp->bonds[i] = old2new[mp->bonds[i]];
3987                         }
3988                 }
3989                 retval = MoleculeRebuildTablesFromConnects(mp);
3990                 if (retval != 0) {
3991                         /*  This error may not happen  */
3992                         s_append_asprintf(errbuf, "Cannot build angle/dihedral/improper tables");
3993                         retval = 1;
3994                         goto abort;
3995                 }
3996                 /*  Undo action: delete all atoms  */
3997                 {
3998                         MolAction *act;
3999                         ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4000                         act = MolActionNew(gMolActionUnmergeMolecule, ig);
4001                         act->frame = mp->cframe;
4002                         MolActionCallback_registerUndo(mp, act);
4003                         MolActionRelease(act);
4004                         IntGroupRelease(ig);
4005                 }
4006         } else {
4007                 /*  Set the new atom positions  */
4008                 ig = IntGroupNewWithPoints(0, mp->natoms, -1);
4009                 MolActionCreateAndPerform(mp, gMolActionSetAtomPositions, ig, mp->natoms, vp);
4010                 IntGroupRelease(ig);
4011                 free(vp);
4012                 vp = NULL;
4013         }
4014         mp->nframes = -1;  /*  Should be recalculated later  */
4015         if (entries == 0)
4016                 return 1;  /*  No atoms  */
4017         return 0;
4018         abort:
4019         if (fp != NULL) {
4020         /*      funlockfile(fp); */
4021                 fclose(fp);
4022         }
4023         if (vp != NULL)
4024                 free(vp);
4025         if (entries == 0)
4026                 return 1;  /*  Maybe different format?  */
4027         return retval;
4028 }
4029
4030 int
4031 MoleculeReadCoordinatesFromDcdFile(Molecule *mp, const char *fname, char **errbuf)
4032 {
4033         DcdRecord dcd;
4034         SFloat32 *xp, *yp, *zp;
4035         Vector *vp, *cp;
4036         IntGroup *ig;
4037         int n, errcount = 0;
4038         *errbuf = NULL;
4039         if (mp == NULL || mp->natoms == 0) {
4040                 s_append_asprintf(errbuf, "Molecule is empty");
4041                 return 1;
4042         }
4043         n = DcdOpen(fname, &dcd);
4044         if (n != 0) {
4045                 switch (n) {
4046                         case -2: s_append_asprintf(errbuf, "Cannot open file"); break;
4047                         case 1:  s_append_asprintf(errbuf, "Premature EOF encountered"); break;
4048                         case 2:  s_append_asprintf(errbuf, "Bad block length of the first section"); break;
4049                         case 3:  s_append_asprintf(errbuf, "\"CORD\" signature is missing"); break;
4050                         case 4:  s_append_asprintf(errbuf, "Bad termination of the first section"); break;
4051                         case 5:  s_append_asprintf(errbuf, "The title section is not correct"); break;
4052                         case 6:  s_append_asprintf(errbuf, "The atom number section is not correct"); break;
4053                         default: s_append_asprintf(errbuf, "Read error in dcd file"); break;
4054                 }
4055                 errcount++;
4056         } else {
4057                 if (dcd.natoms == 0) {
4058                         s_append_asprintf(errbuf, "No atoms were found in the dcd file");
4059                         errcount++;
4060                 } else if (dcd.nframes == 0) {
4061                         s_append_asprintf(errbuf, "No frames were found in the dcd file");
4062                         errcount++;
4063                 }
4064         }
4065         if (errcount > 0) {
4066                 if (n == 0)
4067                         DcdClose(&dcd);
4068                 return 1;
4069         }
4070
4071         vp = (Vector *)calloc(sizeof(Vector), mp->natoms * dcd.nframes);
4072         if (dcd.nextra)
4073                 cp = (Vector *)calloc(sizeof(Vector), dcd.nframes * 4);
4074         else cp = NULL;
4075         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4076         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4077         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4078         ig = IntGroupNewWithPoints(MoleculeGetNumberOfFrames(mp), dcd.nframes, -1);
4079         if (vp == NULL || xp == NULL || yp == NULL || zp == NULL || ig == NULL) {
4080                 s_append_asprintf(errbuf, "Cannot allocate memory");
4081                 if (vp) free(vp);
4082                 if (cp) free(cp);
4083                 if (xp) free(xp);
4084                 if (yp) free(yp);
4085                 if (zp) free(zp);
4086                 if (ig) IntGroupRelease(ig);
4087                 return 1;
4088         }
4089         for (n = 0; n < dcd.nframes; n++) {
4090                 int i;
4091                 Vector *vpp;
4092                 SFloat32 dcdcell[6];
4093                 if (DcdReadFrame(&dcd, n, xp, yp, zp, dcdcell)) {
4094                         s_append_asprintf(errbuf, "Read error in dcd file");
4095                         goto exit;
4096                 }
4097                 for (i = 0, vpp = &vp[n * mp->natoms]; i < dcd.natoms && i < mp->natoms; i++, vpp++) {
4098                         vpp->x = xp[i];
4099                         vpp->y = yp[i];
4100                         vpp->z = zp[i];
4101                 }
4102                 if (cp != NULL) {
4103                         Double sing;
4104                         vpp = &cp[n * 4];
4105                         /*  dcdcell = {a, gamma, b, beta, alpha, c} */
4106                         /*  angles are described either in cosines (Charmm and NAMD > 2.5) or degrees (NAMD 2.5)  */
4107                         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) {
4108                                 dcdcell[4] = cos(dcdcell[4] * kDeg2Rad);  /*  cos(alpha)  */
4109                                 dcdcell[3] = cos(dcdcell[3] * kDeg2Rad);  /*  cos(beta)  */
4110                                 dcdcell[1] = cos(dcdcell[1] * kDeg2Rad);  /*  cos(gamma)  */
4111                         }
4112                         /*  a axis lies along the cartesian x axis  */
4113                         sing = sqrt(1 - dcdcell[1] * dcdcell[1]);
4114                         vpp[0].x = dcdcell[0];
4115                         vpp[0].y = 0;
4116                         vpp[0].z = 0;
4117                         vpp[1].x = dcdcell[2] * dcdcell[1];
4118                         vpp[1].y = dcdcell[2] * sing;
4119                         vpp[1].z = 0;
4120                         vpp[2].x = dcdcell[5] * dcdcell[3];
4121                         vpp[2].y = dcdcell[5] * (dcdcell[4] - dcdcell[3] * dcdcell[1]) / sing;
4122                         vpp[2].z = sqrt(dcdcell[5] * dcdcell[5] - vpp[2].x * vpp[2].x - vpp[2].y * vpp[2].y);
4123                         vpp[3].x = vpp[3].y = vpp[3].z = 0.0;
4124                         if (mp->cell == NULL) {
4125                                 /*  Create periodicity if not present  */
4126                                 MolActionCreateAndPerform(mp, gMolActionSetBox, &vpp[0], &vpp[1], &vpp[2], &vpp[3], 7, 0);
4127                         }
4128                 }
4129         }
4130         if (MolActionCreateAndPerform(mp, gMolActionInsertFrames, ig, mp->natoms * dcd.nframes, vp, (cp == NULL ? 0 : dcd.nframes * 4), cp) != 0)
4131                 s_append_asprintf(errbuf, "Cannot insert frames");
4132         mp->startStep = dcd.nstart;
4133         mp->stepsPerFrame = dcd.ninterval;
4134         mp->psPerStep = dcd.delta;
4135 exit:
4136         DcdClose(&dcd);
4137         if (cp != NULL)
4138                 free(cp);
4139         free(vp);
4140         free(xp);
4141         free(yp);
4142         free(zp);
4143         IntGroupRelease(ig);
4144         if (errcount == 0)
4145                 return 0;
4146         else return 1;
4147 }
4148
4149 int
4150 MoleculeReadExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
4151 {
4152         FILE *fp;
4153         char buf[1024];
4154         int lineNumber;
4155         int i, retval;
4156         Vector v[3], vv;
4157         double d[3];
4158         int n, flag;
4159         char flags[3];
4160         *errbuf = NULL;
4161         fp = fopen(fname, "rb");
4162         if (fp == NULL) {
4163                 s_append_asprintf(errbuf, "Cannot open file");
4164                 return -1;
4165         }
4166         errbuf[0] = 0;
4167         lineNumber = 0;
4168         retval = 0;
4169         flags[0] = flags[1] = flags[2] = 0;
4170         while (ReadLine(buf, sizeof buf, fp, &lineNumber) > 0) {
4171                 if (strncmp(buf, "Bounding box:", 13) == 0) {
4172                         for (i = 0; i < 3; i++) {
4173                                 if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0) {
4174                                         s_append_asprintf(errbuf, "line %d: missing %d component of the bounding box", lineNumber, i + 1);
4175                                         retval = 1;
4176                                         goto abort;
4177                                 }
4178                                 n = sscanf(buf, "%lf %lf %lf %d", &d[0], &d[1], &d[2], &flag);
4179                                 if (n < 3) {
4180                                         vv.x = vv.y = vv.z = 0.0;
4181                                         switch (i) {
4182                                                 case 0: vv.x = d[0]; break;
4183                                                 case 1: vv.y = d[0]; break;
4184                                                 case 2: vv.z = d[0]; break;
4185                                         }
4186                                         if (n == 1 || (n == 2 && d[1] != 0.0))
4187                                                 flags[i] = 1;
4188                                 } else {
4189                                         vv.x = d[0];
4190                                         vv.y = d[1];
4191                                         vv.z = d[2];
4192                                         if (n == 4)
4193                                                 flags[i] = (flag != 0);
4194                                         else
4195                                                 flags[i] = (VecLength2(vv) != 0);
4196                                 }
4197                                 v[i] = vv;
4198                         }
4199                         if (mp->cell != NULL)
4200                                 vv = mp->cell->origin;
4201                         else
4202                                 vv.x = vv.y = vv.z = 0.0;
4203                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4204                 } else if (strncmp(buf, "Bounding box origin:", 20) == 0) {
4205                         if (mp->cell != NULL) {
4206                                 v[0] = mp->cell->axes[0];
4207                                 v[1] = mp->cell->axes[1];
4208                                 v[2] = mp->cell->axes[2];
4209                                 memmove(flags, mp->cell->flags, 3);
4210                         } else {
4211                                 v[0].x = 1.0; v[0].y = v[0].z = 0.0;
4212                                 v[1].y = 1.0; v[1].x = v[1].z = 0.0;
4213                                 v[2].z = 1.0; v[2].x = v[2].y = 0.0;
4214                                 flags[0] = flags[1] = flags[2] = 1.0;
4215                         }
4216                         if (ReadLine(buf, sizeof buf, fp, &lineNumber) <= 0 || (n = sscanf(buf, "%lf %lf %lf", &d[0], &d[1], &d[2]) < 3)) {
4217                                 s_append_asprintf(errbuf, "line %d: wrong format for the bounding box origin", lineNumber);
4218                                 retval = 1;
4219                                 goto abort;
4220                         }
4221                         vv.x = d[0];
4222                         vv.y = d[1];
4223                         vv.z = d[2];
4224                         MoleculeSetPeriodicBox(mp, &v[0], &v[1], &v[2], &vv, flags, 0);
4225                 }
4226         }
4227         fclose(fp);
4228         return 0;
4229 abort:
4230         if (fp != NULL)
4231                 fclose(fp);
4232         return retval;
4233 }
4234                         
4235 int
4236 MoleculeWriteToFile(Molecule *mp, const char *fname, const char *ftype, char **errbuf)
4237 {
4238         int retval;
4239         *errbuf = NULL;
4240         if (ftype == NULL || *ftype == 0) {
4241                 const char *cp;
4242                 cp = strrchr(fname, '.');
4243                 if (cp != NULL)
4244                         ftype = cp + 1;
4245                 else {
4246                         cp = guessMoleculeType(fname);
4247                         if (strcmp(cp, "???") != 0)
4248                                 ftype = cp;
4249                 }
4250         }
4251         if (strcasecmp(ftype, "psf") == 0) {
4252                 retval = MoleculeWriteToPsfFile(mp, fname, errbuf);
4253         } else if (strcasecmp(ftype, "pdb") == 0) {
4254                 retval = MoleculeWriteToPdbFile(mp, fname, errbuf);
4255         } else if (strcasecmp(ftype, "tep") == 0) {
4256                 retval = MoleculeWriteToTepFile(mp, fname, errbuf);
4257         } else {
4258                 s_append_asprintf(errbuf, "The file format should be specified");
4259                 retval = 1;
4260         }
4261         if (retval == 0)
4262                 MoleculeSetPath(mp, fname);
4263         return retval;
4264 }
4265
4266 int
4267 MoleculeWriteToMbsfFile(Molecule *mp, const char *fname, char **errbuf)
4268 {
4269         FILE *fp;
4270         Int i, j, k, n1, n2, n3, n_aniso, nframes, nanchors, n_uff;
4271         Atom *ap;
4272         char *p;
4273         char bufs[6][8];
4274
4275         *errbuf = NULL;
4276         fp = fopen(fname, "wb");
4277         if (fp == NULL) {
4278                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4279                 return 1;
4280         }
4281         errbuf[0] = 0;
4282
4283         nframes = MoleculeFlushFrames(mp);
4284
4285         fprintf(fp, "!:atoms\n");
4286         fprintf(fp, "! idx seg_name res_seq res_name name type charge weight element atomic_number occupancy temp_factor int_charge\n");
4287         n1 = n2 = n3 = n_aniso = nanchors = n_uff = 0;
4288         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4289                 strncpy(bufs[0], ap->segName, 4);
4290                 bufs[0][4] = 0;
4291                 strncpy(bufs[1], ap->resName, 4);
4292                 bufs[1][4] = 0;
4293                 strncpy(bufs[2], ap->aname, 4);
4294                 bufs[2][4] = 0;
4295                 AtomTypeDecodeToString(ap->type, bufs[3]);
4296                 bufs[3][6] = 0;
4297                 strncpy(bufs[4], ap->element, 4);
4298                 bufs[4][2] = 0;
4299                 for (j = 0; j < 5; j++) {
4300                         if (bufs[j][0] == 0) {
4301                                 bufs[j][0] = '_';
4302                                 bufs[j][1] = 0;
4303                         }
4304                         for (k = 0; k < 6; k++) {
4305                                 if (bufs[j][k] == 0)
4306                                         break;
4307                                 if (bufs[j][k] > 0 && bufs[j][k] < ' ')
4308                                         bufs[j][k] = '_';
4309                         }
4310                 }
4311                 if (SYMOP_ALIVE(ap->symop))
4312                         n1++;
4313                 if (ap->fix_force != 0)
4314                         n2++;
4315                 if (ap->mm_exclude || ap->periodic_exclude)
4316                         n3++;
4317                 if (ap->aniso != NULL)
4318                         n_aniso++;
4319                 if (ap->anchor != NULL)
4320                         nanchors++;
4321                 if (ap->uff_type[0] != 0)
4322                         n_uff++;
4323                 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);
4324         }
4325         fprintf(fp, "\n");
4326         
4327         if (n_uff > 0) {
4328                 fprintf(fp, "!:uff_type\n");
4329                 fprintf(fp, "! idx uff_type\n");
4330                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4331                         fprintf(fp, "%d %.5s\n", i, ap->uff_type);
4332                 }
4333                 fprintf(fp, "\n");
4334         }
4335         
4336         if (n1 > 0) {
4337                 fprintf(fp, "!:atoms_symop\n");
4338                 fprintf(fp, "! idx symop symbase\n");
4339                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4340                         int n;
4341                         n = ap->symop.sym * 1000000 + ap->symop.dx * 10000 + ap->symop.dy * 100 + ap->symop.dz;
4342                         fprintf(fp, "%d %d %d\n", i, n, ap->symbase);
4343                 }
4344                 fprintf(fp, "\n");
4345         }
4346         
4347         if (n2 > 0) {
4348                 fprintf(fp, "!:atoms_fix\n");
4349                 fprintf(fp, "! idx fix_force fix_pos\n");
4350                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4351                         fprintf(fp, "%d %f %f %f %f\n", i, ap->fix_force, ap->fix_pos.x, ap->fix_pos.y, ap->fix_pos.z);
4352                 }
4353                 fprintf(fp, "\n");
4354         }
4355         
4356         if (n3 > 0) {
4357                 fprintf(fp, "!:mm_exclude\n");
4358                 fprintf(fp, "! idx mm_exclude periodic_exclude\n");
4359                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4360                         fprintf(fp, "%d %d %d\n", i, ap->mm_exclude, ap->periodic_exclude);
4361                 }
4362                 fprintf(fp, "\n");
4363         }
4364         
4365         if (nanchors > 0) {
4366                 fprintf(fp, "!:pi_anchor\n");
4367                 fprintf(fp, "! idx count; n1 weight1; n2 weight2; ...; nN weightN\n");
4368                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4369                         Int *ip;
4370                         if (ap->anchor == NULL)
4371                                 continue;
4372                         k = ap->anchor->connect.count;
4373                         ip = AtomConnectData(&ap->anchor->connect);
4374                         fprintf(fp, "%d %d\n", i, k);
4375                         for (j = 0; j < k; j++) {
4376                                 fprintf(fp, "%d %f\n", ip[j], ap->anchor->coeffs[j]);
4377                         }
4378                 }
4379                 fprintf(fp, "\n");
4380         }
4381                                 
4382         n1 = nframes;
4383         if (n1 > 0)
4384                 n2 = mp->cframe;
4385         else
4386                 n2 = 0;
4387         for (i = 0; (i == n2 || i < n1); i++) {
4388                 fprintf(fp, "!:positions ; frame %d\n", i);
4389                 fprintf(fp, "! idx x y z [sx sy sz]\n");
4390                 for (j = 0, ap = mp->atoms; j < mp->natoms; j++, ap = ATOM_NEXT(ap)) {
4391                         Vector *vp;
4392                         Byte sig_flag = 0;
4393                         if (i != n2 && i < ap->nframes)
4394                                 vp = ap->frames + i;
4395                         else {
4396                                 vp = &(ap->r);
4397                                 if (ap->sigma.x != 0.0 || ap->sigma.y != 0.0 || ap->sigma.z != 0.0)
4398                                         sig_flag = 1;
4399                         }
4400                         fprintf(fp, "%d %.8f %.8f %.8f", j, vp->x, vp->y, vp->z);
4401                         if (sig_flag) {
4402                                 fprintf(fp, " %.8f %.8f %.8f", ap->sigma.x, ap->sigma.y, ap->sigma.z);
4403                         }
4404                         fprintf(fp, "\n");
4405                 }
4406                 fprintf(fp, "\n");
4407         }
4408         
4409         if (mp->nbonds > 0) {
4410                 fprintf(fp, "!:bonds\n");
4411                 fprintf(fp, "! from1 to1 from2 to2 from3 to3 from4 to4\n");
4412                 for (i = 0; i < mp->nbonds; i++) {
4413                         fprintf(fp, "%d %d%c", mp->bonds[i * 2], mp->bonds[i * 2 + 1], (i % 4 == 3 || i == mp->nbonds - 1 ? '\n' : ' '));
4414                 }
4415                 fprintf(fp, "\n");
4416         }
4417
4418         if (mp->nbondOrders > 0) {
4419                 fprintf(fp, "!:bond_orders\n");
4420                 fprintf(fp, "! order1 order2 order3 order4\n");
4421                 for (i = 0; i < mp->nbondOrders; i++) {
4422                         fprintf(fp, "%.6f%c", mp->bondOrders[i], (i % 4 == 3 || i == mp->nbondOrders - 1 ? '\n' : ' '));
4423                 }
4424                 fprintf(fp, "\n");
4425         }
4426         
4427         if (mp->nangles > 0) {
4428                 fprintf(fp, "!:angles\n");
4429                 fprintf(fp, "! a1 b1 c1 a2 b2 c2 a3 b3 c3\n");
4430                 for (i = 0; i < mp->nangles; i++) {
4431                         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' : ' '));
4432                 }
4433                 fprintf(fp, "\n");
4434         }
4435         
4436         if (mp->ndihedrals > 0) {
4437                 fprintf(fp, "!:dihedrals\n");
4438                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4439                 for (i = 0; i < mp->ndihedrals; i++) {
4440                         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' : ' '));
4441                 }
4442                 fprintf(fp, "\n");
4443         }
4444         
4445         if (mp->nimpropers > 0) {
4446                 fprintf(fp, "!:impropers\n");
4447                 fprintf(fp, "! a1 b1 c1 d1 a2 b2 c2 d2\n");
4448                 for (i = 0; i < mp->nimpropers; i++) {
4449                         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' : ' '));
4450                 }
4451                 fprintf(fp, "\n");
4452         }
4453         
4454         if (mp->cell != NULL) {
4455                 fprintf(fp, "!:xtalcell\n");
4456                 fprintf(fp, "! a b c alpha beta gamma\n");
4457                 fprintf(fp, "! This information is redundant and overridden by the following periodic_box info\n");
4458                 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]);
4459                 fprintf(fp, "\n");
4460
4461                 fprintf(fp, "!:periodic_box\n");
4462                 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");
4463                 for (i = 0; i < 3; i++)
4464                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->axes[i].x, mp->cell->axes[i].y, mp->cell->axes[i].z);
4465                 fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->cell->origin.x, mp->cell->origin.y, mp->cell->origin.z);
4466                 fprintf(fp, "%d %d %d%s\n", mp->cell->flags[0], mp->cell->flags[1], mp->cell->flags[2], (mp->cell->has_sigma ? " 1" : ""));
4467                 if (mp->cell->has_sigma) {
4468                         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]);
4469                 }
4470                 fprintf(fp, "\n");
4471         }
4472         
4473         if (mp->nframe_cells > 0) {
4474                 fprintf(fp, "!:frame_periodic_boxes\n");
4475                 fprintf(fp, "! ax ay az; bx by bz; cx cy cz; ox oy oz\n");
4476                 for (i = 0; i < mp->nframe_cells * 4; i++) {
4477                         fprintf(fp, "%15.8f %15.8f %15.8f\n", mp->frame_cells[i].x, mp->frame_cells[i].y, mp->frame_cells[i].z);
4478                 }
4479                 fprintf(fp, "\n");
4480         }
4481         
4482         if (mp->nsyms > 0) {
4483                 fprintf(fp, "!:symmetry_operations\n");
4484                 fprintf(fp, "! a11 a12 a13; a21 a22 a23; a31 a32 a33; t1 t2 t3\n");
4485                 for (i = 0; i < mp->nsyms; i++) {
4486                         Transform *tp = mp->syms + i;
4487                         const unsigned char s_index_order[12] = {0, 3, 6, 1, 4, 7, 2, 5, 8, 9, 10, 11};
4488                         for (j = 0; j < 12; j++)
4489                                 fprintf(fp, "%11.6f%c", (*tp)[s_index_order[j]], (j % 3 == 2 ? '\n' : ' '));
4490                 }
4491                 fprintf(fp, "\n");
4492         }
4493         
4494         if (n_aniso > 0) {
4495                 fprintf(fp, "!:anisotropic_thermal_parameters\n");
4496                 fprintf(fp, "! b11 b22 b33 b12 b13 b23 [sigma; sb11 sb22 sb33 sb12 sb13 sb23]\n");
4497                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4498                         if (ap->aniso != NULL) {
4499                                 Double *bp = ap->aniso->bij;
4500                                 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" : ""));
4501                                 if (ap->aniso->has_bsig) {
4502                                         bp = ap->aniso->bsig;
4503                                         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]);
4504                                 }
4505                         } else {
4506                                 fprintf(fp, "0 0 0 0 0 0\n");
4507                         }
4508                 }
4509                 fprintf(fp, "\n");              
4510         }
4511         
4512         if (mp->arena != NULL) {
4513                 MDArena *arena = mp->arena;
4514                 fprintf(fp, "!:md_parameters\n");
4515                 fprintf(fp, "log_file %s\n", arena->log_result_name);
4516                 fprintf(fp, "coord_file %s\n", arena->coord_result_name);
4517                 fprintf(fp, "vel_file %s\n", arena->vel_result_name);
4518                 fprintf(fp, "force_file %s\n", arena->force_result_name);
4519                 fprintf(fp, "debug_file %s\n", arena->debug_result_name);
4520                 fprintf(fp, "debug_output_level %d\n", arena->debug_output_level);
4521                 fprintf(fp, "step %d\n", arena->step);
4522                 fprintf(fp, "coord_output_freq %d\n", arena->coord_output_freq);
4523                 fprintf(fp, "energy_output_freq %d\n", arena->energy_output_freq);
4524                 fprintf(fp, "coord_frame %d\n", arena->coord_result_frame);
4525                 fprintf(fp, "timestep %g\n", arena->timestep);
4526                 fprintf(fp, "cutoff %g\n", arena->cutoff);
4527                 fprintf(fp, "electro_cutoff %g\n", arena->electro_cutoff);
4528                 fprintf(fp, "pairlist_distance %g\n", arena->pairlist_distance);
4529                 fprintf(fp, "switch_distance %g\n", arena->switch_distance);
4530                 fprintf(fp, "temperature %g\n", arena->temperature);
4531                 fprintf(fp, "andersen_freq %d\n", arena->andersen_thermo_freq);
4532                 fprintf(fp, "andersen_coupling %g\n", arena->andersen_thermo_coupling);
4533                 fprintf(fp, "random_seed %d\n", arena->random_seed);
4534                 fprintf(fp, "dielectric %g\n", arena->dielectric);
4535                 fprintf(fp, "gradient_convergence %g\n", arena->gradient_convergence);
4536                 fprintf(fp, "coordinate_convergence %g\n", arena->coordinate_convergence);
4537                 fprintf(fp, "use_xplor_shift %d\n", arena->use_xplor_shift);
4538                 fprintf(fp, "scale14_vdw %g\n", arena->scale14_vdw);
4539                 fprintf(fp, "scale14_elect %g\n", arena->scale14_elect);
4540                 fprintf(fp, "relocate_center %d\n", arena->relocate_center);
4541                 fprintf(fp, "surface_probe_radius %g\n", arena->probe_radius);
4542                 fprintf(fp, "surface_tension %g\n", arena->surface_tension);
4543                 fprintf(fp, "surface_potential_freq %d\n", arena->surface_potential_freq);
4544                 fprintf(fp, "use_graphite %d\n", arena->use_graphite);
4545                 fprintf(fp, "alchemical_lambda %g\n", arena->alchem_lambda);
4546                 fprintf(fp, "alchemical_delta_lambda %g\n", arena->alchem_dlambda);
4547                 if (arena->nalchem_flags > 0) {
4548                         fprintf(fp, "alchem_flags %d", arena->nalchem_flags);
4549                         for (i = 0; i < arena->nalchem_flags; i++) {
4550                                 if (i % 60 == 0)
4551                                         fputc('\n', fp);
4552                                 else if (i % 10 == 0)
4553                                         fputc(' ', fp);
4554                                 fputc('0' + arena->alchem_flags[i], fp);
4555                         }
4556                         fputc('\n', fp);
4557                 }
4558                 if (arena->pressure != NULL) {
4559                         Double *dp;
4560                         fprintf(fp, "pressure_freq %d\n", arena->pressure->freq);
4561                         fprintf(fp, "pressure_coupling %g\n", arena->pressure->coupling);
4562                         dp = arena->pressure->apply;
4563                         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]);
4564                         dp = arena->pressure->cell_flexibility;
4565                         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]);
4566                         fprintf(fp, "pressure_fluctuate_cell_origin %g\n", arena->pressure->fluctuate_cell_origin);
4567                         fprintf(fp, "pressure_fluctuate_cell_orientation %g\n", arena->pressure->fluctuate_cell_orientation);
4568                 }
4569                 fprintf(fp, "\n");
4570
4571                 if (mp->par != NULL) {
4572                         Parameter *par = mp->par;
4573                         fprintf(fp, "!:parameters\n");
4574                         ParameterAppendToFile(par, fp);
4575                         fprintf(fp, "\n");
4576                 }
4577                 
4578                 fprintf(fp, "!:velocity\n");
4579                 fprintf(fp, "! idx vx vy vz\n");
4580                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4581                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->v.x, ap->v.y, ap->v.z);
4582                 }
4583                 fprintf(fp, "\n");
4584
4585                 fprintf(fp, "!:force\n");
4586                 fprintf(fp, "! idx fx fy fz\n");
4587                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4588                         fprintf(fp, "%d %.8f %.8f %.8f\n", i, ap->f.x, ap->f.y, ap->f.z);
4589                 }
4590                 fprintf(fp, "\n");
4591         }
4592         
4593         if (mp->mview != NULL) {
4594                 double f[4];
4595                 if (mp->mview->track != NULL) {
4596                         fprintf(fp, "!:trackball\n");
4597                         fprintf(fp, "! scale; trx try trz; theta_deg x y z\n");
4598                         f[0] = TrackballGetScale(mp->mview->track);
4599                         fprintf(fp, "%f\n", f[0]);
4600                         TrackballGetTranslate(mp->mview->track, f);
4601                         fprintf(fp, "%f %f %f\n", f[0], f[1], f[2]);
4602                         TrackballGetRotate(mp->mview->track, f);
4603                         fprintf(fp, "%f %f %f %f\n", f[0], f[1], f[2], f[3]);
4604                         fprintf(fp, "\n");
4605                 }
4606                 fprintf(fp, "!:view\n");
4607                 fprintf(fp, "show_unit_cell %d\n", mp->mview->showUnitCell);
4608                 fprintf(fp, "show_periodic_box %d\n", mp->mview->showPeriodicBox);
4609                 fprintf(fp, "show_expanded_atoms %d\n", mp->mview->showExpandedAtoms);
4610                 fprintf(fp, "show_ellipsoids %d\n", mp->mview->showEllipsoids);
4611                 fprintf(fp, "show_hydrogens %d\n", mp->mview->showHydrogens);
4612                 fprintf(fp, "show_dummy_atoms %d\n", mp->mview->showDummyAtoms);
4613                 fprintf(fp, "show_rotation_center %d\n", mp->mview->showRotationCenter);
4614                 fprintf(fp, "show_graphite_flag %d\n", mp->mview->showGraphiteFlag);
4615                 fprintf(fp, "show_graphite %d\n", mp->mview->showGraphite);
4616                 fprintf(fp, "show_periodic_image_flag %d\n", mp->mview->showPeriodicImageFlag);
4617                 fprintf(fp, "show_periodic_image %d %d %d %d %d %d\n",
4618                                 mp->mview->showPeriodicImage[0], mp->mview->showPeriodicImage[1],
4619                                 mp->mview->showPeriodicImage[2], mp->mview->showPeriodicImage[3],
4620                                 mp->mview->showPeriodicImage[4], mp->mview->showPeriodicImage[5]);
4621                 if (mp->mview->atomRadius != 0.2)
4622                         fprintf(fp, "atom_radius %f\n", mp->mview->atomRadius);
4623                 if (mp->mview->bondRadius != 0.1)
4624                         fprintf(fp, "bond_radius %f\n", mp->mview->bondRadius);
4625                 if (mp->mview->atomResolution != 12)
4626                         fprintf(fp, "atom_resolution %d\n", mp->mview->atomResolution);
4627                 if (mp->mview->bondResolution != 8)
4628                         fprintf(fp, "bond_resolution %d\n", mp->mview->bondResolution);
4629                 fprintf(fp, "\n");
4630         }
4631
4632         if (mp->nmolprops > 0) {
4633                 MolProp *prp;
4634                 for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
4635                         /*  Encode the property name if necessary  */
4636                         char enc[1024];
4637                         n1 = n2 = 0;
4638                         for (p = prp->propname; *p != 0 && n1 < 900; p++) {
4639                                 if (*p > ' ' && *p != '%' && *p < 0x7f) {
4640                                         enc[n1++] = *p;
4641                                         n2 = n1;
4642                                 } else {
4643                                         sprintf(enc + n1, "%%%02x", *p);
4644                                         n1 += 3;
4645                                 }
4646                         }
4647                         if (*p == 0)
4648                                 enc[n1] = 0;
4649                         else {
4650                                 enc[n2] = 0; /* Truncate after last ASCII character */
4651                                 n1 = n2;
4652                         }
4653                         if (n1 == 0) {
4654                                 sprintf(enc, "prop_%d", i + 1);
4655                                 n1 = strlen(enc);
4656                         }
4657                         fprintf(fp, "!:property ; %s\n", enc);
4658                         for (j = 0; j < nframes; j++) {
4659                                 fprintf(fp, "%.18g\n", prp->propvals[j]);
4660                         }
4661                         fprintf(fp, "\n");
4662                 }
4663         }
4664         
4665         if (mp->bset != NULL) {
4666                 /*  Gaussian primitive info  */
4667                 ShellInfo *sp;
4668                 PrimInfo *pp;
4669                 fprintf(fp, "!:gaussian_primitives\n");
4670                 fprintf(fp, "! sym nprims a_idx; A C Csp\n");
4671                 for (i = 0, sp = mp->bset->shells; i < mp->bset->nshells; i++, sp++) {
4672                         switch (sp->sym) {
4673                                 case kGTOType_S:  p = "S";  break;
4674                                 case kGTOType_P:  p = "P";  break;
4675                                 case kGTOType_SP: p = "SP"; break;
4676                                 case kGTOType_D:  p = "D";  break;
4677                                 case kGTOType_D5: p = "D5"; break;
4678                                 case kGTOType_F:  p = "F";  break;
4679                                 case kGTOType_F7: p = "F7"; break;
4680                                 case kGTOType_G:  p = "G";  break;
4681                                 case kGTOType_G9: p = "G9"; break;
4682                                 default: snprintf(bufs[0], 8, "X%d", sp->sym); p = bufs[0]; break;
4683                         }
4684                         fprintf(fp, "%s %d %d\n", p, sp->nprim, sp->a_idx);
4685                         pp = mp->bset->priminfos + sp->p_idx;
4686                         for (j = 0; j < sp->nprim; j++, pp++) {
4687                                 fprintf(fp, "%.18g %.18g %.18g\n", pp->A, pp->C, pp->Csp);
4688                         }
4689                 }
4690                 fprintf(fp, "\n");
4691                 
4692                 /*  MO info  */
4693                 fprintf(fp, "!:mo_info\n");
4694                 fprintf(fp, "! uhf|rhf|rohf ne_alpha ne_beta\n");
4695                 switch (mp->bset->rflag) {
4696                         case 0: p = "UHF"; break;
4697                         case 1: p = "RHF"; break;
4698                         case 2: p = "ROHF"; break;
4699                         default: p = "(unknown)"; break;
4700                 }
4701                 fprintf(fp, "%s %d %d\n", p, mp->bset->ne_alpha, mp->bset->ne_beta);
4702                 fprintf(fp, "\n");
4703
4704                 /*  MO coefficients  */
4705                 fprintf(fp, "!:mo_coefficients\n");
4706                 for (i = 0; i < mp->bset->nmos; i++) {
4707                         fprintf(fp, "MO %d %.18g\n", i + 1, mp->bset->moenergies[i]);
4708                         for (j = 0; j < mp->bset->ncomps; j++) {
4709                                 fprintf(fp, "%.18g%c", mp->bset->mo[i * mp->bset->ncomps + j], (j % 6 == 5 || j == mp->bset->ncomps - 1 ? '\n' : ' '));
4710                         }
4711                 }
4712                 fprintf(fp, "\n");
4713         }
4714
4715         if (mp->mview != NULL && mp->mview->ngraphics > 0) {
4716                 MainViewGraphic *gp;
4717                 fprintf(fp, "!:graphics\n");
4718                 for (i = 0; i < mp->mview->ngraphics; i++) {
4719                         gp = mp->mview->graphics + i;
4720                         switch (gp->kind) {
4721                                 case kMainViewGraphicLine: fprintf(fp, "line\n"); break;
4722                                 case kMainViewGraphicPoly: fprintf(fp, "poly\n"); break;
4723                                 case kMainViewGraphicCylinder: fprintf(fp, "cylinder\n"); break;
4724                                 case kMainViewGraphicCone: fprintf(fp, "cone\n"); break;
4725                                 case kMainViewGraphicEllipsoid: fprintf(fp, "ellipsoid\n"); break;
4726                                 default: fprintf(fp, "unknown\n"); break;
4727                         }
4728                         fprintf(fp, "%d %d\n", gp->closed, gp->visible);
4729                         fprintf(fp, "%.4f %.4f %.4f %.4f\n", gp->rgba[0], gp->rgba[1], gp->rgba[2], gp->rgba[3]);
4730                         fprintf(fp, "%d\n", gp->npoints);
4731                         for (j = 0; j < gp->npoints; j++)
4732                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->points[j * 3], gp->points[j * 3 + 1], gp->points[j * 3 + 2]);
4733                         fprintf(fp, "%d\n", gp->nnormals);
4734                         for (j = 0; j < gp->nnormals; j++)
4735                                 fprintf(fp, "%.6f %.6f %.6f\n", gp->normals[j * 3], gp->normals[j * 3 + 1], gp->normals[j * 3 + 2]);
4736                 }
4737                 fprintf(fp, "\n");
4738         }
4739         
4740         /*  Plug-in in the Ruby world  */
4741         {
4742                 char *outMessage;
4743                 if (MolActionCreateAndPerform(mp, SCRIPT_ACTION(";s"),
4744                                                                           "proc { savembsf_plugin rescue \"Plug-in error: #{$!.to_s}\" }", &outMessage) == 0) {
4745                         if (outMessage[0] != 0) {
4746                                 if (strncmp(outMessage, "Plug-in", 7) == 0) {
4747                                         s_append_asprintf(errbuf, "%s", outMessage);
4748                                 } else {
4749                                         fprintf(fp, "%s\n", outMessage);
4750                                 }
4751                         }
4752                         free(outMessage);
4753                 }
4754         }
4755         
4756         fclose(fp);
4757         return 0;
4758 }
4759
4760 int
4761 MoleculeWriteToPsfFile(Molecule *mp, const char *fname, char **errbuf)
4762 {
4763         FILE *fp;
4764         int i;
4765         Atom *ap;
4766         *errbuf = NULL;
4767         fp = fopen(fname, "wb");
4768         if (fp == NULL) {
4769                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4770                 return 1;
4771         }
4772         fprintf(fp, "PSF\n\n");
4773         fprintf(fp, "       1 !NTITLE\n");
4774         fprintf(fp, " REMARKS FILENAME=\n");
4775         fprintf(fp, "\n");
4776         
4777         /*  Atoms  */
4778         fprintf(fp, "%8d !NATOM\n", mp->natoms);
4779         for (i = 0; i < mp->natoms; i++) {
4780                 const char *fmt;
4781                 ap = ATOM_AT_INDEX(mp->atoms, i);
4782                 fprintf(fp, "%8d ", i + 1);
4783                 if (ap->resSeq >= 10000) {
4784                         fmt = "%-3.3s %-5d ";
4785                 } else {
4786                         fmt = "%-4.4s %-4d ";
4787                 }
4788                 fprintf(fp, fmt, ap->segName, ap->resSeq);
4789                 fprintf(fp, "%-3.3s  %-4.4s %-4.4s   %12.6f  %8.4f           0\n",
4790                         ap->resName, ap->aname, AtomTypeDecodeToString(ap->type, NULL), ap->charge, ap->weight);
4791         }
4792         fprintf(fp, "\n");
4793         
4794         /*  Bonds  */
4795         fprintf(fp, "%8d !NBOND: bonds\n", mp->nbonds);
4796         for (i = 0; i < mp->nbonds * 2; i++) {
4797                 fprintf(fp, "%8d", mp->bonds[i] + 1);
4798                 if (i % 8 == 7)
4799                         fprintf(fp, "\n");
4800         }
4801         if (i % 8 != 0)
4802                 fprintf(fp, "\n");
4803         fprintf(fp, "\n");
4804         
4805         /*  Angles  */
4806         fprintf(fp, "%8d !NTHETA: angles\n", mp->nangles);
4807         for (i = 0; i < mp->nangles * 3; i++) {
4808                 fprintf(fp, "%8d", mp->angles[i] + 1);
4809                 if (i % 9 == 8)
4810                         fprintf(fp, "\n");
4811         }
4812         if (i % 9 != 0)
4813                 fprintf(fp, "\n");
4814         fprintf(fp, "\n");
4815         
4816         /*  Dihedrals  */
4817         fprintf(fp, "%8d !NPHI: dihedrals\n", mp->ndihedrals);
4818         for (i = 0; i < mp->ndihedrals * 4; i++) {
4819                 fprintf(fp, "%8d", mp->dihedrals[i] + 1);
4820                 if (i % 8 == 7)
4821                         fprintf(fp, "\n");
4822         }
4823         if (i % 8 != 0)
4824                 fprintf(fp, "\n");
4825         fprintf(fp, "\n");
4826         
4827         /*  Dihedrals  */
4828         fprintf(fp, "%8d !NIMPHI: impropers\n", mp->nimpropers);
4829         for (i = 0; i < mp->nimpropers * 4; i++) {
4830                 fprintf(fp, "%8d", mp->impropers[i] + 1);
4831                 if (i % 8 == 7)
4832                         fprintf(fp, "\n");
4833         }
4834         if (i % 8 != 0)
4835                 fprintf(fp, "\n");
4836         fprintf(fp, "\n");
4837         
4838         fprintf(fp, "%8d !NDON: donors\n\n", 0);
4839         fprintf(fp, "%8d !NACC: acceptors\n\n", 0);
4840         fprintf(fp, "%8d !NNB: non-bonding exclusions\n\n", 0);
4841         for (i = 0; i < mp->natoms; i++) {
4842                 fprintf(fp, "%8d", 0);
4843                 if (i % 8 == 7)
4844                         fprintf(fp, "\n");
4845         }
4846         if (i % 8 != 0)
4847                 fprintf(fp, "\n");
4848         fprintf(fp, "\n");
4849         fprintf(fp, "%8d !NGRP: groups\n", 1);
4850         fprintf(fp, "       0       0       0\n");
4851         fprintf(fp, "\n");
4852         
4853         i = strlen(fname);
4854         if (i > 5 && strcmp(fname + i - 5, ".psfx") == 0) {
4855                 /*  Extended psf (with coordinates and other info)  */
4856                 fprintf(fp, "%8d !COORD: coordinates\n", mp->natoms);
4857                 for (i = 0; i < mp->natoms; i++) {
4858                         Vector r;
4859                         ap = ATOM_AT_INDEX(mp->atoms, i);
4860                         r = ap->r;
4861                         fprintf(fp, " %.8g %.8g %.8g ! %d,%.4s\n", r.x, r.y, r.z, i + 1, ap->aname);
4862                 }
4863                 fprintf(fp, "\n");
4864         }
4865                 
4866         fclose(fp);
4867         return 0;
4868 }
4869
4870 int
4871 MoleculeWriteToPdbFile(Molecule *mp, const char *fname, char **errbuf)
4872 {
4873         FILE *fp;
4874         int i, j;
4875         Atom *ap;
4876         *errbuf = NULL;
4877         fp = fopen(fname, "wb");
4878         if (fp == NULL) {
4879                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
4880                 return 1;
4881         }
4882         for (i = 0; i < mp->natoms; i++) {
4883                 char buf[6];
4884                 ap = ATOM_AT_INDEX(mp->atoms, i);
4885                 if (ap->resSeq >= 10000) {
4886                         snprintf(buf, sizeof buf, "%c%03d", 'A' + (ap->resSeq - 10000) / 1000, ap->resSeq % 1000);
4887                 } else {
4888                         snprintf(buf, sizeof buf, "%4d", ap->resSeq);
4889                 }
4890                 fprintf(fp, "ATOM  %5d %-4.4s%1.1s%-3.3s %1.1s%4.4s%1.1s   "
4891                                         "%8.3f%8.3f%8.3f %5.2f %5.2f      "
4892                                         "%-4.4s%-2.2s%-2d\n",
4893                         i + 1, ap->aname, " ", ap->resName, " ", buf, " ",
4894                         ap->r.x, ap->r.y, ap->r.z, ap->occupancy, ap->tempFactor,
4895                         ap->segName, ap->element, ap->intCharge);
4896         }
4897         for (i = 0; i < mp->natoms; i++) {
4898                 Int *cp;
4899                 ap = ATOM_AT_INDEX(mp->atoms, i);
4900                 cp = AtomConnectData(&ap->connect);
4901                 for (j = 0; j < ap->connect.count; j++) {
4902                         if (j % 4 == 0) {
4903                                 if (j > 0)
4904                                         fprintf(fp, "\n");
4905                                 fprintf(fp, "CONECT%5d", i + 1);
4906                         }
4907                         fprintf(fp, "%5d", cp[j] + 1);
4908                 }
4909                 if (j > 0)
4910                         fprintf(fp, "\n");
4911         }
4912         fprintf(fp, "END\n");
4913         fclose(fp);
4914         return 0;
4915 }
4916
4917 int
4918 MoleculeWriteToDcdFile(Molecule *mp, const char *fname, char **errbuf)
4919 {
4920         DcdRecord dcd;
4921         SFloat32 *xp, *yp, *zp;
4922         int n;
4923         *errbuf = NULL;
4924         if (mp == NULL || mp->natoms == 0) {
4925                 s_append_asprintf(errbuf, "Molecule is empty");
4926                 return 1;
4927         }
4928         memset(&dcd, 0, sizeof(dcd));
4929         dcd.natoms = mp->natoms;
4930         dcd.nframes = MoleculeGetNumberOfFrames(mp);
4931         if (dcd.nframes == 0) {
4932                 s_append_asprintf(errbuf, "no frame is present");
4933                 return 1;
4934         }
4935         dcd.nstart = mp->startStep;
4936         dcd.ninterval = mp->stepsPerFrame;
4937         if (dcd.ninterval == 0)
4938                 dcd.ninterval = 1;
4939         dcd.nend = dcd.nstart + (dcd.nframes - 1) * dcd.ninterval;
4940         if (mp->cell != NULL)
4941                 dcd.nextra = 1;
4942         dcd.delta = mp->psPerStep;
4943         if (dcd.delta == 0.0)
4944                 dcd.delta = 1.0;
4945         dcd.ncharmver = 24;
4946         n = DcdCreate(fname, &dcd);
4947         if (n != 0) {
4948                 if (n < 0)
4949                         s_append_asprintf(errbuf, "Cannot create dcd file");
4950                 else
4951                         s_append_asprintf(errbuf, "Cannot write dcd header");
4952                 DcdClose(&dcd);
4953                 return 1;
4954         }
4955         
4956         xp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4957         yp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4958         zp = (SFloat32 *)malloc(sizeof(SFloat32) * dcd.natoms);
4959         if (xp == NULL || yp == NULL || zp == NULL) {
4960                 s_append_asprintf(errbuf, "Cannot allocate memory");
4961                 if (xp) free(xp);
4962                 if (yp) free(yp);
4963                 if (zp) free(zp);
4964                 DcdClose(&dcd);
4965                 return 1;
4966         }
4967         for (n = 0; n < dcd.nframes; n++) {
4968                 int i;
4969                 Atom *ap;
4970                 for (i = 0, ap = mp->atoms; i < dcd.natoms && mp->natoms; i++, ap = ATOM_NEXT(ap)) {
4971                         Vector r;
4972                         if (ap->frames == NULL || n >= ap->nframes)
4973                                 r = ap->r;
4974                         else
4975                                 r = ap->frames[n];
4976                         xp[i] = r.x;
4977                         yp[i] = r.y;
4978                         zp[i] = r.z;
4979                 }
4980                 if (i < dcd.natoms) {
4981                         size_t sz = (dcd.natoms - i) * sizeof(SFloat32);
4982                         memset(xp + i, 0, sz);
4983                         memset(yp + i, 0, sz);
4984                         memset(zp + i, 0, sz);
4985                 }
4986                 if (n < mp->nframe_cells && mp->frame_cells != NULL) {
4987                         Vector *cp = &(mp->frame_cells[n * 4]);
4988                         dcd.globalcell[0] = VecLength(cp[0]);
4989                         dcd.globalcell[2] = VecLength(cp[1]);
4990                         dcd.globalcell[5] = VecLength(cp[2]);
4991                         dcd.globalcell[1] = VecDot(cp[0], cp[1]) / (dcd.globalcell[0] * dcd.globalcell[2]);
4992                         dcd.globalcell[3] = VecDot(cp[0], cp[2]) / (dcd.globalcell[0] * dcd.globalcell[5]);
4993                         dcd.globalcell[4] = VecDot(cp[1], cp[2]) / (dcd.globalcell[2] * dcd.globalcell[5]);                     
4994                 }                       
4995                 if (DcdWriteFrame(&dcd, n, xp, yp, zp, dcd.globalcell)) {
4996                         s_append_asprintf(errbuf, "Write error in dcd file");
4997                         goto exit;
4998                 }
4999         }
5000         
5001 exit:
5002         DcdClose(&dcd);
5003         free(xp);
5004         free(yp);
5005         free(zp);
5006         if (errbuf[0] == 0)
5007                 return 0;
5008         else return 1;
5009 }
5010
5011 int
5012 MoleculeWriteExtendedInfo(Molecule *mp, const char *fname, char **errbuf)
5013 {
5014         FILE *fp;
5015         int i;
5016         Vector v;
5017         *errbuf = NULL;
5018         fp = fopen(fname, "wb");
5019         if (fp == NULL) {
5020                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5021                 return 1;
5022         }
5023         if (mp->cell != NULL) {
5024                 fprintf(fp, "Bounding box:\n");
5025                 for (i = 0; i < 3; i++) {
5026                         v = mp->cell->axes[i];
5027                         fprintf(fp, "%.3f %.3f %.3f %d\n", v.x, v.y, v.z, mp->cell->flags[i]);
5028                 }
5029                 fprintf(fp, "Bounding box origin:\n");
5030                 v = mp->cell->origin;
5031                 fprintf(fp, "%.3f %.3f %.3f\n", v.x, v.y, v.z);
5032         }
5033         fclose(fp);
5034         return 0;
5035 }
5036                 
5037  static int
5038 sCompareByElement(const void *ap, const void *bp)
5039 {
5040         return ((*(Atom **)bp)->atomicNumber - (*(Atom **)ap)->atomicNumber);
5041 }
5042
5043 static int
5044 sMakeAdc(int n, int base, Symop symop)
5045 {
5046         int an, sym;
5047         if (SYMOP_ALIVE(symop)) {
5048                 an = base;
5049                 sym = (symop.dx + 5) * 10000 + (symop.dy + 5) * 1000 + (symop.dz + 5) * 100 + symop.sym + 1;
5050         } else {
5051                 an = n;
5052                 sym = 55501;
5053         }
5054         return (an + 1) * 100000 + sym;
5055 }
5056
5057 static int
5058 sCompareAdc(const void *ap, const void *bp)
5059 {
5060         int n = *((Int *)ap) % 100000 - *((Int *)bp) % 100000;
5061         if (n == 0)
5062                 n = *((Int *)ap) / 100000 - *((Int *)bp) / 100000;
5063         return n;
5064 }
5065
5066 static void
5067 sOutputAtomListInstructions(FILE *fp, int natoms, Atom *atoms)
5068 {
5069         int i, j, k, an, sym;
5070         Atom *ap;
5071         Int *adc;
5072         adc = (Int *)malloc(sizeof(Int) * natoms);
5073         if (adc == NULL)
5074                 return;
5075         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5076                 if (ap->exflags & kAtomHiddenFlag)
5077                         continue;
5078                 adc[i] = sMakeAdc(i, ap->symbase, ap->symop);
5079         }
5080         mergesort(adc, natoms, sizeof(Int), sCompareAdc);
5081         
5082         /*  Create the atom list  */
5083         an = sym = -1;
5084         for (i = j = k = 0; i < natoms; i++) {
5085                 int an1 = adc[i] / 100000;
5086                 int sym1 = adc[i] % 100000;
5087                 if (sym == sym1 && an1 == an + 1) {
5088                         /*  Continuous  */
5089                         an = an1;
5090                         k++;
5091                         continue;
5092                 }
5093                 if (k > 0)
5094                         /*  Output the last atom with a minus sign  */
5095                         adc[j++] = -(an * 100000 + sym);
5096                 /*  Output this atom  */
5097                 adc[j++] = adc[i];
5098                 an = an1;
5099                 sym = sym1;
5100                 k = 0;
5101         }
5102         if (k > 0)
5103                 adc[j++] = -(an * 100000 + sym);
5104         
5105         /*  Create the instruction cards  */
5106         for (i = k = 0; i < j; i++) {
5107                 if (k == 0)
5108                         fprintf(fp, "      401");
5109                 fprintf(fp, "%9d", adc[i]);
5110                 k++;
5111                 if (i == j - 1 || k == 6 || (k == 5 && i < j - 2 && adc[i + 2] < 0)) {
5112                         fprintf(fp, "\n");
5113                         k = 0;
5114                 }
5115         }
5116         free(adc);
5117 }
5118
5119 static int
5120 sEllipsoidType(int an)
5121 {
5122         return (an >= 18 ? 3 : (an >= 2 && an != 6 ? 2 : (an > 0 ? 1 : 0)));
5123 }
5124
5125 static void
5126 sOutputAtomTypeInstructions(FILE *fp, int natoms, Atom *atoms)
5127 {
5128         int i;
5129         Atom *ap;
5130         int etype, elast, istart, ilast, n1, n2;
5131         elast = istart = ilast = -1;
5132         for (i = 0, ap = atoms; i <= natoms; i++, ap++) {
5133                 if (i < natoms) {
5134                         if (SYMOP_ALIVE(ap->symop))
5135                                 continue;
5136                         if (ap->exflags & kAtomHiddenFlag)
5137                                 continue;
5138                         etype = sEllipsoidType(ap->atomicNumber);
5139                         if (elast < 0) {
5140                                 istart = ilast = i;
5141                                 elast = etype;
5142                                 continue;
5143                         } else if (elast == etype && ilast == i - 1) {
5144                                 ilast++;
5145                                 continue;
5146                         }
5147                 }
5148                 /*  Output the instruction card for the 'last' block of atoms  */
5149                 switch (etype) {
5150                         case 2:
5151                                 n1 = 4; n2 = 0; break;
5152                         case 3:
5153                                 n1 = 4; n2 = 5; break;
5154                         default:
5155                                 n1 = 1; n2 = 0; break;
5156                 }
5157                 fprintf(fp, "  1   715 %8d        0 %8d        0    0.100    0.000    0.000\n", n1, n2);
5158                 fprintf(fp, "                           %9d%9d\n", istart + 1, ilast + 1);
5159                 elast = etype;
5160                 ilast = istart = i;
5161         }
5162 }
5163
5164 static int
5165 sCompareBondType(const void *ap, const void *bp)
5166 {
5167         /*  Descending order  */
5168         return *((int *)bp) - *((int *)ap);
5169 }
5170
5171 static void
5172 sOutputBondInstructions(FILE *fp, int natoms, Atom *atoms, int overlap_correction)
5173 {
5174         Atom *ap, *ap2;
5175         char buf[96];
5176         int i, j, n[5], an, count, n1, n2, k;
5177         Int *cp;
5178         Int nexbonds;
5179         Int *exbonds;
5180         static const float sBondRad[4] = {0.060, 0.060, 0.060, 0.040};
5181         static const int sBondShade[4] = {5, 3, 1, 1};
5182
5183         n[0] = n[1] = n[2] = n[3] = 0;  /*  Start index of 3rd row atoms (and higher), 2nd row, 1st row, and H */
5184         n[4] = natoms;
5185         for (i = natoms - 1, ap = atoms + i; i >= 0; i--, ap--) {
5186                 an = ap->atomicNumber;
5187                 if (an < 2)
5188                         n[3] = i;
5189                 if (an < 10)
5190                         n[2] = i;
5191                 if (an < 18)
5192                         n[1] = i;
5193         }
5194         nexbonds = 0;
5195         exbonds = NULL;
5196         count = 0;
5197
5198         if (overlap_correction)
5199                 strcpy(buf, "  2  1001    0.000\n");
5200         else
5201                 strcpy(buf, "  2   812\n");
5202         
5203         for (i = 0; i < 4; i++) {
5204                 for (j = i; j < 4; j++) {
5205                         /*  Examine bonds between "group i" and "group j"  */
5206                         Vector dr;
5207                         double d;
5208                         double min_bond = 10000.0;     /*  Minimum distance between bound atoms  */
5209                         double min_nonbond = 10000.0;  /*  Minimum distance between non-bound atoms  */
5210                         double max_bond = -10000.0;    /*  Maximum distance between bound atoms  */
5211                         int count_exbond = 0;          /*  Number of explicit bonds in this group  */
5212                         for (n1 = n[i], ap = atoms + n1; n1 < n[i + 1]; n1++, ap++) {
5213                                 for (n2 = n[j], ap2 = atoms + n2; n2 < n[j + 1]; n2++, ap2++) {
5214                                         if (n1 == n2)
5215                                                 continue;
5216                                         VecSub(dr, ap->r, ap2->r);
5217                                         d = VecLength(dr);
5218                                         cp = AtomConnectData(&ap->connect);
5219                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5220                                                 if (cp[k] == n2)
5221                                                         break;
5222                                         }
5223                                         if (k >= 0) {
5224                                                 /*  n1 and n2 are bound  */
5225                                                 if (d < min_bond)
5226                                                         min_bond = d;
5227                                                 if (d > max_bond)
5228                                                         max_bond = d;
5229                                         } else {
5230                                                 /*  n1 and n2 are not bound  */
5231                                                 if (d < min_nonbond)
5232                                                         min_nonbond = d;
5233                                         }
5234                                 }
5235                         }
5236                         if (min_bond == 10000.0)
5237                                 continue;  /*  No bonds between these groups  */
5238                         min_bond *= 0.9;
5239                         if (max_bond + 0.002 < min_nonbond)
5240                                 max_bond += 0.002;
5241                         else {
5242                                 max_bond = min_nonbond - 0.002;
5243                                 /*  Some bonds may be omitted, so scan all bonds again  */
5244                                 for (n1 = n[i], ap = ATOM_AT_INDEX(atoms, n1); n1 < n[i + 1]; n1++, ap = ATOM_NEXT(ap)) {
5245                                         cp = AtomConnectData(&ap->connect);
5246                                         for (k = ap->connect.count - 1; k >= 0; k--) {
5247                                                 n2 = cp[k];
5248                                                 if (n2 < n[j] || n2 >= n[j + 1])
5249                                                         continue;
5250                                                 ap2 = atoms + n2;
5251                                                 VecSub(dr, ap->r, ap2->r);
5252                                                 d = VecLength(dr);
5253                                                 if (d > max_bond) {
5254                                                         /*  This bond should be explicitly defined  */
5255                                                         Int adc1, adc2;
5256                                                         if (count_exbond == 0) {
5257                                                                 adc1 = -(i + 1);  /*  Bond type  */
5258                                                                 AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5259                                                         }
5260                                                         adc1 = sMakeAdc(n1, ap->symbase, ap->symop);
5261                                                         adc2 = sMakeAdc(n2, ap2->symbase, ap2->symop);
5262                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc1);
5263                                                         AssignArray(&exbonds, &nexbonds, sizeof(Int), nexbonds, &adc2);
5264                                                         count_exbond++;
5265                                                 }
5266                                         }
5267                                 }
5268                         }
5269                         /*  Output the last instruction card  */
5270                         fputs(buf, fp);
5271                         /*  Make a new trailer card  */
5272                         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]);
5273                         count++;
5274                 }
5275         }
5276         if (count > 0) {
5277                 /*  Output the last trailer card  */
5278                 buf[2] = ' ';
5279                 fputs(buf, fp);
5280         }
5281         if (nexbonds > 0) {
5282                 if (count == 0 && overlap_correction) {
5283                         /*  1001 card is not yet written, so write it  */
5284                         buf[2] = ' ';
5285                         fputs(buf, fp);
5286                 }
5287                 snprintf(buf, sizeof(buf), "  1   %3d", (overlap_correction ? 821 : 811));
5288                 k = -exbonds[0] - 1;  /*  Bond type for the first block  */
5289                 i = 1;  /*  Index for exbonds[]  */
5290                 j = 0;  /*  Count in this block  */
5291                 while (i <= nexbonds) {
5292                         if (j >= 29 || i == nexbonds || exbonds[i] < 0) {
5293                                 /*  End of block  */
5294                                 buf[2] = '2';
5295                                 fputs(buf, fp);
5296                                 /*  The trailer card  */
5297                                 fprintf(fp, "                     %3d            %6.3f\n", sBondShade[k], sBondRad[k]);
5298                                 if (i == nexbonds)
5299                                         break;
5300                                 if (exbonds[i] < 0)
5301                                         k = -exbonds[i++] - 1;  /*  The new bond type  */
5302                                 j = 0;
5303                         } else if (j > 0 && j % 3 == 0) {
5304                                 buf[2] = '1';
5305                                 fputs(buf, fp);
5306                         }
5307                         n1 = exbonds[i++];
5308                         n2 = exbonds[i++];
5309                         snprintf(buf + 9 + (j % 3) * 18, sizeof(buf) - 9 - (j % 3) * 18, "%9d%9d\n", n1, n2);
5310                         j++;
5311                 }
5312                 free(exbonds);
5313         }
5314 }
5315
5316 int
5317 MoleculeWriteToTepFile(Molecule *mp, const char *fname, char **errbuf)
5318 {
5319         FILE *fp;
5320         int i, j, natoms, *ip;
5321         Int *cp;
5322         Atom *ap, *atoms, **app;
5323         Double *dp;
5324         static Double sUnit[] = {1, 1, 1, 90, 90, 90};
5325         
5326         *errbuf = NULL;
5327
5328         /*  Create sorted array of atoms  */
5329         natoms = mp->natoms;
5330         atoms = (Atom *)calloc(sizeof(Atom), natoms);
5331         app = (Atom **)calloc(sizeof(Atom *), natoms);
5332         ip = (int *)calloc(sizeof(int), natoms);
5333         if (atoms == NULL || app == NULL || ip == NULL) {
5334                 s_append_asprintf(errbuf, "Cannot allocate memory");
5335                 return 1;
5336         }
5337         /*  Sort the atom pointer by atomic number  */
5338         for (i = 0, ap = mp->atoms; i < natoms; i++, ap = ATOM_NEXT(ap))
5339                 app[i] = ap;
5340         mergesort(app, natoms, sizeof(Atom *), sCompareByElement);
5341         for (i = 0; i < natoms; i++) {
5342                 /*  ip[old_index] is new_index  */
5343                 ip[app[i] - mp->atoms] = i;
5344         }
5345         /*  Copy the atom record to atoms[]  */
5346         /*  The 'v' member contains crystallographic coordinates  */
5347         /*  The connection table and symbase are renumbered  */
5348         /*  Hidden flags are modified to reflect the visibility in the MainView  */
5349         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5350                 AtomDuplicateNoFrame(ap, app[i]);
5351         /*      memmove(ap, app[i], gSizeOfAtomRecord); */
5352                 MoleculeCartesianToXtal(mp, &(ap->v), &(ap->r));
5353                 cp = AtomConnectData(&ap->connect);
5354                 for (j = ap->connect.count - 1; j >= 0; j--) {
5355                         cp[j] = ip[cp[j]];
5356                 }
5357                 if (SYMOP_ALIVE(ap->symop))
5358                         ap->symbase = ip[ap->symbase];
5359                 if (MainView_isAtomHidden(mp->mview, i)) {
5360                         ap->exflags |= kAtomHiddenFlag;
5361                 } else {
5362                         ap->exflags &= ~kAtomHiddenFlag;
5363                 }
5364         }
5365         free(ip);
5366         free(app);
5367         
5368         fp = fopen(fname, "wb");
5369         if (fp == NULL) {
5370                 s_append_asprintf(errbuf, "Cannot write to file %s", fname);
5371                 return 1;
5372         }
5373
5374         /*  Title line  */
5375         fprintf(fp, "Generated by Molby\n");
5376         
5377         /*  XtalCell  */
5378         if (mp->cell != NULL) {
5379                 dp = mp->cell->cell;
5380         } else {
5381                 dp = sUnit;
5382         }
5383         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]);
5384         
5385         /*  Symmetry operations  */
5386         if (mp->nsyms > 0) {
5387                 for (i = 0; i < mp->nsyms; i++) {
5388                         dp = mp->syms[i];
5389                         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]);
5390                 }
5391         } else {
5392                 fprintf(fp, "1             0  1  0  0              0  0  1  0              0  0  0  1\n");
5393         }
5394         
5395         /*  Atoms  */
5396         for (i = 0, ap = atoms; i < natoms; i++, ap++) {
5397                 /*  The 'v' field contains crystallographic coordinates  */
5398                 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);
5399                 if (ap->aniso != NULL) {
5400                         dp = ap->aniso->bij;
5401                         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);
5402                 } else {
5403                         Double temp = ap->tempFactor;
5404                         if (temp <= 0)
5405                                 temp = 1.2;
5406                         fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", temp, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5407                 }
5408         }
5409         /*  Special points  */
5410         {
5411                 Vector camera, lookat, up, xvec, yvec, zvec;
5412                 MainView_getCamera(mp->mview, &camera, &lookat, &up);
5413                 VecSub(zvec, lookat, camera);
5414                 VecCross(xvec, zvec, up);
5415                 NormalizeVec(&xvec, &xvec);
5416                 NormalizeVec(&yvec, &up);
5417                 VecInc(xvec, lookat);
5418                 VecInc(yvec, lookat);
5419                 MoleculeCartesianToXtal(mp, &lookat, &lookat);
5420                 MoleculeCartesianToXtal(mp, &xvec, &xvec);
5421                 MoleculeCartesianToXtal(mp, &yvec, &yvec);
5422                 fprintf(fp, " ORGN                      %9g%9g%9g        0\n", 0.0, 0.0, 0.0);
5423                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5424                 fprintf(fp, " CNTR                      %9g%9g%9g        0\n", lookat.x, lookat.y, lookat.z);
5425                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5426                 fprintf(fp, " X                         %9g%9g%9g        0\n", xvec.x, xvec.y, xvec.z);
5427                 fprintf(fp, " %8.3f%9g%9g%9g%9g%9g%9d\n", 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 6);
5428                 fprintf(fp, " Y                         %9g%9g%9g        0\n", yvec.x, yvec.y, yvec.z);
5429                 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);
5430         }
5431         
5432         /*  Instructions  */
5433         fprintf(fp, "      201\n");
5434         fprintf(fp, "      205       12\n");
5435         fprintf(fp, "      301      6.6      6.6        0      0.8\n");
5436         sOutputAtomListInstructions(fp, natoms, atoms);
5437         fprintf(fp, "      501%4d55501%4d55501%4d55501%4d55501%4d55501                 1\n", natoms + 2, natoms + 2, natoms + 3, natoms + 2, natoms + 4);
5438         fprintf(fp, "      502        1      0.0        2      0.0        3      0.0\n");
5439         fprintf(fp, "      604                               1.538\n");
5440
5441         sOutputBondInstructions(fp, natoms, atoms, 1);
5442         sOutputAtomTypeInstructions(fp, natoms, atoms);
5443         sOutputBondInstructions(fp, natoms, atoms, 0);
5444
5445         for (i = 0; i < natoms; i++) {
5446                 AtomClean(atoms + i);
5447         }
5448         free(atoms);
5449
5450         fprintf(fp, "      202\n");
5451         fprintf(fp, "  0    -1\n");
5452         fclose(fp);
5453         return 0;
5454 }
5455
5456 void
5457 MoleculeDump(Molecule *mol)
5458 {
5459         int i, j;
5460         Int *cp;
5461         Atom *ap;
5462         for (i = 0; i < mol->natoms; i++) {
5463                 char buf1[8];
5464                 ap = ATOM_AT_INDEX(mol->atoms, i);
5465                 snprintf(buf1, sizeof buf1, "%3.4s.%d", ap->resName, ap->resSeq);
5466                 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);
5467                 cp = AtomConnectData(&ap->connect);
5468                 for (j = 0; j < ap->connect.count; j++) {
5469                         fprintf(stderr, "%s%d", (j > 0 ? "," : ""), cp[j]);
5470                 }
5471                 fprintf(stderr, "]\n");
5472         }
5473 }
5474
5475 #pragma mark ====== MD support (including modification of Molecule) ======
5476
5477 /*  Call md_prepare for the MDArena. If MDArena has not been created, a new arena is created.
5478         If something goes wrong, returns 1 (for missing parameters) or -1 (more serious error).
5479     If retmsg is not NULL, a message describing the problem is returned there. This message
5480     must be free'd by the caller.  */
5481 int
5482 MoleculePrepareMDArena(Molecule *mol, int check_only, char **retmsg)
5483 {
5484         const char *msg;
5485         Int nangles, *angles, ndihedrals, *dihedrals, nimpropers, *impropers;
5486         Int missing = 0;
5487         IntGroup *ig1, *ig2, *ig3;
5488         MDArena *arena = mol->arena;
5489
5490         if (arena == NULL) {
5491                 md_arena_new(mol);
5492                 arena = mol->arena;
5493         } else if (arena->xmol != mol)
5494                 md_arena_set_molecule(arena, mol);
5495
5496         arena->is_initialized = 0;
5497         
5498         /*  Rebuild the tables  */
5499         ig1 = ig2 = ig3 = NULL;
5500         nangles = MoleculeFindMissingAngles(mol, &angles);
5501         ndihedrals = MoleculeFindMissingDihedrals(mol, &dihedrals);
5502         nimpropers = MoleculeFindMissingImpropers(mol, &impropers);
5503         if (nangles > 0) {
5504                 ig1 = IntGroupNewWithPoints(mol->nangles, nangles, -1);
5505                 MolActionCreateAndPerform(mol, gMolActionAddAngles, nangles * 3, angles, ig1);
5506                 free(angles);
5507                 IntGroupRelease(ig1);
5508         }
5509         if (ndihedrals > 0) {
5510                 ig2 = IntGroupNewWithPoints(mol->ndihedrals, ndihedrals, -1);
5511                 MolActionCreateAndPerform(mol, gMolActionAddDihedrals, ndihedrals * 4, dihedrals, ig2);
5512                 free(dihedrals);
5513                 IntGroupRelease(ig2);
5514         }
5515         if (nimpropers > 0) {
5516                 ig3 = IntGroupNewWithPoints(mol->nimpropers, nimpropers, -1);
5517                 MolActionCreateAndPerform(mol, gMolActionAddImpropers, nimpropers * 4, impropers, ig3);
5518                 free(impropers);
5519                 IntGroupRelease(ig3);
5520         }
5521         
5522         {
5523                 /*  Update the path information of the molecule before MD setup  */
5524                 char *buf = (char *)malloc(4096);
5525                 MoleculeCallback_pathName(mol, buf, sizeof buf);
5526                 MoleculeSetPath(mol, buf);
5527                 free(buf);
5528         }
5529                 
5530         /*  Prepare parameters and internal information  */
5531         msg = md_prepare(arena, check_only);
5532         
5533         /*  Some parameters are missing?  */
5534         if (msg != NULL) {
5535                 if (strstr(msg, "parameter") != NULL && strstr(msg, "missing") != NULL)
5536                         missing = 1;
5537                 else {
5538                         if (retmsg != NULL)
5539                                 asprintf(retmsg, "cannot initialize for MD: %s", msg);
5540                         return -1;
5541                 }
5542         }
5543         
5544         /*  The local parameter list is updated  */
5545         {
5546                 Int parType, idx;
5547                 if (mol->par == NULL)
5548                         mol->par = ParameterNew();
5549                 for (parType = kFirstParType; parType <= kLastParType; parType++) {
5550                         /*  Delete global and undefined parameters  */
5551                         UnionPar *up, *upbuf;
5552                         Int nparams, count;
5553                         ig1 = IntGroupNew();
5554                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(mol->par, parType, idx)) != NULL; idx++) {
5555                                 if (up->bond.src != 0)
5556                                         IntGroupAdd(ig1, idx, 1);
5557                         }
5558                         if (IntGroupGetCount(ig1) > 0)
5559                                 MolActionCreateAndPerform(mol, gMolActionDeleteParameters, parType, ig1);
5560                         IntGroupRelease(ig1);
5561                         /*  Copy global and undefined parameters from arena and insert to mol->par  */
5562                         nparams = ParameterGetCountForType(arena->par, parType);
5563                         if (nparams == 0)
5564                                 continue;
5565                         upbuf = (UnionPar *)calloc(sizeof(UnionPar), nparams);
5566                         ig1 = IntGroupNew();
5567                         ig2 = IntGroupNew();
5568                         for (idx = 0; (up = ParameterGetUnionParFromTypeAndIndex(arena->par, parType, idx)) != NULL; idx++) {
5569                                 if (up->bond.src > 0)
5570                                         IntGroupAdd(ig1, idx, 1); /* Global parameter */
5571                                 else if (up->bond.src < 0)
5572                                         IntGroupAdd(ig2, idx, 1); /* Undefined parameter */
5573                         }
5574                         if ((count = IntGroupGetCount(ig1)) > 0) {
5575                                 /*  Insert global parameters (at the top)  */
5576                                 ParameterCopy(arena->par, parType, upbuf, ig1);
5577                                 ig3 = IntGroupNewWithPoints(0, count, -1);
5578                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5579                                 IntGroupRelease(ig3);
5580                         }
5581                         if ((count = IntGroupGetCount(ig2)) > 0) {
5582                                 /*  Insert undefined parameters (at the bottom)  */
5583                                 ParameterCopy(arena->par, parType, upbuf, ig2);
5584                                 idx = ParameterGetCountForType(mol->par, parType);
5585                                 ig3 = IntGroupNewWithPoints(idx, count, -1);
5586                                 MolActionCreateAndPerform(mol, gMolActionAddParameters, parType, ig3, count, upbuf);
5587                                 IntGroupRelease(ig3);
5588                         }
5589                         IntGroupRelease(ig2);
5590                         IntGroupRelease(ig1);
5591                         free(upbuf);
5592                 }
5593                 mol->needsMDRebuild = 0;  /*  We know the "modified" parameters are consistent with the MDArena  */
5594         }
5595         
5596         if (missing) {
5597                 if (retmsg != NULL)
5598                         *retmsg = strdup(msg);
5599                 return 1;
5600         } else return 0;
5601 }
5602
5603 #pragma mark ====== Serialize ======
5604
5605 Molecule *
5606 MoleculeDeserialize(const char *data, Int length, Int *timep)
5607 {
5608         Molecule *mp;
5609         Parameter *par;
5610         Atom *ap;
5611 /*      int result; */
5612
5613         mp = MoleculeNew();
5614         if (mp == NULL)
5615                 goto out_of_memory;
5616         par = ParameterNew();
5617         if (par == NULL)
5618                 goto out_of_memory;
5619
5620         while (length >= 12) {
5621                 const char *ptr = data + 8 + sizeof(Int);
5622                 int len = *((const Int *)(data + 8));
5623                 int i, j, n;
5624                 if (strcmp(data, "ATOM") == 0) {
5625                         n = len / gSizeOfAtomRecord;
5626                         NewArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, n);
5627                         memmove(mp->atoms, ptr, len);
5628                 } else if (strcmp(data, "ANISO") == 0) {
5629                         n = len / (sizeof(Int) + sizeof(Aniso));
5630                         for (i = 0; i < n; i++) {
5631                                 j = *((const Int *)ptr);
5632                                 if (j < 0 || j >= mp->natoms)
5633                                         goto bad_format;
5634                                 ap = ATOM_AT_INDEX(mp->atoms, j);
5635                                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
5636                                 if (ap->aniso == NULL)
5637                                         goto out_of_memory;
5638                                 *(ap->aniso) = *((Aniso *)(ptr + sizeof(Int)));
5639                                 ptr += sizeof(Int) + sizeof(Aniso);
5640                         }
5641                 } else if (strcmp(data, "FRAME") == 0) {
5642                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5643                                 if (ap->nframes == 0)
5644                                         continue;
5645                                 n = ap->nframes;
5646                                 ap->frames = NULL;
5647                                 ap->nframes = 0;
5648                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n);
5649                                 if (ap->frames == NULL)
5650                                         goto out_of_memory;
5651                                 memmove(ap->frames, ptr, sizeof(Vector) * ap->nframes);
5652                                 ptr += sizeof(Vector) * ap->nframes;
5653                         }
5654                 } else if (strcmp(data, "EXTCON") == 0) {
5655                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
5656                                 if (ap->connect.count <= ATOM_CONNECT_LIMIT)
5657                                         continue;
5658                                 n = ap->connect.count;
5659                                 ap->connect.count = 0;
5660                                 ap->connect.u.ptr = NULL;
5661                                 NewArray(&(ap->connect.u.ptr), &(ap->connect.count), sizeof(Int), n);
5662                                 memmove(ap->connect.u.ptr, ptr, sizeof(Int) * n);
5663                                 ptr += sizeof(Int) * n;
5664                         }
5665                 } else if (strcmp(data, "BOND") == 0) {
5666                         n = len / (sizeof(Int) * 2);
5667                         NewArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, n);
5668                         memmove(mp->bonds, ptr, len);
5669                 } else if (strcmp(data, "ANGLE") == 0) {
5670                         n = len / (sizeof(Int) * 3);
5671                         NewArray(&mp->angles, &mp->nangles, sizeof(Int) * 3, n);
5672                         memmove(mp->angles, ptr, len);
5673                 } else if (strcmp(data, "DIHED") == 0) {
5674                         n = len / (sizeof(Int) * 4);
5675                         NewArray(&mp->dihedrals, &mp->ndihedrals, sizeof(Int) * 4, n);
5676                         memmove(mp->dihedrals, ptr, len);
5677                 } else if (strcmp(data, "IMPROP") == 0) {
5678                         n = len / (sizeof(Int) * 4);
5679                         NewArray(&mp->impropers, &mp->nimpropers, sizeof(Int) * 4, n);
5680                         memmove(mp->impropers, ptr, len);
5681                 } else if (strcmp(data, "RESIDUE") == 0) {
5682                         n = len / 4;
5683                         NewArray(&mp->residues, &mp->nresidues, 4, n);
5684                         memmove(mp->residues, ptr, len);
5685                 } else if (strcmp(data, "CELL") == 0) {
5686                         mp->cell = (XtalCell *)malloc(sizeof(XtalCell));
5687                         if (mp->cell == NULL)
5688                                 goto out_of_memory;
5689                         memmove(mp->cell, ptr, sizeof(XtalCell));
5690                 } else if (strcmp(data, "SYMOP") == 0) {
5691                         n = len / sizeof(Transform);
5692                         NewArray(&mp->syms, &mp->nsyms, sizeof(Transform), n);
5693                         memmove(mp->syms, ptr, len);
5694                 } else if (strcmp(data, "ANCHOR") == 0) {
5695                         const char *ptr2 = ptr + len;
5696                         while (ptr < ptr2) {
5697                                 PiAnchor an;
5698                                 memset(&an, 0, sizeof(an));
5699                                 i = *((Int *)ptr);
5700                                 if (i >= 0 && i < mp->natoms) {
5701                                         n = *((Int *)(ptr + sizeof(Int)));
5702                                         AtomConnectResize(&(an.connect), n);
5703                                         memmove(AtomConnectData(&(an.connect)), ptr + sizeof(Int) * 2, sizeof(Int) * n);
5704                                         NewArray(&an.coeffs, &an.ncoeffs, sizeof(Double), n);
5705                                         memmove(an.coeffs, ptr + sizeof(Int) * (2 + n), sizeof(Double) * n);
5706                                         ap = ATOM_AT_INDEX(mp->atoms, i);
5707                                         ap->anchor = (PiAnchor *)malloc(sizeof(PiAnchor));
5708                                         memmove(ap->anchor, &an, sizeof(PiAnchor));
5709                                 }
5710                                 ptr += sizeof(Int) * (2 + n) + sizeof(Double) * n;
5711                         }
5712                 } else if (strcmp(data, "TIME") == 0) {
5713                         if (timep != NULL)
5714                                 *timep = *((Int *)ptr);
5715                 } else if (strcmp(data, "BONDPAR") == 0) {
5716                         mp->par = par;
5717                         n = len / sizeof(BondPar);
5718                         NewArray(&par->bondPars, &par->nbondPars, sizeof(BondPar), n);
5719                         memmove(par->bondPars, ptr, len);
5720                 } else if (strcmp(data, "ANGPAR") == 0) {
5721                         mp->par = par;
5722                         n = len / sizeof(AnglePar);
5723                         NewArray(&par->anglePars, &par->nanglePars, sizeof(AnglePar), n);
5724                         memmove(par->anglePars, ptr, len);
5725                 } else if (strcmp(data, "DIHEPAR") == 0) {
5726                         mp->par = par;
5727                         n = len / sizeof(TorsionPar);
5728                         NewArray(&par->dihedralPars, &par->ndihedralPars, sizeof(TorsionPar), n);
5729                         memmove(par->dihedralPars, ptr, len);
5730                 } else if (strcmp(data, "IMPRPAR") == 0) {
5731                         mp->par = par;
5732                         n = len / sizeof(TorsionPar);
5733                         NewArray(&par->improperPars, &par->nimproperPars, sizeof(TorsionPar), n);
5734                         memmove(par->improperPars, ptr, len);
5735                 } else if (strcmp(data, "VDWPAR") == 0) {
5736                         mp->par = par;
5737                         n = len / sizeof(VdwPar);
5738                         NewArray(&par->vdwPars, &par->nvdwPars, sizeof(VdwPar), n);
5739                         memmove(par->vdwPars, ptr, len);
5740                 } else if (strcmp(data, "VDWPPAR") == 0) {
5741                         mp->par = par;
5742                         n = len / sizeof(VdwPairPar);
5743                         NewArray(&par->vdwpPars, &par->nvdwpPars, sizeof(VdwPairPar), n);
5744                         memmove(par->vdwpPars, ptr, len);
5745                 } else if (strcmp(data, "VCUTPAR") == 0) {
5746                         mp->par = par;
5747                         n = len / sizeof(VdwCutoffPar);
5748                         NewArray(&par->vdwCutoffPars, &par->nvdwCutoffPars, sizeof(VdwCutoffPar), n);
5749                         memmove(par->vdwCutoffPars, ptr, len);
5750                 }
5751                 len += 8 + sizeof(Int);
5752                 data += len;
5753                 length -= len;
5754         }
5755         if (mp->par == NULL)
5756                 ParameterRelease(par);
5757 /*      result = MoleculeRebuildTablesFromConnects(mp);
5758         if (result != 0)
5759                 goto bad_format; */
5760         return mp;
5761         
5762   out_of_memory:
5763         Panic("Low memory while deserializing molecule data");
5764         return NULL; /* Not reached */
5765
5766   bad_format:
5767         Panic("internal error: bad format during deserializing molecule data");
5768         return NULL; /* Not reached */
5769 }
5770
5771 char *
5772 MoleculeSerialize(Molecule *mp, Int *outLength, Int *timep)
5773 {
5774         char *ptr, *p;
5775         int len, len_all, i, naniso, nframes, nconnects, nanchors;
5776         Atom *ap;
5777
5778         /*  Array of atoms  */
5779         len = 8 + sizeof(Int) + gSizeOfAtomRecord * mp->natoms;
5780         ptr = (char *)malloc(len);
5781         if (ptr == NULL)
5782                 goto out_of_memory;
5783         memmove(ptr, "ATOM\0\0\0\0", 8);
5784         *((Int *)(ptr + 8)) = gSizeOfAtomRecord * mp->natoms;
5785         p = ptr + 8 + sizeof(Int);
5786         memmove(p, mp->atoms, gSizeOfAtomRecord * mp->natoms);
5787         naniso = nframes = nconnects = nanchors = 0;
5788         for (i = 0; i < mp->natoms; i++) {
5789                 ap = ATOM_AT_INDEX(p, i);
5790                 if (ap->aniso != NULL) {
5791                         naniso++;
5792                         ap->aniso = NULL;
5793                 }
5794                 if (ap->frames != NULL) {
5795                         nframes += ap->nframes;
5796                         ap->frames = NULL;
5797                 }
5798                 if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5799                         nconnects += ap->connect.count;
5800                         ap->connect.u.ptr = NULL;
5801                 }
5802                 if (ap->anchor != NULL) {
5803                         nanchors++;
5804                         ap->anchor = NULL;
5805                 }
5806         }
5807         len_all = len;
5808
5809         /*  Array of aniso  */
5810         if (naniso > 0) {
5811                 len = 8 + sizeof(Int) + (sizeof(Int) + sizeof(Aniso)) * naniso;
5812                 ptr = (char *)realloc(ptr, len_all + len);
5813                 if (ptr == NULL)
5814                         goto out_of_memory;
5815                 p = ptr + len_all;
5816                 memmove(p, "ANISO\0\0\0", 8);
5817                 *((Int *)(p + 8)) = (sizeof(Int) + sizeof(Aniso)) * naniso;
5818                 p += 8 + sizeof(Int);
5819                 for (i = 0; i < mp->natoms; i++) {
5820                         ap = ATOM_AT_INDEX(mp->atoms, i);
5821                         if (ap->aniso != NULL) {
5822                                 *((Int *)p) = i;
5823                                 *((Aniso *)(p + sizeof(Int))) = *(ap->aniso);
5824                                 p += sizeof(Int) + sizeof(Aniso);
5825                         }
5826                 }
5827                 len_all += len;
5828         }
5829         
5830         /*  Array of frames  */
5831         if (nframes > 0) {
5832                 len = 8 + sizeof(Int) + sizeof(Vector) * nframes;
5833                 ptr = (char *)realloc(ptr, len_all + len);
5834                 if (ptr == NULL)
5835                         goto out_of_memory;
5836                 p = ptr + len_all;
5837                 memmove(p, "FRAME\0\0\0", 8);
5838                 *((Int *)(p + 8)) = sizeof(Vector) * nframes;
5839                 p += 8 + sizeof(Int);
5840                 for (i = 0; i < mp->natoms; i++) {
5841                         ap = ATOM_AT_INDEX(mp->atoms, i);
5842                         if (ap->frames != NULL) {
5843                                 memmove(p, ap->frames, sizeof(Vector) * ap->nframes);
5844                                 p += sizeof(Vector) * ap->nframes;
5845                         }
5846                 }
5847                 len_all += len;
5848         }
5849         
5850         /*  Array of connects  */
5851         if (nconnects > 0) {
5852                 len = 8 + sizeof(Int) + sizeof(Int) * nconnects;
5853                 ptr = (char *)realloc(ptr, len_all + len);
5854                 if (ptr == NULL)
5855                         goto out_of_memory;
5856                 p = ptr + len_all;
5857                 memmove(p, "EXTCON\0\0", 8);
5858                 *((Int *)(p + 8)) = sizeof(Int) * nconnects;
5859                 p += 8 + sizeof(Int);
5860                 for (i = 0; i < mp->natoms; i++) {
5861                         ap = ATOM_AT_INDEX(mp->atoms, i);
5862                         if (ap->connect.count > ATOM_CONNECT_LIMIT) {
5863                                 memmove(p, ap->connect.u.ptr, sizeof(Int) * ap->connect.count);
5864                                 p += sizeof(Int) * ap->connect.count;
5865                         }
5866                 }
5867                 len_all += len;
5868         }
5869         
5870         /*  Bonds, angles, dihedrals, impropers  */
5871         if (mp->nbonds > 0) {
5872                 len = 8 + sizeof(Int) + sizeof(Int) * 2 * mp->nbonds;
5873                 ptr = (char *)realloc(ptr, len_all + len);
5874                 if (ptr == NULL)
5875                         goto out_of_memory;
5876                 p = ptr + len_all;
5877                 memmove(p, "BOND\0\0\0\0", 8);
5878                 *((Int *)(p + 8)) = sizeof(Int) * 2 * mp->nbonds;
5879                 p += 8 + sizeof(Int);
5880                 memmove(p, mp->bonds, sizeof(Int) * 2 * mp->nbonds);
5881                 len_all += len;
5882         }
5883         if (mp->nangles > 0) {
5884                 len = 8 + sizeof(Int) + sizeof(Int) * 3 * mp->nangles;
5885                 ptr = (char *)realloc(ptr, len_all + len);
5886                 if (ptr == NULL)
5887                         goto out_of_memory;
5888                 p = ptr + len_all;
5889                 memmove(p, "ANGLE\0\0\0", 8);
5890                 *((Int *)(p + 8)) = sizeof(Int) * 3 * mp->nangles;
5891                 p += 8 + sizeof(Int);
5892                 memmove(p, mp->angles, sizeof(Int) * 3 * mp->nangles);
5893                 len_all += len;
5894         }
5895         if (mp->ndihedrals > 0) {
5896                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->ndihedrals;
5897                 ptr = (char *)realloc(ptr, len_all + len);
5898                 if (ptr == NULL)
5899                         goto out_of_memory;
5900                 p = ptr + len_all;
5901                 memmove(p, "DIHED\0\0\0", 8);
5902                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->ndihedrals;
5903                 p += 8 + sizeof(Int);
5904                 memmove(p, mp->dihedrals, sizeof(Int) * 4 * mp->ndihedrals);
5905                 len_all += len;
5906         }
5907         if (mp->nimpropers > 0) {
5908                 len = 8 + sizeof(Int) + sizeof(Int) * 4 * mp->nimpropers;
5909                 ptr = (char *)realloc(ptr, len_all + len);
5910                 if (ptr == NULL)
5911                         goto out_of_memory;
5912                 p = ptr + len_all;
5913                 memmove(p, "IMPROP\0\0", 8);
5914                 *((Int *)(p + 8)) = sizeof(Int) * 4 * mp->nimpropers;
5915                 p += 8 + sizeof(Int);
5916                 memmove(p, mp->impropers, sizeof(Int) * 4 * mp->nimpropers);
5917                 len_all += len;
5918         }
5919         
5920         /*  Array of residues  */
5921         if (mp->nresidues > 0) {
5922                 len = 8 + sizeof(Int) + 4 * mp->nresidues;
5923                 ptr = (char *)realloc(ptr, len_all + len);
5924                 if (ptr == NULL)
5925                         goto out_of_memory;
5926                 p = ptr + len_all;
5927                 memmove(p, "RESIDUE\0", 8);
5928                 *((Int *)(p + 8)) = 4 * mp->nresidues;
5929                 p += 8 + sizeof(Int);
5930                 memmove(p, mp->residues, 4 * mp->nresidues);
5931                 len_all += len;
5932         }
5933
5934         /*  Unit cell  */
5935         if (mp->cell != NULL) {
5936                 len = 8 + sizeof(Int) + sizeof(XtalCell);
5937                 ptr = (char *)realloc(ptr, len_all + len);
5938                 if (ptr == NULL)
5939                         goto out_of_memory;
5940                 p = ptr + len_all;
5941                 memmove(p, "CELL\0\0\0\0", 8);
5942                 *((Int *)(p + 8)) = sizeof(XtalCell);
5943                 p += 8 + sizeof(Int);
5944                 memmove(p, mp->cell, sizeof(XtalCell));
5945                 len_all += len;
5946         }
5947         
5948         /*  Symmetry operations  */
5949         if (mp->nsyms > 0) {
5950                 len = 8 + sizeof(Int) + sizeof(Transform) * mp->nsyms;
5951                 ptr = (char *)realloc(ptr, len_all + len);
5952                 if (ptr == NULL)
5953                         goto out_of_memory;
5954                 p = ptr + len_all;
5955                 memmove(p, "SYMOP\0\0\0", 8);
5956                 *((Int *)(p + 8)) = sizeof(Transform) * mp->nsyms;
5957                 p += 8 + sizeof(Int);
5958                 memmove(p, mp->syms, sizeof(Transform) * mp->nsyms);
5959                 len_all += len;
5960         }
5961         
5962         /*  Pi-anchors  */
5963         if (nanchors > 0) {
5964                 /*  Estimate the necessary storage first  */
5965                 /*  One entry consists of { atom_index (Int), number_of_connects (Int), connects (Int's), weights (Double's) }  */
5966                 len = 8 + sizeof(Int);
5967                 for (i = 0; i < mp->natoms; i++) {
5968                         ap = ATOM_AT_INDEX(mp->atoms, i);
5969                         if (ap->anchor != NULL)
5970                                 len += sizeof(Int) * 2 + (sizeof(Int) + sizeof(Double)) * ap->anchor->connect.count;
5971                 }
5972                 ptr = (char *)realloc(ptr, len_all + len);
5973                 if (ptr == NULL)
5974                         goto out_of_memory;
5975                 p = ptr + len_all;
5976                 memmove(p, "ANCHOR\0\0", 8);
5977                 *((Int *)(p + 8)) = len - (8 + sizeof(Int));
5978                 p += 8 + sizeof(Int);
5979                 for (i = 0; i < mp->natoms; i++) {
5980                         Int count, *ip;
5981                         ap = ATOM_AT_INDEX(mp->atoms, i);
5982                         if (ap->anchor != NULL) {
5983                                 count = ap->anchor->connect.count;
5984                                 *((Int *)p) = i;
5985                                 *((Int *)(p + sizeof(Int))) = count;
5986                                 p += sizeof(Int) * 2;
5987                                 ip = AtomConnectData(&(ap->anchor->connect));
5988                                 memmove(p, ip, sizeof(Int) * count);
5989                                 p += sizeof(Int) * count;
5990                                 memmove(p, ap->anchor->coeffs, sizeof(Double) * count);
5991                                 p += sizeof(Double) * count;
5992                         }
5993                 }
5994                 len_all += len;
5995         }
5996         
5997         /*  Parameters  */
5998         if (mp->par != NULL) {
5999                 int type;
6000                 for (type = kFirstParType; type <= kLastParType; type++) {
6001                         const char *parname;
6002                         Int parsize, parcount;
6003                         void *parptr;
6004                         switch (type) {
6005                                 case kBondParType:
6006                                         parname = "BONDPAR\0";
6007                                         parsize = sizeof(BondPar);
6008                                         parcount = mp->par->nbondPars;
6009                                         parptr = mp->par->bondPars;
6010                                         break;
6011                                 case kAngleParType:
6012                                         parname = "ANGPAR\0\0";
6013                                         parsize = sizeof(AnglePar);
6014                                         parcount = mp->par->nanglePars;
6015                                         parptr = mp->par->anglePars;
6016                                         break;
6017                                 case kDihedralParType:
6018                                         parname = "DIHEPAR\0";
6019                                         parsize = sizeof(TorsionPar);
6020                                         parcount = mp->par->ndihedralPars;
6021                                         parptr = mp->par->dihedralPars;
6022                                         break;
6023                                 case kImproperParType:
6024                                         parname = "IMPRPAR\0";
6025                                         parsize = sizeof(TorsionPar);
6026                                         parcount = mp->par->nimproperPars;
6027                                         parptr = mp->par->improperPars;
6028                                         break;
6029                                 case kVdwParType:
6030                                         parname = "VDWPAR\0\0";
6031                                         parsize = sizeof(VdwPar);
6032                                         parcount = mp->par->nvdwPars;
6033                                         parptr = mp->par->vdwPars;
6034                                         break;
6035                                 case kVdwPairParType:
6036                                         parname = "VDWPPAR\0";
6037                                         parsize = sizeof(VdwPairPar);
6038                                         parcount = mp->par->nvdwpPars;
6039                                         parptr = mp->par->vdwpPars;
6040                                         break;
6041                                 case kVdwCutoffParType:
6042                                         parname = "VCUTPAR\0";
6043                                         parsize = sizeof(VdwCutoffPar);
6044                                         parcount = mp->par->nvdwCutoffPars;
6045                                         parptr = mp->par->vdwCutoffPars;
6046                                         break;
6047                                 default:
6048                                         continue;
6049                         }
6050                         if (parcount > 0) {
6051                                 len = 8 + sizeof(Int) + parsize * parcount;
6052                                 ptr = (char *)realloc(ptr, len_all + len);
6053                                 if (ptr == NULL)
6054                                         goto out_of_memory;
6055                                 p = ptr + len_all;
6056                                 memmove(p, parname, 8);
6057                                 *((Int *)(p + 8)) = parsize * parcount;
6058                                 p += 8 + sizeof(Int);
6059                                 memmove(p, parptr, parsize * parcount);
6060                                 len_all += len;
6061                         }
6062                 }
6063         }
6064         
6065         /*  Time stamp  */
6066         {
6067                 time_t tm = time(NULL);
6068                 len = 8 + sizeof(Int) + sizeof(Int);
6069                 ptr = (char *)realloc(ptr, len_all + len);
6070                 if (ptr == NULL)
6071                         goto out_of_memory;
6072                 p = ptr + len_all;
6073                 memmove(p, "TIME\0\0\0\0", 8);
6074                 *((Int *)(p + 8)) = sizeof(Int);
6075                 p += 8 + sizeof(Int);
6076                 *((Int *)p) = (Int)tm;
6077                 len_all += len;
6078                 if (timep != NULL)
6079                         *timep = (Int)tm;
6080         }
6081         
6082         if (outLength != NULL)
6083                 *outLength = len_all;
6084         return ptr;
6085
6086   out_of_memory:
6087     Panic("Low memory while serializing a molecule data");
6088         return NULL; /* Not reached */  
6089 }
6090
6091 #pragma mark ====== Search for bonds, angles, dihedrals, impropers ======
6092
6093 static IntGroup *
6094 sMoleculeSearchIncludingAtoms(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6095 {
6096         int i, j;
6097         Int *ip;
6098         IntGroup *gp = NULL;
6099         if (atomgroup == NULL)
6100                 return NULL;
6101         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6102                 for (j = 0; j < nsize; j++) {
6103                         if (IntGroupLookup(atomgroup, ip[j], NULL) != 0) {
6104                                 if (gp == NULL)
6105                                         gp = IntGroupNew();
6106                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6107                                         Panic("Low memory while searching %s", msg);
6108                                 break;
6109                         }
6110                 }
6111         }
6112         return gp;
6113 }
6114
6115 IntGroup *
6116 MoleculeSearchBondsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6117 {
6118         if (mp == NULL)
6119                 return NULL;
6120         return sMoleculeSearchIncludingAtoms(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6121 }
6122
6123 IntGroup *
6124 MoleculeSearchAnglesIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6125 {
6126         if (mp == NULL)
6127                 return NULL;
6128         return sMoleculeSearchIncludingAtoms(mp->nangles, mp->angles, 3, atomgroup, "angles");
6129 }
6130
6131 IntGroup *
6132 MoleculeSearchDihedralsIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6133 {
6134         if (mp == NULL)
6135                 return NULL;
6136         return sMoleculeSearchIncludingAtoms(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6137 }
6138
6139 IntGroup *
6140 MoleculeSearchImpropersIncludingAtoms(Molecule *mp, IntGroup *atomgroup)
6141 {
6142         if (mp == NULL)
6143                 return NULL;
6144         return sMoleculeSearchIncludingAtoms(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6145 }
6146
6147 static IntGroup *
6148 sMoleculeSearchAcrossAtomGroup(int nitems, Int *items, int nsize, IntGroup *atomgroup, const char *msg)
6149 {
6150         int i, j;
6151         Int *ip;
6152         IntGroup *gp = NULL;
6153         if (atomgroup == NULL)
6154                 return NULL;
6155         for (i = 0, ip = items; i < nitems; i++, ip += nsize) {
6156                 int k = -1;
6157                 for (j = 0; j < nsize; j++) {
6158                         int kk;
6159                         kk = (IntGroupLookup(atomgroup, ip[j], NULL) != 0);
6160                         if (k < 0)
6161                                 k = kk;
6162                         else if (k != kk) {
6163                                 /*  This bond etc. crosses the atom group border  */
6164                                 if (gp == NULL)
6165                                         gp = IntGroupNew();
6166                                 if (gp == NULL || IntGroupAdd(gp, i, 1) != 0)
6167                                         Panic("Low memory while searching %s", msg);
6168                                 break;
6169                         }
6170                 }
6171         }
6172         return gp;
6173 }
6174
6175 IntGroup *
6176 MoleculeSearchBondsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6177 {
6178         if (mp == NULL)
6179                 return NULL;
6180         return sMoleculeSearchAcrossAtomGroup(mp->nbonds, mp->bonds, 2, atomgroup, "bonds");
6181 }
6182
6183 IntGroup *
6184 MoleculeSearchAnglesAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6185 {
6186         if (mp == NULL)
6187                 return NULL;
6188         return sMoleculeSearchAcrossAtomGroup(mp->nangles, mp->angles, 3, atomgroup, "angles");
6189 }
6190
6191 IntGroup *
6192 MoleculeSearchDihedralsAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6193 {
6194         if (mp == NULL)
6195                 return NULL;
6196         return sMoleculeSearchAcrossAtomGroup(mp->ndihedrals, mp->dihedrals, 4, atomgroup, "dihedrals");
6197 }
6198
6199 IntGroup *
6200 MoleculeSearchImpropersAcrossAtomGroup(Molecule *mp, IntGroup *atomgroup)
6201 {
6202         if (mp == NULL)
6203                 return NULL;
6204         return sMoleculeSearchAcrossAtomGroup(mp->nimpropers, mp->impropers, 4, atomgroup, "impropers");
6205 }
6206
6207 /*  Subroutine for MoleculeGuessBonds. It can be also used independently, but make sure that *outNbonds / *outBonds
6208     _correctly_ represents an array of two integers (as in mp->nbonds/mp->bonds).  */
6209 /*  Find atoms within the given "distance" from the given position.  */
6210 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6211  the threshold distance is given by the sum of van der Waals radii times limit, and radius is
6212  the van der Waals radius of the atom at the given position. */
6213 /*  Index is the atom index of the given atom; it is only used in returning the "bond" array
6214  to the caller. If index is negative, then (-index) is the real atom index, and
6215  only atoms with lower indices than (-index) are looked for.  */
6216 int
6217 MoleculeFindCloseAtoms(Molecule *mp, const Vector *vp, Double radius, Double limit, Int *outNbonds, Int **outBonds, Int index)
6218 {
6219         Int n2, j, nlim, newbond[2];
6220         Double a2, alim;
6221         Vector dr, r2;
6222         if (index < 0) {
6223                 nlim = index = -index;
6224         } else {
6225                 nlim = mp->natoms;
6226         }
6227         for (j = 0; j < nlim; j++) {
6228                 Atom *bp = ATOM_AT_INDEX(mp->atoms, j);
6229                 if (index == j)
6230                         continue;
6231                 n2 = bp->atomicNumber;
6232                 if (n2 >= 0 && n2 < gCountElementParameters)
6233                         a2 = gElementParameters[n2].radius;
6234                 else a2 = gElementParameters[6].radius;
6235                 r2 = bp->r;
6236                 VecSub(dr, *vp, r2);
6237                 if (limit < 0)
6238                         alim = -limit;
6239                 else
6240                         alim = limit * (radius + a2);
6241                 if (VecLength2(dr) < alim * alim) {
6242                         newbond[0] = index;
6243                         newbond[1] = j;
6244                         /*      MoleculeAddBonds(mp, 1, newbonds); */
6245                         AssignArray(outBonds, outNbonds, sizeof(Int) * 2, *outNbonds, newbond);
6246                 }
6247         }
6248         return 0;
6249 }
6250
6251 /*  Guess the bonds from the coordinates  */
6252 /*  If limit is negative, its absolute value denotes the threshold distance in angstrom; otherwise,
6253     the threshold distance is given by the sum of van der Waals radii times limit.  */
6254 int
6255 MoleculeGuessBonds(Molecule *mp, Double limit, Int *outNbonds, Int **outBonds)
6256 {
6257         Int nbonds, *bonds, i, newbond[2];
6258         Atom *ap;
6259         nbonds = 0;
6260         bonds = NULL;
6261         if (limit == 0.0)
6262                 limit = 1.2;
6263         for (i = 1, ap = ATOM_NEXT(mp->atoms); i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6264                 Vector r = ap->r;
6265                 Int an = ap->atomicNumber;
6266                 Double rad;
6267                 if (an >= 0 && an < gCountElementParameters)
6268                         rad = gElementParameters[an].radius;
6269                 else rad = gElementParameters[6].radius;
6270                 MoleculeFindCloseAtoms(mp, &r, rad, limit, &nbonds, &bonds, -i);
6271         }
6272         if (nbonds > 0) {
6273                 newbond[0] = kInvalidIndex;
6274                 newbond[1] = 0;
6275                 AssignArray(&bonds, &nbonds, sizeof(Int) * 2, nbonds, newbond);
6276                 nbonds--;
6277         }
6278         if (outNbonds != NULL)
6279                 *outNbonds = nbonds;
6280         if (outBonds != NULL)
6281                 *outBonds = bonds;
6282         return 0;
6283 }
6284
6285 /*  Rebuild the bond/angle/dihedral/improper tables from atom.connects[] information  */
6286 int
6287 MoleculeRebuildTablesFromConnects(Molecule *mp)
6288 {
6289         int i, j, k, retval;
6290         Atom *ap;
6291         Int ibuf[6], *cp;
6292         
6293         __MoleculeLock(mp);
6294
6295         /*  Find bonds   */
6296         if (mp->nbonds == 0) {
6297                 for (i = 0; i < mp->natoms; i++) {
6298                         ap = ATOM_AT_INDEX(mp->atoms, i);
6299                         cp = AtomConnectData(&ap->connect);
6300                         for (j = 0; j < ap->connect.count; j++) {
6301                                 k = cp[j];
6302                                 if (i >= k)
6303                                         continue;
6304                                 ibuf[0] = i;
6305                                 ibuf[1] = k;
6306                                 /*  MoleculeAddBonds() should not be used, because it assumes connects[] and
6307                                     bonds are already in sync  */
6308                                 AssignArray(&mp->bonds, &mp->nbonds, sizeof(Int) * 2, mp->nbonds, ibuf);
6309                         /*      retval = MoleculeAddBonds(mp, 1, ibuf);
6310                                 if (retval != 0)
6311                                         goto abort; */
6312                         }
6313                 }
6314         }
6315         
6316         /*  Find angles  */
6317         if (mp->nangles == 0) {
6318                 for (i = 0; i < mp->natoms; i++) {
6319                         ap = ATOM_AT_INDEX(mp->atoms, i);
6320                         cp = AtomConnectData(&ap->connect);
6321                         for (j = 0; j < ap->connect.count; j++) {
6322                                 for (k = j + 1; k < ap->connect.count; k++) {
6323                                         ibuf[0] = cp[j];
6324                                         ibuf[1] = i;
6325                                         ibuf[2] = cp[k];
6326                                         ibuf[3] = -1;
6327                                         retval = MoleculeAddAngles(mp, ibuf, NULL);
6328                                         if (retval < 0)
6329                                                 goto abort;
6330                                 }
6331                         }
6332                 }
6333         }
6334         
6335         /*  Find dihedrals  */
6336         if (mp->ndihedrals == 0) {
6337                 for (i = 0; i < mp->natoms; i++) {
6338                         ap = ATOM_AT_INDEX(mp->atoms, i);
6339                         cp = AtomConnectData(&ap->connect);
6340                         for (j = 0; j < ap->connect.count; j++) {
6341                                 int jj, kk, mm, m;
6342                                 Atom *apjj;
6343                                 Int *cpjj;
6344                                 jj = cp[j];
6345                                 if (i >= jj)
6346                                         continue;
6347                                 apjj = ATOM_AT_INDEX(mp->atoms, jj);
6348                                 cpjj = AtomConnectData(&apjj->connect);
6349                                 for (k = 0; k < ap->connect.count; k++) {
6350                                         if (k == j)
6351                                                 continue;
6352                                         kk = cp[k];
6353                                         for (m = 0; m < apjj->connect.count; m++) {
6354                                                 mm = cpjj[m];
6355                                                 if (mm == i || mm == kk)
6356                                                         continue;
6357                                                 ibuf[0] = kk;
6358                                                 ibuf[1] = i;
6359                                                 ibuf[2] = jj;
6360                                                 ibuf[3] = mm;
6361                                                 ibuf[4] = -1;
6362                                                 retval = MoleculeAddDihedrals(mp, ibuf, NULL);
6363                                                 if (retval < 0)
6364                                                         goto abort;
6365                                         }
6366                                 }
6367                         }
6368                 }
6369         }
6370         
6371         /*  Find impropers  */
6372         if (mp->nimpropers == 0) {
6373                 for (i = 0; i < mp->natoms; i++) {
6374                         int i1, i2, i4, n1, n2, n4;
6375                         ap = ATOM_AT_INDEX(mp->atoms, i);
6376                         cp = AtomConnectData(&ap->connect);
6377                         for (i1 = 0; i1 < ap->connect.count; i1++) {
6378                                 n1 = cp[i1];
6379                                 for (i2 = i1 + 1; i2 < ap->connect.count; i2++) {
6380                                         n2 = cp[i2];
6381                                         for (i4 = i2 + 1; i4 < ap->connect.count; i4++) {
6382                                                 n4 = cp[i4];
6383                                                 ibuf[0] = n1;
6384                                                 ibuf[1] = n2;
6385                                                 ibuf[2] = i;
6386                                                 ibuf[3] = n4;
6387                                                 ibuf[4] = -1;
6388                                                 retval = MoleculeAddImpropers(mp, ibuf, NULL);
6389                                                 if (retval < 0)
6390                                                         goto abort;
6391                                         }
6392                                 }
6393                         }
6394                 }
6395         }
6396
6397         mp->needsMDRebuild = 1;
6398         __MoleculeUnlock(mp);
6399         return 0;
6400
6401   abort:
6402         __MoleculeUnlock(mp);
6403         return retval;
6404 }
6405
6406 int
6407 MoleculeAreAtomsConnected(Molecule *mol, int idx1, int idx2)
6408 {
6409         Atom *ap1 = ATOM_AT_INDEX(mol->atoms, idx1);
6410         if (AtomConnectHasEntry(&ap1->connect, idx2))
6411                 return 1;
6412         else if (ap1->anchor != NULL && AtomConnectHasEntry(&(ap1->anchor->connect), idx2))
6413                 return 2;
6414         else return 0;
6415 }
6416
6417 #pragma mark ====== Atom names ======
6418
6419 /*  Look for the n1-th atom in resno-th residue (n1 is 0-based)  */
6420 int
6421 MoleculeLookupAtomInResidue(Molecule *mp, int n1, int resno)
6422 {
6423         int i, j, lasti;
6424         Atom *ap;
6425         if (mp == NULL || mp->natoms == 0)
6426                 return -1;
6427         lasti = -1;
6428         for (i = j = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
6429                 if (ap->resSeq == resno) {
6430                         lasti = i;
6431                         if (j++ == n1)
6432                                 return i;
6433                 }
6434         }
6435         if (n1 == -1)
6436                 return lasti; /* max */
6437         return -1;
6438 }
6439
6440 int
6441 MoleculeAnalyzeAtomName(const char *s, char *resName, int *resSeq, char *atomName)
6442 {
6443     int n;
6444     char *p;
6445         n = strtol(s, &p, 0);
6446         if (p > s) {
6447                 while (isspace(*p))
6448                         p++;
6449                 if (*p == 0) {
6450                   resName[0] = 0;
6451                   *resSeq = -1;
6452                   atomName[0] = 0;
6453                   return n;
6454                 }
6455         }
6456
6457         if ((p = strchr(s, ':')) != NULL) {
6458                 /*  Residue is specified  */
6459                 char *pp;
6460                 if ((pp = strchr(s, '.')) != NULL && pp < p) {
6461                         /*  Residue number is also specified  */
6462                         char *ppp;
6463                         n = pp - s;
6464                         *resSeq = strtol(pp + 1, &ppp, 0);
6465                         if (ppp == pp + 1)
6466                                 return -2;  /*  Bad format  */
6467                         while (isspace(*ppp))
6468                                 ppp++;
6469                         if (ppp != p)
6470                                 return -2;  /*  Bad format  */
6471                 } else {
6472                         *resSeq = -1;
6473                         /*  Check whether the "residue name" is an integer  */
6474                         n = strtol(s, &pp, 0);
6475                         if (pp > s) {
6476                                 while (isspace(*pp))
6477                                         pp++;
6478                                 if (*pp == 0 || *pp == ':') {
6479                                         *resSeq = n;
6480                                         if (*resSeq < 0)
6481                                                 return -2;  /*  Bad format  */
6482                                 }
6483                         }
6484                         if (*resSeq >= 0)
6485                                 n = 0;
6486                         else
6487                                 n = p - s;
6488                 }
6489                 if (n >= sizeof(resName))
6490                         n = sizeof(resName) - 1;
6491                 strncpy(resName, s, n);
6492                 resName[n] = 0;
6493                 p++;
6494         } else {
6495                 resName[0] = 0;
6496                 *resSeq = -1;
6497                 p = (char *)s;
6498         }
6499         strncpy(atomName, p, 4);
6500         atomName[4] = 0;
6501         return 0;
6502 }
6503
6504 /*  Convert a string to atom index, where string = "((\w+\.)?(\d+):)?(\w+)" or an integer  */
6505 int
6506 MoleculeAtomIndexFromString(Molecule *mp, const char *s)
6507 {
6508         char resName[6];
6509         int resSeq, n;
6510         char atomName[6];
6511         /*      char *p; */
6512
6513         n = MoleculeAnalyzeAtomName(s, resName, &resSeq, atomName);
6514         if (atomName[0] == 0) {
6515           if (n >= mp->natoms)
6516             n = -1;  /* Out of range */
6517           return n;
6518         }
6519         for (n = 0; n < mp->natoms; n++) {
6520                 Atom *ap = ATOM_AT_INDEX(mp->atoms, n);
6521                 if ((resName[0] == 0 || strncmp(resName, ap->resName, 4) == 0)
6522                         && (resSeq < 0 || ap->resSeq == resSeq)
6523                         && strncmp(atomName, ap->aname, 4) == 0) {
6524                         return n;
6525                 }
6526         }
6527         return -1;  /*  Not found  */
6528 }
6529
6530 void
6531 MoleculeGetAtomName(Molecule *mp, int index, char *buf, int bufsize)
6532 {
6533         Atom *ap;
6534         int n;
6535         if (mp == NULL || index < 0 || index >= mp->natoms) {
6536                 buf[0] = 0;
6537                 return;
6538         }
6539         ap = mp->atoms + index;
6540         if (ap->resSeq != 0) {
6541                 n = snprintf(buf, bufsize, "%s%d:", ap->resName, ap->resSeq);
6542                 buf += n;
6543                 bufsize -= n;
6544         }
6545         snprintf(buf, bufsize, "%.4s", ap->aname);
6546 }
6547
6548 #pragma mark ====== Selection ======
6549
6550 static void
6551 sMoleculeNotifyChangeSelection(Molecule *mp)
6552 {
6553         /*  TODO: Finer control of notification types may be necessary  */
6554         MoleculeCallback_notifyModification(mp, 0);
6555 }
6556
6557 void
6558 MoleculeSetSelection(Molecule *mp, IntGroup *select)
6559 {
6560         if (mp == NULL)
6561                 return;
6562         if (select != NULL)
6563                 IntGroupRetain(select);
6564         if (mp->selection != NULL)
6565                 IntGroupRelease(mp->selection);
6566         mp->selection = select;
6567         sMoleculeNotifyChangeSelection(mp);
6568 }
6569
6570 IntGroup *
6571 MoleculeGetSelection(Molecule *mp)
6572 {
6573         if (mp == NULL)
6574                 return NULL;
6575         else return mp->selection;
6576 }
6577
6578 void
6579 MoleculeSelectAtom(Molecule *mp, int n1, int extending)
6580 {
6581         if (mp->selection == NULL)
6582                 mp->selection = IntGroupNew();
6583         if (!extending)
6584                 IntGroupClear(mp->selection);
6585         IntGroupAdd(mp->selection, n1, 1);
6586         sMoleculeNotifyChangeSelection(mp);
6587 }
6588
6589 void
6590 MoleculeUnselectAtom(Molecule *mp, int n1)
6591 {
6592         if (mp->selection != NULL)
6593                 IntGroupRemove(mp->selection, n1, 1);
6594         sMoleculeNotifyChangeSelection(mp);
6595 }
6596
6597 void
6598 MoleculeToggleSelectionOfAtom(Molecule *mp, int n1)
6599 {
6600         if (mp->selection == NULL)
6601                 mp->selection = IntGroupNew();
6602         IntGroupReverse(mp->selection, n1, 1);
6603         sMoleculeNotifyChangeSelection(mp);
6604 }
6605
6606 int
6607 MoleculeIsAtomSelected(Molecule *mp, int n1)
6608 {
6609         if (mp != NULL && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL))
6610                 return 1;
6611         else return 0;
6612 }
6613
6614 int
6615 MoleculeIsBondSelected(Molecule *mp, int n1, int n2)
6616 {
6617         if (mp != NULL && MoleculeAreAtomsConnected(mp, n1, n2) && mp->selection != NULL && IntGroupLookup(mp->selection, n1, NULL) && IntGroupLookup(mp->selection, n2, NULL))
6618                 return 1;
6619         else return 0;
6620 }
6621
6622 IntGroup *
6623 MoleculeModifySelectionByRemovingAtoms(Molecule *mp, IntGroup *selection, IntGroup *remove)
6624 {
6625         int status;
6626         IntGroup *remain, *ig1, *ig2;
6627         ig1 = ig2 = NULL;
6628         remain = IntGroupNewFromIntGroup(remove);
6629         if (remain == NULL)
6630                 status = -1;
6631         else
6632                 status = IntGroupReverse(remain, 0, mp->natoms);
6633         if (status == 0) {
6634                 ig1 = IntGroupNew();
6635                 if (ig1 == NULL)
6636                         status = -1;
6637                 else
6638                         status = IntGroupDifference(selection, remove, ig1);
6639         }
6640         if (status == 0) {
6641                 ig2 = IntGroupNew();
6642                 if (ig2 == NULL)
6643                         status = -1;
6644                 else
6645                         status = IntGroupDeconvolute(ig1, remain, ig2);
6646         }
6647         if (remain != NULL)
6648                 IntGroupRelease(remain);
6649         if (ig1 != NULL)
6650                 IntGroupRelease(ig1);
6651         if (status == 0)
6652                 return ig2;
6653         else {
6654                 if (ig2 != NULL)
6655                         IntGroupRelease(ig2);
6656                 return NULL;
6657         }
6658 }
6659
6660 #pragma mark ====== Atom Equivalence ======
6661
6662 struct sEqList {
6663         int i[2];
6664         struct sEqList *next;
6665         struct sEqList *link;
6666 };
6667
6668 static struct sEqList *sListBase = NULL;
6669 static struct sEqList *sListFree = NULL;
6670
6671 static struct sEqList *
6672 sAllocEqList(void)
6673 {
6674         struct sEqList *lp;
6675         if (sListFree != NULL) {
6676                 lp = sListFree;
6677                 sListFree = lp->next;
6678                 lp->i[0] = lp->i[1] = 0;
6679                 lp->next = NULL;
6680                 return lp;
6681         }
6682         lp = (struct sEqList *)calloc(sizeof(struct sEqList), 1);
6683         lp->link = sListBase;
6684         sListBase = lp;
6685         return lp;
6686 }
6687
6688 static void
6689 sFreeEqList(struct sEqList *list)
6690 {
6691         list->next = sListFree;
6692         sListFree = list;
6693 }
6694
6695 static void
6696 sDeallocateEqLists(void)
6697 {
6698         struct sEqList *lp, *lp_link;
6699         for (lp = sListBase; lp != NULL; lp = lp_link) {
6700                 lp_link = lp->link;
6701                 free(lp);
6702         }
6703         sListBase = NULL;
6704         sListFree = NULL;
6705 }
6706
6707 static int
6708 sExistInEqList(int i, int idx, struct sEqList *list)
6709 {
6710         while (list != NULL) {
6711                 if (list->i[idx] == i)
6712                         return 1;
6713                 list = list->next;
6714         }
6715         return 0;
6716 }
6717
6718 static struct sEqList *
6719 sMoleculeCheckEquivalence(Molecule *mol, int i, int j, struct sEqList *list, int **db, IntGroup *ig)
6720 {
6721         Atom *api, *apj;
6722         struct sEqList *list1, *list2;
6723         Int ii, jj, ni, nj, *cpi, *cpj;
6724         api = ATOM_AT_INDEX(mol->atoms, i);
6725         apj = ATOM_AT_INDEX(mol->atoms, j);
6726         if (api->atomicNumber != apj->atomicNumber)
6727                 return NULL;
6728         list1 = sAllocEqList();
6729         if (list1 == NULL)
6730                 return NULL;
6731         list1->i[0] = i;
6732         list1->i[1] = j;
6733         list1->next = list;
6734         if (i == j || (db[i] != NULL && db[i] == db[j]))
6735                 return list1;
6736         cpi = AtomConnectData(&api->connect);
6737         cpj = AtomConnectData(&apj->connect);
6738         for (ni = 0; ni < api->connect.count; ni++) {
6739                 ii = cpi[ni];
6740                 if (ig != NULL && IntGroupLookupPoint(ig, ii) < 0)
6741                         continue;
6742                 if (sExistInEqList(ii, 0, list1))
6743                         continue;
6744                 list2 = NULL;
6745                 for (nj = 0; nj < apj->connect.count; nj++) {
6746                         jj = cpj[nj];
6747                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6748                                 continue;
6749                         if (sExistInEqList(jj, 1, list1))
6750                                 continue;
6751                         list2 = sMoleculeCheckEquivalence(mol, ii, jj, list1, db, ig);
6752                         if (list2 != NULL)
6753                                 break;
6754                 }
6755                 if (list2 == NULL) {
6756                         sFreeEqList(list1);
6757                         return NULL;    /*  No equivalent to ii  */
6758                 }
6759                 list1 = list2;      /*  ii is OK, try next  */
6760         }
6761         return list1;
6762 }
6763
6764 int
6765 sDBInclude(Int *ip, int i)
6766 {
6767         int j;
6768         if (ip == NULL)
6769                 return -1;
6770         for (j = ip[0] - 1; j >= 0; j--) {
6771                 if (ip[j] == i)
6772                         return j;
6773         }
6774         return -1;
6775 }
6776
6777 Int *
6778 MoleculeSearchEquivalentAtoms(Molecule *mol, IntGroup *ig)
6779 {
6780         Int **db;  /*  List of equivalents for each atom  */
6781         Int *ip, *result;
6782         Atom *api, *apj, *apk;
6783         Int *cpi, *cpj, *ibuf, nibuf;
6784         int i, j, k, ii, jj, kk;
6785         if (mol == NULL || mol->natoms == 0)
6786                 return NULL;
6787         db = (Int **)calloc(sizeof(Int *), mol->natoms);
6788         ibuf = NULL;
6789         nibuf = 0;
6790
6791         /*  Find the equivalent univalent atoms  */
6792         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6793                 if (api->connect.count < 2)
6794                         continue;
6795                 cpi = AtomConnectData(&api->connect);
6796                 for (j = 0; j < api->connect.count; j++) {
6797                         Int n;
6798                         n = 0;
6799                         jj = cpi[j];
6800                         if (ig != NULL && IntGroupLookupPoint(ig, jj) < 0)
6801                                 continue;
6802                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &jj);
6803                         n++;
6804                         apj = ATOM_AT_INDEX(mol->atoms, jj);
6805                         if (apj->connect.count != 1 || db[jj] != NULL)
6806                                 continue;
6807                         cpj = AtomConnectData(&apj->connect);
6808                         for (k = j + 1; k < api->connect.count; k++) {
6809                                 kk = cpj[k];
6810                                 if (ig != NULL && IntGroupLookupPoint(ig, kk) < 0)
6811                                         continue;
6812                                 apk = ATOM_AT_INDEX(mol->atoms, kk);
6813                                 if (apk->connect.count != 1 || db[kk] != NULL)
6814                                         continue;
6815                                 if (apj->atomicNumber == apk->atomicNumber) {
6816                                         AssignArray(&ibuf, &nibuf, sizeof(Int), n, &kk);
6817                                         n++;
6818                                 }
6819                         }
6820                         if (n > 1) {
6821                                 ip = (Int *)calloc(sizeof(Int), n + 1);
6822                                 if (ip == NULL)
6823                                         return NULL;
6824                                 ip[0] = n;
6825                                 memmove(ip + 1, ibuf, sizeof(Int) * n);
6826                                 for (k = 0; k < n; k++)
6827                                         db[ip[k + 1]] = ip;
6828                         }
6829                 }
6830         }
6831         if (ibuf != NULL) {
6832                 free(ibuf);
6833                 ibuf = NULL;
6834         }
6835         
6836         /*  Try matching (i,j) pair  */
6837         for (i = 0, api = mol->atoms; i < mol->natoms; i++, api = ATOM_NEXT(api)) {
6838                 if (ig != NULL && IntGroupLookupPoint(ig, i) < 0)
6839                         continue;
6840                 for (j = i + 1, apj = ATOM_AT_INDEX(mol->atoms, j); j < mol->natoms; j++, apj = ATOM_NEXT(apj)) {
6841                         struct sEqList *list;
6842                         if (ig != NULL && IntGroupLookupPoint(ig, j) < 0)
6843                                 continue;
6844                         if (api->atomicNumber != apj->atomicNumber)
6845                                 continue;  /*  Different elements do not match  */
6846                         if (db[i] != NULL && db[i] == db[j])
6847                                 continue;  /*  Already equivalent  */
6848                         list = sMoleculeCheckEquivalence(mol, i, j, NULL, db, ig);
6849                         if (list == NULL)
6850                                 continue;  /*  (i,j) do not match  */
6851                         while (list != NULL) {
6852                                 ii = list->i[0];
6853                                 jj = list->i[1];
6854                                 if (ii != jj && (db[ii] == NULL || db[ii] != db[jj])) {
6855                                         /*  Merge db[ii] and db[jj]  */
6856                                         k = (db[ii] == NULL ? 1 : db[ii][0]) + (db[jj] == NULL ? 1 : db[jj][0]);
6857                                         ip = (Int *)calloc(sizeof(Int), k + 1);
6858                                         if (ip == NULL)
6859                                                 return NULL;  /*  Out of memory  */
6860                                         if (db[ii] == NULL) {
6861                                                 ip[1] = ii;
6862                                                 k = 2;
6863                                         } else {
6864                                                 memmove(ip + 1, db[ii] + 1, db[ii][0] * sizeof(Int));
6865                                                 k = db[ii][0] + 1;
6866                                         }
6867                                         if (db[jj] == NULL) {
6868                                                 ip[k++] = jj;
6869                                         } else {
6870                                                 memmove(ip + k, db[jj] + 1, db[jj][0] * sizeof(Int));
6871                                                 k += db[jj][0];
6872                                         }
6873                                         ip[0] = k - 1;
6874                                         /*  Free old ones  */
6875                                         if (db[ii] != NULL)
6876                                                 free(db[ii]);
6877                                         if (db[jj] != NULL)
6878                                                 free(db[jj]);
6879                                         for (k = 0; k < ip[0]; k++)
6880                                                 db[ip[k + 1]] = ip;
6881                                         if (0) {
6882                                                 /*  For debug  */
6883                                                 printf("(%d,%d) matched: ", ii, jj);
6884                                                 for (k = 0; k < ip[0]; k++) {
6885                                                         printf("%c%d", (k == 0 ? '[' : ','), ip[k + 1]);
6886                                                 }
6887                                                 printf("]\n");
6888                                         }
6889                                 }
6890                                 list = list->next;
6891                         }
6892                 }
6893         }
6894         
6895         /*  Record the equivalent atoms with the lowest index for each atom  */
6896         result = (Int *)calloc(sizeof(Int), mol->natoms);
6897         for (i = 0; i < mol->natoms; i++)
6898                 result[i] = -1;
6899         for (i = 0; i < mol->natoms; i++) {
6900                 if (result[i] >= 0 || (ip = db[i]) == NULL)
6901                         continue;
6902                 k = mol->natoms;
6903                 for (j = 0; j < ip[0]; j++) {
6904                         kk = ip[j + 1];
6905                         if (kk < k)
6906                                 k = kk;
6907                 }
6908                 for (j = 0; j < ip[0]; j++) {
6909                         result[ip[j + 1]] = k;
6910                         db[ip[j + 1]] = NULL;
6911                 }
6912                 free(ip);
6913         }
6914         sDeallocateEqLists();
6915         return result;
6916 }
6917
6918 #pragma mark ====== Symmetry expansion ======
6919
6920 int
6921 MoleculeGetTransformForSymop(Molecule *mp, Symop symop, Transform *tf, int is_cartesian)
6922 {
6923         Transform t;
6924         if (mp == NULL || mp->cell == NULL)
6925                 return -1;
6926         if (symop.sym >= mp->nsyms && symop.sym != 0)
6927                 return -2;
6928         memmove(*tf, SYMMETRY_AT_INDEX(mp->syms, symop.sym), sizeof(Transform));
6929         (*tf)[9] += symop.dx;
6930         (*tf)[10] += symop.dy;
6931         (*tf)[11] += symop.dz;
6932         if (is_cartesian) {
6933                 TransformMul(t, *tf, mp->cell->rtr);
6934                 TransformMul(*tf, mp->cell->tr, t);
6935         }
6936         return 0;
6937 }
6938
6939 int
6940 MoleculeGetSymopForTransform(Molecule *mp, const Transform tf, Symop *symop, int is_cartesian)
6941 {
6942         Transform t;
6943         int i, j, n[3];
6944         if (mp == NULL || mp->cell == NULL)
6945                 return -1;
6946         if (is_cartesian) {
6947                 TransformMul(t, tf, mp->cell->tr);
6948                 TransformMul(t, mp->cell->rtr, t);
6949         } else {
6950                 memmove(t, tf, sizeof(Transform));
6951         }
6952         for (i = 0; i < mp->nsyms || i == 0; i++) {
6953                 Transform *tp = &(SYMMETRY_AT_INDEX(mp->syms, i));
6954                 for (j = 0; j < 9; j++) {
6955                         if (fabs((*tp)[j] - t[j]) > 1e-4)
6956                                 break;
6957                 }
6958                 if (j == 9) {
6959                         for (j = 9; j < 12; j++) {
6960                                 double f1 = t[j] - (*tp)[j];
6961                                 double f2 = floor(f1 + 0.5);
6962                                 if (fabs(f1 - f2) > 1e-4)
6963                                         break;
6964                                 n[j - 9] = f2;
6965                         }
6966                         if (j == 12) {
6967                                 /*  Found  */
6968                                 symop->sym = i;
6969                                 symop->dx = n[0];
6970                                 symop->dy = n[1];
6971                                 symop->dz = n[2];
6972                                 symop->alive = (SYMOP_ALIVE((*symop)) != 0);
6973                                 return 0;
6974                         }
6975                 }
6976         }
6977         return -3;  /*  Not found  */
6978 }
6979
6980 int
6981 MoleculeTransformBySymop(Molecule *mp, const Vector *vpin, Vector *vpout, Symop symop)
6982 {
6983         if (mp == NULL)
6984                 return 1;
6985         if (symop.sym >= mp->nsyms && symop.sym != 0)
6986                 return 2;
6987         if (mp->cell != NULL /* && !mp->is_xtal_coord */) {
6988                 TransformVec(vpout, mp->cell->rtr, vpin);
6989                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpout);
6990                 vpout->x += symop.dx;
6991                 vpout->y += symop.dy;
6992                 vpout->z += symop.dz;
6993                 TransformVec(vpout, mp->cell->tr, vpout);
6994         } else {
6995                 TransformVec(vpout, SYMMETRY_AT_INDEX(mp->syms, symop.sym), vpin);
6996                 vpout->x += symop.dx;
6997                 vpout->y += symop.dy;
6998                 vpout->z += symop.dz;
6999         }
7000         return 0;
7001 }
7002
7003 /*  Add expanded atoms. Returns the number of newly created atoms.
7004         If indices is non-NULL, it should be an array of Int with at least 
7005         IntGroupGetCount(group) entries, and on return it contains the
7006     indices of the expanded atoms (may be existing atoms if the expanded
7007     atoms are already present)
7008     If allowOverlap is non-zero, then the new atom is created even when the
7009     coordinates coincide with the some other atom (special position) of the
7010     same element; otherwise, such atom will not be created and the existing
7011     atom is returned in indices[].  */
7012 int
7013 MoleculeAddExpandedAtoms(Molecule *mp, Symop symop, IntGroup *group, Int *indices, Int allowOverlap)
7014 {
7015         int i, n, n0, n1, n2, base, count, *table;
7016         Atom *ap;
7017         IntGroupIterator iter;
7018         Transform tr, t1;
7019         Symop symop1;
7020         Atom *ap2;
7021         Vector nr, dr;
7022         
7023         if (mp == NULL || mp->natoms == 0 || group == NULL || (count = IntGroupGetCount(group)) == 0)
7024                 return -1;
7025         if (symop.sym != 0 && symop.sym >= mp->nsyms)
7026                 return -2;
7027
7028         /*  Create atoms, with avoiding duplicates  */
7029         n0 = n1 = mp->natoms;
7030         table = (int *)malloc(sizeof(int) * n0);
7031         if (table == NULL)
7032                 return -3;
7033         for (i = 0; i < n0; i++)
7034                 table[i] = -1;
7035         IntGroupIteratorInit(group, &iter);
7036         MoleculeGetTransformForSymop(mp, symop, &tr, 0);
7037         __MoleculeLock(mp);
7038         for (i = 0; i < count; i++) {
7039                 n = IntGroupIteratorNext(&iter);
7040                 ap = ATOM_AT_INDEX(mp->atoms, n);
7041                 if (SYMOP_ALIVE(ap->symop)) {
7042                         /*  Calculate the cumulative symop  */
7043                         Transform tr2;
7044                         MoleculeGetTransformForSymop(mp, ap->symop, &t1, 0);
7045                         TransformMul(tr2, tr, t1);
7046                         if (MoleculeGetSymopForTransform(mp, tr2, &symop1, 0) != 0) {
7047                                 if (indices != NULL)
7048                                         indices[i] = -1;
7049                                 continue;  /*  Skip this atom  */
7050                         }
7051                         base = ap->symbase;
7052                 } else {
7053                         symop1 = symop;
7054                         base = n;
7055                 }
7056
7057                 /*  Calculate the expande position  */
7058                 MoleculeTransformBySymop(mp, &(ap->r), &nr, symop);
7059                 
7060                 /*  Is this expansion already present?  */
7061                 for (n2 = 0, ap2 = mp->atoms; n2 < n0; n2++, ap2 = ATOM_NEXT(ap2)) {
7062                         /*  Symmetry operation and the base atom are the same  */
7063                         if (ap2->symbase == base && SYMOP_EQUAL(symop1, ap2->symop))
7064                                 break;
7065                         /*  Atomic number and the position are the same  */
7066                         if (ap2->atomicNumber == ap->atomicNumber && allowOverlap == 0) {
7067                                 VecSub(dr, ap2->r, nr);
7068                                 if (VecLength2(dr) < 1e-6)
7069                                         break;
7070                         }
7071                 }
7072                 if (n2 < n0) {
7073                         /*  If yes, then skip it  */
7074                         if (indices != NULL)
7075                                 indices[i] = n2;
7076                         continue;
7077                 } else {
7078                         /*  Create a new atom  */
7079                         Atom newAtom;
7080                         AtomDuplicate(&newAtom, ap);
7081                         MoleculeCreateAnAtom(mp, &newAtom, -1);
7082                         AtomClean(&newAtom);
7083                         ap2 = ATOM_AT_INDEX(mp->atoms, mp->natoms - 1);
7084                         ap2->r = nr;
7085                         ap2->symbase = base;
7086                         ap2->symop = symop1;
7087                         ap2->symop.alive = (symop1.dx != 0 || symop1.dy != 0 || symop1.dz != 0 || symop1.sym != 0);
7088                         table[n] = n1;  /*  The index of the new atom  */
7089                         MoleculeSetAnisoBySymop(mp, n1);  /*  Recalculate anisotropic parameters according to symop  */
7090                         if (indices != NULL)
7091                                 indices[i] = n1;
7092                         n1++;
7093                 }
7094         }
7095         IntGroupIteratorRelease(&iter);
7096
7097         /*  Create bonds  */
7098         for (i = n0; i < n1; i++) {
7099                 Int b[2], j;
7100                 ap = ATOM_AT_INDEX(mp->atoms, i);
7101                 if (SYMOP_ALIVE(ap->symop) && MoleculeGetTransformForSymop(mp, ap->symop, &tr, 1) == 0) {
7102                         /*  For each connected atom, look for the transformed atom  */
7103                         Int *cp;
7104                         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7105                         cp = AtomConnectData(&ap2->connect);
7106                         n2 = ap2->connect.count;
7107                         for (n = 0; n < n2; n++) {
7108                                 Atom *apn = ATOM_AT_INDEX(mp->atoms, cp[n]);
7109                                 nr = apn->r;
7110                                 TransformVec(&nr, tr, &nr);
7111                                 /*  Look for the bonded atom transformed by ap->symop  */
7112                                 for (j = 0, ap2 = mp->atoms; j < mp->natoms; j++, ap2 = ATOM_NEXT(ap2)) {
7113                                         if (ap2->symbase == cp[n] && SYMOP_EQUAL(ap->symop, ap2->symop))
7114                                                 break;
7115                                         VecSub(dr, nr, ap2->r);
7116                                         if (ap2->atomicNumber == apn->atomicNumber && VecLength2(dr) < 1e-6)
7117                                                 break;
7118                                 }
7119                                 if (j < mp->natoms) {
7120                                         /*  Bond i-j is created  */
7121                                         b[0] = i;
7122                                         b[1] = j;
7123                                         if (MoleculeLookupBond(mp, b[0], b[1]) < 0)
7124                                                 MoleculeAddBonds(mp, 1, b, NULL, 1);
7125                                 }
7126                         }
7127                 }
7128         }
7129         mp->needsMDRebuild = 1;
7130         __MoleculeUnlock(mp);
7131         free(table);
7132         return n1 - n0;  /*  The number of added atoms  */
7133 }
7134
7135 /*  Recalculate the coordinates of symmetry expanded atoms.
7136     (Also recalculate the positions of pi-anchor atoms)
7137         Returns the number of affected atoms.
7138     If group is non-NULL, only the expanded atoms whose base atoms are in the
7139     given group are considered.
7140         If groupout and vpout are non-NULL, the indices of the affected atoms
7141         and the original positions are returned (for undo operation).
7142         The pointers returned in *groupout and *vpout must be released and 
7143         free()'ed by the caller  */
7144 int
7145 MoleculeAmendBySymmetry(Molecule *mp, IntGroup *group, IntGroup **groupout, Vector **vpout)
7146 {
7147         int i, count;
7148         Atom *ap, *bp;
7149         Vector nr, dr;
7150         IntGroup *ig = NULL;
7151         Vector *vp = NULL;
7152         
7153         if (mp == NULL || mp->natoms == 0)
7154                 return 0;
7155
7156         __MoleculeLock(mp);
7157         count = 0;
7158         if (mp->nsyms != 0) {
7159                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7160                         if (!SYMOP_ALIVE(ap->symop))
7161                                 continue;
7162                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
7163                                 continue;
7164                         bp = ATOM_AT_INDEX(mp->atoms, ap->symbase);
7165                         MoleculeTransformBySymop(mp, &(bp->r), &nr, ap->symop);
7166                         VecSub(dr, nr, ap->r);
7167                         if (VecLength2(dr) < 1e-20)
7168                                 continue;
7169                         if (groupout != NULL) {
7170                                 if (ig == NULL) {
7171                                         ig = IntGroupNew();
7172                                         vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7173                                 }
7174                                 vp[count] = ap->r;
7175                                 IntGroupAdd(ig, i, 1);
7176                         }
7177                         ap->r = nr;
7178                         count++;
7179                 }
7180         }
7181         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
7182                 Int *ip, j, n;
7183                 if (ap->anchor == NULL)
7184                         continue;
7185                 if (group != NULL) {
7186                         if (IntGroupLookup(group, i, NULL) == 0) {
7187                                 n = ap->anchor->connect.count;
7188                                 ip = AtomConnectData(&(ap->anchor->connect));
7189                                 for (j = 0; j < n; j++) {
7190                                         if (IntGroupLookup(group, ip[j], NULL) != 0)
7191                                                 break;
7192                                 }
7193                                 if (j == n)
7194                                         continue;  /*  This pi-anchor should not be modified  */
7195                         }
7196                 }
7197                 nr = ap->r;
7198                 MoleculeCalculatePiAnchorPosition(mp, i);
7199                 VecSub(dr, nr, ap->r);
7200                 if (VecLength2(dr) < 1e-20) {
7201                         ap->r = nr;  /*  No change  */
7202                         continue;
7203                 }
7204                 if (groupout != NULL) {
7205                         if (ig == NULL) {
7206                                 ig = IntGroupNew();
7207                                 vp = (Vector *)calloc(sizeof(Vector), mp->natoms);
7208                         }
7209                         vp[count] = nr;
7210                         IntGroupAdd(ig, i, 1);
7211                 }
7212                 count++;
7213         }
7214         mp->needsMDCopyCoordinates = 1;
7215         __MoleculeUnlock(mp);
7216
7217         if (count > 0) {
7218                 if (groupout != NULL && vpout != NULL) {
7219                         *groupout = ig;
7220                         *vpout = (Vector *)realloc(vp, sizeof(Vector) * count);
7221                 } else {
7222                         IntGroupRelease(ig);
7223                         free(vp);
7224                 }
7225         } else {
7226                 if (groupout != NULL && vpout != NULL) {
7227                         *groupout = NULL;
7228                         *vpout = NULL;
7229                 }
7230         }
7231         return count;
7232 }
7233
7234 #pragma mark ====== Show/hide atoms ======
7235
7236 static void
7237 sMoleculeNotifyChangeAppearance(Molecule *mp)
7238 {
7239         /*  TODO: Finer control of notification types may be necessary  */
7240         MoleculeCallback_notifyModification(mp, 0);
7241 }
7242
7243
7244 static void
7245 sMoleculeUnselectHiddenAtoms(Molecule *mp)
7246 {
7247         int i;
7248         if (mp == NULL || mp->selection == NULL)
7249                 return;
7250         for (i = 0; i < mp->natoms; i++) {
7251                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7252                 if ((ap->exflags & kAtomHiddenFlag) && IntGroupLookupPoint(mp->selection, i) >= 0)
7253                         IntGroupRemove(mp->selection, i, 1);
7254         }
7255         sMoleculeNotifyChangeAppearance(mp);
7256 }
7257
7258 int
7259 MoleculeShowAllAtoms(Molecule *mp)
7260 {
7261         int i;
7262         if (mp == NULL)
7263                 return 0;
7264         for (i = 0; i < mp->natoms; i++) {
7265                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7266                 ap->exflags &= ~kAtomHiddenFlag;
7267         }
7268         sMoleculeNotifyChangeAppearance(mp);
7269         return 1;
7270 }
7271
7272 int
7273 MoleculeShowReverse(Molecule *mp)
7274 {
7275         int i;
7276         if (mp == NULL)
7277                 return 0;
7278         for (i = 0; i < mp->natoms; i++) {
7279                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7280                 ap->exflags ^= kAtomHiddenFlag;
7281         }
7282         sMoleculeUnselectHiddenAtoms(mp);
7283         sMoleculeNotifyChangeAppearance(mp);
7284         return 1;
7285 }
7286
7287 int
7288 MoleculeHideAtoms(Molecule *mp, IntGroup *ig)
7289 {
7290         int i;
7291         if (mp == NULL || ig == NULL)
7292                 return 0;
7293         for (i = 0; i < mp->natoms; i++) {
7294                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
7295                 if (ap->exflags & kAtomHiddenFlag)
7296                         continue;  /*  Already hidden  */
7297                 if (IntGroupLookupPoint(ig, i) >= 0)
7298                         ap->exflags |= kAtomHiddenFlag;
7299         }
7300         sMoleculeUnselectHiddenAtoms(mp);
7301         sMoleculeNotifyChangeAppearance(mp);
7302         return 1;
7303 }
7304
7305 #pragma mark ====== Reversible Editing ======
7306
7307 /*
7308 static void
7309 sMoleculeNotifyModification(Molecule *mp)
7310 {
7311         **  TODO: Finer control of notification types may be necessary  **
7312         MoleculeCallback_notifyModification(mp, 0);
7313 }
7314 */
7315
7316 /*  Insert new[0,1,2,...] to old[n0,n1,n2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7317 int
7318 sInsertElementsToArrayAtPositions(void *objs, int nobjs, const void *newobjs, int nnewobjs, size_t size, IntGroup *where)
7319 {
7320         int n1, n2, n3, i;
7321         if (where == NULL) {
7322                 /*  Append the new objects at the end  */
7323                 memmove((char *)objs + size * nobjs, (char *)newobjs, size * nnewobjs);
7324                 return 0;
7325         }
7326         n1 = IntGroupGetCount(where);  /*  Position to get new object  */
7327         n2 = nobjs;                    /*  Position to get old object  */
7328         n3 = n1 + n2;                  /*  Position to place new/old object  */
7329         for (i = IntGroupGetIntervalCount(where) - 1; i >= 0; i--) {
7330                 int start = IntGroupGetStartPoint(where, i);
7331                 int end = IntGroupGetEndPoint(where, i);
7332                 if (end < n3) {
7333                         /*  old[end-(n3-n2)..n2-1] is moved to old[end..n3-1]  */
7334                         memmove((char *)objs + size * end, (char *)objs + size * (end - (n3 - n2)), size * (n3 - end));
7335                         n2 = end - (n3 - n2);
7336                         n3 = end;
7337                 }
7338                 /*  new[n1-(end-start)..n1-1] is moved to old[n3-(end-start)..n3-1]  */
7339                 memmove((char *)objs + size * (n3 - (end - start)), (char *)newobjs + size * (n1 - (end - start)), size * (end - start));
7340                 n3 -= end - start;
7341                 n1 -= end - start;
7342         }
7343         return 0;
7344 }
7345
7346 /*  Move objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7347 int
7348 sRemoveElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7349 {
7350         int n1, n2, n3, start, end, i;
7351         if (where == NULL || IntGroupGetCount(where) == 0)
7352                 return 0;  /*  No operation  */
7353         if (objs == NULL || nobjs == 0)
7354                 return 1;  /*  Bad argument  */
7355         n1 = 0;  /*  Position to move remaining elements to */
7356         n2 = 0;  /*  Position to move remaining elements from  */
7357         n3 = 0;  /*  Position to move removed elements to  */
7358         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7359                 end = IntGroupGetEndPoint(where, i);
7360                 if (n2 < start) {
7361                         /*  Move (start - n2) elements from objs[n2] to objs[n1]  */
7362                         if (n1 < n2)
7363                                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (start - n2));
7364                         n1 += start - n2;
7365                         n2 = start;
7366                 }
7367                 /*  Move (end - start) elements from objs[n2] to clip[n3]  */
7368                 if (clip != NULL)
7369                         memmove((char *)clip + size * n3, (char *)objs + size * n2, size * (end - start));
7370                 n3 += (end - start);
7371                 n2 += (end - start);
7372         }
7373         /*  Move (nobjs - n2) elements from objs[n2] to objs[n1]  */
7374         if (nobjs > n2)
7375                 memmove((char *)objs + size * n1, (char *)objs + size * n2, size * (nobjs - n2));
7376         return 0;
7377 }
7378
7379 /*  Copy objs[n0,n1,n2,...] to clip[0,1,2,...], where {n0,n1,n2,...} is the points in IntGroup  */
7380 int
7381 sCopyElementsFromArrayAtPositions(void *objs, int nobjs, void *clip, size_t size, IntGroup *where)
7382 {
7383         int n1, start, end, i;
7384         if (objs == NULL || where == NULL)
7385                 return 1;  /*  Bad argument  */
7386         n1 = 0;  /*  Position to move removed elements to  */
7387         for (i = 0; (start = IntGroupGetStartPoint(where, i)) >= 0; i++) {
7388                 end = IntGroupGetEndPoint(where, i);
7389                 /*  Copy (end - start) elements from objs[start] to clip[n1]  */
7390                 if (clip != NULL)
7391                         memmove((char *)clip + size * n1, (char *)objs + size * start, size * (end - start));
7392                 n1 += (end - start);
7393         }
7394         return 0;
7395 }
7396
7397 /*  Create a new atom with no bonding information. ap must _not_ be inside the given molecule
7398    (Use AtomDuplicate() first) */
7399 int
7400 MoleculeCreateAnAtom(Molecule *mp, const Atom *ap, int pos)
7401 {
7402     Atom *ap1, *api;
7403         int i;
7404         if (mp == NULL || ap == NULL || mp->noModifyTopology)
7405                 return -1;
7406         __MoleculeLock(mp);
7407         if (pos < 0 || pos >= mp->natoms)
7408                 pos = mp->natoms;
7409         ap1 = AssignArray(&mp->atoms, &mp->natoms, gSizeOfAtomRecord, mp->natoms, NULL);
7410         if (ap1 == NULL)
7411                 goto error;  /*  Out of memory  */
7412         ap1 = ATOM_AT_INDEX(mp->atoms, pos);
7413         if (pos < mp->natoms - 1) {
7414                 memmove(ATOM_AT_INDEX(mp->atoms, pos + 1), ATOM_AT_INDEX(mp->atoms, pos), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7415         }
7416         if (AtomDuplicate(ap1, ap) == NULL) {
7417                 /*  Cannot duplicate: restore the original state  */
7418                 memmove(ATOM_AT_INDEX(mp->atoms, pos), ATOM_AT_INDEX(mp->atoms, pos + 1), gSizeOfAtomRecord * (mp->natoms - 1 - pos));
7419                 mp->natoms--;
7420                 goto error;
7421         }
7422         ap1->connect.count = 0;
7423         if (ap1->resSeq >= mp->nresidues)
7424                 AssignArray(&mp->residues, &mp->nresidues, 4, ap1->resSeq, ap1->resName);
7425         if (ap1->resName[0] == 0)
7426           strncpy(ap1->resName, mp->residues[ap1->resSeq], 4);
7427         if (ap1->segName[0] == 0)
7428           strncpy(ap1->segName, "MAIN", 4);
7429         if (pos < mp->natoms - 1) {
7430                 /*  Renumber the connect table, bonds, angles, etc. */
7431                 for (i = 0, api = ATOM_AT_INDEX(mp->atoms, i); i < mp->natoms; i++, api = ATOM_NEXT(api)) {
7432                         int j;
7433                         Int *cp;
7434                         cp = AtomConnectData(&api->connect);
7435                         for (j = 0; j < api->connect.count; j++) {
7436                                 if (cp[j] >= pos)
7437                                         cp[j]++;
7438                         }
7439                         if (api->anchor != NULL) {
7440                                 cp = AtomConnectData(&api->anchor->connect);
7441                                 for (j = 0; j < api->anchor->connect.count; j++) {
7442                                         if (cp[j] >= pos)
7443                                                 cp[j]++;
7444                                 }
7445                         }
7446                 }
7447                 for (i = 0; i < mp->nbonds * 2; i++) {
7448                         if (mp->bonds[i] >= pos)
7449                                 mp->bonds[i]++;
7450                 }
7451                 for (i = 0; i < mp->nangles * 3; i++) {
7452                         if (mp->angles[i] >= pos)
7453                                 mp->angles[i]++;
7454                 }
7455                 for (i = 0; i < mp->ndihedrals * 4; i++) {
7456                         if (mp->dihedrals[i] >= pos)
7457                                 mp->dihedrals[i]++;
7458                 }
7459                 for (i = 0; i < mp->nimpropers * 4; i++) {
7460                         if (mp->impropers[i] >= pos)
7461                                 mp->impropers[i]++;
7462                 }
7463         }
7464         mp->nframes = -1;  /*  Should be recalculated later  */
7465         MoleculeIncrementModifyCount(mp);
7466         mp->needsMDRebuild = 1;
7467         __MoleculeUnlock(mp);
7468         return pos;
7469 error:
7470         __MoleculeUnlock(mp);
7471         return -1;
7472 }
7473
7474 #if defined(DEBUG)
7475
7476 static int s_error_count;
7477
7478 static int
7479 s_fprintf(FILE *fp, const char *fmt, ...)
7480 {
7481         va_list va;
7482         va_start(va, fmt);
7483         s_error_count++;
7484         return vfprintf(fp, fmt, va);
7485 }
7486
7487 int
7488 MoleculeCheckSanity(Molecule *mol)
7489 {
7490         const char *fail = "Sanity check failure";
7491         Int i, j, *ip, c[4];
7492         Atom *ap;
7493         s_error_count = 0;
7494         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
7495                 if (ap->resSeq >= mol->nresidues)
7496                         s_fprintf(stderr, "%s: atom %d residue %d but nresidues %d\n", fail, i, ap->resSeq, mol->nresidues);
7497                 if (ap->type != 0 && ap->type < kAtomTypeMinimum)
7498                         s_fprintf(stderr, "%s: atom %d atom type %d less than minimum\n", fail, i, ap->type);
7499                 if (ap->atomicNumber < 0 || ap->atomicNumber > 113)
7500                         s_fprintf(stderr, "%s: atom %d atomic number %d\n", fail, i, ap->atomicNumber);
7501                 ip = AtomConnectData(&ap->connect);
7502                 for (j = 0; j < ap->connect.count; j++) {
7503                         if (ip[j] < 0 || ip[j] >= mol->natoms)
7504                                 s_fprintf(stderr, "%s: atom %d connect[%d] = %d out of range\n", fail, i, j, ip[j]);
7505                         if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[j])->connect), i) == 0)
7506                                 s_fprintf(stderr, "%s: atom %d has connect %d but atom %d has no connect %d\n", fail, i, ip[j], ip[j], i);
7507                 }
7508         }
7509         for (i = 0, ip = mol->bonds; i < mol->nbonds; i++, ip += 2) {
7510                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms)
7511                         s_fprintf(stderr, "%s: bond %d %d-%d out of range\n", fail, i, ip[0], ip[1]);
7512                 if (AtomConnectHasEntry(&(ATOM_AT_INDEX(mol->atoms, ip[0])->connect), ip[1]) == 0)
7513                         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]);
7514         }
7515         for (i = 0, ip = mol->angles; i < mol->nangles; i++, ip += 3) {
7516                 if (ip[0] < 0 || ip[0] >= mol->natoms || ip[1] < 0 || ip[1] >= mol->natoms || ip[2] < 0 || ip[2] >= mol->natoms)
7517                         s_fprintf(stderr, "%s: angle %d %d-%d-%d out of range\n", fail, i, ip[0], ip[1], ip[2]);
7518                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7519                 if (c[0] == 0)
7520                         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]);
7521                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7522                 if (c[1] == 0)
7523                         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]);
7524                 if (c[0] == 2 && c[1] == 2)
7525                         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]);
7526         }
7527         for (i = 0, ip = mol->dihedrals; i < mol->ndihedrals; i++, ip += 4) {
7528                 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)
7529                         s_fprintf(stderr, "%s: dihedral %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7530                 c[0] = MoleculeAreAtomsConnected(mol, ip[1], ip[0]);
7531                 c[1] = MoleculeAreAtomsConnected(mol, ip[1], ip[2]);
7532                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7533                 if (c[0] == 0)
7534                         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]);
7535                 if (c[1] == 0)
7536                         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]);
7537                 if (c[2] == 0)
7538                         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]);
7539         }
7540         for (i = 0, ip = mol->impropers; i < mol->nimpropers; i++, ip += 4) {
7541                 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)
7542                         s_fprintf(stderr, "%s: improper %d %d-%d-%d%d out of range\n", fail, i, ip[0], ip[1], ip[2], ip[3]);
7543                 c[0] = MoleculeAreAtomsConnected(mol, ip[2], ip[0]);
7544                 c[1] = MoleculeAreAtomsConnected(mol, ip[2], ip[1]);
7545                 c[2] = MoleculeAreAtomsConnected(mol, ip[2], ip[3]);
7546                 if (c[0] == 0)
7547                         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]);
7548                 if (c[1] == 0)
7549                         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]);
7550                 if (c[2] == 0)
7551                         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]);
7552         }
7553         return s_error_count;
7554 }
7555 #endif
7556
7557 /*  Merge two molecules. We use this procedure for all add-atom operations.  */
7558 /*  resSeqOffset is an offset to add to the (non-zero) residue numbers in src. */
7559 /*  If nactions and actions are non-NULL, then the corresponding undo actions are created and returned. */
7560 /*  If forUndo is non-zero, then only the atoms are inserted; other information should be inserted
7561     separately by other undo actions.  */
7562 int
7563 MoleculeMerge(Molecule *dst, Molecule *src, IntGroup *where, Int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
7564 {
7565         Int nsrc, ndst;
7566         Int i, j, n1, n2, n3, n4, *cp;
7567         Int *new2old, *old2new;
7568         IntGroup *ig;
7569         Atom *ap;
7570         MolAction *act;
7571         
7572         if (dst == NULL || src == NULL || src->natoms == 0 || (where != NULL && IntGroupGetIntervalCount(where) == 0))
7573                 return 0;  /*  Do nothing  */
7574
7575         if (dst->noModifyTopology)
7576                 return 1;  /*  Prohibited operation  */
7577
7578         if (where != NULL && IntGroupGetCount(where) != src->natoms)
7579                 return 1;  /*  Bad parameter  */
7580
7581         if (nactions != NULL)
7582                 *nactions = 0;
7583         if (actions != NULL)
7584                 *actions = NULL;
7585         act = NULL;
7586
7587         __MoleculeLock(dst);
7588
7589         nsrc = src->natoms;
7590         ndst = dst->natoms;
7591         if (resSeqOffset < 0)
7592                 resSeqOffset = 0;
7593
7594         /*  Atom index table. For "old" index, 0..ndst-1 are for atoms in dst,
7595             and ndst..ndst+nsrc-1 are for atoms in src.  */ 
7596         new2old = (Int *)calloc(sizeof(Int), (ndst + nsrc) * 2);
7597         if (new2old == NULL)
7598                 goto panic;
7599         old2new = new2old + ndst + nsrc;
7600         n1 = 0;  /*  dst index  */
7601         n2 = 0;  /*  src index  */
7602         n3 = 0;  /*  "merged" index  */
7603         i = 0;
7604         while (n1 < ndst || n2 < nsrc) {
7605                 if (where == NULL || (n4 = IntGroupGetStartPoint(where, i)) < 0)
7606                         n4 = ndst - n1;
7607                 else n4 -= n3;
7608                 /*  n4 elements from dst[n1] will go to merged[n3]  */
7609                 for (j = 0; j < n4; j++) {
7610                         old2new[n1 + j] = n3 + j;
7611                         new2old[n3 + j] = n1 + j;
7612                 }
7613                 n3 += n4;
7614                 n1 += n4;
7615                 if (where == NULL || (n4 = IntGroupGetInterval(where, i)) < 0)
7616                         n4 = nsrc - n2;
7617                 /*  n4 elements from src[n2] will go to merged[n3]  */
7618                 for (j = 0; j < n4; j++) {
7619                         old2new[ndst + n2 + j] = n3 + j;
7620                         new2old[n3 + j] = ndst + n2 + j;
7621                 }
7622                 n3 += n4;
7623                 n2 += n4;
7624                 i++;
7625         }
7626
7627         /*  Expand the destination array  */
7628         if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst + nsrc - 1, NULL) == NULL)
7629                 goto panic;
7630
7631         /*  Move the atoms  */
7632         if (where == NULL) {
7633                 /*  Duplicate atoms to the end of the destination array  */
7634                 for (i = 0; i < nsrc; i++) {
7635                         ap = ATOM_AT_INDEX(dst->atoms, ndst + i);
7636                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7637                                 goto panic;
7638                         if (forUndo)  /*  For undo action, all bonds come from another undo action, so connection info are cleared */
7639                                 AtomConnectResize(&ap->connect, 0);
7640                 }
7641         } else {
7642                 /*  Duplicate to a temporary storage and then insert  */
7643                 Atom *tempatoms = (Atom *)malloc(gSizeOfAtomRecord * nsrc);
7644                 if (tempatoms == NULL)
7645                         goto panic;
7646                 for (i = 0; i < nsrc; i++) {
7647                         ap = ATOM_AT_INDEX(tempatoms, i);
7648                         if (AtomDuplicate(ap, ATOM_AT_INDEX(src->atoms, i)) == NULL)
7649                                 goto panic;
7650                         if (forUndo)  /*  See above  */
7651                                 AtomConnectResize(&ap->connect, 0);                             
7652                 }
7653                 if (sInsertElementsToArrayAtPositions(dst->atoms, ndst, tempatoms, nsrc, gSizeOfAtomRecord, where) != 0)
7654                         goto panic;
7655                 free(tempatoms);
7656         }
7657         dst->natoms = ndst + nsrc;
7658
7659         /*  Renumber the atom indices in connect[] and symbase, and modify the residue numbers  */
7660         for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
7661                 if (new2old[i] < ndst) {
7662                         /*  This atom is from dst  */
7663                         n1 = 0;
7664                 } else {
7665                         /*  This atom is from src  */
7666                         n1 = ndst;  /*  Offset to the internal number  */
7667                         if (ap->resSeq != 0)
7668                                 ap->resSeq += resSeqOffset;  /*  Modify residue number  */
7669                 }
7670                 cp = AtomConnectData(&ap->connect);
7671                 for (j = 0; j < ap->connect.count; j++)
7672                         cp[j] = old2new[cp[j] + n1];
7673                 if (SYMOP_ALIVE(ap->symop))
7674                         ap->symbase = old2new[ap->symbase + n1];
7675                 if (ap->anchor != NULL) {
7676                         cp = AtomConnectData(&ap->anchor->connect);
7677                         for (j = 0; j < ap->anchor->connect.count; j++)
7678                                 cp[j] = old2new[cp[j] + n1];
7679                 }
7680         }
7681         
7682         /*  Move the bonds, angles, dihedrals, impropers  */
7683         for (i = 0; i < 4; i++) {
7684                 Int *nitems, *nitems_src;
7685                 Int **items, **items_src;
7686                 Int nsize;  /*  Number of Ints in one element  */
7687                 switch (i) {
7688                         case 0:
7689                                 nitems = &dst->nbonds; items = &dst->bonds; nsize = 2; break;
7690                         case 1:
7691                                 nitems = &dst->nangles; items = &dst->angles; nsize = 3; break;
7692                         case 2:
7693                                 nitems = &dst->ndihedrals; items = &dst->dihedrals; nsize = 4; break;
7694                         case 3:
7695                                 nitems = &dst->nimpropers; items = &dst->impropers; nsize = 4; break;
7696                 }
7697                 nitems_src = (Int *)((char *)src + ((char *)nitems - (char *)dst));
7698                 items_src = (Int **)((char *)src + ((char *)items - (char *)dst));
7699                 if (forUndo) {
7700                         /*  During undo, no bonds etc. are copied from src; they will be taken care later
7701                             by undo actions  */
7702                         n1 = *nitems;
7703                         n2 = 0;
7704                 } else {
7705                         /*  Keep the old number of entries in dst, because it is updated by AssignArray()  */
7706                         n1 = *nitems;
7707                         /*  Also keep the old number of entries in src, in case src and dst point the same molecule  */
7708                         n2 = *nitems_src;
7709                         /*  Expand the array  */
7710                         if (AssignArray(items, nitems, sizeof(Int) * nsize, *nitems + *nitems_src - 1, NULL) == NULL)
7711                                 goto panic;
7712                         /*  Copy the items  */
7713                         memmove(*items + n1 * nsize, *items_src, sizeof(Int) * nsize * n2);
7714                         if (i == 0) {
7715                                 /*  Copy the bond order info if present */
7716                                 Int nn1 = dst->nbondOrders;
7717                                 if (dst->bondOrders != NULL || src->bondOrders != NULL) {
7718                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), dst->nbonds - 1, NULL) == NULL)
7719                                                 goto panic;
7720                                         memset(dst->bondOrders + nn1, 0, sizeof(Double) * (dst->nbonds - nn1));
7721                                         if (src->bondOrders != NULL)
7722                                                 memmove(dst->bondOrders + n1, src->bondOrders, sizeof(Double) * n2);
7723                                 }
7724                         }
7725                 }
7726                 /*  Renumber  */
7727                 for (j = 0; j < n1 * nsize; j++)
7728                         (*items)[j] = old2new[(*items)[j]];
7729                 for (j = n1 * nsize; j < (n1 + n2) * nsize; j++)
7730                         (*items)[j] = old2new[(*items)[j] + ndst];
7731                 if (forUndo == 0 && actions != NULL) {
7732                         ig = IntGroupNewWithPoints(n1, n2, -1);
7733                         switch (i) {
7734                                 case 0: act = MolActionNew(gMolActionDeleteBonds, ig); break;
7735                                 case 1: act = MolActionNew(gMolActionDeleteAngles, ig); break;
7736                                 case 2: act = MolActionNew(gMolActionDeleteDihedrals, ig); break;
7737                                 case 3: act = MolActionNew(gMolActionDeleteImpropers, ig); break;
7738                         }
7739                         IntGroupRelease(ig);
7740                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7741                         act = NULL;
7742                 }
7743         }
7744         
7745         /*  Renumber existing parameters  */
7746         if (dst->par != NULL) {
7747                 int type;
7748                 for (type = kFirstParType; type <= kLastParType; type++) {
7749                         UnionPar *up1;
7750                         n1 = ParameterGetCountForType(dst->par, type);
7751                         for (i = 0; i < n1; i++) {
7752                                 up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7753                                 ParameterRenumberAtoms(type, up1, ndst, old2new);
7754                         }
7755                 }
7756         }
7757
7758         /*  Merge parameters from src  */
7759         if (src->par != NULL && forUndo == 0) {
7760                 UnionPar *up1, *up2;
7761                 int type;
7762                 if (dst->par == NULL)
7763                         dst->par = ParameterNew();
7764                 else {
7765                         /*  Renumber existing parameters  */
7766                         for (type = kFirstParType; type <= kLastParType; type++) {
7767                                 n1 = ParameterGetCountForType(dst->par, type);
7768                                 for (i = 0; i < n1; i++) {
7769                                         up1 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, i);
7770                                         ParameterRenumberAtoms(type, up1, ndst, old2new);
7771                                 }
7772                         }
7773                 }
7774                 ig = IntGroupNew();
7775                 for (type = kFirstParType; type <= kLastParType; type++) {
7776                         n1 = ParameterGetCountForType(src->par, type);
7777                         n2 = ParameterGetCountForType(dst->par, type);
7778                         if (n1 == 0)
7779                                 continue;
7780                         /*  Determine which parameter should be copied from src to dst  */
7781                         for (i = 0; i < n1; i++) {
7782                                 UInt types[4];
7783                                 up1 = ParameterGetUnionParFromTypeAndIndex(src->par, type, i);
7784                                 n3 = ParameterGetAtomTypes(type, up1, types);
7785                                 for (j = 0; j < n3; j++) {
7786                                         /*  If it includes explicit atom index, then it should be copied  */
7787                                         if (types[j] < kAtomTypeMinimum) {
7788                                                 IntGroupAdd(ig, i, 1);
7789                                                 break;
7790                                         }
7791                                 }
7792                                 if (j == n3) {
7793                                         for (j = 0; j < n2; j++) {
7794                                                 up2 = ParameterGetUnionParFromTypeAndIndex(dst->par, type, j);
7795                                                 if (ParameterCompare(up1, up2, type))
7796                                                         break;
7797                                         }
7798                                         if (j >= n2)
7799                                                 /*  This is an unknown parameter; should be copied  */
7800                                                 IntGroupAdd(ig, i, 1);
7801                                 }
7802                         }
7803                         n1 = IntGroupGetCount(ig);
7804                         if (n1 == 0)
7805                                 continue;
7806                         up1 = (UnionPar *)calloc(sizeof(UnionPar), n1);
7807                         if (up1 == NULL)
7808                                 goto panic;
7809                         /*  Copy parameters and renumber indices if necessary  */
7810                         for (i = j = 0; i < n1; i++) {
7811                                 up2 = ParameterGetUnionParFromTypeAndIndex(src->par, type, IntGroupGetNthPoint(ig, i));
7812                                 if (up2 == NULL)
7813                                         continue;
7814                                 up1[j] = *up2;
7815                                 ParameterRenumberAtoms(type, up1 + j, nsrc, old2new + ndst);
7816                                 j++;
7817                         }
7818                         /*  Merge parameters  */
7819                         IntGroupClear(ig);
7820                         IntGroupAdd(ig, n2, j);
7821                         if (ParameterInsert(dst->par, type, up1, ig) < j)
7822                                 goto panic;
7823                         if (actions != NULL) {
7824                                 act = MolActionNew(gMolActionDeleteParameters, type, ig);
7825                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7826                                 act = NULL;
7827                         }
7828                         IntGroupClear(ig);
7829                         free(up1);
7830                 }
7831                 IntGroupRelease(ig);
7832         }
7833         
7834         /*  Copy the residues if necessary  */
7835         /*  src[1..src->nresidues-1] should become dst[1+resSeqOffset..src->nresidues+resSeqOffset-1];
7836             However, 1+resSeqOffset should not overwrite the existing residue in dst;
7837                 i.e. if 1+resSeqOffset is less than dst->nresidues, copy should start from src[dst->nresidues-resSeqOffset] instead of src[1].  */
7838         if (forUndo == 0) {
7839                 n1 = dst->nresidues;
7840                 if (1 + resSeqOffset < n1) {
7841                         n2 = n1;
7842                 } else n2 = 1 + resSeqOffset; /* n2 is the start index of residues from src[] */
7843                 if (src->nresidues > 1 && n1 < src->nresidues + resSeqOffset) {
7844                         if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), src->nresidues + resSeqOffset - 1, NULL) == NULL)
7845                                 goto panic;
7846                         memmove(dst->residues + n2, src->residues + n2 - resSeqOffset, sizeof(dst->residues[0]) * (src->nresidues - (n2 - resSeqOffset)));
7847                         if (nactions != NULL) {
7848                                 act = MolActionNew(gMolActionChangeNumberOfResidues, n1);
7849                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
7850                                 act = NULL;
7851                         }
7852                 }
7853         }
7854
7855         MoleculeCleanUpResidueTable(dst);
7856         
7857         free(new2old);
7858         dst->nframes = -1;  /*  Should be recalculated later  */
7859
7860         MoleculeIncrementModifyCount(dst);
7861         dst->needsMDRebuild = 1;
7862         __MoleculeUnlock(dst);
7863         return 0;
7864
7865   panic:
7866         __MoleculeUnlock(dst);
7867     Panic("Low memory while adding atoms");
7868         return 1;  /*  Not reached  */
7869 }
7870
7871 /*  Unmerge the molecule. If necessary, the undo actions are stored in nactions/actions array.
7872     (The nactions/actions array must be initialized by the caller)  */
7873 static int
7874 sMoleculeUnmergeSub(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, int moveFlag, Int *nactions, MolAction ***actions, Int forUndo)
7875 {
7876         Int nsrc, ndst, nsrcnew;
7877         Int i, j, n1, n2, n3, n4, *cp;
7878         Int *new2old, *old2new;
7879         IntGroup *move_g, *del_g, *remain_g, *dst_par_g, *remove_par_g;
7880         Molecule *dst;
7881         Atom *ap, *dst_ap;
7882         UnionPar *up;
7883         MolAction *act;
7884
7885         if (src == NULL || src->natoms == 0 || where == NULL || IntGroupGetIntervalCount(where) == 0) {
7886                 /*  Do nothing  */
7887                 if (dstp != NULL)
7888                         *dstp = NULL;
7889                 return 0;
7890         }
7891         
7892         if (src->noModifyTopology && moveFlag)
7893                 return 1;  /*  Prohibit editing  */
7894
7895         if ((ndst = IntGroupGetCount(where)) > src->natoms)
7896                 return 1;  /*  Bad parameter  */
7897
7898         __MoleculeLock(src);
7899         
7900         act = NULL;
7901         
7902         nsrc = src->natoms;
7903         nsrcnew = nsrc - ndst;
7904         if (resSeqOffset < 0)
7905                 resSeqOffset = 0;
7906
7907         /*  Atom index table. For "new" index, 0..nsrcnew-1 are for atoms remaining in src,
7908             and nsrcnew..nsrc-1 are for atoms moved into dst.  */ 
7909         new2old = (Int *)calloc(sizeof(Int), nsrc * 2);
7910         if (new2old == NULL)
7911                 goto panic;
7912         old2new = new2old + nsrc;
7913         n1 = 0;  /*  src index  */
7914         n2 = 0;  /*  dst index  */
7915         n3 = 0;  /*  src index after "unmerge"  */
7916         i = 0;
7917         while (n1 < nsrc || n2 < ndst) {
7918                 if ((n4 = IntGroupGetStartPoint(where, i)) < 0)
7919                         n4 = nsrc - n1;
7920                 else n4 -= n1;
7921                 /*  n4 elements from src[n1] will go to unmerged[n3]  */
7922                 for (j = 0; j < n4; j++) {
7923                         old2new[n1 + j] = n3 + j;
7924                         new2old[n3 + j] = n1 + j;
7925                 }
7926                 n3 += n4;
7927                 n1 += n4;
7928                 if ((n4 = IntGroupGetInterval(where, i)) < 0)
7929                         n4 = nsrc - n1;
7930                 /*  n4 elements from src[n1] will go to dst[n2]  */
7931                 for (j = 0; j < n4; j++) {
7932                         old2new[n1 + j] = nsrcnew + n2 + j;
7933                         new2old[nsrcnew + n2 + j] = n1 + j;
7934                 }
7935                 n1 += n4;
7936                 n2 += n4;
7937                 i++;
7938         }
7939
7940         /*  Atoms to remain in the source group  */
7941         if (moveFlag) {
7942                 remain_g = IntGroupNewWithPoints(0, nsrc, -1);
7943                 IntGroupRemoveIntGroup(remain_g, where);
7944         } else remain_g = NULL;
7945         
7946         /*  Find parameters to be moved to the dst (dst_par_g), and to be removed from the src (remove_par_g) */
7947         if (src->par != NULL) {
7948                 dst_par_g = IntGroupNew();
7949                 if (moveFlag)
7950                         remove_par_g = IntGroupNew();
7951                 else remove_par_g = NULL;
7952                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
7953                         n2 = ParameterGetCountForType(src->par, n1);
7954                         if (n2 == 0)
7955                                 continue;
7956                         for (i = 0; i < n2; i++) {
7957                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
7958                                 if (ParameterIsRelevantToAtomGroup(n1, up, src->atoms, where)) {
7959                                         /*  This parameter is to be copied to dst  */
7960                                         IntGroupAdd(dst_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7961                                 }
7962                                 if (moveFlag && !ParameterIsRelevantToAtomGroup(n1, up, src->atoms, remain_g)) {
7963                                         /*  This parameter is to be removed  */
7964                                         IntGroupAdd(remove_par_g, i + (n1 - kFirstParType) * kParameterIndexOffset, 1);
7965                                 }
7966                         }
7967                 }
7968         } else dst_par_g = remove_par_g = NULL;
7969         
7970         /*  Pi anchors should be modified if the anchor and its component atoms become separated between
7971             src anc dst  */
7972         if (moveFlag) {
7973                 Int ibufsize, *ibuf, flag_i, flag_j;
7974                 ibufsize = 8;
7975                 ibuf = (Int *)malloc(sizeof(Int) * ibufsize);
7976                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
7977                         if (ap->anchor == NULL)
7978                                 continue;
7979                         flag_i = (old2new[i] < nsrcnew);
7980                         cp = AtomConnectData(&ap->anchor->connect);
7981                         for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
7982                                 flag_j = (old2new[cp[j]] < nsrcnew);
7983                                 if (flag_i == flag_j) {
7984                                         if (n1 >= ibufsize) {
7985                                                 ibufsize += 8;
7986                                                 ibuf = (Int *)realloc(ibuf, sizeof(Int) * ibufsize);
7987                                         }
7988                                         ibuf[n1++] = cp[j];
7989                                 }
7990                         }
7991                         if (n1 < j) {
7992                                 /*  Need to modify the pi anchor list  */
7993                                 if (n1 <= 1)
7994                                         n1 = 0;
7995                                 MolActionCreateAndPerform(src, SCRIPT_ACTION("isI"), "set_atom_attr", i, "anchor_list", n1, ibuf);
7996                         }
7997                 }
7998         }
7999         
8000         /*  Make a new molecule  */
8001         if (dstp != NULL) {
8002                 dst = MoleculeNew();
8003                 if (dst == NULL)
8004                         goto panic;
8005                 /*  Expand the destination array  */
8006                 if (AssignArray(&(dst->atoms), &(dst->natoms), gSizeOfAtomRecord, ndst - 1, NULL) == NULL)
8007                         goto panic;
8008                 dst_ap = dst->atoms;
8009         } else {
8010                 dst = NULL;
8011                 dst_ap = (Atom *)calloc(sizeof(Atom), ndst);
8012                 if (dst_ap == NULL)
8013                         goto panic;
8014         }
8015         
8016         /*  Move the atoms  */
8017         if (moveFlag) {
8018                 if (sRemoveElementsFromArrayAtPositions(src->atoms, src->natoms, dst_ap, gSizeOfAtomRecord, where) != 0)
8019                         goto panic;
8020                 src->natoms = nsrcnew;
8021                 if (dst == NULL) {
8022                         /*  The atom record must be deallocated correctly  */
8023                         for (i = 0; i < ndst; i++)
8024                                 AtomClean(ATOM_AT_INDEX(dst_ap, i));
8025                 }
8026         } else {
8027                 if (dst != NULL) {
8028                         for (i = 0; (n1 = IntGroupGetNthPoint(where, i)) >= 0; i++)
8029                                 AtomDuplicate(ATOM_AT_INDEX(dst_ap, i), ATOM_AT_INDEX(src->atoms, n1));
8030                 }
8031         }
8032         
8033         if (dst == NULL) {
8034                 /*  The dummy destination array is no longer needed  */
8035                 free(dst_ap);
8036                 dst_ap = NULL;
8037         }
8038         
8039         /*  Renumber the atom indices in connect[] (src) */
8040         if (moveFlag) {
8041                 for (i = 0, ap = src->atoms; i < src->natoms; i++, ap = ATOM_NEXT(ap)) {
8042                         cp = AtomConnectData(&ap->connect);
8043                         for (j = n1 = 0; j < ap->connect.count; j++) {
8044                                 n2 = old2new[cp[j]];
8045                                 if (n2 < nsrcnew)
8046                                         cp[n1++] = n2;
8047                         }
8048                         AtomConnectResize(&ap->connect, n1);
8049                         if (ap->anchor != NULL) {
8050                                 cp = AtomConnectData(&ap->anchor->connect);
8051                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8052                                         n2 = old2new[cp[j]];
8053                                         if (n2 < nsrcnew)
8054                                                 cp[n1++] = n2;
8055                                 }
8056                                 if (n1 != ap->anchor->connect.count) {
8057                                         /*  This should not happen!!  */
8058                                         AtomConnectResize(&ap->anchor->connect, n1);
8059                                         fprintf(stderr, "Internal error in sMoleculeUnmergeSub (line %d)\n", __LINE__);
8060                                         if (n1 == 0) {
8061                                                 free(ap->anchor->coeffs);
8062                                                 free(ap->anchor);
8063                                                 ap->anchor = NULL;
8064                                         }
8065                                 }
8066                         }
8067                 }
8068         }
8069         
8070         /*  Renumber the atom indices in connect[] (dst)  */
8071         if (dst != NULL) {
8072                 for (i = 0, ap = dst->atoms; i < dst->natoms; i++, ap = ATOM_NEXT(ap)) {
8073                         if (ap->resSeq != 0 && ap->resSeq - resSeqOffset >= 0)
8074                                 ap->resSeq -= resSeqOffset;
8075                         else ap->resSeq = 0;
8076                         cp = AtomConnectData(&ap->connect);
8077                         for (j = n1 = 0; j < ap->connect.count; j++) {
8078                                 n2 = old2new[cp[j]] - nsrcnew;
8079                                 if (n2 >= 0)
8080                                         cp[n1++] = n2;
8081                         }
8082                         AtomConnectResize(&ap->connect, n1);
8083                         if (ap->anchor != NULL) {
8084                                 cp = AtomConnectData(&ap->anchor->connect);
8085                                 for (j = n1 = 0; j < ap->anchor->connect.count; j++) {
8086                                         n2 = old2new[cp[j]] - nsrcnew;
8087                                         if (n2 >= 0)
8088                                                 cp[n1++] = n2;
8089                                 }
8090                                 if (n1 != ap->anchor->connect.count) {
8091                                         /*  This can happen, and the anchor info is silently modified  */
8092                                         if (n1 <= 1) {
8093                                                 AtomConnectResize(&ap->anchor->connect, 0);
8094                                                 free(ap->anchor->coeffs);
8095                                                 free(ap->anchor);
8096                                                 ap->anchor = NULL;
8097                                         } else {
8098                                                 Double d;
8099                                                 AtomConnectResize(&ap->anchor->connect, n1);
8100                                                 d = 0.0;
8101                                                 for (j = 0; j < n1; j++)
8102                                                         d += ap->anchor->coeffs[j];
8103                                                 for (j = 0; j < n1; j++)
8104                                                         ap->anchor->coeffs[j] /= d;
8105                                                 MoleculeCalculatePiAnchorPosition(dst, i);
8106                                         }
8107                                 }
8108                         }
8109                 }
8110         }
8111
8112         /*  Separate the bonds, angles, dihedrals, impropers  */
8113         /*  TODO: Improper torsions should also be copied!  */
8114         move_g = IntGroupNew();
8115         if (move_g == NULL)
8116                 goto panic;
8117         for (i = 3; i >= 0; i--) {
8118                 Int *nitems, *nitems_dst;
8119                 Int **items, **items_dst;
8120                 Int nsize;  /*  Number of Ints in one element  */
8121                 unsigned char *counts;
8122                 del_g = IntGroupNew();
8123                 switch (i) {
8124                         case 0:
8125                                 nitems = &src->nbonds; items = &src->bonds; nsize = 2; break;
8126                         case 1:
8127                                 nitems = &src->nangles; items = &src->angles; nsize = 3; break;
8128                         case 2:
8129                                 nitems = &src->ndihedrals; items = &src->dihedrals; nsize = 4; break;
8130                         case 3:
8131                                 nitems = &src->nimpropers; items = &src->impropers; nsize = 4; break;
8132                         default:
8133                                 nitems = NULL; items = NULL; nsize = 0; break;  /*  Not reached  */
8134                 }
8135                 if (dst != NULL) {
8136                         nitems_dst = (Int *)((char *)dst + ((char *)nitems - (char *)src));
8137                         items_dst = (Int **)((char *)dst + ((char *)items - (char *)src));
8138                 } else {
8139                         nitems_dst = NULL;
8140                         items_dst = NULL;
8141                 }
8142                 counts = (unsigned char *)calloc(1, *nitems);
8143                 /*  Find the entries that should be moved to dst  */
8144                 n2 = 0;
8145                 for (j = 0; j < *nitems * nsize; j++) {
8146                         n1 = old2new[(*items)[j]];
8147                         if (n1 >= nsrcnew)
8148                                 counts[j / nsize]++; /* Count the atom belonging to dst */ 
8149                 }
8150                 for (j = n2 = n3 = 0; j < *nitems; j++) {
8151                         if (counts[j] > 0) {
8152                                 /*  Remove from src  */
8153                                 n2++;
8154                                 if (IntGroupAdd(del_g, j, 1) != 0)
8155                                         goto panic;
8156                                 if (counts[j] == nsize) {
8157                                         /*  Move to dst  */
8158                                         n3++;
8159                                         if (IntGroupAdd(move_g, j, 1) != 0)
8160                                                 goto panic;
8161                                 }
8162                         }
8163                 }
8164                 if (n2 > 0) {
8165                         /*  Expand the destination array  */
8166                         if (items_dst != NULL && n3 > 0) {
8167                                 if (AssignArray(items_dst, nitems_dst, sizeof(Int) * nsize, n3 - 1, NULL) == NULL)
8168                                         goto panic;
8169                                 if (sCopyElementsFromArrayAtPositions(*items, *nitems, *items_dst, sizeof(Int) * nsize, move_g) != 0)
8170                                         goto panic;
8171                                 if (i == 0 && src->bondOrders != NULL) {
8172                                         if (AssignArray(&dst->bondOrders, &dst->nbondOrders, sizeof(Double), n3 - 1, NULL) == NULL)
8173                                                 goto panic;
8174                                         if (sCopyElementsFromArrayAtPositions(src->bondOrders, src->nbondOrders, dst->bondOrders, sizeof(Double), move_g) != 0)
8175                                                 goto panic;
8176                                 }
8177                         }
8178                         /*  Remove from src  */
8179                         if (moveFlag && forUndo == 0) {
8180                                 if (nactions != NULL) {
8181                                         Int k, *ip;
8182                                         Double *dp;
8183                                         ip = (Int *)malloc(sizeof(Int) * nsize * n2);
8184                                         for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8185                                                 memmove(ip + j * nsize, *items + k * nsize, sizeof(Int) * nsize);
8186                                         if (i == 0 && src->bondOrders != NULL) {
8187                                                 dp = (Double *)malloc(sizeof(Double) * n2);
8188                                                 for (j = 0; (k = IntGroupGetNthPoint(del_g, j)) >= 0; j++)
8189                                                         dp[j] = src->bondOrders[k];
8190                                         } else dp = NULL;
8191                                         switch (i) {
8192                                                 case 0:
8193                                                         act = MolActionNew(gMolActionAddBondsForUndo, n2 * nsize, ip, del_g); break;
8194                                                 case 1:
8195                                                         act = MolActionNew(gMolActionAddAngles, n2 * nsize, ip, del_g); break;
8196                                                 case 2:
8197                                                         act = MolActionNew(gMolActionAddDihedrals, n2 * nsize, ip, del_g); break;
8198                                                 case 3:
8199                                                         act = MolActionNew(gMolActionAddImpropers, n2 * nsize, ip, del_g); break;
8200                                         }
8201                                         if (act != NULL) {
8202                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8203                                                 act = NULL;
8204                                         }
8205                                         free(ip);
8206                                         if (dp != NULL) {
8207                                                 act = MolActionNew(gMolActionAssignBondOrders, n2, dp, del_g);
8208                                                 AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8209                                                 act = NULL;
8210                                                 free(dp);
8211                                         }
8212                                 }
8213                                 if (sRemoveElementsFromArrayAtPositions(*items, *nitems, NULL, sizeof(Int) * nsize, del_g) != 0)
8214                                         goto panic;
8215                                 (*nitems) -= n2;
8216                         }
8217                 }
8218                 /*  Renumber the entries  */
8219                 if (moveFlag) {
8220                         for (j = 0; j < *nitems * nsize; j++) {
8221                                 (*items)[j] = old2new[(*items)[j]];
8222                         }
8223                 }
8224                 if (items_dst != NULL) {
8225                         for (j = 0; j < *nitems_dst * nsize; j++) {
8226                                 (*items_dst)[j] = old2new[(*items_dst)[j]] - nsrcnew;
8227                         }
8228                 }
8229                 free(counts);
8230                 IntGroupClear(move_g);
8231                 IntGroupRelease(del_g);
8232         }
8233         IntGroupRelease(move_g);
8234         
8235         /*  Copy the residues  */
8236         if (dst != NULL) {
8237                 /*  src[i] will become dst[i - resSeqOffset] (src->nresidues > i >= 1 + resSeqOffset)  */
8238                 n1 = src->nresidues - resSeqOffset;  /*  This will be dst->nresidues (if >0)  */
8239                 if (AssignArray(&dst->residues, &dst->nresidues, sizeof(dst->residues[0]), (n1 > 0 ? n1 - 1: 0), NULL) == NULL)
8240                         goto panic;
8241                 if (n1 > 1) {
8242                         memmove(dst->residues + 1, src->residues + resSeqOffset + 1, sizeof(dst->residues[0]) * (n1 - 1));
8243                 }
8244         }
8245
8246         /*  Copy the parameters to dst */
8247         if (dst != NULL && dst_par_g != NULL && (n2 = IntGroupGetCount(dst_par_g)) > 0) {
8248                 IntGroup *dst_new_g = IntGroupNew();
8249                 Int dst_par_count[kLastParType - kFirstParType + 1];
8250                 if (dst_new_g == NULL)
8251                         goto panic;
8252                 for (i = 0; i <= kLastParType - kFirstParType; i++)
8253                         dst_par_count[i] = 0;
8254                 up = (UnionPar *)calloc(sizeof(UnionPar), n2);
8255                 if (up == NULL)
8256                         goto panic;
8257                 if (ParameterCopy(src->par, kFirstParType, up, dst_par_g) < n2)
8258                         goto panic;
8259                 /*  Renumber the explicit atom indices  */
8260                 for (i = 0; i < nsrc; i++)
8261                         old2new[i] -= nsrcnew;  /*  new indices for atoms in dst; otherwise negative numbers  */
8262                 for (i = 0; i < n2; i++) {
8263                         /*  Renumber the indices, and count the number of parameters for each type  */
8264                         n1 = kFirstParType + IntGroupGetNthPoint(dst_par_g, i) / kParameterIndexOffset;
8265                         dst_par_count[n1 - kFirstParType]++;
8266                         ParameterRenumberAtoms(n1, up + i, nsrc, old2new);
8267                 }
8268                 for (i = 0; i < nsrc; i++)
8269                         old2new[i] += nsrcnew;
8270                 if (dst->par == NULL)
8271                         dst->par = ParameterNew();
8272                 for (i = 0; i <= kLastParType - kFirstParType; i++) {
8273                         if (dst_par_count[i] > 0)
8274                                 IntGroupAdd(dst_new_g, i * kParameterIndexOffset, dst_par_count[i]);
8275                 }
8276                 if (ParameterInsert(dst->par, kFirstParType, up, dst_new_g) < n2)
8277                         goto panic;
8278                 free(up);
8279                 IntGroupRelease(dst_new_g);
8280         }
8281         IntGroupRelease(dst_par_g);
8282
8283         /*  Remove the unused parameter. Note: the parameters that are in remove_par_g and not in 
8284             dst_par_g will disappear. To support undo, these parameters should be taken care separately.  */
8285         if (forUndo == 0 && remove_par_g != NULL && (n2 = IntGroupGetCount(remove_par_g)) > 0) {
8286                 UnionPar *up = (UnionPar *)malloc(sizeof(UnionPar) * n2);
8287                 ParameterDelete(src->par, kFirstParType, up, remove_par_g);
8288                 if (nactions != NULL) {
8289                         act = MolActionNew(gMolActionAddParameters, kFirstParType, remove_par_g, n2, up);
8290                         AssignArray(actions, nactions, sizeof(MolAction *), *nactions, &act);
8291                         act = NULL;
8292                 }
8293                 free(up);
8294         }
8295         IntGroupRelease(remove_par_g);
8296         
8297         /*  Renumber the parameter records remaining in the src  */
8298         if (moveFlag) {
8299                 for (n1 = kFirstParType; n1 <= kLastParType; n1++) {
8300                         n2 = ParameterGetCountForType(src->par, n1);
8301                         for (i = 0; i < n2; i++) {
8302                                 up = ParameterGetUnionParFromTypeAndIndex(src->par, n1, i);
8303                                 ParameterRenumberAtoms(n1, up, nsrc, old2new);
8304                         }
8305                 }
8306         }
8307
8308         /*  Clean up  */
8309         IntGroupRelease(remain_g);
8310         MoleculeCleanUpResidueTable(src);
8311         if (dst != NULL)
8312                 MoleculeCleanUpResidueTable(dst);
8313         free(new2old);
8314
8315         src->nframes = -1;  /*  Should be recalculated later  */
8316         if (dst != NULL)
8317                 dst->nframes = -1;  /*  Should be recalculated later  */
8318
8319         
8320         if (dstp != NULL)
8321                 *dstp = dst;
8322
8323         MoleculeIncrementModifyCount(src);
8324         src->needsMDRebuild = 1;
8325         __MoleculeUnlock(src);
8326         
8327         return 0;
8328
8329   panic:
8330         __MoleculeUnlock(src);
8331 /*    Panic("Low memory while removing atoms"); */
8332         return -1;
8333 }
8334
8335 /*  Separate molecule into two parts. The atoms specified by 'where' are moved
8336     from src to a new molecule, which is returned as *dstp. Dstp can be NULL, 
8337         in which case the moved atoms are discarded.  */
8338 int
8339 MoleculeUnmerge(Molecule *src, Molecule **dstp, IntGroup *where, int resSeqOffset, Int *nactions, MolAction ***actions, Int forUndo)
8340 {
8341         return sMoleculeUnmergeSub(src, dstp, where, resSeqOffset, 1, nactions, actions, forUndo);
8342 }
8343
8344 /*  Extract atoms from a given molecule into two parts. The atoms specified by 
8345         'where' are copied from src to a new molecule, which is returned as *dstp.
8346     If dummyFlag is non-zero, then the atoms that are not included in the group 
8347         but are connected to any atoms in the group are converted to "dummy" atoms 
8348         (i.e. with element "Du" and names beginning with an underscore) and included 
8349         in the new molecule object.  */
8350 int
8351 MoleculeExtract(Molecule *src, Molecule **dstp, IntGroup *where, int dummyFlag)
8352 {
8353         int retval;
8354
8355         /*  Extract the fragment  */
8356         retval = sMoleculeUnmergeSub(src, dstp, where, 0, 0, NULL, NULL, 0);
8357         if (retval != 0)
8358                 return retval;
8359
8360         if (dummyFlag) {
8361
8362                 /*  Search bonds crossing the molecule border  */
8363                 IntGroup *ig = MoleculeSearchBondsAcrossAtomGroup(src, where);
8364                 if (ig != NULL) {
8365                         IntGroupIterator iter;
8366                         Int i, idx;
8367                         idx = 1;
8368                         IntGroupIteratorInit(ig, &iter);
8369                         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8370                                 /*  The atoms at the border  */
8371                                 Int n1, n2, nn[3];
8372                                 Atom a, *ap;
8373                                 n1 = src->bonds[i*2];
8374                                 n2 = src->bonds[i*2+1];
8375                                 if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0) {
8376                                         int w = n1;
8377                                         n1 = n2;
8378                                         n2 = w;
8379                                         if ((nn[0] = IntGroupLookupPoint(where, n1)) < 0)
8380                                                 continue;  /*  Actually this is an internal error  */
8381                                 }
8382                                 /*  n1 is in *where, n2 is not; nn[0] is the index of atom n1 in the new molecule  */
8383                                 /*  Create a new dummy atom with the same segment/residue info with n1
8384                                     and the same position as n2  */
8385                                 ap = ATOM_AT_INDEX(src->atoms, n1);
8386                                 memset(&a, 0, gSizeOfAtomRecord);
8387                                 a.segSeq = ap->segSeq;
8388                                 memmove(a.segName, ap->segName, 4);
8389                                 a.resSeq = ap->resSeq;
8390                                 memmove(a.resName, ap->resName, 4);
8391                                 ElementToString(0, a.element);  /*  "Du"  */
8392                                 snprintf(a.aname, 4, "_%d", idx++);
8393                                 a.r = ATOM_AT_INDEX(src->atoms, n2)->r;
8394                                 /*  Add the dummy atom to the new molecule; nn[1] is the index
8395                                     of the new dummy atom in the new molecule  */
8396                                 nn[1] = MoleculeCreateAnAtom(*dstp, &a, -1);
8397                                 /*  Connect nn1 and nn2  */
8398                                 nn[2] = kInvalidIndex;
8399                                 MoleculeAddBonds(*dstp, 1, nn, NULL, 1);
8400                         }
8401                         IntGroupIteratorRelease(&iter);
8402                         IntGroupRelease(ig);
8403                 }
8404         }
8405         
8406         return 0;
8407 }
8408
8409 int
8410 MoleculeAddBonds(Molecule *mp, Int nbonds, const Int *bonds, IntGroup *where, Int autoGenerate)
8411 {
8412         Int nangles, ndihedrals;
8413         Int *angles, *dihedrals;
8414         Int i, j, k, kk, n1, n2, cn1, cn2;
8415         Int *cp1, *cp2;
8416         Int temp[4];
8417         Atom *ap1, *ap2, *ap3;
8418         
8419         if (mp == NULL || bonds == NULL || nbonds <= 0)
8420                 return 0;
8421         if (mp->noModifyTopology)
8422                 return -4;  /*  Prohibited operation  */
8423
8424         /*  Note: Duplicates and validity are not checked (the caller must do that)  */
8425
8426         __MoleculeLock(mp);
8427
8428         n1 = mp->nbonds;
8429         if (AssignArray(&(mp->bonds), &(mp->nbonds), sizeof(Int) * 2, n1 + nbonds - 1, NULL) == NULL
8430                 || sInsertElementsToArrayAtPositions(mp->bonds, n1, bonds, nbonds, sizeof(Int) * 2, where) != 0) {
8431                 __MoleculeUnlock(mp);
8432                 return -4;  /*  Out of memory  */
8433         }
8434         if (mp->bondOrders != NULL) {
8435                 /*  Expand the bond order info (all new entries are zero)  */
8436                 Double *dp = (Double *)calloc(sizeof(Double), nbonds);
8437                 if (dp == NULL)
8438                         return -4;
8439                 if (AssignArray(&(mp->bondOrders), &(mp->nbondOrders), sizeof(Double), n1 + nbonds - 1, NULL) == NULL
8440                         || sInsertElementsToArrayAtPositions(mp->bondOrders, n1, dp, nbonds, sizeof(Double), where) != 0) {
8441                         __MoleculeUnlock(mp);
8442                         free(dp);
8443                         return -4;
8444                 }
8445                 free(dp);
8446         }
8447         
8448         angles = dihedrals = NULL;
8449         nangles = ndihedrals = 0;
8450         
8451         /*  Add connects[], and angles/dihedrals (if autoGenerate is true)  */
8452         for (i = 0; i < nbonds; i++) {
8453                 
8454                 /*  One entry at time  */
8455                 /*  (Otherwise, duplicate entries of angles and dihedrals result)  */
8456                 n1 = bonds[i * 2];
8457                 n2 = bonds[i * 2 + 1];
8458                 
8459                 ap1 = ATOM_AT_INDEX(mp->atoms, n1);
8460                 AtomConnectInsertEntry(&ap1->connect, -1, n2);
8461                 ap2 = ATOM_AT_INDEX(mp->atoms, n2);
8462                 AtomConnectInsertEntry(&ap2->connect, -1, n1);
8463         
8464                 /*  Add angles and dihedrals  */
8465                 if (autoGenerate) {
8466                         AtomConnect *ac1, *ac2;
8467                         if (ap1->anchor == NULL || ap2->anchor == NULL) {
8468                                 /*  N1-N2-{XY} or N2-N1-{XY} angles (X: connected atom, Y: constitute atom of pi-anchor)  */
8469                                 for (j = 0; j < 4; j++) {
8470                                         switch (j) {
8471                                                 case 0: temp[0] = n1; temp[1] = n2; ac1 = &ap2->connect; break;  /* N1-N2-X */
8472                                                 case 1: if (ap2->anchor == NULL) continue; else ac1 = &ap2->anchor->connect; break; /* N1-N2-Y */
8473                                                 case 2: temp[0] = n2; temp[1] = n1; ac1 = &ap1->connect; break;  /* N2-N1-X */
8474                                                 case 3: if (ap1->anchor == NULL) continue; else ac1 = &ap1->anchor->connect; break; /* N2-N1-Y */
8475                                         }
8476                                         cp1 = AtomConnectData(ac1);
8477                                         cn1 = ac1->count;
8478                                         for (k = 0; k < cn1; k++) {
8479                                                 temp[2] = cp1[k];
8480                                                 if (temp[2] == temp[0])
8481                                                         continue;
8482                                                 ap3 = ATOM_AT_INDEX(mp->atoms, temp[2]);
8483                                                 if (ap3->anchor != NULL) {
8484                                                         /*  Avoid X-anchor-anchor angle (anchor-X-anchor is allowed)  */
8485                                                         if ((j < 2 && ap2->anchor != NULL) || (j >= 2 && ap1->anchor != NULL))
8486                                                                 continue;
8487                                                 }
8488                                                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8489                                                         goto panic;
8490                                                 /*  Dihedrals N1-N2-X-{XY} or N2-N1-X-{XY}  */
8491                                                 if (j == 1 || j == 3)
8492                                                         continue;
8493                                                 cp2 = AtomConnectData(&ap3->connect);
8494                                                 for (kk = 0; kk < ap3->connect.count; kk++) {
8495                                                         temp[3] = cp2[kk];
8496                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8497                                                                 continue;
8498                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8499                                                                 goto panic;
8500                                                 }
8501                                                 if (ap3->anchor != NULL) {
8502                                                         /*  N1-N2-X-Y or N2-N1-X-Y  */
8503                                                         /*  for Y, only the first constitute atom is considered  */
8504                                                         cp2 = AtomConnectData(&ap3->anchor->connect);
8505                                                         temp[3] = cp2[0];
8506                                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8507                                                                 continue;
8508                                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8509                                                                 goto panic;
8510                                                 }
8511                                         }
8512                                 }
8513                         }
8514                         /*  X-N1-N2-X dihedrals  */
8515                         /*  Y-N1-N2-anchor is allowed, but the force may be zero if the angle N1-N2-anchor is */
8516                         /*  close to 180 deg (e.g. in ferrocene, C-anchor-Fe-anchor dihedral should be k=0)  */
8517                         if (ap1->anchor == NULL) {
8518                                 ac1 = &ap1->connect;
8519                                 cn1 = ac1->count;
8520                         } else {
8521                                 ac1 = &ap1->anchor->connect;
8522                                 cn1 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8523                         }
8524                         if (ap2->anchor == NULL) {
8525                                 ac2 = &ap2->connect;
8526                                 cn2 = ac2->count;
8527                         } else {
8528                                 ac2 = &ap2->anchor->connect;
8529                                 cn2 = 1;  /*  Only the first constitute atom of pi-anchor is considered  */
8530                         }
8531                         temp[1] = n1;
8532                         temp[2] = n2;
8533                         cp1 = AtomConnectData(ac1);
8534                         cp2 = AtomConnectData(ac2);
8535                         for (j = 0; j < cn1; j++) {
8536                                 temp[0] = cp1[j];
8537                                 if (temp[0] == temp[2])
8538                                         continue;
8539                                 for (k = 0; k < cn2; k++) {
8540                                         temp[3] = cp2[k];
8541                                         if (temp[3] == temp[0] || temp[3] == temp[1])
8542                                                 continue;
8543                                         if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8544                                                 goto panic;
8545                                 }
8546                         }
8547                 }
8548         }
8549         
8550         if (angles != NULL) {
8551                 temp[0] = kInvalidIndex;
8552                 if (AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, temp) == NULL)
8553                         goto panic;
8554                 MoleculeAddAngles(mp, angles, NULL);
8555                 free(angles);
8556         }
8557         if (dihedrals != NULL) {
8558                 temp[0] = kInvalidIndex;
8559                 if (AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, temp) == NULL)
8560                         goto panic;
8561                 MoleculeAddDihedrals(mp, dihedrals, NULL);
8562                 free(dihedrals);
8563         }
8564
8565         MoleculeIncrementModifyCount(mp);
8566         mp->needsMDRebuild = 1;
8567         __MoleculeUnlock(mp);
8568
8569         return nbonds;
8570
8571   panic:
8572         __MoleculeUnlock(mp);
8573         Panic("Low memory while adding bonds");
8574         return -1;  /*  Not reached  */
8575 }
8576
8577 /*  Delete bonds  */
8578 /*  The deleted angles and dihedrals are stored in outRemoval.  */
8579 /*  (*outRemoval) is an array of integers, containing:
8580       [0..na*3-1]: the angle indices
8581       [na*3..na*3+nd*4-1]: the dihedral indices
8582           [na*3+nd*4..na*3+nd*4+ni*4-1]: the improper indices
8583     *outRemovedPos is an intgroup denoting the positions of the removed angles/dihedrals/impropers.
8584           the angle indices are included as they are,
8585       the dihedral indices are offset by ATOMS_MAX_NUMBER,
8586       the improper indices are offset by ATOMS_MAX_NUMBER*2.
8587     Note: the removed bond indices are not returned, because the caller should already know them.  */
8588 int
8589 MoleculeDeleteBonds(Molecule *mp, Int *bonds, IntGroup *where, Int **outRemoved, IntGroup **outRemovedPos)
8590 {
8591         Int i, j, n1, n2, nw;
8592         Int *ip, *jp, na, nd, ni;
8593         IntGroup *ag, *dg, *ig;
8594         Atom *ap;
8595         IntGroupIterator iter;
8596
8597         if (mp == NULL)
8598                 return 0;
8599         if (mp->noModifyTopology)
8600                 return -4;  /*  Prohibited operation  */
8601
8602         __MoleculeLock(mp);
8603
8604         /*  Update connects[]  */
8605         IntGroupIteratorInit(where, &iter);
8606         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8607                 n1 = mp->bonds[i * 2];
8608                 n2 = mp->bonds[i * 2 + 1];
8609                 ap = ATOM_AT_INDEX(mp->atoms, n1);
8610                 ip = AtomConnectData(&ap->connect);
8611                 for (j = 0; j < ap->connect.count; j++) {
8612                         if (ip[j] == n2) {
8613                                 AtomConnectDeleteEntry(&ap->connect, j);
8614                                 break;
8615                         }
8616                 }
8617                 ap = ATOM_AT_INDEX(mp->atoms, n2);
8618                 ip = AtomConnectData(&ap->connect);
8619                 for (j = 0; j < ap->connect.count; j++) {
8620                         if (ip[j] == n1) {
8621                                 AtomConnectDeleteEntry(&ap->connect, j);
8622                                 break;
8623                         }
8624                 }
8625         }
8626         
8627         /*  Remove bonds, angles, dihedrals, impropers  */
8628         ag = IntGroupNew();
8629         dg = ig = NULL;
8630         na = nd = ni = 0;
8631         
8632         nw = IntGroupGetCount(where);
8633         jp = (Int *)malloc(sizeof(Int) * nw * 2);
8634         j = 0;
8635         IntGroupIteratorReset(&iter);
8636         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8637                 jp[j++] = mp->bonds[i * 2];
8638                 jp[j++] = mp->bonds[i * 2 + 1];
8639         }
8640         IntGroupIteratorRelease(&iter);
8641
8642         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8643                 for (j = 0; j < nw; j++) {
8644                         n1 = jp[j * 2];
8645                         n2 = jp[j * 2 + 1];
8646                         if ((ip[0] == n1 && ip[1] == n2)
8647                                 || (ip[1] == n1 && ip[0] == n2)
8648                                 || (ip[1] == n1 && ip[2] == n2)
8649                                 || (ip[2] == n1 && ip[1] == n2)) {
8650                                 if (IntGroupAdd(ag, i, 1) != 0)
8651                                         goto panic;
8652                                 na++;
8653                                 break;
8654                         }
8655                 }
8656         }
8657         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8658                 for (j = 0; j < nw; j++) {
8659                         n1 = jp[j * 2];
8660                         n2 = jp[j * 2 + 1];
8661                         if ((ip[0] == n1 && ip[1] == n2)
8662                          || (ip[1] == n1 && ip[0] == n2)
8663                          || (ip[1] == n1 && ip[2] == n2)
8664                          || (ip[2] == n1 && ip[1] == n2)
8665                          || (ip[2] == n1 && ip[3] == n2)
8666                          || (ip[3] == n1 && ip[2] == n2)) {
8667                                 if (dg == NULL)
8668                                         dg = IntGroupNew();
8669                                 if (IntGroupAdd(dg, i, 1) != 0)
8670                                         goto panic;
8671                                 nd++;
8672                                 break;
8673                         }
8674                 }
8675         }
8676         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
8677                 for (j = 0; j < nw; j++) {
8678                         n1 = jp[j * 2];
8679                         n2 = jp[j * 2 + 1];
8680                         if ((ip[0] == n1 && ip[2] == n2)
8681                          || (ip[1] == n1 && ip[2] == n2)
8682                          || (ip[3] == n1 && ip[2] == n2)
8683                          || (ip[0] == n2 && ip[2] == n1)
8684                          || (ip[1] == n2 && ip[2] == n1)
8685                          || (ip[3] == n2 && ip[2] == n1)) {
8686                                 if (ig == NULL)
8687                                         ig = IntGroupNew();
8688                                 if (IntGroupAdd(ig, i, 1) != 0)
8689                                         goto panic;
8690                                 ni++;
8691                                 break;
8692                         }
8693                 }
8694         }
8695         free(jp);
8696         
8697         if (sRemoveElementsFromArrayAtPositions(mp->bonds, mp->nbonds, bonds, sizeof(Int) * 2, where) != 0)
8698                 goto panic;
8699         mp->nbonds -= IntGroupGetCount(where);
8700         if (mp->nbonds == 0) {
8701                 free(mp->bonds);
8702                 mp->bonds = NULL;
8703         }
8704         if (mp->bondOrders != NULL) {
8705                 if (sRemoveElementsFromArrayAtPositions(mp->bondOrders, mp->nbondOrders, NULL, sizeof(Double), where) != 0)
8706                         goto panic;
8707                 mp->nbondOrders -= IntGroupGetCount(where);
8708                 if (mp->nbondOrders == 0) {
8709                         free(mp->bondOrders);
8710                         mp->bondOrders = NULL;
8711                 }
8712         }
8713         if (na == 0 && nd == 0 && ni == 0)
8714                 ip = NULL;
8715         else
8716                 ip = (Int *)malloc(sizeof(Int) * (na * 3 + nd * 4 + ni * 4));
8717         if (na > 0)
8718                 MoleculeDeleteAngles(mp, ip, ag);
8719         if (nd > 0)
8720                 MoleculeDeleteDihedrals(mp, ip + na * 3, dg);
8721         if (ni > 0)
8722                 MoleculeDeleteImpropers(mp, ip + na * 3 + nd * 4, ig);
8723         if (ip != NULL) {
8724                 IntGroupOffset(dg, ATOMS_MAX_NUMBER);
8725                 IntGroupOffset(ig, ATOMS_MAX_NUMBER * 2);
8726                 IntGroupAddIntGroup(ag, dg);
8727                 IntGroupAddIntGroup(ag, ig);
8728                 IntGroupRelease(dg);
8729                 IntGroupRelease(ig);
8730         }
8731
8732         if (IntGroupGetCount(ag) == 0) {
8733                 IntGroupRelease(ag);
8734                 ag = NULL;
8735         }
8736         
8737         *outRemoved = ip;
8738         *outRemovedPos = ag;
8739
8740         MoleculeIncrementModifyCount(mp);
8741         mp->needsMDRebuild = 1;
8742         __MoleculeUnlock(mp);
8743
8744         return na * 3 + nd * 4 + ni * 4;
8745
8746   panic:
8747         __MoleculeUnlock(mp);
8748         Panic("Low memory while removing bonds");
8749         return -1;  /*  Not reached  */
8750 }
8751
8752 int
8753 MoleculeAssignBondOrders(Molecule *mp, const Double *orders, IntGroup *where)
8754 {
8755         Int i, j;
8756         IntGroupIterator iter;
8757         if (mp == NULL || orders == NULL || mp->nbonds == 0)
8758                 return 0;
8759         if (mp->noModifyTopology)
8760                 return -4;  /*  Prohibited operation  */
8761         if (mp->bondOrders == NULL) {
8762                 AssignArray(&mp->bondOrders, &mp->nbondOrders, sizeof(Double), mp->nbonds - 1, NULL);
8763                 memset(mp->bondOrders, 0, sizeof(Double) * mp->nbondOrders);
8764         }
8765         IntGroupIteratorInit(where, &iter);
8766         j = 0;
8767         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8768                 if (i >= mp->nbondOrders)
8769                         break;
8770                 mp->bondOrders[i] = orders[j++];
8771         }
8772         IntGroupIteratorRelease(&iter);
8773         return 0;
8774 }
8775
8776 int
8777 MoleculeGetBondOrders(Molecule *mp, Double *outOrders, IntGroup *where)
8778 {
8779         Int i, j;
8780         IntGroupIterator iter;
8781         if (mp == NULL || mp->nbonds == 0)
8782                 return 0;
8783         if (mp->bondOrders == NULL) {
8784                 /*  Returns all zero  */
8785                 i = IntGroupGetCount(where);
8786                 for (j = 0; j < i; j++)
8787                         outOrders[j] = 0.0;
8788         } else {
8789                 IntGroupIteratorInit(where, &iter);
8790                 j = 0;
8791                 while ((i = IntGroupIteratorNext(&iter)) >= 0) {
8792                         if (i < mp->nbondOrders)
8793                                 outOrders[j] = mp->bondOrders[i];
8794                         else outOrders[j] = 0.0;
8795                         j++;
8796                 }
8797         }
8798         return 0;
8799 }
8800
8801 int
8802 MoleculeAddAngles(Molecule *mp, const Int *angles, IntGroup *where)
8803 {
8804         int n1, nc;
8805         if (mp == NULL || angles == NULL)
8806                 return 0;
8807         if (mp->noModifyTopology)
8808                 return -4;  /*  Prohibited operation  */
8809
8810         __MoleculeLock(mp);
8811         if (where != NULL)
8812                 nc = IntGroupGetCount(where);
8813         else {
8814                 for (n1 = 0; angles[n1 * 3] >= 0; n1++)
8815                         ;
8816                 nc = n1;
8817         }
8818         if (nc > 0) {
8819                 n1 = mp->nangles;
8820                 if (AssignArray(&(mp->angles), &(mp->nangles), sizeof(Int) * 3, n1 + nc - 1, NULL) == NULL
8821                         || sInsertElementsToArrayAtPositions(mp->angles, n1, angles, nc, sizeof(Int) * 3, where) != 0) {
8822                         __MoleculeUnlock(mp);
8823                         Panic("Low memory while adding angles");
8824                 }
8825         }
8826         mp->needsMDRebuild = 1;
8827         __MoleculeUnlock(mp);
8828         return nc;
8829 }
8830
8831 int
8832 MoleculeDeleteAngles(Molecule *mp, Int *angles, IntGroup *where)
8833 {
8834         int nc;
8835         if (mp == NULL || where == NULL)
8836                 return 0;
8837         if (mp->noModifyTopology)
8838                 return -4;  /*  Prohibited operation  */
8839         __MoleculeLock(mp);
8840         if (sRemoveElementsFromArrayAtPositions(mp->angles, mp->nangles, angles, sizeof(Int) * 3, where) != 0) {
8841                 __MoleculeUnlock(mp);
8842                 Panic("Bad argument while deleting angles");
8843         }
8844         mp->nangles -= (nc = IntGroupGetCount(where));
8845         if (mp->nangles == 0) {
8846                 free(mp->angles);
8847                 mp->angles = NULL;
8848         }
8849         mp->needsMDRebuild = 1;
8850         __MoleculeUnlock(mp);
8851         return nc;
8852 }
8853
8854 int
8855 MoleculeAddDihedrals(Molecule *mp, const Int *dihedrals, IntGroup *where)
8856 {
8857         int n1, nc;
8858         if (mp == NULL || dihedrals == NULL)
8859                 return 0;
8860         if (mp->noModifyTopology)
8861                 return -4;  /*  Prohibited operation  */
8862         if (where != NULL)
8863                 nc = IntGroupGetCount(where);
8864         else {
8865                 for (n1 = 0; dihedrals[n1 * 4] >= 0; n1++)
8866                         ;
8867                 nc = n1;
8868         }
8869         if (nc <= 0)
8870                 return 0;
8871         n1 = mp->ndihedrals;
8872         __MoleculeLock(mp);
8873         if (AssignArray(&(mp->dihedrals), &(mp->ndihedrals), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8874         || sInsertElementsToArrayAtPositions(mp->dihedrals, n1, dihedrals, nc, sizeof(Int) * 4, where) != 0) {
8875                 __MoleculeUnlock(mp);
8876                 Panic("Low memory while adding dihedrals");
8877         }
8878         mp->needsMDRebuild = 1;
8879         __MoleculeUnlock(mp);
8880         return nc;
8881 }
8882
8883 int
8884 MoleculeDeleteDihedrals(Molecule *mp, Int *dihedrals, IntGroup *where)
8885 {       
8886         int nc;
8887         if (mp == NULL || where == NULL)
8888                 return 0;
8889         if (mp->noModifyTopology)
8890                 return -4;  /*  Prohibited operation  */
8891         __MoleculeLock(mp);
8892         if (sRemoveElementsFromArrayAtPositions(mp->dihedrals, mp->ndihedrals, dihedrals, sizeof(Int) * 4, where) != 0) {
8893                 __MoleculeUnlock(mp);
8894                 Panic("Internal error: bad argument while deleting dihedrals");
8895         }
8896         mp->ndihedrals -= (nc = IntGroupGetCount(where));
8897         if (mp->ndihedrals == 0) {
8898                 free(mp->dihedrals);
8899                 mp->dihedrals = NULL;
8900         }
8901         mp->needsMDRebuild = 1;
8902         __MoleculeUnlock(mp);
8903         return nc;
8904 }
8905
8906 int
8907 MoleculeAddImpropers(Molecule *mp, const Int *impropers, IntGroup *where)
8908 {
8909         int n1, nc;
8910         if (mp == NULL || impropers == NULL)
8911                 return 0;
8912         if (mp->noModifyTopology)
8913                 return -4;  /*  Prohibited operation  */
8914         if (where != NULL)
8915                 nc = IntGroupGetCount(where);
8916         else {
8917                 for (n1 = 0; impropers[n1 * 4] >= 0; n1++)
8918                         ;
8919                 nc = n1;
8920         }
8921         if (nc <= 0)
8922                 return 0;
8923         n1 = mp->nimpropers;
8924         __MoleculeLock(mp);
8925         if (AssignArray(&(mp->impropers), &(mp->nimpropers), sizeof(Int) * 4, n1 + nc - 1, NULL) == NULL
8926         || sInsertElementsToArrayAtPositions(mp->impropers, n1, impropers, nc, sizeof(Int) * 4, where) != 0) {
8927                 __MoleculeUnlock(mp);
8928                 Panic("Low memory while adding impropers");
8929         }
8930         mp->needsMDRebuild = 1;
8931         __MoleculeUnlock(mp);
8932         return nc;
8933 }
8934
8935 int
8936 MoleculeDeleteImpropers(Molecule *mp, Int *impropers, IntGroup *where)
8937 {
8938         int nc;
8939         if (mp == NULL || where == NULL)
8940                 return 0;
8941         if (mp->noModifyTopology)
8942                 return -4;  /*  Prohibited operation  */
8943         __MoleculeLock(mp);
8944         if (sRemoveElementsFromArrayAtPositions(mp->impropers, mp->nimpropers, impropers, sizeof(Int) * 4, where) != 0) {
8945                 __MoleculeUnlock(mp);
8946                 Panic("Internal error: bad argument while deleting impropers");
8947         }
8948         mp->nimpropers -= (nc = IntGroupGetCount(where));
8949         if (mp->impropers == NULL) {
8950                 free(mp->impropers);
8951                 mp->impropers = NULL;
8952         }
8953         __MoleculeUnlock(mp);
8954         return nc;
8955 }
8956
8957 int
8958 MoleculeLookupBond(Molecule *mp, Int n1, Int n2)
8959 {
8960         Int i, *ip;
8961         if (mp == NULL || mp->bonds == NULL)
8962                 return -1;
8963         for (i = 0, ip = mp->bonds; i < mp->nbonds; i++, ip += 2) {
8964                 if ((n1 == ip[0] && n2 == ip[1]) || (n1 == ip[1] && n2 == ip[0]))
8965                         return i;
8966         }
8967         return -1;
8968 }
8969
8970 int
8971 MoleculeLookupAngle(Molecule *mp, Int n1, Int n2, Int n3)
8972 {
8973         Int i, *ip;
8974         if (mp == NULL || mp->angles == NULL)
8975                 return -1;
8976         for (i = 0, ip = mp->angles; i < mp->nangles; i++, ip += 3) {
8977                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2]) ||
8978                         (n1 == ip[2] && n2 == ip[1] && n3 == ip[0]))
8979                         return i;
8980         }
8981         return -1;
8982 }
8983
8984 int
8985 MoleculeLookupDihedral(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
8986 {
8987         Int i, *ip;
8988         if (mp == NULL || mp->dihedrals == NULL)
8989                 return -1;
8990         for (i = 0, ip = mp->dihedrals; i < mp->ndihedrals; i++, ip += 4) {
8991                 if ((n1 == ip[0] && n2 == ip[1] && n3 == ip[2] && n4 == ip[3]) ||
8992                         (n1 == ip[3] && n2 == ip[2] && n3 == ip[1] && n4 == ip[0]))
8993                         return i;
8994         }
8995         return -1;
8996 }
8997
8998 int
8999 MoleculeLookupImproper(Molecule *mp, Int n1, Int n2, Int n3, Int n4)
9000 {
9001         Int i, *ip;
9002         if (mp == NULL || mp->impropers == NULL)
9003                 return -1;
9004         for (i = 0, ip = mp->impropers; i < mp->nimpropers; i++, ip += 4) {
9005                 if (n3 != ip[2])
9006                         continue;
9007                 if ((n1 == ip[0] && ((n2 == ip[1] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[1]))) ||
9008                         (n1 == ip[1] && ((n2 == ip[0] && n4 == ip[3]) || (n2 == ip[3] && n4 == ip[0]))) ||
9009                         (n1 == ip[3] && ((n2 == ip[0] && n4 == ip[1]) || (n2 == ip[1] && n4 == ip[0]))))
9010                         return i;
9011         }
9012         return -1;
9013 }
9014
9015 /*  Remove the bond at bondIndex and create two dummy atoms instead.
9016     The dummy atoms are placed at the end of atoms[], and the residue
9017         numbers are the same as the root atoms (i.e. the atoms to which
9018         the dummy atoms are connected). The indices are returned in
9019         dummyIndices[0,1].  */
9020 int
9021 MoleculeConvertBondToDummies(Molecule *mp, Int bondIndex, Int *dummyIndices)
9022 {
9023         Int roots[3], newBonds[5];
9024         Vector dr;
9025         Atom *rootp[2];
9026         Atom na[2], *nap;
9027         int i, natoms;
9028         IntGroup *ig;
9029         if (mp == NULL || mp->noModifyTopology)
9030                 return 0;
9031         if (bondIndex < 0 || bondIndex >= mp->nbonds)
9032                 return -1;
9033         roots[0] = mp->bonds[bondIndex * 2];
9034         roots[1] = mp->bonds[bondIndex * 2 + 1];
9035         roots[2] = kInvalidIndex;
9036         rootp[0] = ATOM_AT_INDEX(mp->atoms, roots[0]);
9037         rootp[1] = ATOM_AT_INDEX(mp->atoms, roots[1]);
9038         VecSub(dr, rootp[0]->r, rootp[1]->r);
9039         for (i = 0; i < 2; i++) {
9040                 float w;
9041                 nap = &na[i];
9042                 memmove(nap, rootp[i], sizeof(na));
9043                 nap->aname[0] = '*';
9044                 strcpy(nap->element, "Du");
9045                 nap->type = 0;
9046                 nap->charge = nap->weight = 0.0;
9047                 nap->atomicNumber = 0;
9048                 nap->connect.count = 0;
9049                 w = (i == 0 ? 0.4 : -0.4);
9050                 VecScaleInc(nap->r, dr, w);
9051                 VecZero(nap->v);
9052                 VecZero(nap->f);
9053                 nap->intCharge = 0;
9054                 nap->exflags = 0;
9055         }
9056
9057         /*  Expand atoms array and append the dummy atoms at the end  */
9058         __MoleculeLock(mp);
9059         natoms = mp->natoms;
9060         if (AssignArray(&(mp->atoms), &(mp->natoms), gSizeOfAtomRecord, natoms + 1, NULL) == NULL)
9061                 goto panic;
9062         memmove(&mp->atoms[natoms], na, gSizeOfAtomRecord * 2);
9063         dummyIndices[0] = natoms;
9064         dummyIndices[1] = natoms + 1;
9065
9066         /*  Remove the old bond and create new bonds  */
9067         ig = IntGroupNewWithPoints(bondIndex, 1, -1);
9068         if (ig == NULL)
9069                 goto panic;
9070         MoleculeDeleteBonds(mp, NULL, ig, NULL, NULL);
9071         IntGroupRelease(ig);
9072         newBonds[0] = roots[0];
9073         newBonds[1] = dummyIndices[0];
9074         newBonds[2] = roots[1];
9075         newBonds[3] = dummyIndices[1];
9076         newBonds[4] = kInvalidIndex;
9077         
9078         i = (MoleculeAddBonds(mp, 2, newBonds, NULL, 1) < 0 ? -1 : 0);
9079         mp->needsMDRebuild = 1;
9080         __MoleculeUnlock(mp);
9081         return i;
9082
9083 panic:
9084         __MoleculeUnlock(mp);
9085         Panic("Low memory during creating dummy atoms");
9086         return 1;
9087 }
9088
9089 /*  Remove two dummy atoms at dummyIndices[0], dummyIndices[1] and create
9090     a bond between the two root atoms. The value bondIndex is used as a
9091         hint where to store the new bond; if 0 <= bondIndex <= nbonds, then
9092         the new bond is stored as the bondIndex'th bond; otherwise, bondIndex
9093         is ignored and the new bond is stored at the end of bonds[].  */
9094 int
9095 MoleculeConvertDummiesToBond(Molecule *mp, Int bondIndex, Int *dummyIndices)
9096 {
9097         return 0;
9098 }
9099
9100 /*
9101 Int
9102 MoleculeReplaceAllAngles(Molecule *mol, Int nangles, const Int *angles, Int **outAngles)
9103 {
9104         Int n1, *np1;
9105         if (mol == NULL || mol->noModifyTopology)
9106                 return -1;
9107         n1 = mol->nangles;
9108         np1 = mol->angles;
9109         mol->nangles = 0;
9110         mol->angles = NULL;
9111         if (nangles > 0) {
9112                 __MoleculeLock(mol);
9113                 NewArray(&mol->angles, &mol->nangles, sizeof(Int) * 3, nangles);
9114                 memmove(mol->angles, angles, sizeof(Int) * 3 * nangles);
9115                 mol->needsMDRebuild = 1;
9116                 __MoleculeUnlock(mol);
9117         }
9118         *outAngles = np1;
9119         return n1;
9120 }
9121                                                 
9122 Int
9123 MoleculeReplaceAllDihedrals(Molecule *mol, Int ndihedrals, const Int *dihedrals, Int **outDihedrals)
9124 {
9125         Int n1, *np1;
9126         if (mol == NULL || mol->noModifyTopology)
9127                 return -1;
9128         n1 = mol->ndihedrals;
9129         np1 = mol->dihedrals;
9130         mol->ndihedrals = 0;
9131         mol->dihedrals = NULL;
9132         if (ndihedrals > 0) {
9133                 __MoleculeLock(mol);
9134                 NewArray(&mol->dihedrals, &mol->ndihedrals, sizeof(Int) * 4, ndihedrals);
9135                 memmove(mol->dihedrals, dihedrals, sizeof(Int) * 4 * ndihedrals);
9136                 mol->needsMDRebuild = 1;
9137                 __MoleculeUnlock(mol);
9138         }
9139         *outDihedrals = np1;
9140         return n1;
9141 }
9142
9143 Int
9144 MoleculeReplaceAllImpropers(Molecule *mol, Int nimpropers, const Int *impropers, Int **outImpropers)
9145 {
9146         Int n1, *np1;
9147         if (mol == NULL || mol->noModifyTopology)
9148                 return -1;
9149         n1 = mol->nimpropers;
9150         np1 = mol->impropers;
9151         mol->nimpropers = 0;
9152         mol->impropers = NULL;
9153         if (nimpropers > 0) {
9154                 __MoleculeLock(mol);
9155                 NewArray(&mol->impropers, &mol->nimpropers, sizeof(Int) * 4, nimpropers);
9156                 memmove(mol->impropers, impropers, sizeof(Int) * 4 * nimpropers);
9157                 mol->needsMDRebuild = 1;
9158                 __MoleculeUnlock(mol);
9159         }
9160         *outImpropers = np1;
9161         return n1;
9162 }
9163 */
9164
9165 Int
9166 MoleculeFindMissingAngles(Molecule *mol, Int **outAngles)
9167 {
9168         Int i, j, k, *ip;
9169         Atom *ap;
9170         Int nangles;
9171         Int *angles;
9172         
9173         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9174                 return 0;  /*  molecule is empty  */
9175         if (mol->noModifyTopology)
9176                 return -1;
9177         nangles = 0;
9178         angles = NULL;
9179         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
9180                 Int *cp = AtomConnectData(&ap->connect);
9181                 if (ap->anchor != NULL)
9182                         continue;
9183                 for (j = 0; j < ap->connect.count; j++) {
9184                         Int j0 = cp[j];
9185                         if (ATOM_AT_INDEX(mol->atoms, j0)->anchor != NULL)
9186                                 continue;
9187                         for (k = j + 1; k < ap->connect.count; k++) {
9188                                 Int k0 = cp[k];
9189                                 if (ATOM_AT_INDEX(mol->atoms, k0)->anchor != NULL)
9190                                         continue;
9191                                 if (MoleculeLookupAngle(mol, j0, i, k0) < 0) {
9192                                         ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9193                                         ip[0] = j0;
9194                                         ip[1] = i;
9195                                         ip[2] = k0;
9196                                 }
9197                         }
9198                 }
9199         }
9200         if (nangles > 0) {
9201                 ip = (Int *)AssignArray(&angles, &nangles, sizeof(Int) * 3, nangles, NULL);
9202                 ip[0] = -1;
9203                 nangles--;
9204         }
9205         if (outAngles != NULL)
9206                 *outAngles = angles;
9207         return nangles;
9208 }
9209
9210 Int
9211 MoleculeFindMissingDihedrals(Molecule *mol, Int **outDihedrals)
9212 {
9213         Int n1, n2, n3, n4, *ip, *cp2, *cp3;
9214         Atom *ap2, *ap3;
9215         Int ndihedrals;
9216         Int *dihedrals;
9217         
9218         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9219                 return 0;  /*  molecule is empty  */
9220         ndihedrals = 0;
9221         dihedrals = NULL;
9222         for (n2 = 0, ap2 = mol->atoms; n2 < mol->natoms; n2++, ap2 = ATOM_NEXT(ap2)) {
9223                 Int i1, i3, i4, *ip;
9224                 if (ap2->anchor != NULL)
9225                         continue;
9226                 cp2 = AtomConnectData(&ap2->connect);
9227                 for (i3 = 0; i3 < ap2->connect.count; i3++) {
9228                         n3 = cp2[i3];
9229                         if (n2 > n3)
9230                                 continue;
9231                         ap3 = ATOM_AT_INDEX(mol->atoms, n3);
9232                         if (ap3->anchor != NULL)
9233                                 continue;
9234                         cp3 = AtomConnectData(&ap3->connect);
9235                         for (i1 = 0; i1 < ap2->connect.count; i1++) {
9236                                 n1 = cp2[i1];
9237                                 if (n1 == n3)
9238                                         continue;
9239                                 if (ATOM_AT_INDEX(mol->atoms, n1)->anchor != NULL)
9240                                         continue;
9241                                 for (i4 = 0; i4 < ap3->connect.count; i4++) {
9242                                         n4 = cp3[i4];
9243                                         if (n2 == n4 || n1 == n4)
9244                                                 continue;
9245                                         if (ATOM_AT_INDEX(mol->atoms, n4)->anchor != NULL)
9246                                                 continue;
9247                                         if (MoleculeLookupDihedral(mol, n1, n2, n3, n4) < 0) {
9248                                                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9249                                                 ip[0] = n1;
9250                                                 ip[1] = n2;
9251                                                 ip[2] = n3;
9252                                                 ip[3] = n4;
9253                                         }
9254                                 }
9255                         }
9256                 }
9257         }
9258         if (ndihedrals > 0) {
9259                 ip = (Int *)AssignArray(&dihedrals, &ndihedrals, sizeof(Int) * 4, ndihedrals, NULL);
9260                 ip[0] = -1;
9261                 ndihedrals--;
9262         }
9263         if (outDihedrals != NULL)
9264                 *outDihedrals = dihedrals;
9265         return ndihedrals;
9266 }
9267
9268 Int
9269 MoleculeFindMissingImpropers(Molecule *mol, Int **outImpropers)
9270 {
9271         Int n1, n2, n3, n4, t1, t2, t3, t4, *ip, *cp;
9272         Parameter *par = mol->par;
9273         Atom *ap, *ap3;
9274         Int nimpropers;
9275         Int *impropers;
9276         
9277         if (mol == NULL || mol->natoms == 0 || mol->atoms == NULL)
9278                 return 0;  /*  molecule is empty  */
9279         if ((par == NULL || par->nimproperPars == 0) && (gBuiltinParameters == NULL || gBuiltinParameters->nimproperPars == 0))
9280                 return 0;  /*  No improper parameters are defined  */
9281         nimpropers = 0;
9282         impropers = NULL;
9283         ap = mol->atoms;
9284         for (n3 = 0, ap3 = ap; n3 < mol->natoms; n3++, ap3 = ATOM_NEXT(ap3)) {
9285                 Int i1, i2, i4, found, *ip;
9286                 t3 = ap3->type;
9287                 cp = AtomConnectData(&ap3->connect);
9288                 for (i1 = 0; i1 < ap3->connect.count; i1++) {
9289                         n1 = cp[i1];
9290                         t1 = ATOM_AT_INDEX(ap, n1)->type;
9291                         for (i2 = i1 + 1; i2 < ap3->connect.count; i2++) {
9292                                 n2 = cp[i2];
9293                                 t2 = ATOM_AT_INDEX(ap, n2)->type;
9294                                 for (i4 = i2 + 1; i4 < ap3->connect.count; i4++) {
9295                                         n4 = cp[i4];
9296                                         t4 = ATOM_AT_INDEX(ap, n4)->type;
9297                                         found = 0;
9298                                         if (ParameterLookupImproperPar(par, t1, t2, t3, t4, n1, n2, n3, n4, 0) != NULL)
9299                                                 found = 1;
9300                                         else if (ParameterLookupImproperPar(gBuiltinParameters, t1, t2, t3, t4, -1, -1, -1, -1, 0) != NULL)
9301                                                 found = 1;
9302                                         if (found && MoleculeLookupImproper(mol, n1, n2, n3, n4) < 0) {
9303                                                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9304                                                 ip[0] = n1;
9305                                                 ip[1] = n2;
9306                                                 ip[2] = n3;
9307                                                 ip[3] = n4;
9308                                         }
9309                                 }
9310                         }
9311                 }
9312         }
9313         if (nimpropers > 0) {
9314                 ip = (Int *)AssignArray(&impropers, &nimpropers, sizeof(Int) * 4, nimpropers, NULL);
9315                 ip[0] = -1;
9316                 nimpropers--;
9317         }
9318         if (outImpropers != NULL)
9319                 *outImpropers = impropers;
9320         return nimpropers;
9321 }
9322
9323 #pragma mark ====== Residues ======
9324
9325 void
9326 MoleculeCleanUpResidueTable(Molecule *mp)
9327 {
9328         int i, maxres;
9329         Atom *ap;
9330         if (mp == NULL || mp->natoms == 0)
9331                 return;
9332         maxres = 0;
9333         __MoleculeLock(mp);
9334         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9335                 if (ap->resSeq >= maxres)
9336                         maxres = ap->resSeq + 1;
9337                 if (ap->resSeq < mp->nresidues) {
9338                         if (strncmp(ap->resName, mp->residues[ap->resSeq], 4) != 0)
9339                                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9340                 } else {
9341                         AssignArray(&mp->residues, &mp->nresidues, 4, ap->resSeq, ap->resName);
9342                 }
9343         }
9344         if (maxres < mp->nresidues)
9345                 mp->nresidues = maxres;
9346         __MoleculeUnlock(mp);
9347 }
9348
9349 /*  Change the number of residues. If nresidues is greater than the current value,
9350     then the array mp->residues is expanded with null names. If nresidues is smaller
9351         than the current value, mp->nresidues is set to the smallest possible value
9352         that is no smaller than nresidues and larger than any of the resSeq values.  */
9353 int
9354 MoleculeChangeNumberOfResidues(Molecule *mp, int nresidues)
9355 {
9356         int n;
9357         if (mp == NULL)
9358                 return 0;
9359         if (mp->nresidues == nresidues)
9360                 return nresidues;
9361         else if (mp->nresidues < nresidues) {
9362                 __MoleculeLock(mp);
9363                 n = mp->nresidues;
9364                 AssignArray(&(mp->residues), &(mp->nresidues), 4, nresidues - 1, NULL);
9365                 while (n < nresidues)
9366                         mp->residues[n++][0] = 0;
9367                 __MoleculeUnlock(mp);
9368                 return nresidues;
9369         } else {
9370                 int i;
9371                 Atom *ap;
9372                 n = nresidues;
9373                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9374                         if (ap->resSeq >= n)
9375                                 n = ap->resSeq + 1;
9376                 }
9377                 mp->nresidues = n;
9378                 return n;
9379         }
9380 }
9381
9382 int
9383 MoleculeChangeResidueNumberWithArray(Molecule *mp, IntGroup *group, Int *resSeqs)
9384 {
9385         IntGroupIterator iter;
9386         int withArray, resSeq, maxSeq;
9387         int i, j;
9388         Atom *ap;
9389         
9390         /*  If LSB of resSeqs is 1, then a constant value is used for all specified atoms  */
9391         if (((uintptr_t)resSeqs & 1) == 0) {
9392                 withArray = 1;
9393                 resSeq = 0;
9394         } else {
9395                 withArray = 0;
9396                 resSeq = ((uintptr_t)resSeqs - 1) / 2;
9397         }
9398         
9399         IntGroupIteratorInit(group, &iter);
9400
9401         /*  Change resSeqs  */
9402         maxSeq = 0;
9403         j = 0;
9404         __MoleculeLock(mp);
9405         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9406                 ap = ATOM_AT_INDEX(mp->atoms, i);
9407                 if (withArray)
9408                         resSeq = resSeqs[j++];
9409                 if (resSeq > maxSeq)
9410                         maxSeq = resSeq;
9411                 ap->resSeq = resSeq;
9412         }
9413         __MoleculeUnlock(mp);
9414
9415         /*  Expand array if necessary  */
9416         if (maxSeq >= mp->nresidues)
9417                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9418
9419         /*  Synchronize resName and residues[]  */
9420         __MoleculeLock(mp);
9421         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9422                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9423                         continue;
9424                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9425         }
9426         IntGroupIteratorRelease(&iter);
9427         __MoleculeUnlock(mp);
9428         
9429         MoleculeIncrementModifyCount(mp);
9430         
9431         return 0;
9432 }
9433
9434 int
9435 MoleculeChangeResidueNumber(Molecule *mp, IntGroup *group, int resSeq)
9436 {
9437         return MoleculeChangeResidueNumberWithArray(mp, group, (Int *)(intptr_t)(resSeq * 2 + 1));
9438 }
9439
9440 /*  Offset the residue numbers by a certain amount. The argument nresidues, if non-negative,
9441     specifies the mp->nresidues after modifying the residue numbers.
9442         If all atoms are modified, then the table of residue names is also shifted. Otherwise,
9443         the table of residue names is not touched. */
9444 int
9445 MoleculeOffsetResidueNumbers(Molecule *mp, IntGroup *group, int offset, int nresidues)
9446 {
9447         int i, maxSeq, nmodatoms;
9448         Atom *ap;
9449         IntGroupIterator iter;
9450         IntGroupIteratorInit(group, &iter);
9451         maxSeq = 0;
9452         if (nresidues < 0)
9453                 nresidues = mp->nresidues;
9454         nmodatoms = 0;
9455         __MoleculeLock(mp);
9456         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
9457                 ap = ATOM_AT_INDEX(mp->atoms, i);
9458                 ap->resSeq += offset;
9459                 if (ap->resSeq < 0) {
9460                         /*  Bad argument; undo change and returns this index + 1  */
9461                         int bad_index = i;
9462                         ap->resSeq -= offset;
9463                         while ((i = IntGroupIteratorLast(&iter)) >= 0) {
9464                                 ap = ATOM_AT_INDEX(mp->atoms, i);
9465                                 ap->resSeq -= offset;
9466                         }
9467                         IntGroupIteratorRelease(&iter);
9468                         return bad_index + 1;
9469                 }
9470                 if (ap->resSeq > maxSeq)
9471                         maxSeq = ap->resSeq;
9472                 nmodatoms++;
9473         }
9474         if (maxSeq >= nresidues)
9475                 nresidues = maxSeq + 1;
9476         if (offset < 0 && nmodatoms == mp->natoms) {
9477                 /*  Shift the residue names downward  */
9478                 memmove(mp->residues, mp->residues - offset, 4 * (mp->nresidues + offset));
9479         }
9480         __MoleculeUnlock(mp);
9481         MoleculeChangeNumberOfResidues(mp, nresidues);
9482         if (offset > 0 && nmodatoms == mp->natoms) {
9483                 /*  Shift the residue names upward  */
9484                 __MoleculeLock(mp);
9485                 memmove(mp->residues + offset, mp->residues, 4 * (mp->nresidues - offset));
9486                 __MoleculeUnlock(mp);
9487         }
9488         IntGroupIteratorRelease(&iter);
9489
9490         MoleculeIncrementModifyCount(mp);
9491         
9492         return 0;
9493 }
9494
9495 /*  Change residue names for the specified residue numbers. Names is an array of
9496     chars containing argc*4 characters, and every 4 characters represent a
9497         residue name; characters '\x01'-'\x1f' are converted to '\0', which allow 
9498         names to be handled as a C string.  */
9499 int
9500 MoleculeChangeResidueNames(Molecule *mp, int argc, Int *resSeqs, char *names)
9501 {
9502         int i, maxSeq;
9503         Atom *ap;
9504         maxSeq = 0;
9505         for (i = 0; i < argc; i++) {
9506                 if (maxSeq < resSeqs[i])
9507                         maxSeq = resSeqs[i];
9508         }
9509         if (maxSeq >= mp->nresidues)
9510                 MoleculeChangeNumberOfResidues(mp, maxSeq + 1);
9511         __MoleculeLock(mp);
9512         for (i = 0; i < argc; i++) {
9513                 char *p = mp->residues[resSeqs[i]];
9514                 int j;
9515                 strncpy(p, names + i * 4, 4);
9516                 for (j = 0; j < 4; j++) {
9517                         if (p[j] >= 0 && p[j] < 0x20)
9518                                 p[j] = 0;
9519                 }
9520         }
9521         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9522                 strncpy(ap->resName, mp->residues[ap->resSeq], 4);
9523         }
9524         __MoleculeUnlock(mp);
9525
9526         MoleculeIncrementModifyCount(mp);
9527         
9528         return 0;
9529 }
9530
9531 /*  Returns the maximum residue number actually used  */
9532 int
9533 MoleculeMaximumResidueNumber(Molecule *mp, IntGroup *group)
9534 {
9535         int i, maxSeq;
9536         Atom *ap;
9537         maxSeq = -1;
9538         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9539                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9540                         continue;
9541                 if (ap->resSeq > maxSeq)
9542                         maxSeq = ap->resSeq;
9543         }
9544         return maxSeq;
9545 }
9546
9547 /*  Returns the minimum residue number actually used  */
9548 int
9549 MoleculeMinimumResidueNumber(Molecule *mp, IntGroup *group)
9550 {
9551         int i, minSeq;
9552         Atom *ap;
9553         minSeq = ATOMS_MAX_NUMBER;
9554         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9555                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9556                         continue;
9557                 if (ap->resSeq < minSeq)
9558                         minSeq = ap->resSeq;
9559         }
9560         return (minSeq == ATOMS_MAX_NUMBER ? -1 : minSeq);
9561 }
9562
9563 #pragma mark ====== Sort by Residues ======
9564
9565 static int
9566 sAtomSortComparator(const void *a, const void *b)
9567 {
9568         const Atom *ap, *bp;
9569         ap = *((const Atom **)a);
9570         bp = *((const Atom **)b);
9571         if (ap->resSeq == bp->resSeq) {
9572                 /*  Retain the original order (i.e. atom with larger pointer address is larger)  */
9573                 if (ap < bp)
9574                         return -1;
9575                 else if (ap > bp)
9576                         return 1;
9577                 else return 0;
9578         } else {
9579                 /*  Compare the residue sequence. However, residue sequence 0 is always larger.  */
9580                 if (ap->resSeq == 0)
9581                         return 1;
9582                 else if (bp->resSeq == 0)
9583                         return -1;
9584                 else if (ap->resSeq < bp->resSeq)
9585                         return -1;
9586                 else if (ap->resSeq > bp->resSeq)
9587                         return 1;
9588                 else return 0;
9589         }
9590 }
9591
9592 static void
9593 sMoleculeReorder(Molecule *mp)
9594 {
9595         int i, res, prevRes;
9596         Atom **apArray;
9597         Int *old2new;
9598         Atom *newAtoms;
9599         if (mp == NULL || mp->natoms <= 1)
9600                 return;
9601
9602         /*  Sort the atoms, bonds, etc. */
9603         apArray = (Atom **)calloc(sizeof(Atom *), mp->natoms);
9604         old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9605         newAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9606         if (apArray == NULL || old2new == NULL || newAtoms == NULL)
9607                 Panic("Low memory during reordering atoms");
9608         for (i = 0; i < mp->natoms; i++)
9609                 apArray[i] = ATOM_AT_INDEX(mp->atoms, i);
9610
9611         /*  Sort the atoms. Note: apArray is an array of "Pointer to Atom"  */
9612         qsort(apArray, mp->natoms, sizeof(Atom *), sAtomSortComparator);
9613         
9614         /*  Make a table of 'which atom becomes which'  */
9615         for (i = 0; i < mp->natoms; i++) {
9616                 int j = ((char *)(apArray[i]) - (char *)(mp->atoms)) / gSizeOfAtomRecord;
9617                 old2new[j] = i;
9618         }
9619         
9620         /*  Renumber the bonds, etc.  */
9621         for (i = 0; i < mp->nbonds * 2; i++) {
9622                 mp->bonds[i] = old2new[mp->bonds[i]];
9623         }
9624         for (i = 0; i < mp->nangles * 3; i++) {
9625                 mp->angles[i] = old2new[mp->angles[i]];
9626         }
9627         for (i = 0; i < mp->ndihedrals * 4; i++) {
9628                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9629         }
9630         for (i = 0; i < mp->nimpropers * 4; i++) {
9631                 mp->impropers[i] = old2new[mp->impropers[i]];
9632         }
9633         for (i = 0; i < mp->natoms; i++) {
9634                 Int *ip, j;
9635                 ip = AtomConnectData(&(apArray[i]->connect));
9636                 for (j = 0; j < apArray[i]->connect.count; j++, ip++)
9637                         *ip = old2new[*ip];
9638         }
9639         
9640         /*  Renumber the residues so that the residue numbers are contiguous  */
9641         res = prevRes = 0;
9642         for (i = 0; i < mp->natoms; i++) {
9643                 if (apArray[i]->resSeq == 0)
9644                         break;
9645                 if (apArray[i]->resSeq != prevRes) {
9646                         res++;
9647                         prevRes = apArray[i]->resSeq;
9648                         if (prevRes != res) {
9649                                 strncpy(mp->residues[res], mp->residues[prevRes], 4);
9650                         }
9651                 }
9652                 apArray[i]->resSeq = res;
9653         }
9654         mp->nresidues = res + 1;
9655
9656         /*  Sort the atoms and copy back to atoms[] */
9657         for (i = 0; i < mp->natoms; i++) {
9658                 memmove(ATOM_AT_INDEX(newAtoms, i), apArray[i], gSizeOfAtomRecord);
9659         }
9660         memmove(mp->atoms, apArray, gSizeOfAtomRecord * mp->natoms);
9661         
9662         /*  Free the locally allocated storage  */
9663         free(newAtoms);
9664         free(old2new);
9665         free(apArray);
9666 }
9667
9668 /*  Renumber atoms  */
9669 int
9670 MoleculeRenumberAtoms(Molecule *mp, const Int *new2old, Int *old2new_out, Int isize)
9671 {
9672         Int *old2new, i, j, retval;
9673         Atom *saveAtoms;
9674         if (mp == NULL)
9675                 return 0;
9676         if (mp->noModifyTopology)
9677                 return -1;
9678         if (old2new_out != NULL)
9679                 old2new = old2new_out;
9680         else
9681                 old2new = (Int *)calloc(sizeof(Int), mp->natoms);
9682         saveAtoms = (Atom *)calloc(gSizeOfAtomRecord, mp->natoms);
9683         if (old2new == NULL || saveAtoms == NULL)
9684                 Panic("Low memory during reordering atoms");
9685         memmove(saveAtoms, mp->atoms, gSizeOfAtomRecord * mp->natoms);
9686         __MoleculeLock(mp);
9687         for (i = 0; i < mp->natoms; i++)
9688                 old2new[i] = -1;
9689         for (i = 0; i < isize && i < mp->natoms; i++) {
9690                 j = new2old[i];
9691                 if (j < 0 || j >= mp->natoms) {
9692                         retval = 1; /* Out of range */
9693                         goto end;
9694                 }
9695                 if (old2new[j] != -1) {
9696                         retval = 2;  /*  Duplicate entry  */
9697                         goto end;
9698                 }
9699                 old2new[j] = i;
9700         }
9701         if (i < mp->natoms) {
9702                 for (j = 0; j < mp->natoms; j++) {
9703                         if (old2new[j] != -1)
9704                                 continue;
9705                         old2new[j] = i++;
9706                 }
9707         }
9708         if (i != mp->natoms) {
9709                 retval = 3;  /*  Internal inconsistency  */
9710                 goto end;
9711         }
9712
9713         /*  Renumber the bonds, etc.  */
9714         for (i = 0; i < mp->nbonds * 2; i++) {
9715                 mp->bonds[i] = old2new[mp->bonds[i]];
9716         }
9717         for (i = 0; i < mp->nangles * 3; i++) {
9718                 mp->angles[i] = old2new[mp->angles[i]];
9719         }
9720         for (i = 0; i < mp->ndihedrals * 4; i++) {
9721                 mp->dihedrals[i] = old2new[mp->dihedrals[i]];
9722         }
9723         for (i = 0; i < mp->nimpropers * 4; i++) {
9724                 mp->impropers[i] = old2new[mp->impropers[i]];
9725         }
9726         /*  Renumber the connection table and pi anchor table  */
9727         for (i = 0; i < mp->natoms; i++) {
9728                 Atom *ap = ATOM_AT_INDEX(saveAtoms, i);
9729                 Int *ip = AtomConnectData(&ap->connect);
9730                 for (j = 0; j < ap->connect.count; j++, ip++)
9731                         *ip = old2new[*ip];
9732                 if (ap->anchor != NULL) {
9733                         ip = AtomConnectData(&ap->anchor->connect);
9734                         for (j = 0; j < ap->anchor->connect.count; j++, ip++)
9735                                 *ip = old2new[*ip];
9736                 }
9737         }
9738         
9739         if (mp->par != NULL) {
9740                 /*  Renumber the parameters  */
9741                 int n;
9742                 for (j = kFirstParType; j <= kLastParType; j++) {
9743                         n = ParameterGetCountForType(mp->par, j);
9744                         for (i = 0; i < n; i++) {
9745                                 UnionPar *up = ParameterGetUnionParFromTypeAndIndex(mp->par, j, i);
9746                                 if (up != NULL)
9747                                         ParameterRenumberAtoms(j, up, mp->natoms, old2new);
9748                         }
9749                 }
9750         }
9751         
9752         /*  Renumber the atoms  */
9753         for (i = 0; i < mp->natoms; i++)
9754                 memmove(ATOM_AT_INDEX(mp->atoms, old2new[i]), ATOM_AT_INDEX(saveAtoms, i), gSizeOfAtomRecord);
9755         retval = 0;
9756         
9757         MoleculeIncrementModifyCount(mp);
9758         mp->needsMDRebuild = 1;
9759
9760   end:
9761         __MoleculeUnlock(mp);
9762         free(saveAtoms);
9763         if (old2new_out == NULL)
9764                 free(old2new);
9765         return retval;
9766 }
9767
9768 #pragma mark ====== Coordinate Transform ======
9769
9770 void
9771 MoleculeTransform(Molecule *mp, Transform tr, IntGroup *group)
9772 {
9773         int i;
9774         Atom *ap;
9775         Symop new_symop;
9776         Transform rtr, symtr;
9777         if (mp == NULL || tr == NULL)
9778                 return;
9779         TransformInvert(rtr, tr);
9780         __MoleculeLock(mp);
9781         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9782                 if (group == NULL || IntGroupLookup(group, i, NULL) != 0) {
9783                         TransformVec(&ap->r, tr, &ap->r);
9784                         if (!SYMOP_ALIVE(ap->symop))
9785                                 continue;
9786                         /*  Transform symop  */
9787                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9788                                 continue;
9789                         TransformMul(symtr, tr, symtr);
9790                         if (group == NULL || IntGroupLookup(group, ap->symbase, NULL) != 0)
9791                                 TransformMul(symtr, symtr, rtr);
9792                 } else {
9793                         if (!SYMOP_ALIVE(ap->symop))
9794                                 continue;
9795                         /*  Transform symop if the base atom is transformed  */
9796                         if (group != NULL && IntGroupLookup(group, ap->symbase, NULL) == 0)
9797                                 continue;
9798                         if (MoleculeGetTransformForSymop(mp, ap->symop, &symtr, 1) != 0)
9799                                 continue;
9800                         TransformMul(symtr, symtr, rtr);
9801                 }
9802                 if (MoleculeGetSymopForTransform(mp, symtr, &new_symop, 1) != 0)
9803                         continue;
9804                 ap->symop = new_symop;
9805         }
9806         mp->needsMDCopyCoordinates = 1;
9807         __MoleculeUnlock(mp);
9808         sMoleculeNotifyChangeAppearance(mp);
9809 }
9810
9811 /*
9812 void
9813 MoleculeMove(Molecule *mp, Transform tr, IntGroup *group)
9814 {
9815         int i;
9816         Atom *ap;
9817         if (mp == NULL || tr == NULL)
9818                 return;
9819         __MoleculeLock(mp);
9820         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9821                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9822                         continue;
9823                 TransformVec(&ap->r, tr, &ap->r);
9824         }
9825         mp->needsMDCopyCoordinates = 1;
9826         __MoleculeUnlock(mp);
9827         sMoleculeNotifyChangeAppearance(mp);
9828 }
9829 */
9830
9831 void
9832 MoleculeTranslate(Molecule *mp, const Vector *vp, IntGroup *group)
9833 {
9834         Transform tr;
9835         if (mp == NULL || vp == NULL)
9836                 return;
9837         memset(tr, 0, sizeof(tr));
9838         tr[0] = tr[4] = tr[8] = 1.0;
9839         tr[9] = vp->x;
9840         tr[10] = vp->y;
9841         tr[11] = vp->z;
9842         MoleculeTransform(mp, tr, group);
9843 }
9844
9845 void
9846 MoleculeRotate(Molecule *mp, const Vector *axis, Double angle, const Vector *center, IntGroup *group)
9847 {
9848         Transform tr;
9849         TransformForRotation(tr, axis, angle, center);
9850         MoleculeTransform(mp, tr, group);
9851 }
9852
9853 int
9854 MoleculeCenterOfMass(Molecule *mp, Vector *center, IntGroup *group)
9855 {
9856         int i;
9857         Atom *ap;
9858         Double w;
9859         if (mp == NULL || center == NULL)
9860                 return 1;
9861         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9862                 return 2;   /*  Empty molecule  */
9863         w = 0.0;
9864         center->x = center->y = center->z = 0.0;
9865         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9866                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9867                         continue;
9868                 VecScaleInc(*center, ap->r, ap->weight);
9869                 w += ap->weight;
9870         }
9871         if (w < 1e-7)
9872                 return 3;  /*  Atomic weights are not defined?  */
9873         w = 1.0 / w;
9874         VecScaleSelf(*center, w);
9875         return 0;
9876 }
9877
9878 int
9879 MoleculeBounds(Molecule *mp, Vector *min, Vector *max, IntGroup *group)
9880 {
9881         Vector vmin, vmax;
9882         int i;
9883         Atom *ap;
9884         if (mp == NULL)
9885                 return 1;
9886         if (mp->natoms == 0 || (group != NULL && IntGroupGetCount(group) == 0))
9887                 return 2;   /*  Empty molecule  */
9888         vmin.x = vmin.y = vmin.z = 1e50;
9889         vmax.x = vmax.y = vmax.z = -1e50;
9890         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
9891                 if (group != NULL && IntGroupLookup(group, i, NULL) == 0)
9892                         continue;
9893                 if (vmin.x > ap->r.x)
9894                         vmin.x = ap->r.x;
9895                 if (vmin.y > ap->r.y)
9896                         vmin.y = ap->r.y;
9897                 if (vmin.z > ap->r.z)
9898                         vmin.z = ap->r.z;
9899                 if (vmax.x < ap->r.x)
9900                         vmax.x = ap->r.x;
9901                 if (vmax.y < ap->r.y)
9902                         vmax.y = ap->r.y;
9903                 if (vmax.z < ap->r.z)
9904                         vmax.z = ap->r.z;
9905         }
9906         if (min != NULL)
9907                 *min = vmin;
9908         if (max != NULL)
9909                 *max = vmax;
9910         return 0;       
9911 }
9912
9913 #pragma mark ====== Measurements ======
9914
9915 Double
9916 MoleculeMeasureBond(Molecule *mp, const Vector *vp1, const Vector *vp2)
9917 {
9918         Vector r1, r2;
9919 /*      if (mp->is_xtal_coord) {
9920                 TransformVec(&r1, mp->cell->tr, vp1);
9921                 TransformVec(&r2, mp->cell->tr, vp2);
9922         } else */ {
9923                 r1 = *vp1;
9924                 r2 = *vp2;
9925         }
9926         VecDec(r1, r2);
9927         return VecLength(r1);
9928 }
9929
9930 Double
9931 MoleculeMeasureAngle(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3)
9932 {
9933         Vector r1, r2, r3;
9934         double w;
9935 /*      if (mp->is_xtal_coord) {
9936                 TransformVec(&r1, mp->cell->tr, vp1);
9937                 TransformVec(&r2, mp->cell->tr, vp2);
9938                 TransformVec(&r3, mp->cell->tr, vp3);
9939         } else */ {
9940                 r1 = *vp1;
9941                 r2 = *vp2;
9942                 r3 = *vp3;
9943         }
9944         VecDec(r1, r2);
9945         VecDec(r3, r2);
9946         w = VecLength(r1) * VecLength(r3);
9947         if (w < 1e-20)
9948                 return NAN;
9949         return acos(VecDot(r1, r3) / w) * kRad2Deg;
9950 }
9951
9952 Double
9953 MoleculeMeasureDihedral(Molecule *mp, const Vector *vp1, const Vector *vp2, const Vector *vp3, const Vector *vp4)
9954 {
9955         Vector r1, r2, r3, r4, r21, r32, r43, v1, v2, v3;
9956         double w1, w2, w3;
9957 /*      if (mp->is_xtal_coord) {
9958                 TransformVec(&r1, mp->cell->tr, vp1);
9959                 TransformVec(&r2, mp->cell->tr, vp2);
9960                 TransformVec(&r3, mp->cell->tr, vp3);
9961                 TransformVec(&r4, mp->cell->tr, vp4);
9962         } else */ {
9963                 r1 = *vp1;
9964                 r2 = *vp2;
9965                 r3 = *vp3;
9966                 r4 = *vp4;
9967         }
9968         VecSub(r21, r1, r2);
9969         VecSub(r32, r2, r3);
9970         VecSub(r43, r3, r4);
9971         VecCross(v1, r21, r32);
9972         VecCross(v2, r32, r43);
9973         VecCross(v3, r32, v1);
9974         w1 = VecLength(v1);
9975         w2 = VecLength(v2);
9976         w3 = VecLength(v3);
9977         if (w1 < 1e-10 || w2 < 1e-10 || w3 < 1e-10) {
9978                 return NAN;
9979         } else {
9980                 w1 = 1.0 / w1;
9981                 w2 = 1.0 / w2;
9982                 w3 = 1.0 / w3;
9983                 VecScaleSelf(v1, w1);
9984                 VecScaleSelf(v2, w2);
9985                 VecScaleSelf(v3, w3);
9986                 return -atan2(VecDot(v3, v2), VecDot(v1, v2)) * kRad2Deg;
9987         }
9988 }
9989
9990 #pragma mark ====== XtalCell Parameters ======
9991
9992 void
9993 MoleculeXtalToCartesian(Molecule *mp, Vector *dst, const Vector *src)
9994 {
9995         if (mp->cell != NULL) {
9996                 TransformVec(dst, mp->cell->tr, src);
9997         } else *dst = *src;
9998 }
9999
10000 void
10001 MoleculeCartesianToXtal(Molecule *mp, Vector *dst, const Vector *src)
10002 {
10003         if (mp->cell != NULL) {
10004                 TransformVec(dst, mp->cell->rtr, src);
10005         } else *dst = *src;
10006 }
10007
10008 int
10009 MoleculeCalculateCellFromAxes(XtalCell *cp, int calc_abc)
10010 {
10011         static Transform identityTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0};
10012         int n1, n2, n3;
10013         Vector *vp1, *vp2, *vp3;
10014         Vector v1, v2;
10015
10016         if (cp == NULL)
10017                 return 0;
10018         for (n1 = 0; n1 < 3; n1++) {
10019                 if (cp->flags[n1] != 0)
10020                         break;
10021         }
10022         if (n1 == 3) {
10023                 /*  All directions are non-periodic  */
10024                 memmove(&(cp->tr), &identityTransform, sizeof(Transform));
10025                 memmove(&(cp->rtr), &identityTransform, sizeof(Transform));
10026         } else {
10027                 n2 = (n1 + 1) % 3;
10028                 n3 = (n1 + 2) % 3;
10029                 vp1 = &(cp->axes[n1]);
10030                 vp2 = &(cp->axes[n2]);
10031                 vp3 = &(cp->axes[n3]);
10032                 cp->tr[n1*3] = vp1->x;
10033                 cp->tr[n1*3+1] = vp1->y;
10034                 cp->tr[n1*3+2] = vp1->z;
10035                 cp->tr[9] = cp->origin.x;
10036                 cp->tr[10] = cp->origin.y;
10037                 cp->tr[11] = cp->origin.z;
10038                 if (cp->flags[n2] == 0 || cp->flags[n3] == 0) {
10039                         /*  1-dimensional or 2-dimensional system  */
10040                         /*  Create "dummy" axes, so that transforms between internal and cartesian coordinates are
10041                          possible with a single matrix  */
10042                         if (cp->flags[n2] == 0 && cp->flags[n3] == 0) {
10043                                 /*  1-dimensional  */
10044                                 static Vector xvec = {1, 0, 0}, yvec = {0, 1, 0};
10045                                 VecCross(v1, *vp1, xvec);
10046                                 VecCross(v2, *vp1, yvec);
10047                                 if (VecLength2(v1) < VecLength2(v2))
10048                                         v1 = v2;
10049                                 VecCross(v2, *vp1, v1);
10050                                 if (NormalizeVec(&v1, &v1) || NormalizeVec(&v2, &v2))
10051                                         return -1;   /*  Non-regular transform  */
10052                         } else if (cp->flags[n2] == 0) {
10053                                 v2 = *vp3;
10054                                 VecCross(v1, v2, *vp1);
10055                                 if (NormalizeVec(&v1, &v1))
10056                                         return -1;  /*  Non-regular transform  */
10057                         } else {
10058                                 v1 = *vp2;
10059                                 VecCross(v2, *vp1, v1);
10060                                 if (NormalizeVec(&v2, &v2))
10061                                         return -1;  /*  Non-regular transform  */
10062                         }
10063                         cp->tr[n2*3] = v1.x;
10064                         cp->tr[n2*3+1] = v1.y;
10065                         cp->tr[n2*3+2] = v1.z;
10066                         cp->tr[n3*3] = v2.x;
10067                         cp->tr[n3*3+1] = v2.y;
10068                         cp->tr[n3*3+2] = v2.z;
10069                 } else {
10070                         VecCross(v1, *vp1, *vp2);
10071                         if (fabs(VecDot(v1, *vp3)) < 1e-7)
10072                                 return -1;  /*  Non-regular transform  */
10073                         cp->tr[n2*3] = vp2->x;
10074                         cp->tr[n2*3+1] = vp2->y;
10075                         cp->tr[n2*3+2] = vp2->z;
10076                         cp->tr[n3*3] = vp3->x;
10077                         cp->tr[n3*3+1] = vp3->y;
10078                         cp->tr[n3*3+2] = vp3->z;
10079                 }
10080         }
10081         if (TransformInvert(cp->rtr, cp->tr))
10082                 return -1;  /*  Non-regular transform  */
10083
10084         /*  Calculate the reciprocal cell parameters  */
10085         cp->rcell[0] = sqrt(cp->rtr[0] * cp->rtr[0] + cp->rtr[3] * cp->rtr[3] + cp->rtr[6] * cp->rtr[6]);
10086         cp->rcell[1] = sqrt(cp->rtr[1] * cp->rtr[1] + cp->rtr[4] * cp->rtr[4] + cp->rtr[7] * cp->rtr[7]);
10087         cp->rcell[2] = sqrt(cp->rtr[2] * cp->rtr[2] + cp->rtr[5] * cp->rtr[5] + cp->rtr[8] * cp->rtr[8]);
10088         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;
10089         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;
10090         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;
10091         
10092         if (calc_abc) {
10093                 /*  Calculate a, b, c, alpha, beta, gamma  */
10094                 cp->cell[0] = sqrt(cp->tr[0] * cp->tr[0] + cp->tr[1] * cp->tr[1] + cp->tr[2] * cp->tr[2]);
10095                 cp->cell[1] = sqrt(cp->tr[3] * cp->tr[3] + cp->tr[4] * cp->tr[4] + cp->tr[5] * cp->tr[5]);
10096                 cp->cell[2] = sqrt(cp->tr[6] * cp->tr[6] + cp->tr[7] * cp->tr[7] + cp->tr[8] * cp->tr[8]);
10097                 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;
10098                 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;
10099                 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;
10100         }
10101         return 0;
10102 }
10103
10104 void
10105 MoleculeSetCell(Molecule *mp, Double a, Double b, Double c, Double alpha, Double beta, Double gamma, int convertCoordinates)
10106 {
10107         XtalCell *cp;
10108         int i;
10109         Atom *ap;
10110         Transform cmat;
10111         if (mp == NULL)
10112                 return;
10113         __MoleculeLock(mp);
10114         memset(&cmat, 0, sizeof(Transform));
10115         if (mp->cell != NULL)
10116                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10117         else
10118                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10119         if (a == 0.0) {
10120                 if (mp->cell != NULL) {
10121                         free(mp->cell);
10122                         mp->needsMDRebuild = 1;
10123                 }
10124                 mp->cell = NULL;
10125         } else {
10126                 cp = mp->cell;
10127                 if (cp == NULL) {
10128                         cp = (XtalCell *)calloc(sizeof(XtalCell), 1);
10129                         if (cp == NULL)
10130                                 Panic("Low memory during setting cell parameters");
10131                         mp->cell = cp;
10132                         mp->needsMDRebuild = 1;
10133                 }
10134                 /*  alpha, beta, gamma are in degree  */
10135                 cp->cell[0] = a;
10136                 cp->cell[1] = b;
10137                 cp->cell[2] = c;
10138                 cp->cell[3] = alpha;
10139                 cp->cell[4] = beta;
10140                 cp->cell[5] = gamma;
10141                 if (fabs(alpha - 90) < 0.0001 && fabs(beta - 90) < 0.0001 && fabs(gamma - 90) > 0.0001) {
10142                         /*  c unique (hexagonal etc.)  */
10143                         Double cosa, cosb, sinb, cosg;
10144                         cosa = cos(alpha * kDeg2Rad);
10145                         cosb = cos(beta * kDeg2Rad);
10146                         sinb = sin(beta * kDeg2Rad);
10147                         cosg = cos(gamma * kDeg2Rad);
10148                         cp->axes[0].x = a * sinb;
10149                         cp->axes[0].y = 0;
10150                         cp->axes[0].z = a * cosb;
10151                         cp->axes[1].x = b * (cosg - cosa * cosb) / sinb;
10152                         cp->axes[1].z = b * cosa;
10153                         cp->axes[1].y = sqrt(b * b - cp->axes[1].x * cp->axes[1].x - cp->axes[1].z * cp->axes[1].z);
10154                         cp->axes[2].x = 0;
10155                         cp->axes[2].y = 0;
10156                         cp->axes[2].z = c;
10157                 } else {
10158                         /*  b unique  */
10159                         Double cosg, sing, cosa, cosb;
10160                         cosa = cos(alpha * kDeg2Rad);
10161                         cosb = cos(beta * kDeg2Rad);
10162                         cosg = cos(gamma * kDeg2Rad);
10163                         sing = sin(gamma * kDeg2Rad);
10164                         cp->axes[0].x = a * sing;
10165                         cp->axes[0].y = a * cosg;
10166                         cp->axes[0].z = 0;
10167                         cp->axes[1].x = 0;
10168                         cp->axes[1].y = b;
10169                         cp->axes[1].z = 0;
10170                         cp->axes[2].x = c * (cosb - cosa * cosg) / sing;
10171                         cp->axes[2].y = c * cosa;
10172                         cp->axes[2].z = sqrt(c * c - cp->axes[2].x * cp->axes[2].x - cp->axes[2].y * cp->axes[2].y);
10173                 }
10174                 cp->origin.x = cp->origin.y = cp->origin.z = 0.0;
10175                 cp->flags[0] = cp->flags[1] = cp->flags[2] = 1;
10176                 MoleculeCalculateCellFromAxes(cp, 0);
10177                 TransformMul(cmat, cp->tr, cmat);
10178         }
10179         
10180         /*  Update the coordinates (if requested)  */
10181         if (convertCoordinates) {
10182                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10183                         TransformVec(&(ap->r), cmat, &(ap->r));
10184                 }
10185         }
10186         
10187         /*  Update the anisotropic parameters  */
10188         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10189                 Aniso *anp = ap->aniso;
10190                 if (anp != NULL) {
10191                         MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10192                 }
10193         }
10194         __MoleculeUnlock(mp);
10195         sMoleculeNotifyChangeAppearance(mp);
10196 }
10197
10198 void
10199 MoleculeSetAniso(Molecule *mp, int n1, int type, Double x11, Double x22, Double x33, Double x12, Double x13, Double x23, const Double *sigmaptr)
10200 {
10201         Double d, dx;
10202         int u = 0;
10203         const Double log2 = 0.693147180559945;
10204         const Double pi22 = 19.7392088021787;  /* 2*pi**2 */
10205         Transform m1, m2;
10206         Aniso *anp;
10207         XtalCell *cp;
10208         Vector axis[3];
10209         Double val[3];
10210         if (mp == NULL || n1 < 0 || n1 >= mp->natoms)
10211                 return;
10212         anp = mp->atoms[n1].aniso;
10213         __MoleculeLock(mp);
10214         if (anp == NULL) {
10215                 anp = (Aniso *)calloc(sizeof(Aniso), 1);
10216                 if (anp == NULL) {
10217                         __MoleculeUnlock(mp);
10218                         Panic("Low memory during setting anisotropic atom parameters");
10219                 }
10220                 mp->atoms[n1].aniso = anp;
10221         }
10222         switch (type) {
10223                 case 1: d = 1; dx = 0.5; break;
10224                 case 2: d = log2; dx = log2; break;
10225                 case 3: d = log2; dx = log2 * 0.5; break;
10226                 case 4: u = 1; d = 0.25; dx = 0.25; break;
10227                 case 5: u = 1; d = 0.25; dx = 0.125; break;
10228                 case 8: u = 1; d = pi22; dx = pi22; break;
10229                 case 9: u = 1; d = pi22; dx = pi22 * 0.5; break;
10230                 case 10: d = pi22; dx = pi22; break;
10231                 default: d = dx = 1; break;
10232         }
10233         anp->bij[0] = x11 * d;
10234         anp->bij[1] = x22 * d;
10235         anp->bij[2] = x33 * d;
10236         anp->bij[3] = x12 * dx;
10237         anp->bij[4] = x13 * dx;
10238         anp->bij[5] = x23 * dx;
10239         if (sigmaptr != NULL) {
10240                 anp->has_bsig = 1;
10241                 anp->bsig[0] = sigmaptr[0] * d;
10242                 anp->bsig[1] = sigmaptr[1] * d;
10243                 anp->bsig[2] = sigmaptr[2] * d;
10244                 anp->bsig[3] = sigmaptr[3] * dx;
10245                 anp->bsig[4] = sigmaptr[4] * dx;
10246                 anp->bsig[5] = sigmaptr[5] * dx;
10247         } else {
10248                 anp->has_bsig = 0;
10249                 anp->bsig[0] = anp->bsig[1] = anp->bsig[2] = anp->bsig[3] = anp->bsig[4] = anp->bsig[5] = 0.0;
10250         }
10251         cp = mp->cell;
10252         if (cp != NULL && u == 1) {
10253                 anp->bij[0] *= cp->rcell[0] * cp->rcell[0];
10254                 anp->bij[1] *= cp->rcell[1] * cp->rcell[1];
10255                 anp->bij[2] *= cp->rcell[2] * cp->rcell[2];
10256                 anp->bij[3] *= cp->rcell[0] * cp->rcell[1]; /* * cos(cp->rcell[5] * kDeg2Rad); */
10257                 anp->bij[4] *= cp->rcell[2] * cp->rcell[0]; /* * cos(cp->rcell[3] * kDeg2Rad); */
10258                 anp->bij[5] *= cp->rcell[1] * cp->rcell[2]; /* * cos(cp->rcell[4] * kDeg2Rad); */
10259                 if (sigmaptr != NULL) {
10260                         anp->bsig[0] *= cp->rcell[0] * cp->rcell[0];
10261                         anp->bsig[1] *= cp->rcell[1] * cp->rcell[1];
10262                         anp->bsig[2] *= cp->rcell[2] * cp->rcell[2];
10263                         anp->bsig[3] *= cp->rcell[0] * cp->rcell[1];
10264                         anp->bsig[4] *= cp->rcell[2] * cp->rcell[0];
10265                         anp->bsig[5] *= cp->rcell[1] * cp->rcell[2];
10266                 }
10267         }
10268         
10269         /*  Calculate the principal axes (in Cartesian coordinates)  */
10270         /*  The principal axes are the eigenvectors of matrix At(B^-1)A, where
10271                 B is (bij) and A is the reciprocal conversion matrix, i.e. x = Az
10272                 in which x and z are the crystal-space and cartesian coordinates. */
10273         m1[0] = anp->bij[0] / pi22;
10274         m1[4] = anp->bij[1] / pi22;
10275         m1[8] = anp->bij[2] / pi22;
10276         m1[1] = m1[3] = anp->bij[3] / pi22;
10277         m1[2] = m1[6] = anp->bij[4] / pi22;
10278         m1[5] = m1[7] = anp->bij[5] / pi22;
10279         MatrixInvert(m1, m1);
10280         if (cp != NULL) {
10281                 memmove(m2, cp->rtr, sizeof(Mat33));
10282                 MatrixMul(m1, m1, m2);
10283                 MatrixTranspose(m2, m2);
10284                 MatrixMul(m1, m2, m1);
10285         }
10286         MatrixSymDiagonalize(m1, val, axis);
10287         for (u = 0; u < 3; u++) {
10288         anp->eigval[u] = val[u];
10289                 if (val[u] < 0) {
10290                         fprintf(stderr, "Non-positive definite thermal parameters for atom %.4s\n", mp->atoms[n1].aname);
10291                         val[u] = 0.001;
10292                 } else {
10293                         val[u] = 1 / sqrt(val[u]);
10294                 }
10295                 anp->pmat[u*3] = axis[u].x * val[u];
10296                 anp->pmat[u*3+1] = axis[u].y * val[u];
10297                 anp->pmat[u*3+2] = axis[u].z * val[u];
10298         }
10299         __MoleculeUnlock(mp);
10300 }
10301
10302 /*  Set the anisotropic parameter for atom idx according to the symop. If symop is not alive, nothing is done. */
10303 void
10304 MoleculeSetAnisoBySymop(Molecule *mp, int idx)
10305 {
10306         Atom *ap, *ap2;
10307         Transform t1, t2;
10308         if (mp == NULL || idx < 0 || idx >= mp->natoms)
10309                 return;
10310         ap = ATOM_AT_INDEX(mp->atoms, idx);
10311         if (!SYMOP_ALIVE(ap->symop))
10312                 return;
10313         ap2 = ATOM_AT_INDEX(mp->atoms, ap->symbase);
10314         if (ap2->aniso == NULL) {
10315                 if (ap->aniso != NULL) {
10316                         free(ap->aniso);
10317                         ap->aniso = NULL;
10318                 }
10319                 return;
10320         }
10321         if (ap->aniso == NULL)
10322                 ap->aniso = (Aniso *)calloc(sizeof(Aniso), 1);
10323         if (ap->symop.sym == 0 || ap->symop.sym >= mp->nsyms) {
10324                 /*  Just copy the aniso parameters  */
10325                 memmove(ap->aniso, ap2->aniso, sizeof(Aniso));
10326                 return;
10327         }
10328         memmove(t1, SYMMETRY_AT_INDEX(mp->syms, ap->symop.sym), sizeof(Transform));
10329         t1[9] = t1[10] = t1[11] = 0.0;
10330         memset(t2, 0, sizeof(Transform));
10331         t2[0] = ap2->aniso->bij[0];
10332         t2[4] = ap2->aniso->bij[1];
10333         t2[8] = ap2->aniso->bij[2];
10334         t2[1] = t2[3] = ap2->aniso->bij[3];
10335         t2[2] = t2[6] = ap2->aniso->bij[4];
10336         t2[5] = t2[7] = ap2->aniso->bij[5];
10337         TransformMul(t2, t1, t2);
10338         TransformInvert(t1, t1);
10339         TransformMul(t2, t2, t1);
10340         MoleculeSetAniso(mp, idx, 0, t2[0], t2[4], t2[8], t2[1], t2[2], t2[5], (ap2->aniso->has_bsig ? ap2->aniso->bsig : NULL));
10341 }
10342
10343 int
10344 MoleculeSetPeriodicBox(Molecule *mp, const Vector *ax, const Vector *ay, const Vector *az, const Vector *ao, const char *periodic, int convertCoordinates)
10345 {
10346         static Vector zeroVec = {0, 0, 0};
10347         XtalCell b;
10348         Transform cmat;
10349         int i, n;
10350         Atom *ap;
10351         if (mp == NULL)
10352                 return 0;
10353         if (mp->cell != NULL)
10354                 memmove(&cmat, &(mp->cell->rtr), sizeof(Transform));
10355         else
10356                 memmove(&cmat, &gIdentityTransform, sizeof(Transform));
10357         if (ax == NULL) {
10358                 if (mp->cell != NULL) {
10359                         free(mp->cell);
10360                         mp->needsMDRebuild = 1;
10361                 }
10362                 mp->cell = NULL;
10363                 return 0;
10364         }       
10365         memset(&b, 0, sizeof(b));
10366         b.axes[0] = (ax != NULL ? *ax : zeroVec);
10367         b.axes[1] = (ay != NULL ? *ay : zeroVec);
10368         b.axes[2] = (az != NULL ? *az : zeroVec);
10369         b.origin = *ao;
10370         memmove(b.flags, periodic, 3);
10371         if (MoleculeCalculateCellFromAxes(&b, 1) < 0)
10372                 return -1;
10373         __MoleculeLock(mp);
10374         if (mp->cell == NULL) {
10375                 mp->needsMDRebuild = 1;
10376         } else {
10377                 if (mp->cell->has_sigma) {
10378                         /*  Keep the sigma  */
10379                         b.has_sigma = 1;
10380                         memmove(b.cellsigma, mp->cell->cellsigma, sizeof(mp->cell->cellsigma));
10381                 }
10382                 if ((b.flags[0] != mp->cell->flags[0]) || (b.flags[1] != mp->cell->flags[1]) || (b.flags[2] != mp->cell->flags[2])) {
10383                         mp->needsMDRebuild = 1;
10384                 }
10385                 free(mp->cell);
10386         }
10387         mp->cell = (XtalCell *)calloc(sizeof(XtalCell), 1);
10388         if (mp->cell != NULL) {
10389                 memmove(mp->cell, &b, sizeof(XtalCell));
10390                 TransformMul(cmat, b.tr, cmat);
10391                 /*  Update the coordinates (if requested)  */
10392                 if (convertCoordinates) {
10393                         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10394                                 TransformVec(&(ap->r), cmat, &(ap->r));
10395                         }
10396                 }
10397                 
10398                 /*  Update the anisotropic parameters  */
10399                 for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10400                         Aniso *anp = ap->aniso;
10401                         if (anp != NULL) {
10402                                 MoleculeSetAniso(mp, i, 0, anp->bij[0], anp->bij[1], anp->bij[2], anp->bij[3], anp->bij[4], anp->bij[5], anp->bsig);
10403                         }
10404                 }
10405                 n = 0;
10406         } else n = -2;  /*  Out of memory  */
10407         __MoleculeUnlock(mp);
10408         sMoleculeNotifyChangeAppearance(mp);
10409         return n;
10410 }
10411
10412 #pragma mark ====== Fragment manipulation ======
10413
10414 static void
10415 sMoleculeFragmentSub(Molecule *mp, int idx, IntGroup *result, IntGroup *exatoms)
10416 {
10417         Atom *ap;
10418         Int i, *cp, idx2;
10419         if (exatoms != NULL && IntGroupLookup(exatoms, idx, NULL))
10420                 return;
10421         IntGroupAdd(result, idx, 1);
10422         ap = ATOM_AT_INDEX(mp->atoms, idx);
10423         cp = AtomConnectData(&ap->connect);
10424         for (i = 0; i < ap->connect.count; i++) {
10425                 idx2 = cp[i];
10426                 if (IntGroupLookup(result, idx2, NULL))
10427                         continue;
10428                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, idx2)->anchor != NULL)
10429                         continue;  /*  bond between two pi_anchors is ignored  */
10430                 sMoleculeFragmentSub(mp, idx2, result, exatoms);
10431         }
10432         if (ap->anchor != NULL) {
10433                 cp = AtomConnectData(&ap->anchor->connect);
10434                 for (i = 0; i < ap->anchor->connect.count; i++) {
10435                         idx2 = cp[i];
10436                         if (IntGroupLookup(result, idx2, NULL))
10437                                 continue;
10438                         sMoleculeFragmentSub(mp, idx2, result, exatoms);
10439                 }
10440         }
10441 }
10442
10443 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10444     not containing the atoms in exatoms  */
10445 IntGroup *
10446 MoleculeFragmentExcludingAtomGroup(Molecule *mp, int n1, IntGroup *exatoms)
10447 {
10448         IntGroup *result;
10449         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10450                 return NULL;
10451         result = IntGroupNew();
10452         sMoleculeFragmentSub(mp, n1, result, exatoms);
10453         return result;
10454 }
10455
10456 /*  The molecular fragment (= interconnected atoms) containing the atom n1 and
10457     not containing the atoms n2, n3, ... (terminated by -1)  */
10458 IntGroup *
10459 MoleculeFragmentExcludingAtoms(Molecule *mp, int n1, int argc, int *argv)
10460 {
10461         int i;
10462         IntGroup *exatoms, *result;
10463         if (mp == NULL || mp->natoms == 0 || n1 < 0 || n1 >= mp->natoms)
10464                 return NULL;
10465         exatoms = IntGroupNew();
10466         for (i = 0; i < argc; i++)
10467                 IntGroupAdd(exatoms, argv[i], 1);
10468         result = IntGroupNew();
10469         sMoleculeFragmentSub(mp, n1, result, exatoms);
10470         IntGroupRelease(exatoms);
10471         return result;
10472 }
10473
10474 /*  The molecular fragment (= interconnected atoms) containing the atoms in inatoms and
10475     not containing the atoms in exatoms  */
10476 IntGroup *
10477 MoleculeFragmentWithAtomGroups(Molecule *mp, IntGroup *inatoms, IntGroup *exatoms)
10478 {
10479         IntGroupIterator iter;
10480         IntGroup *result;
10481         int i;
10482         if (mp == NULL || mp->natoms == 0 || inatoms == NULL || IntGroupGetCount(inatoms) == 0)
10483                 return NULL;
10484         IntGroupIteratorInit(inatoms, &iter);
10485         result = IntGroupNew();
10486         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
10487                 sMoleculeFragmentSub(mp, i, result, exatoms);
10488         }
10489         IntGroupIteratorRelease(&iter);
10490         return result;
10491 }
10492
10493 /*  Returns non-zero if the given group is 'detachable' in the molecule, i.e. the
10494     group is bound to the rest of the molecule via only one bond.
10495         If the result is true, then the atoms belonging to the (only) bond are returned
10496         in *n1 and *n2, *n1 being the atom belonging to the fragment. The pointers n1
10497         and n2 can be NULL, if those informations are not needed.  */
10498 int
10499 MoleculeIsFragmentDetachable(Molecule *mp, IntGroup *group, int *n1, int *n2)
10500 {
10501         Int i, i1, i2, j, k, bond_count, nval1, nval2, *cp;
10502         Atom *ap;
10503         if (mp == NULL || mp->natoms == 0 || group == NULL)
10504                 return 0;  /*  Invalid arguments  */
10505         bond_count = 0;
10506         for (i = 0; (i1 = IntGroupGetStartPoint(group, i)) >= 0; i++) {
10507                 i2 = IntGroupGetEndPoint(group, i);
10508                 for (j = i1; j < i2; j++) {
10509                         if (j < 0 || j >= mp->natoms)
10510                                 return 0;  /*  Invalid atom group  */
10511                         ap = ATOM_AT_INDEX(mp->atoms, j);
10512                         cp = AtomConnectData(&ap->connect);
10513                         for (k = 0; k < ap->connect.count; k++) {
10514                                 if (ap->anchor != NULL && ATOM_AT_INDEX(mp->atoms, cp[k])->anchor != NULL)
10515                                         continue;  /*  Ignore bond between two pi_anchors  */
10516                                 if (IntGroupLookup(group, cp[k], NULL) == 0) {
10517                                         bond_count++;
10518                                         nval1 = j;
10519                                         nval2 = cp[k];
10520                                         if (bond_count > 1)
10521                                                 return 0;  /*  Too many bonds  */
10522                                 }
10523                         }
10524                         if (ap->anchor != NULL) {
10525                                 cp = AtomConnectData(&ap->anchor->connect);
10526                                 for (k = 0; k < ap->anchor->connect.count; k++) {
10527                                         if (IntGroupLookup(group, cp[k], NULL) == 0) {
10528                                                 bond_count++;
10529                                                 nval1 = j;
10530                                                 nval2 = cp[k];
10531                                                 if (bond_count > 1)
10532                                                         return 0;  /*  Too many bonds  */
10533                                         }
10534                                 }                                       
10535                         }
10536                 }
10537         }
10538         if (bond_count == 1) {
10539                 if (n1 != NULL)
10540                         *n1 = nval1;
10541                 if (n2 != NULL)
10542                         *n2 = nval2;
10543                 return 1;
10544         } else {
10545                 return 0;
10546         }       
10547 }
10548
10549 /*  Returns non-zero if the given group is 'rotatable' in the molecule. The group
10550     is said to be 'rotatable' when either of the following conditions are met; (1)
10551         the group is detachable, or (2) the group consists of two bonded atoms that define
10552         a detachable fragment. If it is rotatable, the group to rotate is returned to rotGroup
10553         (either a new IntGroup or 'group' with incremented reference count; thus the caller
10554         is responsible for releasing the returned value).  */
10555 int
10556 MoleculeIsFragmentRotatable(Molecule *mp, IntGroup *group, int *n1, int *n2, IntGroup **rotGroup)
10557 {
10558         int i1, i2;
10559         if (MoleculeIsFragmentDetachable(mp, group, n1, n2)) {
10560                 if (rotGroup != NULL) {
10561                         IntGroupRetain(group);
10562                         *rotGroup = group;
10563                 }
10564                 return 1;
10565         }
10566         if (group != NULL && IntGroupGetCount(group) == 2) {
10567                 i1 = IntGroupGetNthPoint(group, 0);
10568                 i2 = IntGroupGetNthPoint(group, 1);
10569                 if (MoleculeAreAtomsConnected(mp, i1, i2)) {
10570                         IntGroup *frag = MoleculeFragmentExcludingAtoms(mp, i2, 1, &i1);
10571                         if (frag == NULL)
10572                                 return 0;
10573                         i1 = MoleculeIsFragmentDetachable(mp, frag, n1, n2);
10574                         if (i1 == 0) {
10575                                 IntGroupRelease(frag);
10576                                 if (rotGroup != NULL)
10577                                         *rotGroup = NULL;
10578                                 return 0;
10579                         }
10580                         if (rotGroup != NULL)
10581                                 *rotGroup = frag;
10582                         else if (frag != NULL)
10583                                 IntGroupRelease(frag);
10584                         return i1;
10585                 }
10586         }
10587         return 0;
10588 }
10589
10590 #pragma mark ====== Multiple frame ======
10591
10592 int
10593 MoleculeGetNumberOfFrames(Molecule *mp)
10594 {
10595         if (mp == NULL)
10596                 return 0;
10597         if (mp->nframes <= 0) {
10598                 /*  Recalculate  */
10599                 int i, n;
10600                 Atom *ap;
10601                 for (i = n = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10602                         if (ap->nframes > n)
10603                                 n = ap->nframes;
10604                 }
10605                 if (n == 0)
10606                         n = 1;
10607                 mp->nframes = n;
10608         }
10609         return mp->nframes;
10610 }
10611
10612 int
10613 MoleculeInsertFrames(Molecule *mp, IntGroup *group, const Vector *inFrame, const Vector *inFrameCell)
10614 {
10615         int i, j, count, n_new, n_old, natoms, exframes, last_inserted;
10616         Vector *tempv, *vp;
10617         Atom *ap;
10618         MolProp *prp;
10619         Double *dp;
10620         
10621         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(group)) <= 0)
10622                 return -1;
10623
10624         n_old = MoleculeGetNumberOfFrames(mp);
10625         n_new = n_old + count;
10626         last_inserted = IntGroupGetNthPoint(group, count - 1);
10627         if (n_new <= last_inserted) {
10628                 exframes = last_inserted - n_new + 1;  /*  number of extra frames that will be silently inserted  */
10629                 n_new += exframes;
10630         } else exframes = 0;
10631
10632         tempv = (Vector *)malloc(sizeof(Vector) * n_new * 4);  /*  "*4" for handling cells  */
10633         if (tempv == NULL)
10634                 return -1;
10635
10636         __MoleculeLock(mp);
10637
10638         /*  Copy back the current coordinates  */
10639         /*  No change in the current coordinates, but the frame buffer is updated  */
10640         MoleculeSelectFrame(mp, mp->cframe, 1); 
10641         
10642         /*  Expand ap->frames for all atoms  */
10643         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10644                 Int n = ap->nframes;
10645                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), n_new - 1, NULL);
10646                 if (ap->frames == NULL) {
10647                         __MoleculeUnlock(mp);
10648                         return -1;
10649                 }
10650                 for (j = n; j < n_new; j++)
10651                         ap->frames[j] = ap->r;
10652         }
10653         if (mp->cell != NULL) {
10654                 j = mp->nframe_cells;
10655                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, n_new - 1, NULL);
10656                 for (i = j; i < n_new; i++) {
10657                         /*  Set the current cell parameters to the expanded frames  */
10658                         mp->frame_cells[i * 4] = mp->cell->axes[0];
10659                         mp->frame_cells[i * 4 + 1] = mp->cell->axes[1];
10660                         mp->frame_cells[i * 4 + 2] = mp->cell->axes[2];
10661                         mp->frame_cells[i * 4 + 3] = mp->cell->origin;
10662                 }
10663         }
10664         
10665         /*  Expand propvals for all properties  */
10666         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
10667                 dp = (Double *)realloc(prp->propvals, sizeof(Double) * n_new);
10668                 if (dp == NULL) {
10669                         __MoleculeUnlock(mp);
10670                         return -1;
10671                 }
10672                 for (j = n_old; j < n_new; j++)
10673                         dp[j] = 0.0;
10674                 prp->propvals = dp;
10675         }
10676         
10677         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10678         /*  s = t = 0,  */
10679         /*  tempv[0..n0-1] <- ap[0..n0-1], s += n0,
10680             tempv[n0..n1-1] <- inFrame[0..(n1-n0-1)], t += n1-n0,
10681                 tempv[n1..n2-1] <- ap[s..s+(n2-n1-1)], s += n2-n1,
10682                 tempv[n2..n3-1] <- inFrame[t..t+(n3-n2-1)], t += n3-n2,
10683                 ...
10684                 tempv[nl..n_new-1] <- ap[s..s+(n_new-nl-1)], s += n_new-nl
10685                 At last, s will become n_old and t will become count.  */
10686         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10687                 int s, t, ns, ne, mult;
10688                 Vector cr;
10689                 ne = s = t = 0;
10690                 if (i == mp->natoms) {
10691                         if (mp->cell == NULL || mp->frame_cells == NULL)
10692                                 continue;
10693                         vp = mp->frame_cells;
10694                         mult = 4;
10695                 } else if (i < mp->natoms) {
10696                         cr = ap->r;
10697                         vp = ap->frames;
10698                         mult = 1;
10699                 } else {
10700                         dp = prp->propvals;
10701                 }
10702                 for (j = 0; (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10703                         if (ns > ne) {
10704                                 if (i <= mp->natoms)
10705                                         memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (ns - ne));
10706                                 else
10707                                         memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (ns - ne)); 
10708                                 s += ns - ne;
10709                         }
10710                         ne = IntGroupGetEndPoint(group, j);
10711                         while (ns < ne) {
10712                                 if (i == mp->natoms) {
10713                                         if (inFrameCell != NULL) {
10714                                                 tempv[ns * 4] = inFrameCell[t * 4];
10715                                                 tempv[ns * 4 + 1] = inFrameCell[t * 4 + 1];
10716                                                 tempv[ns * 4 + 2] = inFrameCell[t * 4 + 2];
10717                                                 tempv[ns * 4 + 3] = inFrameCell[t * 4 + 3];
10718                                         } else {
10719                                                 tempv[ns * 4] = mp->cell->axes[0];
10720                                                 tempv[ns * 4 + 1] = mp->cell->axes[1];
10721                                                 tempv[ns * 4 + 2] = mp->cell->axes[2];
10722                                                 tempv[ns * 4 + 3] = mp->cell->origin;
10723                                         }
10724                                 } else if (i < mp->natoms) {
10725                                         if (inFrame != NULL)
10726                                                 tempv[ns] = inFrame[natoms * t + i];
10727                                         else
10728                                                 tempv[ns] = cr;
10729                                 } else {
10730                                         ((Double *)tempv)[ns] = 0.0;
10731                                 }
10732                                 t++;
10733                                 ns++;
10734                         }
10735                 }
10736                 if (n_new > ne) {
10737                         if (i <= mp->natoms)
10738                                 memmove(tempv + ne * mult, vp + s * mult, sizeof(Vector) * mult * (n_new - ne));
10739                         else
10740                                 memmove((Double *)tempv + ne, dp + s, sizeof(Double) * (n_new - ne));
10741                         s += n_new - ne;
10742                 }
10743                 if (i < mp->natoms)
10744                         ap->nframes = n_new;
10745                 if (i <= mp->natoms) {
10746                         memmove(vp, tempv, sizeof(Vector) * mult * n_new);
10747                         if (i < mp->natoms) {
10748                                 ap->nframes = n_new;
10749                                 ap = ATOM_NEXT(ap);
10750                         }
10751                 } else {
10752                         memmove(dp, (Double *)tempv, sizeof(Double) * n_new);
10753                         prp++;
10754                 }
10755         }
10756         free(tempv);
10757         mp->nframes = n_new;
10758         MoleculeSelectFrame(mp, last_inserted, 0);
10759         MoleculeIncrementModifyCount(mp);
10760         __MoleculeUnlock(mp);
10761         return count;
10762 }
10763
10764 int
10765 MoleculeRemoveFrames(Molecule *mp, IntGroup *inGroup, Vector *outFrame, Vector *outFrameCell)
10766 {
10767         int i, count, n_new, n_old, natoms, nframes, old_count, new_cframe;
10768         Vector *tempv, *vp;
10769         Atom *ap;
10770         MolProp *prp;
10771         IntGroup *group, *group2;
10772
10773         if (mp == NULL || (natoms = mp->natoms) == 0 || (count = IntGroupGetCount(inGroup)) <= 0)
10774                 return -1;
10775
10776         /*  outFrame[] should have enough size for Vector * natoms * group.count  */
10777         memset(outFrame, 0, sizeof(Vector) * natoms * count);
10778         if (mp->cell != NULL && mp->frame_cells != NULL)
10779                 memset(outFrameCell, 0, sizeof(Vector) * 4 * count);
10780
10781         n_old = MoleculeGetNumberOfFrames(mp);
10782         if (n_old == 1)
10783                 return -2;  /*  Cannot delete last frame  */
10784
10785         group = IntGroupNew();
10786         group2 = IntGroupNewWithPoints(0, n_old, -1);
10787         IntGroupIntersect(inGroup, group2, group);
10788         IntGroupRelease(group2);
10789         count = IntGroupGetCount(group);
10790         n_new = n_old - count;
10791         if (n_new < 1) {
10792                 IntGroupRelease(group);
10793                 return -2;  /*  Trying to delete too many frames  */
10794         }
10795         tempv = (Vector *)malloc(sizeof(Vector) * n_old * 4);  /*  "*4" for handling cells  */
10796         if (tempv == NULL) {
10797                 IntGroupRelease(group);
10798                 return -1;
10799         }
10800
10801         __MoleculeLock(mp);
10802
10803         /*  Copy back the current coordinates  */
10804         /*  No change in the current coordinates, but the frame buffer is updated  */
10805         MoleculeSelectFrame(mp, mp->cframe, 1); 
10806
10807         /*  Determine which frame should be selected after removal is completed  */
10808         {
10809                 int n1;
10810                 if (IntGroupLookup(group, mp->cframe, &i)) {
10811                         /*  cframe will be removed  */
10812                         n1 = IntGroupGetStartPoint(group, i) - 1;
10813                         if (n1 < 0)
10814                                 n1 = IntGroupGetEndPoint(group, i);
10815                 } else n1 = mp->cframe;
10816                 /*  Change to that frame  */
10817                 MoleculeSelectFrame(mp, n1, 0);
10818                 group2 = IntGroupNewFromIntGroup(group);
10819                 IntGroupReverse(group2, 0, n_old);
10820                 new_cframe = IntGroupLookupPoint(group2, n1);
10821                 if (new_cframe < 0)
10822                         return -3;  /*  This cannot happen  */
10823                 IntGroupRelease(group2);
10824         }
10825
10826         /*  group = [n0..n1-1, n2..n3-1, ...]  */
10827         /*  s = t = 0, */
10828         /*  tempv[0..n0-1] -> ap[0..n0-1], s += n0,
10829             tempv[n0..n1-1] -> outFrame[0..(n1-n0-1)], t += n1-n0,
10830                 tempv[n1..n2-1] -> ap[s..s+(n2-n1-1)], s += n2-n1,
10831                 tempv[n2..n3-1] -> outFrame[t..t+(n3-n2-1)], t += n3-n2,
10832                 ...
10833                 tempv[nl..n_old-1] -> ap[s..s+(n_old-nl-1)], s += n_old-nl
10834                 At last, s will become n_new and t will become count.  */
10835         nframes = 0;
10836         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
10837                 int s, t, j, ns, ne;
10838                 int mult;
10839                 /*  if i == mp->natoms, mp->frame_cells is handled  */
10840                 if (i == mp->natoms) {
10841                         if (mp->cell == NULL || mp->frame_cells == NULL)
10842                                 continue;
10843                         mult = 4 * sizeof(Vector);
10844                         vp = mp->frame_cells;
10845                         old_count = n_old;
10846                 } else if (i < mp->natoms) {
10847                         mult = sizeof(Vector);
10848                         vp = ap->frames;
10849                         if (vp == NULL) {
10850                                 NewArray(&ap->frames, &ap->nframes, sizeof(Vector), n_old);
10851                                 if (ap->frames == NULL) {
10852                                         __MoleculeUnlock(mp);
10853                                         return -1;
10854                                 }
10855                                 vp = ap->frames;
10856                         }
10857                         old_count = ap->nframes;
10858                 } else {
10859                         mult = sizeof(Double);
10860                         vp = (Vector *)prp->propvals;
10861                         old_count = n_old;
10862                 }
10863
10864                 /*  Copy vp to tempv  */
10865                 memset(tempv, 0, mult * n_old);
10866                 memmove(tempv, vp, mult * (old_count > n_old ? n_old : old_count));
10867                 ne = ns = s = t = 0;
10868                 for (j = 0; ns < n_old && (ns = IntGroupGetStartPoint(group, j)) >= 0; j++) {
10869                         if (ns > n_old)
10870                                 ns = n_old;
10871                         if (ns > ne) {
10872                                 memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (ns - ne));
10873                                 s += ns - ne;
10874                         }
10875                         ne = IntGroupGetEndPoint(group, j);
10876                         if (ne > n_old)
10877                                 ne = n_old;
10878                         while (ns < ne) {
10879                                 if (i < mp->natoms)
10880                                         outFrame[natoms * t + i] = tempv[ns];
10881                                 else if (i == mp->natoms) {
10882                                         if (outFrameCell != NULL) {
10883                                                 outFrameCell[t * 4] = tempv[ns * 4];
10884                                                 outFrameCell[t * 4 + 1] = tempv[ns * 4 + 1];
10885                                                 outFrameCell[t * 4 + 2] = tempv[ns * 4 + 2];
10886                                                 outFrameCell[t * 4 + 3] = tempv[ns * 4 + 3];
10887                                         }
10888                                 }
10889                                 t++;
10890                                 ns++;
10891                         }
10892                 }
10893                 if (n_old > ne) {
10894                         memmove((char *)vp + s * mult, (char *)tempv + ne * mult, mult * (n_old - ne));
10895                         s += n_old - ne;
10896                 }
10897                 if (i < mp->natoms)
10898                         ap->nframes = s;
10899                 if (nframes < s)
10900                         nframes = s;
10901                 if (s <= 1) {
10902                         if (i < mp->natoms) {
10903                                 free(ap->frames);
10904                                 ap->frames = NULL;
10905                                 ap->nframes = 0;
10906                         } else if (i == mp->natoms) {
10907                                 free(mp->frame_cells);
10908                                 mp->frame_cells = NULL;
10909                                 mp->nframe_cells = 0;
10910                         } else {
10911                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double));
10912                         }
10913                 } else {
10914                         if (i < mp->natoms) {
10915                                 AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), s - 1, NULL);
10916                                 ap->nframes = s;
10917                         } else if (i == mp->natoms) {
10918                                 AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, s - 1, NULL);
10919                                 mp->nframe_cells = s;
10920                         } else {
10921                                 prp->propvals = (Double *)realloc(prp->propvals, sizeof(Double) * s);
10922                         }
10923                 }
10924                 if (i < mp->natoms) {
10925                         ap = ATOM_NEXT(ap);
10926                 } else if (i > mp->natoms) {
10927                         prp++;
10928                 }
10929         }
10930         free(tempv);
10931         mp->nframes = nframes;
10932         
10933         /*  Select the "last" frame; do not "copy back" the coordinates to the frame table  */
10934 /*      i = (mp->cframe >= nframes ? nframes - 1 : mp->cframe); */
10935         MoleculeSelectFrame(mp, new_cframe, 0);
10936
10937         IntGroupRelease(group);
10938
10939         MoleculeIncrementModifyCount(mp);
10940         __MoleculeUnlock(mp);
10941         return count;
10942 }
10943
10944 int
10945 MoleculeSelectFrame(Molecule *mp, int frame, int copyback)
10946 {
10947         int i, cframe, nframes, modified;
10948         Atom *ap;
10949         cframe = mp->cframe;
10950         nframes = MoleculeGetNumberOfFrames(mp);
10951         if (frame == -1)
10952                 frame = mp->cframe;
10953         if (mp == NULL || mp->natoms == 0 || frame < 0 || frame >= nframes)
10954                 return -1;
10955         modified = 0;
10956         __MoleculeLock(mp);
10957         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
10958                 if (copyback && cframe >= 0 && cframe < ap->nframes) {
10959                         /*  Write the current coordinate back to the frame array  */
10960                         ap->frames[cframe] = ap->r;
10961                 }
10962                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < ap->nframes) {
10963                         /*  Read the coordinate from the frame array  */
10964                         ap->r = ap->frames[frame];
10965                         modified = 1;
10966                 }
10967         }
10968
10969         if (mp->cell != NULL && mp->frame_cells != NULL) {
10970                 /*  Write the current cell back to the frame_cells array  */
10971                 if (copyback && cframe >= 0) {
10972                         Vector *vp = (Vector *)AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, cframe, NULL);
10973                         vp[0] = mp->cell->axes[0];
10974                         vp[1] = mp->cell->axes[1];
10975                         vp[2] = mp->cell->axes[2];
10976                         vp[3] = mp->cell->origin;
10977                 }
10978                 /*  Set the cell from the frame array  */
10979                 if ((frame != cframe || copyback == 0) && frame >= 0 && frame < mp->nframe_cells) {
10980                         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);
10981                         modified = 1;
10982                         MoleculeAmendBySymmetry(mp, NULL, NULL, NULL);
10983                 }
10984         }
10985         mp->cframe = frame;
10986         if (modified)
10987                 mp->needsMDCopyCoordinates = 1;
10988         __MoleculeUnlock(mp);
10989         sMoleculeNotifyChangeAppearance(mp);
10990         return frame;
10991 }
10992
10993 /*  If molecule is multi-frame, then flush the current information to the frame buffer.
10994     Returns the number of frames.  */
10995 int
10996 MoleculeFlushFrames(Molecule *mp)
10997 {
10998         int nframes = MoleculeGetNumberOfFrames(mp);
10999         if (nframes > 1)
11000                 MoleculeSelectFrame(mp, mp->cframe, 1);
11001         return nframes;
11002 }
11003
11004 int
11005 MoleculeReorderFrames(Molecule *mp, const Int *old_idx)
11006 {
11007         Int *ip, i, j, n, nframes;
11008         Double *dp;
11009         Atom *ap;
11010         MolProp *prp;
11011         if (mp == NULL || old_idx == NULL)
11012                 return 0;
11013         nframes = MoleculeGetNumberOfFrames(mp);
11014         MoleculeFlushFrames(mp);
11015         ip = (Int *)malloc(sizeof(Int) * nframes);
11016         if (ip == NULL)
11017                 return -1;  /*  Out of memory  */
11018         memset(ip, 0, sizeof(Int) * nframes);
11019         /*  Check the argument  */
11020         for (i = 0; i < nframes; i++) {
11021                 j = old_idx[i];
11022                 if (j < 0 || j >= nframes || ip[j] != 0) {
11023                         free(ip);
11024                         return -2;  /*  Bad argument  */
11025                 }
11026                 ip[j] = 1;
11027         }
11028         free(ip);
11029         dp = (Double *)malloc(sizeof(Double) * nframes * 12);
11030         for (i = 0, ap = mp->atoms, prp = mp->molprops; i <= mp->natoms + mp->nmolprops; i++) {
11031                 for (j = 0; j < nframes; j++) {
11032                         n = old_idx[j];
11033                         if (i < mp->natoms) {
11034                                 ((Vector *)dp)[j] = (n < ap->nframes ? ap->frames[n] : ap->r);
11035                         } else if (i == mp->natoms) {
11036                                 if (mp->cell != NULL) {
11037                                         if (n < mp->nframe_cells && mp->frame_cells != NULL)
11038                                                 memmove(dp + j * 12, mp->frame_cells + n * 4, sizeof(Vector) * 4);
11039                                         else {
11040                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[0];
11041                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[1];
11042                                                 ((Vector *)dp)[j * 4] = mp->cell->axes[2];
11043                                                 ((Vector *)dp)[j * 4] = mp->cell->origin;
11044                                         }
11045                                 }
11046                         } else {
11047                                 dp[j] = prp->propvals[n];
11048                         }
11049                 }
11050                 for (j = 0; j < nframes; j++) {
11051                         if (i < mp->natoms) {
11052                                 if (ap->nframes <= j)
11053                                         AssignArray(&ap->frames, &ap->nframes, sizeof(Vector), nframes - 1, NULL);
11054                                 ap->frames[j] = ((Vector *)dp)[j];
11055                         } else if (i == mp->natoms) {
11056                                 if (mp->cell != NULL) {
11057                                         AssignArray(&mp->frame_cells, &mp->nframe_cells, sizeof(Vector) * 4, nframes - 1, NULL);
11058                                         memmove(mp->frame_cells + j * 4, dp + j * 12, sizeof(Vector) * 4);
11059                                 }
11060                         } else {
11061                                 prp->propvals[j] = dp[j];
11062                         }
11063                 }
11064                 if (i < mp->natoms)
11065                         ap = ATOM_NEXT(ap);
11066                 else if (i > mp->natoms)
11067                         prp++;
11068         }
11069         free(dp);
11070         MoleculeSelectFrame(mp, mp->cframe, 0);
11071         return 0;
11072 }
11073
11074 #pragma mark ====== Molecule Propeties ======
11075
11076 int
11077 MoleculeCreateProperty(Molecule *mp, const char *name)
11078 {
11079         int i;
11080         MolProp *prp;
11081         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11082                 if (strcmp(prp->propname, name) == 0)
11083                         return -(i + 1);
11084         }
11085         prp = (MolProp *)calloc(sizeof(MolProp), 1);
11086         if (prp == NULL)
11087                 return -10000;
11088         prp->propname = strdup(name);
11089         if (prp->propname == NULL)
11090                 return -10000;
11091         i = MoleculeGetNumberOfFrames(mp);
11092         prp->propvals = (Double *)calloc(sizeof(Double), i);
11093         if (prp->propvals == NULL)
11094                 return -10000;
11095         AssignArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), mp->nmolprops, prp);
11096         free(prp);
11097         return mp->nmolprops - 1;
11098 }
11099
11100 int
11101 MoleculeLookUpProperty(Molecule *mp, const char *name)
11102 {
11103         int i;
11104         MolProp *prp;
11105         for (i = 0, prp = mp->molprops; i < mp->nmolprops; i++, prp++) {
11106                 if (strcmp(prp->propname, name) == 0)
11107                         return i;
11108         }
11109         return -1;
11110 }
11111
11112 int
11113 MoleculeDeletePropertyAtIndex(Molecule *mp, int idx)
11114 {
11115         if (idx >= 0 && idx < mp->nmolprops) {
11116                 free(mp->molprops[idx].propname);
11117                 free(mp->molprops[idx].propvals);
11118                 DeleteArray(&mp->molprops, &mp->nmolprops, sizeof(MolProp), idx, 1, NULL);
11119                 return idx;
11120         }
11121         return -1;
11122 }
11123
11124 int
11125 MoleculeSetProperty(Molecule *mp, int idx, IntGroup *ig, const Double *values)
11126 {
11127         IntGroupIterator iter;
11128         int i, n, nframes;
11129         if (idx < 0 || idx >= mp->nmolprops)
11130                 return -1;
11131         IntGroupIteratorInit(ig, &iter);
11132         nframes = MoleculeGetNumberOfFrames(mp);
11133         n = 0;
11134         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11135                 if (i >= nframes)
11136                         break;
11137                 mp->molprops[idx].propvals[i] = values[n];
11138                 n++;
11139         }
11140         IntGroupIteratorRelease(&iter);
11141         return n;
11142 }
11143
11144 int
11145 MoleculeGetProperty(Molecule *mp, int idx, IntGroup *ig, Double *outValues)
11146 {
11147         IntGroupIterator iter;
11148         int i, n, nframes;
11149         if (idx < 0 || idx >= mp->nmolprops)
11150                 return -1;
11151         IntGroupIteratorInit(ig, &iter);
11152         nframes = MoleculeGetNumberOfFrames(mp);
11153         n = 0;
11154         while ((i = IntGroupIteratorNext(&iter)) >= 0) {
11155                 if (i >= nframes)
11156                         break;
11157                 outValues[n] = mp->molprops[idx].propvals[i];
11158                 n++;
11159         }
11160         IntGroupIteratorRelease(&iter);
11161         return n;
11162 }
11163
11164 #pragma mark ====== Pi Atoms ======
11165
11166 static inline void
11167 sMoleculeCalculatePiAnchorPosition(Atom *ap, Atom *atoms)
11168 {
11169         Int *cp, j, n;
11170         Atom *ap2;
11171         cp = AtomConnectData(&ap->anchor->connect);
11172         n = ap->anchor->connect.count;
11173         VecZero(ap->r);
11174         for (j = 0; j < n; j++) {
11175                 Double w = ap->anchor->coeffs[j];
11176                 ap2 = ATOM_AT_INDEX(atoms, cp[j]);
11177                 VecScaleInc(ap->r, ap2->r, w);
11178         }       
11179 }
11180
11181 void
11182 MoleculeUpdatePiAnchorPositions(Molecule *mol)
11183 {
11184         Int i;
11185         Atom *ap;
11186         for (i = 0, ap = mol->atoms; i < mol->natoms; i++, ap = ATOM_NEXT(ap)) {
11187                 if (ap->anchor == NULL)
11188                         continue;
11189                 sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11190         }
11191 }
11192
11193 void
11194 MoleculeCalculatePiAnchorPosition(Molecule *mol, int idx)
11195 {
11196         Atom *ap;
11197         if (mol == NULL || idx < 0 || idx >= mol->natoms)
11198                 return;
11199         ap = ATOM_AT_INDEX(mol->atoms, idx);
11200         if (ap->anchor == NULL)
11201                 return;
11202         sMoleculeCalculatePiAnchorPosition(ap, mol->atoms);
11203 }
11204
11205 int
11206 MoleculeSetPiAnchorList(Molecule *mol, Int idx, Int nentries, Int *entries, Double *weights, Int *nUndoActions, struct MolAction ***undoActions)
11207 {
11208         Atom *ap;
11209         Int *ip, i, j, n, *np;
11210         Double d;
11211         if (mol == NULL || idx < 0 || idx >= mol->natoms || nentries <= 1)
11212                 return -1;  /*  Invalid argument  */
11213         if (weights != NULL) {
11214                 d = 0.0;
11215                 for (i = 0; i < nentries; i++) {
11216                         if (weights[i] <= 0.0) {
11217                                 return 10;  /*  Weights must be positive  */
11218                         }
11219                         d += weights[i];
11220                 }
11221                 d = 1.0 / d;
11222         } else d = 1.0 / nentries;
11223         ap = ATOM_AT_INDEX(mol->atoms, idx);
11224         if (ap->anchor != NULL) {
11225                 /*  Already an anchor: check if bonds/angles/dihedrals have entries related to this anchor  */
11226                 IntGroup *bg, *ag, *dg, *ig;
11227                 Int *ibuf, ibufsize;
11228                 MolAction *act;
11229                 bg = ag = dg = ig = NULL;
11230                 ip = AtomConnectData(&ap->anchor->connect);
11231                 for (i = 0; i < ap->anchor->connect.count; i++) {
11232                         n = ip[i];
11233                         for (j = 0; j < nentries; j++) {
11234                                 if (n == entries[j])
11235                                         break;
11236                         }
11237                         if (j == nentries) {
11238                                 /*  This entry will disappear: if any bond/angle/dihedral has idx-n pair, that should be removed.  */
11239                                 for (j = 0, np = mol->bonds; j < mol->nbonds; j++, np += 2) {
11240                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[0])) {
11241                                                 if (bg == NULL)
11242                                                         bg = IntGroupNew();
11243                                                 IntGroupAdd(bg, j, 1);
11244                                         }
11245                                 }
11246                                 for (j = 0, np = mol->angles; j < mol->nangles; j++, np += 3) {
11247                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) ||
11248                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1])) {
11249                                                 if (ag == NULL)
11250                                                         ag = IntGroupNew();
11251                                                 IntGroupAdd(ag, j, 1);
11252                                         }
11253                                 }
11254                                 for (j = 0, np = mol->dihedrals; j < mol->ndihedrals; j++, np += 4) {
11255                                         if ((idx == np[0] && n == np[1]) || (idx == np[1] && n == np[2]) || (idx == np[2] && n == np[3]) ||
11256                                                 (idx == np[1] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[3] && n == np[2])) {
11257                                                 if (dg == NULL)
11258                                                         dg = IntGroupNew();
11259                                                 IntGroupAdd(dg, j, 1);
11260                                         }
11261                                 }
11262                                 for (j = 0, np = mol->impropers; j < mol->nimpropers; j++, np += 4) {
11263                                         if ((idx == np[0] && n == np[2]) || (idx == np[1] && n == np[2]) || (idx == np[3] && n == np[2]) ||
11264                                                 (idx == np[2] && n == np[0]) || (idx == np[2] && n == np[1]) || (idx == np[2] && n == np[3])) {
11265                                                 if (ig == NULL)
11266                                                         ig = IntGroupNew();
11267                                                 IntGroupAdd(ig, j, 1);
11268                                         }
11269                                 }
11270                         }
11271                 }
11272                 ibuf = NULL;
11273                 ibufsize = 0;
11274                 if (ig != NULL) {
11275                         /*  Delete impropers (with undo info) */
11276                         i = IntGroupGetCount(ig);
11277                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11278                         MoleculeDeleteImpropers(mol, ibuf, ig);
11279                         if (nUndoActions != NULL && undoActions != NULL) {
11280                                 act = MolActionNew(gMolActionAddImpropers, i * 4, ibuf, ig);
11281                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11282                         }
11283                         IntGroupRelease(ig);
11284                 }
11285                 if (dg != NULL) {
11286                         /*  Delete dihedrals (with undo info)  */
11287                         i = IntGroupGetCount(dg);
11288                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 4 - 1, NULL);
11289                         MoleculeDeleteDihedrals(mol, ibuf, dg);
11290                         if (nUndoActions != NULL && undoActions != NULL) {
11291                                 act = MolActionNew(gMolActionAddDihedrals, i * 4, ibuf, dg);
11292                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11293                         }
11294                         IntGroupRelease(dg);
11295                 }
11296                 if (ag != NULL) {
11297                         /*  Delete angles (with undo info) */
11298                         i = IntGroupGetCount(ag);
11299                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 3 - 1, NULL);
11300                         MoleculeDeleteAngles(mol, ibuf, ag);
11301                         if (nUndoActions != NULL && undoActions != NULL) {
11302                                 act = MolActionNew(gMolActionAddAngles, i * 3, ibuf, ag);
11303                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11304                         }
11305                         IntGroupRelease(ag);
11306                 }
11307                 if (bg != NULL) {
11308                         /*  Delete bonds (with undo info) */
11309                         i = IntGroupGetCount(bg);
11310                         AssignArray(&ibuf, &ibufsize, sizeof(Int), i * 2 - 1, NULL);
11311                         MoleculeDeleteBonds(mol, ibuf, bg, NULL, NULL);
11312                         if (nUndoActions != NULL && undoActions != NULL) {
11313                                 act = MolActionNew(gMolActionAddBondsForUndo, i * 2, ibuf, bg);
11314                                 AssignArray(undoActions, nUndoActions, sizeof(MolAction *), *nUndoActions, &act);
11315                         }
11316                         IntGroupRelease(bg);
11317                 }
11318         } else {
11319                 ap->anchor = (PiAnchor *)calloc(sizeof(PiAnchor), 1);
11320         }
11321         AtomConnectResize(&ap->anchor->connect, nentries);
11322         memmove(AtomConnectData(&ap->anchor->connect), entries, sizeof(Int) * nentries);
11323         AssignArray(&ap->anchor->coeffs, &ap->anchor->ncoeffs, sizeof(Double), nentries - 1, NULL);
11324         if (weights != NULL) {
11325                 memmove(ap->anchor->coeffs, weights, sizeof(Double) * nentries);
11326                 for (i = 0; i < nentries; i++)
11327                         ap->anchor->coeffs[i] *= d;   /*  Normalize weight  */
11328         } else {
11329                 for (i = 0; i < nentries; i++)
11330                         ap->anchor->coeffs[i] = d;
11331         }
11332         MoleculeCalculatePiAnchorPosition(mol, idx);
11333         return 0;
11334 }
11335
11336 #pragma mark ====== MO calculation ======
11337
11338 /*  Calculate an MO value for a single point.  */
11339 /*  Index is the MO number (1-based); 0 denotes "arbitrary vector"  */
11340 /*  tmp is an array of (natoms * 4) atoms, and used to store dr and |dr|^2 for each atom.  */
11341 static Double
11342 sCalcMOPoint(Molecule *mp, const BasisSet *bset, Int index, const Vector *vp, Double *tmp)
11343 {
11344         ShellInfo *sp;
11345         PrimInfo *pp;
11346         Double val, tval, *cnp, *tmpp, *mobasep, *mop;
11347         Int i, j;
11348         /*  Cache dr and |dr|^2  */
11349         if (index == 0)
11350                 index = bset->nmos + 1;
11351         for (i = 0; i < mp->natoms; i++) {
11352                 Vector r;
11353                 r = ATOM_AT_INDEX(mp->atoms, i)->r;
11354                 tmp[i * 4] = r.x = (vp->x - r.x) * kAngstrom2Bohr;
11355                 tmp[i * 4 + 1] = r.y = (vp->y - r.y) * kAngstrom2Bohr;
11356                 tmp[i * 4 + 2] = r.z = (vp->z - r.z) * kAngstrom2Bohr;
11357                 tmp[i * 4 + 3] = r.x * r.x + r.y * r.y + r.z * r.z;
11358         }
11359         /*  Iterate over all shells  */
11360         val = 0.0;
11361         mobasep = bset->mo + (index - 1) * bset->ncomps;
11362         for (i = 0, sp = bset->shells; i < bset->nshells; i++, sp++) {
11363                 pp = bset->priminfos + sp->p_idx;
11364                 cnp = bset->cns + sp->cn_idx;
11365                 if (sp->a_idx >= mp->natoms)
11366                         return 0.0; /*  This may happen when molecule is edited after setting up MO info  */
11367                 tmpp = tmp + sp->a_idx * 4;
11368                 mop = mobasep + sp->m_idx;
11369                 switch (sp->sym) {
11370                         case kGTOType_S: {
11371                                 tval = 0;
11372                                 for (j = 0; j < sp->nprim; j++) {
11373                                         tval += *cnp++ * exp(-pp->A * tmpp[3]);
11374                                         pp++;
11375                                 }
11376                                 val += mop[0] * tval;
11377                                 break;
11378                         }
11379                         case kGTOType_P: {
11380                                 Double x, y, z;
11381                                 x = y = z = 0;
11382                                 for (j = 0; j < sp->nprim; j++) {
11383                                         tval = exp(-pp->A * tmpp[3]);
11384                                         x += *cnp++ * tval;
11385                                         y += *cnp++ * tval;
11386                                         z += *cnp++ * tval;
11387                                         pp++;
11388                                 }
11389                                 x *= mop[0] * tmpp[0];
11390                                 y *= mop[1] * tmpp[1];
11391                                 z *= mop[2] * tmpp[2];
11392                                 val += x + y + z;
11393                                 break;
11394                         }
11395                         case kGTOType_SP: {
11396                                 Double t, x, y, z;
11397                                 t = x = y = z = 0;
11398                                 for (j = 0; j < sp->nprim; j++) {
11399                                         tval = exp(-pp->A * tmpp[3]);
11400                                         t += *cnp++ * tval;
11401                                         x += *cnp++ * tval;
11402                                         y += *cnp++ * tval;
11403                                         z += *cnp++ * tval;
11404                                         pp++;
11405                                 }
11406                                 t *= mop[0];
11407                                 x *= mop[1] * tmpp[0];
11408                                 y *= mop[2] * tmpp[1];
11409                                 z *= mop[3] * tmpp[2];
11410                                 val += t + x + y + z;
11411                                 break;
11412                         }
11413                         case kGTOType_D: {
11414                                 Double xx, yy, zz, xy, xz, yz;
11415                                 xx = yy = zz = xy = xz = yz = 0;
11416                                 for (j = 0; j < sp->nprim; j++) {
11417                                         tval = exp(-pp->A * tmpp[3]);
11418                                         xx += *cnp++ * tval;
11419                                         yy += *cnp++ * tval;
11420                                         zz += *cnp++ * tval;
11421                                         xy += *cnp++ * tval;
11422                                         xz += *cnp++ * tval;
11423                                         yz += *cnp++ * tval;
11424                                         pp++;
11425                                 }
11426                                 xx *= mop[0] * tmpp[0] * tmpp[0];
11427                                 yy *= mop[1] * tmpp[1] * tmpp[1];
11428                                 zz *= mop[2] * tmpp[2] * tmpp[2];
11429                                 xy *= mop[3] * tmpp[0] * tmpp[1];
11430                                 xz *= mop[4] * tmpp[0] * tmpp[2];
11431                                 yz *= mop[5] * tmpp[1] * tmpp[2];
11432                                 val += xx + yy + zz + xy + xz + yz;
11433                                 break;
11434                         }
11435                         case kGTOType_D5: {
11436                                 Double d0, d1p, d1n, d2p, d2n;
11437                                 d0 = d1p = d1n = d2p = d2n = 0;
11438                                 for (j = 0; j < sp->nprim; j++) {
11439                                         tval = exp(-pp->A * tmpp[3]);
11440                                         d0 += *cnp++ * tval;
11441                                         d1p += *cnp++ * tval;
11442                                         d1n += *cnp++ * tval;
11443                                         d2p += *cnp++ * tval;
11444                                         d2n += *cnp++ * tval;
11445                                         pp++;
11446                                 }
11447                                 d0 *= mop[0] * (3 * tmpp[2] * tmpp[2] - tmpp[3]);
11448                                 d1p *= mop[1] * tmpp[0] * tmpp[2];
11449                                 d1n *= mop[2] * tmpp[1] * tmpp[2];
11450                                 d2p *= mop[3] * (tmpp[0] * tmpp[0] - tmpp[1] * tmpp[1]);
11451                                 d2n *= mop[4] * tmpp[0] * tmpp[1];
11452                                 val += d0 + d1p + d1n + d2p + d2n;
11453                                 break;
11454                         }
11455                         /*  TODO: Support F/F7 and G/G9 type orbitals  */
11456                 }
11457         }
11458         return val;
11459 }
11460
11461 /*  Calculate one MO. The input vectors are angstrom unit (changed from bohr unit: 20140520)  */
11462 /*  mono is the MO number (1-based); 0 denotes "arbitrary vector" */
11463 int
11464 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)
11465 {
11466         int ix, iy, iz, n, nn;
11467         Cube *cp;
11468         Double *tmp;
11469         if (mp == NULL || mp->bset == NULL)
11470                 return -1;
11471         if (mp->bset->cns == NULL) {
11472                 if (sSetupGaussianCoefficients(mp->bset) != 0)
11473                         return -1;
11474         }
11475         if (mp->bset->natoms_bs > mp->natoms)
11476                 return -3;  /*  Number of atoms is smaller than expected (internal error)  */
11477         
11478         cp = (Cube *)calloc(sizeof(Cube), 1);
11479         if (cp == NULL) {
11480                 return -1;
11481         }
11482         cp->dp = (Double *)calloc(sizeof(Double), nx * ny * nz);
11483         if (cp->dp == NULL) {
11484                 free(cp);
11485                 return -1;
11486         }
11487         cp->idn = mono;
11488         cp->origin = *op;
11489         cp->dx = *dxp;
11490         cp->dy = *dyp;
11491         cp->dz = *dzp;
11492         cp->nx = nx;
11493         cp->ny = ny;
11494         cp->nz = nz;
11495         
11496         /*  TODO: use multithread  */
11497         tmp = (Double *)calloc(sizeof(Double), mp->bset->natoms_bs * 4);
11498         if (tmp == NULL) {
11499                 free(cp->dp);
11500                 free(cp);
11501                 return -1;
11502         }
11503         n = nn = 0;
11504         for (ix = 0; ix < nx; ix++) {
11505                 Vector p;
11506                 for (iy = 0; iy < ny; iy++) {
11507                         for (iz = 0; iz < nz; iz++) {
11508                                 p.x = op->x + dxp->x * ix + dyp->x * iy + dzp->x * iz;
11509                                 p.y = op->y + dxp->y * ix + dyp->y * iy + dzp->y * iz;
11510                                 p.z = op->z + dxp->z * ix + dyp->z * iy + dzp->z * iz;
11511                                 cp->dp[n++] = sCalcMOPoint(mp, mp->bset, mono, &p, tmp);
11512                         }
11513                         if (callback != NULL && n - nn > 100) {
11514                                 nn = n;
11515                                 if ((*callback)((double)n / ((double)nx * ny * nz), ref) != 0) {
11516                                         free(cp->dp);
11517                                         free(cp);
11518                                         free(tmp);
11519                                         return -2;  /*  User interrupt  */
11520                                 }
11521                         }
11522                 }
11523         }
11524         free(tmp);
11525
11526         AssignArray(&(mp->bset->cubes), &(mp->bset->ncubes), sizeof(Cube *), mp->bset->ncubes, &cp);
11527         return mp->bset->ncubes - 1;
11528 }
11529
11530 /*  Output values are in angstrom unit (changed from bohr unit: 20140520)  */
11531 int
11532 MoleculeGetDefaultMOGrid(Molecule *mp, Int npoints, Vector *op, Vector *xp, Vector *yp, Vector *zp, Int *nx, Int *ny, Int *nz)
11533 {
11534         int i;
11535         Vector rmin, rmax, r;
11536         Double dr, dx, dy, dz;
11537         Atom *ap;
11538         if (mp == NULL || mp->bset == NULL)
11539                 return -1;
11540         if (npoints <= 0)
11541                 npoints = 1000000;
11542         rmin.x = rmin.y = rmin.z = 1e10;
11543         rmax.x = rmax.y = rmax.z = -1e10;
11544         for (i = 0, ap = mp->atoms; i < mp->natoms; i++, ap = ATOM_NEXT(ap)) {
11545                 dr = RadiusForAtomicNumber(ap->atomicNumber);
11546                 r = ap->r;
11547                 if (dr == 0.0)
11548                         dr = 1.0;
11549                 dr = dr * 3.0 + 2.0;
11550                 if (rmin.x > r.x - dr)
11551                         rmin.x = r.x - dr;
11552                 if (rmin.y > r.y - dr)
11553                         rmin.y = r.y - dr;
11554                 if (rmin.z > r.z - dr)
11555                         rmin.z = r.z - dr;
11556                 if (rmax.x < r.x + dr)
11557                         rmax.x = r.x + dr;
11558                 if (rmax.y < r.y + dr)
11559                         rmax.y = r.y + dr;
11560                 if (rmax.z < r.z + dr)
11561                         rmax.z = r.z + dr;
11562         }
11563         dx = rmax.x - rmin.x;
11564         dy = rmax.y - rmin.y;
11565         dz = rmax.z - rmin.z;
11566         dr = pow(dx * dy * dz / npoints, 1.0/3.0);
11567         *nx = floor(dx / dr + 0.5);
11568         *ny = floor(dy / dr + 0.5);
11569         *nz = floor(dz / dr + 0.5);
11570         if (*nx == 0)
11571                 *nx = 1;
11572         if (*ny == 0)
11573                 *ny = 1;
11574         if (*nz == 0)
11575                 *nz = 1;
11576         *op = rmin;
11577         xp->x = yp->y = zp->z = dr;
11578         xp->y = xp->z = yp->x = yp->z = zp->x = zp->y = 0.0;
11579         return 0;
11580 }
11581
11582 const Cube *
11583 MoleculeGetCubeAtIndex(Molecule *mp, Int index)
11584 {
11585         if (mp == NULL || mp->bset == NULL || index < 0 || index >= mp->bset->ncubes)
11586                 return NULL;
11587         return mp->bset->cubes[index];
11588 }
11589
11590 int
11591 MoleculeLookUpCubeWithMONumber(Molecule *mp, Int mono)
11592 {
11593         int i;
11594         if (mp == NULL || mp->bset == NULL)
11595                 return -1;
11596         for (i = 0; i < mp->bset->ncubes; i++) {
11597                 if (mp->bset->cubes[i]->idn == mono)
11598                         return i;
11599         }
11600         return -1;
11601 }
11602
11603 int
11604 MoleculeClearCubeAtIndex(Molecule *mp, Int index)
11605 {
11606         int n;
11607         if (mp == NULL || mp->bset == NULL || index < 0 || index >= (n = mp->bset->ncubes))
11608                 return -1;
11609         CubeRelease(mp->bset->cubes[index]);
11610         if (index < n - 1)
11611                 memmove(mp->bset->cubes + index, mp->bset->cubes + index + 1, sizeof(Cube *) * (n - index - 1));
11612         if (--(mp->bset->ncubes) == 0) {
11613                 free(mp->bset->cubes);
11614                 mp->bset->cubes = NULL;
11615         }
11616         return mp->bset->ncubes;
11617 }
11618
11619 int
11620 MoleculeOutputCube(Molecule *mp, Int index, const char *fname, const char *comment)
11621 {
11622         const Cube *cp;
11623         int i, j, k, n;
11624         FILE *fp;
11625         if (mp == NULL || mp->bset == NULL)
11626                 return -1;  /*  Molecule or the basis set information is empty  */
11627         cp = MoleculeGetCubeAtIndex(mp, index);
11628         if (cp == NULL)
11629                 return -2;  /*  MO not yet calculated  */
11630         fp = fopen(fname, "wb");
11631         if (fp == NULL)
11632                 return -3;  /*  Cannot create file  */
11633
11634         /*  Comment lines  */
11635         fprintf(fp, "%s MO=%d\n", comment, cp->idn);
11636         fprintf(fp, " MO coefficients\n");
11637         
11638         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", -(mp->bset->natoms_bs),
11639                         cp->origin.x * kAngstrom2Bohr, cp->origin.y * kAngstrom2Bohr, cp->origin.z * kAngstrom2Bohr);
11640         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nx,
11641                         cp->dx.x * kAngstrom2Bohr, cp->dx.y * kAngstrom2Bohr, cp->dx.z * kAngstrom2Bohr);
11642         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->ny,
11643                         cp->dy.x * kAngstrom2Bohr, cp->dy.y * kAngstrom2Bohr, cp->dy.z * kAngstrom2Bohr);
11644         fprintf(fp, "%5d %11.6f %11.6f %11.6f\n", cp->nz,
11645                         cp->dz.x * kAngstrom2Bohr, cp->dz.y * kAngstrom2Bohr, cp->dz.z * kAngstrom2Bohr);
11646         
11647         /*  Atomic information  */
11648         for (i = 0; i < mp->natoms; i++) {
11649                 Atom *ap = ATOM_AT_INDEX(mp->atoms, i);
11650                 /*  The second number should actually be the effective charge  */
11651                 fprintf(fp, "%5d %11.6f %11.6f %11.6f %11.6f\n", ap->atomicNumber, (double)ap->atomicNumber,
11652                                 ap->r.x * kAngstrom2Bohr, ap->r.y * kAngstrom2Bohr, ap->r.z * kAngstrom2Bohr);
11653         }
11654         fprintf(fp, "%5d%5d\n", 1, 1);
11655         
11656         /*  3D data  */
11657         for (i = n = 0; i < cp->nx; i++) {
11658                 for (j = 0; j < cp->ny; j++) {
11659                         for (k = 0; k < cp->nz; k++) {
11660                                 /*  On Windows, the "%e" format writes the exponent in 3 digits, but
11661                                     this is not standard. So we avoid using %e  */
11662                                 Double d = cp->dp[n++];
11663                                 int exponent;
11664                                 Double base;
11665                                 if (d >= -1.0e-90 && d <= 1.0e-90) {
11666                                         exponent = 0;
11667                                         base = 0.0;
11668                                 } else {
11669                                         exponent = (int)floor(log10(fabs(d)));
11670                                         base = d * pow(10, -1.0 * exponent);
11671                                 }
11672                                 fprintf(fp, " %8.5fe%+03d", base, exponent);
11673                         /*      fprintf(fp, " %12.5e", d); */
11674                                 if (k == cp->nz - 1 || k % 6 == 5)
11675                                         fprintf(fp, "\n");
11676                         }
11677                 }
11678         }
11679         fclose(fp);
11680         return 0;
11681 }
11682
11683 #pragma mark ====== Marching Cube (for isosurface) ======
11684
11685 MCube *
11686 MoleculeClearMCube(Molecule *mol, Int nx, Int ny, Int nz, const Vector *origin, Double dx, Double dy, Double dz)
11687 {
11688         MCube *mc = mol->mcube;
11689         int i;
11690         float rgba[8] = { 1, 1, 1, 0.6, 0, 0, 1, 0.6 };
11691         if (mc != NULL) {
11692                 free(mc->dp);
11693                 free(mc->radii);
11694                 free(mc->c[0].fp);
11695                 free(mc->c[0].cubepoints);
11696                 free(mc->c[0].triangles);
11697                 free(mc->c[1].fp);
11698                 free(mc->c[1].cubepoints);
11699                 free(mc->c[1].triangles);
11700                 memmove(rgba, mc->c[0].rgba, sizeof(float) * 4);
11701                 memmove(rgba + 4, mc->c[1].rgba, sizeof(float) * 4);
11702                 free(mc);
11703                 mol->mcube = NULL;
11704         }
11705         if (nx > 0 && ny > 0 && nz > 0) {
11706                 mc = (MCube *)calloc(sizeof(MCube), 1);
11707                 mc->idn = -1;
11708                 /*  round up to nearest 4N+1 integer  */
11709                 dx *= nx;
11710                 dy *= ny;
11711                 dz *= nz;
11712                 mc->nx = (nx + 2) / 4 * 4 + 1;
11713                 mc->ny = (ny + 2) / 4 * 4 + 1;
11714                 mc->nz = (nz + 2) / 4 * 4 + 1;
11715                 mc->dx = dx / mc->nx;
11716                 mc->dy = dy / mc->ny;
11717                 mc->dz = dz / mc->nz;
11718                 mc->origin = *origin;
11719                 mc->dp = (Double *)malloc(sizeof(Double) * mc->nx * mc->ny * mc->nz);
11720                 if (mc->dp == NULL) {
11721                         free(mc);
11722                         return NULL;
11723                 }
11724                 mc->radii = (Double *)calloc(sizeof(Double), mol->natoms);
11725                 if (mc->radii == NULL) {
11726                         free(mc->dp);
11727                         free(mc);
11728                         return NULL;
11729                 }
11730                 mc->nradii = mol->natoms;
11731                 mc->c[0].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11732                 mc->c[1].fp = (unsigned char *)calloc(sizeof(unsigned char), mc->nx * mc->ny * mc->nz);
11733                 if (mc->c[0].fp == NULL || mc->c[1].fp == NULL) {
11734                         free(mc->c[0].fp);
11735                         free(mc->c[1].fp);
11736                         free(mc->dp);
11737                         free(mc->radii);
11738                         free(mc);
11739                         return NULL;
11740                 }
11741                 for (i = 0; i < mc->nx * mc->ny * mc->nz; i++) {
11742                         mc->dp[i] = DBL_MAX;
11743                 }
11744                 memmove(mc->c[0].rgba, rgba, sizeof(float) * 4);
11745                 memmove(mc->c[1].rgba, rgba + 4, sizeof(float) * 4);
11746                 mol->mcube = mc;
11747         }
11748         MoleculeCallback_notifyModification(mol, 0);
11749         return mol->mcube;
11750 }
11751
11752 static int sMarchingCubeTable[256][16] = {
11753         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11754         {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11755         {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11756         {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11757         {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11758         {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11759         {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11760         {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11761         {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11762         {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11763         {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11764         {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11765         {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11766         {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11767         {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11768         {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11769         {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11770         {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11771         {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11772         {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11773         {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11774         {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
11775         {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11776         {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11777         {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11778         {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11779         {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11780         {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
11781         {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
11782         {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
11783         {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11784         {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
11785         {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11786         {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11787         {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11788         {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11789         {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11790         {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11791         {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11792         {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
11793         {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11794         {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
11795         {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
11796         {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
11797         {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
11798         {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
11799         {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
11800         {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
11801         {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11802         {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11803         {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11804         {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11805         {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
11806         {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
11807         {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
11808         {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
11809         {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
11810         {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11811         {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
11812         {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
11813         {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
11814         {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
11815         {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
11816         {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11817         {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11818         {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11819         {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11820         {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11821         {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11822         {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
11823         {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11824         {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
11825         {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11826         {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11827         {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
11828         {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
11829         {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11830         {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11831         {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
11832         {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
11833         {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11834         {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
11835         {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
11836         {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
11837         {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
11838         {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
11839         {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
11840         {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
11841         {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
11842         {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
11843         {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
11844         {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
11845         {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
11846         {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
11847         {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
11848         {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
11849         {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11850         {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
11851         {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11852         {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11853         {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11854         {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
11855         {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11856         {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
11857         {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
11858         {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
11859         {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
11860         {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
11861         {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
11862         {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
11863         {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
11864         {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11865         {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11866         {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
11867         {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
11868         {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
11869         {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11870         {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
11871         {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
11872         {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11873         {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
11874         {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
11875         {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
11876         {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
11877         {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
11878         {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11879         {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
11880         {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11881         {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11882         {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11883         {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11884         {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11885         {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11886         {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11887         {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
11888         {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
11889         {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11890         {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11891         {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
11892         {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
11893         {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11894         {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
11895         {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
11896         {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
11897         {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11898         {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11899         {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
11900         {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
11901         {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
11902         {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
11903         {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
11904         {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
11905         {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11906         {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11907         {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
11908         {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
11909         {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
11910         {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
11911         {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
11912         {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11913         {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11914         {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
11915         {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11916         {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
11917         {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
11918         {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
11919         {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
11920         {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
11921         {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
11922         {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
11923         {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
11924         {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
11925         {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
11926         {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
11927         {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
11928         {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
11929         {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
11930         {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
11931         {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
11932         {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
11933         {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
11934         {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
11935         {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
11936         {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
11937         {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
11938         {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
11939         {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
11940         {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11941         {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
11942         {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
11943         {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11944         {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11945         {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11946         {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
11947         {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
11948         {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
11949         {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11950         {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
11951         {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
11952         {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
11953         {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11954         {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
11955         {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
11956         {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
11957         {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11958         {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
11959         {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
11960         {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11961         {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11962         {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
11963         {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
11964         {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
11965         {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
11966         {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
11967         {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
11968         {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11969         {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
11970         {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
11971         {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
11972         {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
11973         {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
11974         {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11975         {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
11976         {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11977         {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11978         {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
11979         {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
11980         {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
11981         {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
11982         {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
11983         {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
11984         {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
11985         {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
11986         {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
11987         {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
11988         {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11989         {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
11990         {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
11991         {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11992         {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11993         {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11994         {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
11995         {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
11996         {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
11997         {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
11998         {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
11999         {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12000         {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12001         {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
12002         {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12003         {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
12004         {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12005         {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12006         {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12007         {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
12008         {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
12009 };
12010
12011 /*  Recalculate the MCube  */
12012 /*  If idn < 0, then the current grid settings and values are unchanged, and */
12013 /*  only the marching cubes are regenerated.  */
12014 int
12015 MoleculeUpdateMCube(Molecule *mol, int idn)
12016 {
12017         Int retval, step, sn;
12018         Int n, ix, iy, iz, nx, ny, nz;
12019         Int nn, iix, iiy, iiz;
12020         Int ncubepoints, c1, c2, c3;
12021         Int *ip;
12022         Double thres, *tmp, dd;
12023         Vector p;
12024         MCube *mc;
12025         MCubePoint *mcp;
12026         Atom *ap;
12027
12028         if (mol == NULL || mol->bset == NULL || mol->mcube == NULL)
12029                 return -1;
12030         if (mol->bset->cns == NULL) {
12031                 if (sSetupGaussianCoefficients(mol->bset) != 0)
12032                         return -1;
12033         }
12034         if (mol->bset->natoms_bs > mol->natoms)
12035                 return -1;  /*  Number of atoms is smaller than expected  */
12036
12037         mc = mol->mcube;
12038         if (idn >= 0) {
12039                 ShellInfo *sp;
12040                 Double *mobasep, *mop, mopmax;
12041                 Double xmin, xmax, ymin, ymax, zmin, zmax;
12042                 /*  Clear mcube values  */
12043                 for (ix = 0; ix < mc->nx * mc->ny * mc->nz; ix++) {
12044                         mc->dp[ix] = DBL_MAX;
12045                         mc->c[0].fp[ix] = 0;
12046                         mc->c[1].fp[ix] = 0;
12047                 }
12048                 mc->idn = idn;
12049                 /*  Estimate the orbital sizes  */
12050                 mc->radii = (Double *)realloc(mc->radii, sizeof(Double) * mol->natoms);
12051                 if (mc->radii == NULL)
12052                         return -2;  /*  Out of memory  */
12053                 mc->nradii = mol->natoms;
12054                 if (mc->idn == mol->bset->nmos + 1) {
12055                         /*  Total electron density  */
12056                         for (ix = 0; ix < mol->natoms; ix++)
12057                                 mc->radii[ix] = 1.0;
12058                         mopmax = 1.0;
12059                 } else {
12060                         memset(mc->radii, 0, sizeof(Double) * mc->nradii);
12061                         mobasep = mol->bset->mo + (mc->idn == 0 ? mol->bset->nmos : mc->idn - 1) * mol->bset->ncomps;
12062                         mopmax = 0.0;
12063                         for (ix = 0, sp = mol->bset->shells; ix < mol->bset->nshells; ix++, sp++) {
12064                                 if (sp->a_idx >= mol->natoms)
12065                                         continue;  /*  This may happen when molecule is edited after setting up MO info  */
12066                                 mop = mobasep + sp->m_idx;
12067                                 for (iy = 0; iy < sp->ncomp; iy++) {
12068                                         dd = fabs(mop[iy]);
12069                                         if (dd > mc->radii[sp->a_idx])
12070                                                 mc->radii[sp->a_idx] = dd;
12071                                         if (dd > mopmax)
12072                                                 mopmax = dd;
12073                                 }
12074                         }
12075                 }
12076                 xmin = ymin = zmin = 1e10;
12077                 xmax = ymax = zmax = -1e10;
12078                 for (ix = 0, ap = mol->atoms; ix < mol->natoms; ix++, ap = ATOM_NEXT(ap)) {
12079                         dd = RadiusForAtomicNumber(ap->atomicNumber);
12080                         dd = (dd * 2.0 + 1.0) * (mc->radii[ix] / mopmax) * (mc->expand > 0.0 ? mc->expand : 1.0);
12081                         mc->radii[ix] = dd;
12082                         p = ap->r;
12083                         dd += 0.1;
12084                         if (p.x - dd < xmin)
12085                                 xmin = p.x - dd;
12086                         if (p.y - dd < ymin)
12087                                 ymin = p.y - dd;
12088                         if (p.z - dd < zmin)
12089                                 zmin = p.z - dd;
12090                         if (p.x + dd > xmax)
12091                                 xmax = p.x + dd;
12092                         if (p.y + dd > ymax)
12093                                 ymax = p.y + dd;
12094                         if (p.z + dd > zmax)
12095                                 zmax = p.z + dd;
12096                 }
12097                 mc->origin.x = xmin;
12098                 mc->origin.y = ymin;
12099                 mc->origin.z = zmin;
12100                 mc->dx = (xmax - xmin) / mc->nx;
12101                 mc->dy = (ymax - ymin) / mc->ny;
12102                 mc->dz = (zmax - zmin) / mc->nz;
12103         }
12104         
12105         /*  Temporary work area  */
12106         tmp = (Double *)calloc(sizeof(Double), mol->bset->natoms_bs * 4);
12107         if (tmp == NULL)
12108                 return -2;
12109         
12110         /*  TODO: use multithread  */
12111         nx = mc->nx;
12112         ny = mc->ny;
12113         nz = mc->nz;
12114         step = 4;
12115         
12116 #if 1
12117         /*  Calculate points within certain distances from atoms  */
12118         for (nn = 0, ap = mol->atoms; nn < mol->natoms; nn++, ap = ATOM_NEXT(ap)) {
12119         /*      dd = RadiusForAtomicNumber(ap->atomicNumber);
12120                 if (dd == 0.0)
12121                         dd = 1.0;
12122                 dd = dd * 1.5 + 1.0; */
12123                 dd = mc->radii[nn];
12124                 p.x = ap->r.x - dd - mc->origin.x;
12125                 p.y = ap->r.y - dd - mc->origin.y;
12126                 p.z = ap->r.z - dd - mc->origin.z;
12127                 c1 = p.x / mc->dx;
12128                 c2 = p.y / mc->dy;
12129                 c3 = p.z / mc->dz;
12130                 iix = c1 + ceil(dd * 2.0 / mc->dx);
12131                 iiy = c2 + ceil(dd * 2.0 / mc->dy);
12132                 iiz = c3 + ceil(dd * 2.0 / mc->dz);
12133                 if (c1 < 0)
12134                         c1 = 0;
12135                 if (c2 < 0)
12136                         c2 = 0;
12137                 if (c3 < 0)
12138                         c3 = 0;
12139                 if (iix >= nx)
12140                         iix = nx - 1;
12141                 if (iiy >= ny)
12142                         iiy = ny - 1;
12143                 if (iiz >= nz)
12144                         iiz = nz - 1;
12145                 for (ix = c1; ix <= iix; ix++) {
12146                         p.x = mc->origin.x + mc->dx * ix;
12147                         for (iy = c2; iy <= iiy; iy++) {
12148                                 p.y = mc->origin.y + mc->dy * iy;
12149                                 for (iz = c3; iz <= iiz; iz++) {
12150                                         n = (ix * ny + iy) * nz + iz;
12151                                         if (mc->dp[n] == DBL_MAX) {
12152                                                 p.z = mc->origin.z + mc->dz * iz;
12153                                                 if (mc->idn == mol->bset->nmos + 1) {
12154                                                         /*  Total electron density  */
12155                                                         Int ne_alpha, ne_beta;
12156                                                         mc->dp[n] = 0.0;
12157                                                         ne_alpha = mol->bset->ne_alpha;
12158                                                         ne_beta = mol->bset->ne_beta;
12159                                                         if (mol->bset->rflag == 2 && ne_alpha < ne_beta) {
12160                                                                 /*  ROHF case: ensure ne_alpha >= ne_beta  */
12161                                                                 ne_beta = ne_alpha;
12162                                                                 ne_alpha = mol->bset->ne_beta;
12163                                                         }
12164                                                         for (sn = 1; sn <= ne_alpha; sn++) {
12165                                                                 dd = sCalcMOPoint(mol, mol->bset, sn, &p, tmp);
12166                                                                 dd = dd * dd;
12167                                                                 if (mol->bset->rflag != 0 && sn <= ne_beta)
12168                                                                         dd *= 2;
12169                                                                 mc->dp[n] += dd;
12170                                                         }
12171                                                         if (mol->bset->rflag == 0) {
12172                                                                 for (sn = 1; sn <= ne_beta; sn++) {
12173                                                                         dd = sCalcMOPoint(mol, mol->bset, sn + mol->bset->ncomps, &p, tmp);
12174                                                                         mc->dp[n] += dd * dd;
12175                                                                 }
12176                                                         }
12177                                                 } else {
12178                                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12179                                                 }
12180                                         }
12181                                 }
12182                         }
12183                 }
12184         }
12185         
12186 #else
12187         /*  (i * step, j * step, k * step)  */
12188         for (ix = 0; ix < nx; ix += step) {
12189                 for (iy = 0; iy < ny; iy += step) {
12190                         for (iz = 0; iz < nz; iz += step) {
12191                                 n = (ix * ny + iy) * nz + iz;
12192                                 if (mc->dp[n] == DBL_MAX) {
12193                                         p.x = mc->origin.x + mc->dx * ix;
12194                                         p.y = mc->origin.y + mc->dy * iy;
12195                                         p.z = mc->origin.z + mc->dz * iz;
12196                                         mc->dp[n] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12197                                 }
12198                                 n += step;
12199                         }
12200                 }
12201         }
12202         
12203         /*  Intermediate points  */
12204         for (step = 4; step > 1; step /= 2) {
12205                 hstep = step / 2;
12206                 for (sn = 0; sn <= 1; sn++) {
12207                         n = 0;
12208                         for (ix = 0; ix < nx - 1; ix += step) {
12209                                 for (iy = 0; iy < ny - 1; iy += step) {
12210                                         for (iz = 0; iz < nz - 1; iz += step) {
12211                                                 flags = 0;
12212                                                 thres = mc->thres * (sn == 0 ? 1 : -1);
12213                                                 n = (ix * ny + iy) * nz + iz;
12214                                                 if (mc->dp[n] == DBL_MAX || mc->dp[n + step * (nz * (ny + 1) + 1)] == DBL_MAX)
12215                                                         continue;
12216                                                 /*  (ix, iy, iz)  */
12217                                                 if (mc->dp[n] >= thres)
12218                                                         flags |= 1;
12219                                                 /*  (ix + step, iy, iz)  */
12220                                                 if (mc->dp[n + step * ny * nz] >= thres)
12221                                                         flags |= 2;
12222                                                 /*  (ix, iy + step, iz)  */
12223                                                 if (mc->dp[n + step * nz] >= thres)
12224                                                         flags |= 4;
12225                                                 /*  (ix + 4, iy + step, iz)  */
12226                                                 if (mc->dp[n + step * nz * (ny + 1)] >= thres)
12227                                                         flags |= 8;
12228                                                 /*  (ix, iy, iz + step)  */
12229                                                 if (mc->dp[n + step] >= thres)
12230                                                         flags |= 16;
12231                                                 if (mc->dp[n + step * (ny * nz + 1)] >= thres)
12232                                                         flags |= 32;
12233                                                 /*  (ix, iy + step, iz + step)  */
12234                                                 if (mc->dp[n + step * (nz + 1)] >= thres)
12235                                                         flags |= 64;
12236                                                 /*  (ix + step, iy + step, iz + step)  */
12237                                                 if (mc->dp[n + step * (nz * (ny + 1) + 1)] >= thres)
12238                                                         flags |= 128;
12239                                                 if (flags != 0 && flags != 255) {
12240                                                         /*  Calc the intermediate points  */
12241                                                         for (iix = 0; iix <= step; iix += hstep) {
12242                                                                 for (iiy = 0; iiy <= step; iiy += hstep) {
12243                                                                         for (iiz = 0; iiz <= step; iiz += hstep) {
12244                                                                                 if (iix % step == 0 && iiy % step == 0 && iiz % step == 0)
12245                                                                                         continue;
12246                                                                                 nn = n + (iix * ny + iiy) * nz + iiz;
12247                                                                                 if (mc->dp[nn] == DBL_MAX) {
12248                                                                                         p.x = mc->origin.x + mc->dx * (ix + iix);
12249                                                                                         p.y = mc->origin.y + mc->dy * (iy + iiy);
12250                                                                                         p.z = mc->origin.z + mc->dz * (iz + iiz);
12251                                                                                         mc->dp[nn] = sCalcMOPoint(mol, mol->bset, mc->idn, &p, tmp);
12252                                                                                 }
12253                                                                         }
12254                                                                 }
12255                                                         }
12256                                                 }
12257                                         }
12258                                 }
12259                         }
12260                 }
12261         }
12262         
12263 #endif
12264
12265         free(tmp);
12266         
12267         /*  Calculate vertex positions and normal vectors  */
12268         for (sn = 0; sn <= 1; sn++) {
12269                 n = 0;
12270                 thres = mc->thres * (sn == 0 ? 1 : -1);
12271                 VecZero(p);
12272                 for (ix = 0; ix < nx - 1; ix++) {
12273                         for (iy = 0; iy < ny - 1; iy++) {
12274                                 for (iz = 0; iz < nz - 1; iz++) {
12275                                         Double dd0, dd1;
12276                                         nn = (ix * ny + iy) * nz + iz;
12277                                         dd0 = mc->dp[nn];
12278                                         if (dd0 == DBL_MAX)
12279                                                 continue;
12280                                         if (0) {
12281                                                 dd1 = mc->dp[nn + ny * nz];
12282                                                 if (dd1 != DBL_MAX)
12283                                                         p.x = (dd1 - dd0) / mc->dx;
12284                                                 else if (ix > 0 && (dd1 = mc->dp[nn - ny * nz]) != DBL_MAX)
12285                                                         p.x = (dd0 - dd1) / mc->dx;
12286                                                 else continue;  /*  Cannot define gradient  */
12287                                                 dd1 = mc->dp[nn + nz];
12288                                                 if (dd1 != DBL_MAX)
12289                                                         p.y = (dd1 - dd0) / mc->dy;
12290                                                 else if (iy > 0 && (dd1 = mc->dp[nn - nz]) != DBL_MAX)
12291                                                         p.y = (dd0 - dd1) / mc->dy;
12292                                                 else continue;
12293                                                 dd1 = mc->dp[nn + 1];
12294                                                 if (dd1 != DBL_MAX)
12295                                                         p.z = (dd1 - dd0) / mc->dz;
12296                                                 else if (iz > 0 && (dd1 = mc->dp[nn - 1]) != DBL_MAX)
12297                                                         p.z = (dd0 - dd1) / mc->dz;
12298                                                 else continue;
12299                                                 NormalizeVec(&p, &p);
12300                                         }
12301                                         if (n + 3 >= mc->c[sn].ncubepoints) {
12302                                                 /*  Expand cubepoints[] array  */
12303                                                 mc->c[sn].cubepoints = (MCubePoint *)realloc(mc->c[sn].cubepoints, sizeof(MCubePoint) * (mc->c[sn].ncubepoints + 8192));
12304                                                 if (mc->c[sn].cubepoints == NULL) {
12305                                                         mc->c[sn].ncubepoints = 0;
12306                                                         retval = -3;
12307                                                         goto end;
12308                                                 }
12309                                                 mc->c[sn].ncubepoints += 8192;
12310                                         }
12311                                         mcp = mc->c[sn].cubepoints + n;
12312                                         iix = (dd0 >= thres ? 1 : -1);
12313                                         /*  (x, y, z)->(x + 1, y, z)  */
12314                                         dd1 = mc->dp[nn + ny * nz];
12315                                         if (dd1 != DBL_MAX) {
12316                                                 iiy = (dd1 >= thres ? 1 : -1);
12317                                                 if (iix != iiy) {
12318                                                         /*  Register  */
12319                                                         mcp->key = nn * 3;
12320                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12321                                                         mcp->pos[0] = mc->origin.x + mc->dx * (ix + mcp->d);
12322                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12323                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12324                                                         mcp->grad[0] = p.x;
12325                                                         mcp->grad[1] = p.y;
12326                                                         mcp->grad[2] = p.z;
12327                                                         mcp++;
12328                                                         n++;
12329                                                 }
12330                                         }
12331                                         /*  (x, y, z)->(x, y + 1, z)  */
12332                                         dd1 = mc->dp[nn + nz];
12333                                         if (dd1 != DBL_MAX) {
12334                                                 iiy = (dd1 >= thres ? 1 : -1);
12335                                                 if (iix != iiy) {
12336                                                         /*  Register  */
12337                                                         mcp->key = nn * 3 + 1;
12338                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12339                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12340                                                         mcp->pos[1] = mc->origin.y + mc->dy * (iy + mcp->d);
12341                                                         mcp->pos[2] = mc->origin.z + mc->dz * iz;
12342                                                         mcp->grad[0] = p.x;
12343                                                         mcp->grad[1] = p.y;
12344                                                         mcp->grad[2] = p.z;
12345                                                         mcp++;
12346                                                         n++;
12347                                                 }
12348                                         }
12349                                         /*  (x, y, z)->(x, y, z + 1)  */
12350                                         dd1 = mc->dp[nn + 1];
12351                                         if (dd1 != DBL_MAX) {
12352                                                 iiy = (dd1 >= thres ? 1 : -1);
12353                                                 if (iix != iiy) {
12354                                                         /*  Register  */
12355                                                         mcp->key = nn * 3 + 2;
12356                                                         mcp->d = (thres - dd0) / (dd1 - dd0);
12357                                                         mcp->pos[0] = mc->origin.x + mc->dx * ix;
12358                                                         mcp->pos[1] = mc->origin.y + mc->dy * iy;
12359                                                         mcp->pos[2] = mc->origin.z + mc->dz * (iz + mcp->d);
12360                                                         mcp->grad[0] = p.x;
12361                                                         mcp->grad[1] = p.y;
12362                                                         mcp->grad[2] = p.z;
12363                                                         mcp++;
12364                                                         n++;
12365                                                 }
12366                                         }
12367                                 }
12368                         }
12369                 }
12370                 if (n < mc->c[sn].ncubepoints)
12371                         mc->c[sn].cubepoints[n].key = -1;  /*  End mark  */
12372                 ncubepoints = n;
12373                 if (ncubepoints < 3) {
12374                         /*  Less than 3 points: no triangles  */
12375                         if (mc->c[sn].ntriangles > 0)
12376                                 mc->c[sn].triangles[0] = -1;  /*  End mark  */
12377                         continue;
12378                 }
12379                 
12380                 /*  Create triangle table  */
12381                 n = 0;
12382                 for (ix = 0; ix < nx - 1; ix++) {
12383                         for (iy = 0; iy < ny - 1; iy++) {
12384                                 for (iz = 0; iz < nz - 1; iz++) {
12385                                         nn = (ix * ny + iy) * nz + iz;
12386                                         iix = 0;
12387                                         if ((dd = mc->dp[nn]) == DBL_MAX)
12388                                                 continue;
12389                                         else if (dd >= thres)
12390                                                 iix |= 1;
12391                                         if ((dd = mc->dp[nn + ny * nz]) == DBL_MAX)
12392                                                 continue;
12393                                         else if (dd >= thres)
12394                                                 iix |= 2;
12395                                         if ((dd = mc->dp[nn + ny * nz + nz]) == DBL_MAX)
12396                                                 continue;
12397                                         else if (dd >= thres)
12398                                                 iix |= 4;
12399                                         if ((dd = mc->dp[nn + nz]) == DBL_MAX)
12400                                                 continue;
12401                                         else if (dd >= thres)
12402                                                 iix |= 8;
12403                                         if ((dd = mc->dp[nn + 1]) == DBL_MAX)
12404                                                 continue;
12405                                         else if (dd >= thres)
12406                                                 iix |= 16;
12407                                         if ((dd = mc->dp[nn + ny * nz + 1]) == DBL_MAX)
12408                                                 continue;
12409                                         else if (dd >= thres)
12410                                                 iix |= 32;
12411                                         if ((dd = mc->dp[nn + ny * nz + nz + 1]) == DBL_MAX)
12412                                                 continue;
12413                                         else if (dd >= thres)
12414                                                 iix |= 64;
12415                                         if ((dd = mc->dp[nn + nz + 1]) == DBL_MAX)
12416                                                 continue;
12417                                         else if (dd >= thres)
12418                                                 iix |= 128;
12419                                         for (iiy = 0; iiy < 15; iiy++) {
12420                                                 nn = sMarchingCubeTable[iix][iiy];
12421                                                 if (nn < 0)
12422                                                         break;
12423                                                 /*  key index for edges 0-11  */
12424                                                 switch (nn) {
12425                                                         case 0:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3;     break;
12426                                                         case 1:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12427                                                         case 2:  iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3;     break;
12428                                                         case 3:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 1; break;
12429                                                         case 4:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3;     break;
12430                                                         case 5:  iiz = (((ix + 1) * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12431                                                         case 6:  iiz = (( ix      * ny + iy + 1) * nz + iz + 1) * 3;     break;
12432                                                         case 7:  iiz = (( ix      * ny + iy    ) * nz + iz + 1) * 3 + 1; break;
12433                                                         case 8:  iiz = (( ix      * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12434                                                         case 9:  iiz = (((ix + 1) * ny + iy    ) * nz + iz    ) * 3 + 2; break;
12435                                                         case 10: iiz = (((ix + 1) * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12436                                                         case 11: iiz = (( ix      * ny + iy + 1) * nz + iz    ) * 3 + 2; break;
12437                                                         default:
12438                                                                 /*  Skip this triangle  */
12439                                                                 iiy = (iiy - iiy % 3) + 2;
12440                                                                 n = n - n % 3;
12441                                                                 continue;
12442                                                 }
12443                                                 /*  Look for the key index in cubepoints  */
12444                                                 c1 = 0;
12445                                                 c3 = ncubepoints - 1;
12446                                                 mcp = mc->c[sn].cubepoints;
12447                                                 while (1) {
12448                                                         int w;
12449                                                         /*  c1 is always less than c3  */
12450                                                         if (c1 + 1 == c3) {
12451                                                                 /*  end of search  */
12452                                                                 if (mcp[c1].key == iiz) {
12453                                                                         c2 = c1;
12454                                                                 } else if (mcp[c3].key == iiz) {
12455                                                                         c2 = c3;
12456                                                                 } else {
12457                                                                         c2 = -1;
12458                                                                 }
12459                                                                 break;
12460                                                         }
12461                                                         c2 = (c1 + c3) / 2;
12462                                                         w = mcp[c2].key - iiz;
12463                                                         if (w == 0)
12464                                                                 break;
12465                                                         if (w < 0) {
12466                                                                 c1 = c2;
12467                                                         } else {
12468                                                                 c3 = c2;
12469                                                         }
12470                                                 }
12471                                                 if (c2 < 0) {
12472                                                         /*  Not found: skip this triangle  */
12473                                                         iiy = (iiy - iiy % 3) + 2;
12474                                                         n = n - n % 3;
12475                                                         continue;
12476                                                 }
12477                                                 if (n + 1 >= mc->c[sn].ntriangles) {
12478                                                         /*  Expand triangles[] array  */
12479                                                         mc->c[sn].triangles = (Int *)realloc(mc->c[sn].triangles, sizeof(Int) * (mc->c[sn].ntriangles + 8192));
12480                                                         if (mc->c[sn].triangles == NULL) {
12481                                                                 mc->c[sn].ntriangles = 0;
12482                                                                 retval = -4;
12483                                                                 goto end;
12484                                                         }
12485                                                         mc->c[sn].ntriangles += 8192;
12486                                                 }
12487                                                 mc->c[sn].triangles[n] = c2;
12488                                                 n++;
12489                                         }
12490                                 }
12491                         }
12492                 }
12493                 if (n < mc->c[sn].ntriangles)
12494                         mc->c[sn].triangles[n] = -1;  /*  End mark  */
12495                 
12496                 /*  Estimate the normal vector  */
12497                 for (n = 0, ip = mc->c[sn].triangles; ip[n] >= 0; n += 3) {
12498                         Vector v[3];
12499                         for (ix = 0; ix < 3; ix++) {
12500                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12501                                 v[ix].x = mcp->pos[0];
12502                                 v[ix].y = mcp->pos[1];
12503                                 v[ix].z = mcp->pos[2];
12504                         }
12505                         VecDec(v[2], v[0]);
12506                         VecDec(v[1], v[0]);
12507                         VecCross(v[0], v[1], v[2]);
12508                         NormalizeVec(v, v);
12509                         for (ix = 0; ix < 3; ix++) {
12510                                 mcp = &(mc->c[sn].cubepoints[ip[n + ix]]);
12511                                 mcp->grad[0] += v[0].x;
12512                                 mcp->grad[1] += v[0].y;
12513                                 mcp->grad[2] += v[0].z;
12514                         }
12515                 }
12516                 for (n = 0, mcp = mc->c[sn].cubepoints; mcp->key >= 0; mcp++) {
12517                         if (mcp->grad[0] != 0.0 || mcp->grad[1] != 0.0 || mcp->grad[2] != 0.0) {
12518                                 dd = 1.0 / sqrt(mcp->grad[0] * mcp->grad[0] + mcp->grad[1] * mcp->grad[1] + mcp->grad[2] * mcp->grad[2]);
12519                                 if (mc->thres < 0.0)
12520                                         dd = -dd;
12521                                 mcp->grad[0] *= dd;
12522                                 mcp->grad[1] *= dd;
12523                                 mcp->grad[2] *= dd;
12524                         }
12525                 }
12526         }
12527         retval = 0;
12528         MoleculeCallback_notifyModification(mol, 0);
12529 end:
12530         /*  For debug  */
12531         if (0) {
12532                 char *MyAppCallback_getDocumentHomeDir(void);
12533                 FILE *fp;
12534                 char *s;
12535                 Double dmax, dmin;
12536                 asprintf(&s, "%s/%s", MyAppCallback_getDocumentHomeDir(), "mcube_log.txt");
12537                 fp = fopen(s, "w");
12538                 dmax = -1e8;
12539                 dmin = 1e8;
12540                 for (n = 0; n < mc->nx * mc->ny * mc->nz; n++) {
12541                         if (mc->dp[n] == DBL_MAX)
12542                                 continue;
12543                         if (dmax < mc->dp[n])
12544                                 dmax = mc->dp[n];
12545                         if (dmin > mc->dp[n])
12546                                 dmin = mc->dp[n];
12547                 }
12548                 dmax = fabs(dmax);
12549                 dmin = fabs(dmin);
12550                 if (dmax < dmin)
12551                         dmax = dmin;
12552                 dmax = 1.001 * dmax;
12553                 fprintf(fp, "thres = %g = 100\n", mc->thres);
12554                 for (iz = 0; iz < mc->nz; iz++) {
12555                         fprintf(fp, "z = %d\n", iz);
12556                         for (iy = 0; iy < mc->ny; iy++) {
12557                                 for (ix = 0; ix < mc->nx; ix++) {
12558                                         n = (ix * ny + iy) * nz + iz;
12559                                         dd = mc->dp[n];
12560                                         if (dd == DBL_MAX)
12561                                                 fprintf(fp, " XXX ");
12562                                         else {
12563                                                 dd = dd * 100 / mc->thres;
12564                                                 if (dd > 999.0)
12565                                                         dd = 999.0;
12566                                                 else if (dd < -999.0)
12567                                                         dd = -999.0;
12568                                                 fprintf(fp, "%4d ", (int)(dd));
12569                                         }
12570                                 }
12571                                 fprintf(fp, "\n");
12572                         }
12573                         fprintf(fp, "\n");
12574                 }
12575                 
12576                 for (sn = 0; sn <= 1; sn++) {
12577                         for (n = 0; n < mc->c[sn].ncubepoints; n++) {
12578                                 MCubePoint *mcp = mc->c[sn].cubepoints + n;
12579                                 nn = mcp->key;
12580                                 if (nn == -1)
12581                                         break;
12582                                 iix = nn % 3;
12583                                 iz = nn / 3 % mc->nz;
12584                                 iy = nn / (3 * mc->nz) % mc->ny;
12585                                 ix = nn / (3 * mc->nz * mc->ny);
12586                                 fprintf(fp, "%c%d:[%d,%d,%d,%d] (%g,[%g,%g,%g],[%g,%g,%g])\n", (sn == 0 ? 'p' : 'P'),
12587                                                 n, ix, iy, iz, iix,
12588                                                 mcp->d, mcp->pos[0], mcp->pos[1], mcp->pos[2], mcp->grad[0], mcp->grad[1], mcp->grad[2]);
12589                         }
12590                         for (n = 0; n < mc->c[sn].ntriangles; n += 3) {
12591                                 if (mc->c[sn].triangles[n] < 0)
12592                                         break;
12593                                 fprintf(fp, "%c%d:(%d,%d,%d)\n", (sn == 0 ? 't' : 'T'), n / 3,
12594                                                 mc->c[sn].triangles[n], mc->c[sn].triangles[n + 1], mc->c[sn].triangles[n + 2]);
12595                         }
12596                 }
12597                 fclose(fp);
12598         }
12599         
12600         return retval;
12601 }
12602
12603 void
12604 MoleculeDeallocateMCube(MCube *mcube)
12605 {
12606         free(mcube->dp);
12607         free(mcube->radii);
12608         free(mcube->c[0].cubepoints);
12609         free(mcube->c[0].triangles);
12610         free(mcube->c[1].cubepoints);
12611         free(mcube->c[1].triangles);
12612         free(mcube);
12613 }