
#if defined(DARWIN) || defined(FREEBSD)
#else
#include <malloc.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#define NUMAT 2000
#define MAXBND  2*NUMAT
#define MAXANG  3*NUMAT
#define MAXTORS 4*NUMAT
#define MXCON 10
#define MAX13 MXCON*3
#define MAX14 MXCON*9
#define NUMADD 50000
static int addat = NUMADD;

typedef struct { double coo[NUMAT][3];
		 double q[NUMAT];
		 double fr[NUMAT][3];
		 double ftmp[NUMAT];
		 int iaton[NUMAT];
		 int iopt[NUMAT];
		 int n13[NUMAT];
		 int i13[NUMAT][MAX13];
		 int n14[NUMAT];
		 int i14[NUMAT][MAX14];
		 int iconn[NUMAT][MXCON+1];
		 short int ityp[NUMAT];
		 
               } ATOMSTRU;

static ATOMSTRU *atomptr;

typedef struct { 
		  double bl[MAXBND];
		  double bk[MAXBND];
		  double ango[MAXANG];
		  double ak[MAXANG];
		  double trs1[MAXTORS][4];
		  double trs2[MAXTORS][4];
		  double trs3[MAXTORS][4];
		  double trs4[MAXTORS][4];
		  double trsi1[MAXTORS][4];
		  double trsi2[MAXTORS][4];
		  int maxbnd;
		  int maxang;
		  int maxtors;
		  int nbnd;
		  int ibnd[MAXBND][2];
		  int nang;
		  int iang[MAXANG][3];
		  int nt;
		  int it[MAXTORS][4];
		  int nti;
		  int iti[MAXTORS][4];
               } FORSTRU;

static FORSTRU *forvarptr;

typedef struct { double coot[NUMAT][3];
		 double fort[NUMAT][3];
		 double zr[NUMAT][3];
		 double y[NUMAT][3];
		 double yt[NUMAT][3];
		 double pt[NUMAT][3];
		 double s[NUMAT][3];
               } OPTSCRSTRU;

static OPTSCRSTRU *optscr;

typedef struct { double *coo;
		 double *q;
		 double *fr;
		 double *ftmp;
		 double *coot;
		 double *frt;
		 double *zr;
		 double *y;
		 double *yt;
		 double *pt;
		 double *s;
		 int *iaton;
		 int *iopt;
		 int *n13;
		 int *i13;
		 int *n14;
		 int *i14;
		 int *iconn;
		 short int *ityp;
		 int *nbnd;
		 int *nang;
		 int *nt;
		 int *nti;
		 int *ibnd;
		 int *iang;
		 int *it;
		 int *iti;
		 double *bl;
		 double *bk;
		 double *ango;
		 double *ak;
		 double *trs1;
		 double *trs2;
		 double *trs3;
		 double *trs4;
		 double *trsi1;
		 double *trsi2;
		 int *mxnat;
		 int *iatoms;
		 int mxorg;
		 int *maxbnd;
		 int *maxang;
		 int *maxtors;
               } COOSTRU;

static COOSTRU xyz;
static COOSTRU TMPxyz;

#if defined(VMS) || defined(UNDERSC)
parptr(nptr, fptr, ffptr, nitem)
#else
#ifdef CRAY
PARPTR(nptr, fptr, ffptr, nitem)
#else
parptr_(nptr, fptr, ffptr, nitem)
#endif
#endif

int *nptr;
float *fptr;
float *ffptr;
int *nitem;
{

    switch(*nptr) {
    case 0: atomptr   = (ATOMSTRU *) fptr; 
	    xyz.coo   = (double *) atomptr->coo;
	    xyz.q     = (double *) atomptr->q;
	    xyz.fr   = (double *) atomptr->fr;
	    xyz.ftmp  = (double *) atomptr->ftmp;
	    xyz.iaton = (int *) atomptr->iaton;
	    xyz.iopt  = (int *) atomptr->iopt;
	    xyz.n13   = (int *) atomptr->n13;
	    xyz.i13   = (int *) atomptr->i13;
	    xyz.n14   = (int *) atomptr->n14;
	    xyz.i14   = (int *) atomptr->i14;
	    xyz.iconn = (int *) atomptr->iconn;
	    xyz.ityp  = (short int *) atomptr->ityp;
	    break;
    case 1: forvarptr = (FORSTRU *) fptr; 
	    xyz.maxbnd =  (int *) &forvarptr->maxbnd;
	    xyz.maxang =  (int *) &forvarptr->maxang;
	    xyz.maxtors = (int *) &forvarptr->maxtors;
	    xyz.nbnd  = (int *) &forvarptr->nbnd;
	    xyz.nang  = (int *) &forvarptr->nang;
	    xyz.nt    = (int *) &forvarptr->nt;
	    xyz.nti   = (int *) &forvarptr->nti;
	    xyz.ibnd  = (int *) forvarptr->ibnd;
	    xyz.iang  = (int *) forvarptr->iang;
	    xyz.it    = (int *) forvarptr->it;
	    xyz.iti   = (int *) forvarptr->iti;
	    xyz.bl    = (double *) forvarptr->bl;
	    xyz.bk    = (double *) forvarptr->bk;
	    xyz.ango  = (double *) forvarptr->ango;
	    xyz.ak    = (double *) forvarptr->ak;
	    xyz.trs1  = (double *) forvarptr->trs1;
	    xyz.trs2  = (double *) forvarptr->trs2;
	    xyz.trs3  = (double *) forvarptr->trs3;
	    xyz.trs4  = (double *) forvarptr->trs4;
	    xyz.trsi1 = (double *) forvarptr->trsi1;
	    xyz.trsi2 = (double *) forvarptr->trsi2;
	    break;
    case 2: 
	    xyz.iatoms = nitem;
	    xyz.mxorg = NUMAT;
	    break;
    case 3: optscr = (OPTSCRSTRU *) fptr; 
	    xyz.coot  = (double *) optscr->coot;
	    xyz.frt   = (double *) optscr->fort;
	    xyz.zr    = (double *) optscr->zr;
	    xyz.y     = (double *) optscr->y;
	    xyz.yt    = (double *) optscr->yt;
	    xyz.pt    = (double *) optscr->pt;
	    xyz.s     = (double *) optscr->s;
	    break;
    case 4: 
	    xyz.mxnat = nitem;
	    break;
    }
}

#if defined(VMS) || defined(UNDERSC)
allcoo(ZSizep)
#else
#ifdef CRAY
ALLCOO(ZSizep)
#else
allcoo_(ZSizep)
#endif
#endif
int *ZSizep;
{
   int memstat;
   double d;
   int i;
   short int j;
   int ZSize;

   ZSize = *xyz.mxnat + *ZSizep;
   memstat = 1;

   TMPxyz = xyz;

   if ((xyz.coo = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.q = (double *) malloc((sizeof d)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.fr = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.coot = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.frt = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.zr = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.y = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.yt = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.pt = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.s = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.iaton = (int *) malloc((sizeof i)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.iopt = (int *) malloc((sizeof i)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.ftmp = (double *) malloc((sizeof d)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.n13 = (int *) malloc((sizeof i)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.i13 = (int *) malloc((sizeof i)*ZSize*(MAX13))) == NULL) {
	memstat = 0;
   }

   if ((xyz.n14 = (int *) malloc((sizeof i)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.i14 = (int *) malloc((sizeof i)*ZSize*(MAX14))) == NULL) {
	memstat = 0;
   }

   if ((xyz.iconn = (int *) malloc((sizeof i)*ZSize*(MXCON+1))) == NULL) {
	memstat = 0;
   }

   if ((xyz.ityp = (short int *) malloc((sizeof j)*ZSize)) == NULL) {
	memstat = 0;
   }

   if ((xyz.ibnd = (int *) malloc((sizeof i)*ZSize*2*2)) == NULL) {
	memstat = 0;
   }

   if ((xyz.iang = (int *) malloc((sizeof i)*ZSize*3*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.it = (int *) malloc((sizeof i)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.iti = (int *) malloc((sizeof i)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.bl = (double *) malloc((sizeof d)*ZSize*2)) == NULL) {
	memstat = 0;
   }

   if ((xyz.bk = (double *) malloc((sizeof d)*ZSize*2)) == NULL) {
	memstat = 0;
   }

   if ((xyz.ango = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.ak = (double *) malloc((sizeof d)*ZSize*3)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trs1 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trs2 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trs3 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trs4 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trsi1 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if ((xyz.trsi2 = (double *) malloc((sizeof d)*ZSize*4*4)) == NULL) {
	memstat = 0;
   }

   if (!memstat) {
	fprintf(stderr,"Out of memory\n");
	xyz = TMPxyz;
   } else {
	for (i=0; i < xyz.mxorg; i++) {
	   for (j=0; j<3; j++) {
		xyz.coo[i*3+j] = TMPxyz.coo[i*3+j];
		xyz.fr[i*3+j] = TMPxyz.fr[i*3+j];
	   }
	   xyz.q[i] = TMPxyz.q[i];
	   xyz.iaton[i] = TMPxyz.iaton[i];
	   xyz.iopt[i] = TMPxyz.iopt[i];
	   xyz.n13[i] = TMPxyz.n13[i];
	   xyz.n14[i] = TMPxyz.n14[i];
	   xyz.ityp[i] = TMPxyz.ityp[i];
	   for (j=0; j<MXCON; j++)
		xyz.iconn[i*(MXCON+1)+j] = TMPxyz.iconn[i*(MXCON+1)+j];
	   for (j=0; j<MAX13; j++)
		xyz.i13[i*MAX13+j] = TMPxyz.i13[i*MAX13+j];
	   for (j=0; j<MAX14; j++)
		xyz.i13[i*MAX14+j] = TMPxyz.i14[i*MAX14+j];
	   for (j=0; j<2; j++)
		xyz.ibnd[i*2+j] = TMPxyz.ibnd[i*2+j];
	   for (j=0; j<3; j++)
		xyz.iang[i*3+j] = TMPxyz.iang[i*3+j];
	   for (j=0; j<4; j++)
		xyz.it[i*4+j] = TMPxyz.it[i*4+j];
	   for (j=0; j<4; j++)
		xyz.iti[i*4+j] = TMPxyz.iti[i*4+j];
	   xyz.bl[i] = TMPxyz.bl[i];
	   xyz.bk[i] = TMPxyz.bk[i];
	   xyz.ango[i] = TMPxyz.ango[i];
	   xyz.ak[i] = TMPxyz.ak[i];
	   for (j=0; j<4; j++) {
		xyz.trs1[i*4+j] = TMPxyz.trs1[i*4+j];
		xyz.trs2[i*4+j] = TMPxyz.trs2[i*4+j];
		xyz.trs3[i*4+j] = TMPxyz.trs3[i*4+j];
		xyz.trs4[i*4+j] = TMPxyz.trs4[i*4+j];
		xyz.trsi1[i*4+j] = TMPxyz.trsi1[i*4+j];
		xyz.trsi2[i*4+j] = TMPxyz.trsi2[i*4+j];
	   }
	}

	if (*TMPxyz.mxnat != TMPxyz.mxorg) {
	   free(TMPxyz.coo);
	   free(TMPxyz.q);
	   free(TMPxyz.fr);
	   free(TMPxyz.coot);
	   free(TMPxyz.frt);
	   free(TMPxyz.zr);
	   free(TMPxyz.y);
	   free(TMPxyz.yt);
	   free(TMPxyz.pt);
	   free(TMPxyz.s);
	   free(TMPxyz.ftmp);
	   free(TMPxyz.iaton);
	   free(TMPxyz.iopt);
	   free(TMPxyz.n13);
	   free(TMPxyz.i13);
	   free(TMPxyz.n14);
	   free(TMPxyz.i14);
	   free(TMPxyz.iconn);
	   free(TMPxyz.ityp);
	   free(TMPxyz.ibnd);
	   free(TMPxyz.iang);
	   free(TMPxyz.it);
	   free(TMPxyz.iti);
	   free(TMPxyz.bl);
	   free(TMPxyz.bk);
	   free(TMPxyz.ango);
	   free(TMPxyz.ak);
	   free(TMPxyz.trs1);
	   free(TMPxyz.trs2);
	   free(TMPxyz.trs3);
	   free(TMPxyz.trs4);
	   free(TMPxyz.trsi1);
	   free(TMPxyz.trsi2);
	}
	*xyz.mxnat   = ZSize;
	*xyz.maxbnd  = ZSize*2;
	*xyz.maxang  = ZSize*3;
	*xyz.maxtors = ZSize*4;
   }
}

#if defined(VMS) || defined(UNDERSC)
angarr(istat)
#else
#ifdef CRAY
ANGARR(istat)
#else
angarr_(istat)
#endif
#endif
int *istat;
{
#if defined(VMS) || defined(UNDERSC)
	angard(istat,
#else
#ifdef CRAY
	ANGARD(istat,
#else
	angard_(istat,
#endif
#endif
	xyz.nang,xyz.iang,xyz.ango,xyz.ak,xyz.maxang,xyz.iconn,xyz.ityp);
}

#if defined(VMS) || defined(UNDERSC)
bndarr(istat)
#else
#ifdef CRAY
BNDARR(istat)
#else
bndarr_(istat)
#endif
#endif
int *istat;
{
#if defined(VMS) || defined(UNDERSC)
	bndard(istat,
#else
#ifdef CRAY
	BNDARD(istat,
#else
	bndard_(istat,
#endif
#endif
	xyz.nbnd,xyz.ibnd,xyz.bl,xyz.bk,xyz.maxbnd,xyz.iconn,xyz.ityp);
}

#if defined(VMS) || defined(UNDERSC)
itrarr(istat)
#else
#ifdef CRAY
ITRARR(istat)
#else
itrarr_(istat)
#endif
#endif
int *istat;
{
#if defined(VMS) || defined(UNDERSC)
	itrard(istat,
#else
#ifdef CRAY
	ITRARD(istat,
#else
	itrard_(istat,
#endif
#endif
		xyz.maxtors,xyz.nti,xyz.iti,
		xyz.trsi1,xyz.trsi2,xyz.iconn,xyz.ityp);
}

#if defined(VMS) || defined(UNDERSC)
torarr(istat)
#else
#ifdef CRAY
TORARR(istat)
#else
torarr_(istat)
#endif
#endif
int *istat;
{
#if defined(VMS) || defined(UNDERSC)
	torard(istat,
#else
#ifdef CRAY
	TORARD(istat,
#else
	torard_(istat,
#endif
#endif
		xyz.maxtors,xyz.nbnd,xyz.ibnd,
		xyz.nt,xyz.it,xyz.trs1,xyz.trs2,xyz.trs3,xyz.trs4,
		xyz.iconn,xyz.ityp);
}

#if defined(VMS) || defined(UNDERSC)
asschg()
#else
#ifdef CRAY
ASSCHG()
#else
asschg_()
#endif
#endif
{
#if defined(VMS) || defined(UNDERSC)
	asschd(
#else
#ifdef CRAY
	ASSCHD(
#else
	asschd_(
#endif
#endif
		xyz.ityp,xyz.q);
}

#if defined(VMS) || defined(UNDERSC)
conn34(istat)
#else
#ifdef CRAY
CONN34(istat)
#else
conn34_(istat)
#endif
#endif
int *istat;
{
#if defined(VMS) || defined(UNDERSC)
	cond34(istat,
#else
#ifdef CRAY
	COND34(istat,
#else
	cond34_(istat,
#endif
#endif
		xyz.n13,xyz.i13,xyz.n14,xyz.i14,xyz.iconn);
}

#if defined(VMS) || defined(UNDERSC)
getinp(igtinp)
#else
#ifdef CRAY
GETINP(igtinp)
#else
getinp_(igtinp)
#endif
#endif
int *igtinp;
{
#if defined(VMS) || defined(UNDERSC)
	getind(igtinp,
#else
#ifdef CRAY
	GETIND(igtinp,
#else
	getind_(igtinp,
#endif
#endif
		xyz.iconn,xyz.iopt,xyz.ityp,xyz.coo,xyz.q);
	if (*igtinp == -1) {
           fprintf(stderr,"xyz.iatoms %d\n",*xyz.iatoms);
	   addat = *xyz.iatoms;
#if defined(VMS) || defined(UNDERSC)
	   allcoo(&addat);
#else
#ifdef CRAY
	   ALLCOO(&addat);
#else
	   allcoo_(&addat);
#endif
#endif
#if defined(VMS) || defined(UNDERSC)
	   getind(igtinp,
#else
#ifdef CRAY
	   GETIND(igtinp,
#else
	   getind_(igtinp,
#endif
#endif
		xyz.iconn,xyz.iopt,xyz.ityp,xyz.coo,xyz.q);
	}
}

#if defined(VMS) || defined(UNDERSC)
wrtout(iun,emin)
#else
#ifdef CRAY
WRTOUT(iun,emin)
#else
wrtout_(iun,emin)
#endif
#endif
int *iun;
double *emin;
{
#if defined(VMS) || defined(UNDERSC)
	wrtoud(iun,emin,
#else
#ifdef CRAY
	WRTOUD(iun,emin,
#else
	wrtoud_(iun,emin,
#endif
#endif
		xyz.iconn,xyz.ityp,xyz.coo,xyz.q);
}

#if defined(VMS) || defined(UNDERSC)
enegrd(energy,par,forces)
#else
#ifdef CRAY
ENEGRD(energy,par,forces)
#else
enegrd_(energy,par,forces)
#endif
#endif
double *energy;
double *par;
double *forces;
{
#if defined(VMS) || defined(UNDERSC)
	enegdd(energy,par,forces,
#else
#ifdef CRAY
	ENEGDD(energy,par,forces,
#else
	enegdd_(energy,par,forces,
#endif
#endif
		xyz.ftmp,
		xyz.n13,xyz.i13,xyz.n14,xyz.i14,
		xyz.nbnd,xyz.ibnd,xyz.bl,xyz.bk,
		xyz.nang,xyz.iang,xyz.ango,xyz.ak,
		xyz.nt,xyz.it,xyz.trs1,xyz.trs2,xyz.trs3,xyz.trs4,
		xyz.nti,xyz.iti,xyz.trsi1,xyz.trsi2,
		xyz.q,xyz.iconn,xyz.ityp);
}

#if defined(VMS) || defined(UNDERSC)
optimise(energy,gtol,nsd)
#else
#ifdef CRAY
OPTIMISE(energy,gtol,nsd)
#else
optimise_(energy,gtol,nsd)
#endif
#endif
double *energy;
double *gtol;
int *nsd;
{
#if defined(VMS) || defined(UNDERSC)
	optimisd(energy,gtol,nsd,
#else
#ifdef CRAY
	OPTIMISD(energy,gtol,nsd,
#else
	optimisd_(energy,gtol,nsd,
#endif
#endif
		xyz.coo,xyz.coot,xyz.fr,xyz.frt,xyz.zr,xyz.y,xyz.yt,
		xyz.pt,xyz.s);
}
