/* Steven Andrews, 12/02, 1/03 */
/* See documentation called Ising doc */
/* Copyright 2003 by Steven Andrews.  Permission is granted
   for non-commercial use of and modifications to the code. */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "Ising.h"

#define RGTS(s) (s)
#define DWNS(s) (s&~3|s+1&3)
#define LFTS(s) (s&~3|s+2&3)
#define UPPS(s) (s&~3|s+3&3)
#define RGTH(s) (s)
#define RDWH(s) (s&~7|((s&7)+1)%6)
#define LDWH(s) (s&~7|((s&7)+2)%6)
#define LFTH(s) (s&~7|((s&7)+3)%6)
#define LUPH(s) (s&~7|((s&7)+4)%6)
#define RUPH(s) (s&~7|((s&7)+5)%6)
#define RGTT(s) (s)
#define LDWT(s) (s&~3|((s&3)+1)%3)
#define LUPT(s) (s&~3|((s&3)+2)%3)

int clsizeS(int *g,int nx,int ny,int ix,int iy);
int clsizeH(int *g,int nx,int ny,int ix,int iy);
int clsizeT(int *g,int nx,int ny,int ix,int iy);


Lattice SetUpIsing(char shape,int nx,int ny,int spc,int *nunit,int rot) {
	int i,ix,iy,s,*g,ntot,sm,max;
	Lattice lt;

	if(shape!='S'&&shape!='H'&&shape!='T') return NULL;
	if(nx<1||nx>RAND_MAX+1||ny<1||ny>RAND_MAX+1) return NULL;
	if(spc<1) return NULL;
	if(nunit) for(ntot=0,s=0;s<spc;s++) ntot+=nunit[s];
	else ntot=0;
	if(nx*ny<ntot) return NULL;
	if(rot<0||rot>1) return NULL;

	lt=(Lattice)malloc(sizeof(struct LatticeType));
	if(!lt) return NULL;
	lt->g=NULL;
	lt->shape=shape;
	lt->nx=nx;
	lt->ny=ny;
	lt->spc=spc;
	lt->rot=rot;
	lt->g=g=(int*)calloc(nx*ny,sizeof(int));
	if(!g) {free(lt);return NULL;}
	if(!nunit) {
		for(i=0;i<nx*ny;i++) g[i]=0;
		return lt; }
	sm=-1;
	max=nx*ny-ntot;
	for(s=0;s<spc;s++)
		if(nunit[s]>max) {
			max=nunit[s];
			sm=s; }
	for(i=0;i<nx*ny;i++) g[i]=sm+1;
	for(s=-1;s<spc;s++)
		if(s!=sm)
			for(i=0;i<(s==-1?nx*ny-ntot:nunit[s]);)
				if(g[(iy=rand()%ny)*nx+(ix=rand()%nx)]==sm+1) {
					g[iy*nx+ix]=s+1;
					i++; }
	if(rot) {
		if(shape=='S') rot=4;
		else if(shape=='H') rot=6;
		else rot=3;
		for(i=0;i<nx*ny;i++) {
			g[i]*=shape=='H'?8:4;
			if(g[i]) g[i]+=rand()%rot; }}
	return lt; }


void FreeIsing(Lattice lt) {
	if(!lt) return;
	if(lt->g) free(lt->g);
	free(lt);
	return; }


void DisplayIsing(Lattice lt) {
	int ix,iy,nx,ny,*g,spc,rot;
	char c,shape;

	if(!lt||!lt->g) {
		printf("NULL lattice.\n");
		return; }
	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	shape=lt->shape;
	spc=lt->spc;
	if(lt->rot) rot=shape=='H'?8:4;
	else rot=0;
	if(spc==1) c='x'-1;
	else c='a'-1;
	for(ix=0;ix<(rot?3*nx:2*nx);ix++) putchar('-');
	putchar('\n');
	for(iy=0;iy<ny;iy++) {
		if(shape=='H'&&iy&1||shape=='T'&&iy&2) putchar(' ');
		for(ix=0;ix<nx;ix++)
			if(g[iy*nx+ix]&&rot) printf("%c%i ",c+g[iy*nx+ix]/rot,g[iy*nx+ix]%rot);
			else if(rot) printf("   ");
			else if(g[iy*nx+ix]) printf("%c ",c+g[iy*nx+ix]);
			else printf("  ");
		putchar('\n'); }
	for(ix=0;ix<(rot?3*nx:2*nx);ix++) putchar('-');
	putchar('\n');
	return; }


void MetropolisIsingS(Lattice lt,int itmax,float *eps) {
	static int hrz[8]={1,1,0,-1,-1,-1,0,1};
	static int vrt[8]={0,1,1,1,0,-1,-1,-1};
	static int hrzo[16]={1,1,1,0,0,-1,-1,-1,-1,0,0,1,0,0,0,0};
	static int vrto[16]={0,0,1,1,1,1,0,0,-1,-1,-1,-1,0,0,0,0};
	int rnd,ix,iy,jx,jy,b,nx,ny,*g,small;
	int is,js,is2,js2,isspc,jsspc,spc,rot;
	int kir,kid,kil,kiu,kjr,kjd,kjl,kju;
	float de;
	int prob[9],db;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	if((nx*ny&-nx*ny)!=nx*ny) return;
	spc=lt->spc;
	rot=lt->rot;
	small=nx*ny<=RAND_MAX+1?nx*ny-1:0;
	nx--;
	ny--;
	for(b=0;nx>>b&1;b+=1);

	if(rot) {
		spc=(spc+1)*4;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			kir=g[iy<<b|(ix+1)&nx];
			kid=g[((iy+1)&ny)<<b|ix];
			kil=g[iy<<b|(ix-1)&nx];
			kiu=g[((iy-1)&ny)<<b|ix];
			rnd=rand();
			if((rnd&12)==12) {													// S, rot, no translation
				de=-eps[RGTS(is)*spc+LFTS(kir)];
				de-=eps[DWNS(is)*spc+UPPS(kid)];
				de-=eps[LFTS(is)*spc+RGTS(kil)];
				de-=eps[UPPS(is)*spc+DWNS(kiu)];
				g[iy<<b|ix]=is2=is&~3|rnd>>4&3;
				de+=eps[RGTS(is2)*spc+LFTS(kir)];
				de+=eps[DWNS(is2)*spc+UPPS(kid)];
				de+=eps[LFTS(is2)*spc+RGTS(kil)];
				de+=eps[UPPS(is2)*spc+DWNS(kiu)];
				if(de>0&&rand()>RAND_MAX*exp(-de))
					g[iy<<b|ix]=is; }
			else {																			// S, rot, with translation
				jx=(ix+hrzo[rnd&15])&nx;
				jy=(iy+vrto[rnd&15])&ny;
				js=g[jy<<b|jx];
				kjr=g[jy<<b|(jx+1)&nx];
				kjd=g[((jy+1)&ny)<<b|jx];
				kjl=g[jy<<b|(jx-1)&nx];
				kju=g[((jy-1)&ny)<<b|jx];
				de=-eps[RGTS(is)*spc+LFTS(kir)];
				de-=eps[DWNS(is)*spc+UPPS(kid)];
				de-=eps[LFTS(is)*spc+RGTS(kil)];
				de-=eps[UPPS(is)*spc+DWNS(kiu)];
				de-=eps[RGTS(js)*spc+LFTS(kjr)];
				de-=eps[DWNS(js)*spc+UPPS(kjd)];
				de-=eps[LFTS(js)*spc+RGTS(kjl)];
				de-=eps[UPPS(js)*spc+DWNS(kju)];
				g[iy<<b|ix]=is2=js?(js&~3|rnd>>4&3):0;
				g[jy<<b|jx]=js2=is&~3|rnd>>6&3;
				kir=g[iy<<b|(ix+1)&nx];
				kid=g[((iy+1)&ny)<<b|ix];
				kil=g[iy<<b|(ix-1)&nx];
				kiu=g[((iy-1)&ny)<<b|ix];
				kjr=g[jy<<b|(jx+1)&nx];
				kjd=g[((jy+1)&ny)<<b|jx];
				kjl=g[jy<<b|(jx-1)&nx];
				kju=g[((jy-1)&ny)<<b|jx];
				de+=eps[RGTS(is2)*spc+LFTS(kir)];
				de+=eps[DWNS(is2)*spc+UPPS(kid)];
				de+=eps[LFTS(is2)*spc+RGTS(kil)];
				de+=eps[UPPS(is2)*spc+DWNS(kiu)];
				de+=eps[RGTS(js2)*spc+LFTS(kjr)];
				de+=eps[DWNS(js2)*spc+UPPS(kjd)];
				de+=eps[LFTS(js2)*spc+RGTS(kjl)];
				de+=eps[UPPS(js2)*spc+DWNS(kju)];
				if(de>0&&rand()>RAND_MAX*exp(-de)) {
					g[iy<<b|ix]=is;
					g[jy<<b|jx]=js; }}}}
	else if(spc>1) {																// S, no rotation
		spc++;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			rnd=rand()&7;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			js=g[jy<<b|jx];
			isspc=is*spc;
			jsspc=js*spc;
			de=-eps[isspc+g[iy<<b|(ix+1)&nx]];
			de-=eps[isspc+g[((iy+1)&ny)<<b|ix]];
			de-=eps[isspc+g[iy<<b|(ix-1)&nx]];
			de-=eps[isspc+g[((iy-1)&ny)<<b|ix]];
			de-=eps[jsspc+g[jy<<b|(jx+1)&nx]];
			de-=eps[jsspc+g[((jy+1)&ny)<<b|jx]];
			de-=eps[jsspc+g[jy<<b|(jx-1)&nx]];
			de-=eps[jsspc+g[((jy-1)&ny)<<b|jx]];
			g[iy<<b|ix]=js;
			g[jy<<b|jx]=is;
			de+=eps[isspc+g[jy<<b|(jx+1)&nx]];
			de+=eps[isspc+g[((jy+1)&ny)<<b|jx]];
			de+=eps[isspc+g[jy<<b|(jx-1)&nx]];
			de+=eps[isspc+g[((jy-1)&ny)<<b|jx]];
			de+=eps[jsspc+g[iy<<b|(ix+1)&nx]];
			de+=eps[jsspc+g[((iy+1)&ny)<<b|ix]];
			de+=eps[jsspc+g[iy<<b|(ix-1)&nx]];
			de+=eps[jsspc+g[((iy-1)&ny)<<b|ix]];
			if(de>0&&rand()>RAND_MAX*exp(-de)) {
				g[iy<<b|ix]=is;
				g[jy<<b|jx]=js; }}}
	else {																					// S, no rotation, no species
		for(db=-4;db<=4;db++) prob[db+4]=exp(-db**eps)<1?RAND_MAX*exp(-db**eps):RAND_MAX;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			rnd=rand()&7;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			if(!g[jy<<b|jx]) {
				g[iy<<b|ix]=0;
				db=-g[iy<<b|(ix+1)&nx];
				db-=g[((iy+1)&ny)<<b|ix];
				db-=g[iy<<b|(ix-1)&nx];
				db-=g[((iy-1)&ny)<<b|ix];
				db+=g[jy<<b|(jx+1)&nx];
				db+=g[((jy+1)&ny)<<b|jx];
				db+=g[jy<<b|(jx-1)&nx];
				db+=g[((jy-1)&ny)<<b|jx];
				if(prob[db+4]==RAND_MAX||rand()<=prob[db+4]) g[jy<<b|jx]=1;
				else g[iy<<b|ix]=1; }}}
	return; }


void MetropolisIsingH(Lattice lt,int itmax,float *eps) {
	static int hrz[16]={0,0, 0,0, 1,1, 0,1, -1,0, -1,-1, -1,0, 0,1};
	static int vrt[16]={0,0, 0,0, 0,0, 1,1, 1,1, 0,0, -1,-1, -1,-1};
	int rnd,ix,iy,jx,jy,nx,ny,b,*g,small;
	int is,is2,js,js2,spc,isspc,jsspc,rot;
	int ki0,ki1,ki2,ki3,ki4,ki5,kj0,kj1,kj2,kj3,kj4,kj5;
	float de;
	int prob[13],db;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	if((nx*ny&-nx*ny)!=nx*ny) return;
	spc=lt->spc;
	rot=lt->rot;
	small=nx*ny<=RAND_MAX+1?nx*ny-1:0;
	nx--;
	ny--;
	for(b=0;nx>>b&1;b+=1);

	if(rot) {
		spc=(spc+1)*8;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			if(iy&1) {
				ki0=g[iy<<b|(ix+1)&nx];
				ki1=g[((iy+1)&ny)<<b|(ix+1)&nx];
				ki2=g[((iy+1)&ny)<<b|ix];
				ki3=g[iy<<b|(ix-1)&nx];
				ki4=g[((iy-1)&ny)<<b|ix];
				ki5=g[((iy-1)&ny)<<b|(ix+1)&nx];
				rnd=rand()|1; }
			else {
				ki0=g[iy<<b|(ix+1)&nx];
				ki1=g[((iy+1)&ny)<<b|ix];
				ki2=g[((iy+1)&ny)<<b|(ix-1)&nx];
				ki3=g[iy<<b|(ix-1)&nx];
				ki4=g[((iy-1)&ny)<<b|(ix-1)&nx];
				ki5=g[((iy-1)&ny)<<b|ix];
				rnd=rand()&~1; }
			if(!(rnd&12)) {															// H, rot, no translation
				de=-eps[RGTH(is)*spc+LFTH(ki0)];
				de-=eps[RDWH(is)*spc+LUPH(ki1)];
				de-=eps[LDWH(is)*spc+RUPH(ki2)];
				de-=eps[LFTH(is)*spc+RGTH(ki3)];
				de-=eps[LUPH(is)*spc+RDWH(ki4)];
				de-=eps[RUPH(is)*spc+LDWH(ki5)];
				g[iy<<b|ix]=is2=is&~7|(rnd>>4)%6;
				de+=eps[RGTH(is2)*spc+LFTH(ki0)];
				de+=eps[RDWH(is2)*spc+LUPH(ki1)];
				de+=eps[LDWH(is2)*spc+RUPH(ki2)];
				de+=eps[LFTH(is2)*spc+RGTH(ki3)];
				de+=eps[LUPH(is2)*spc+RDWH(ki4)];
				de+=eps[RUPH(is2)*spc+LDWH(ki5)];
				if(de>0&&rand()>RAND_MAX*exp(-de))
					g[iy<<b|ix]=is; }
			else {																			// H, rot, with translation
				jx=(ix+hrz[rnd&15])&nx;
				jy=(iy+vrt[rnd&15])&ny;
				js=g[jy<<b|jx];
				if(jy&1) {
					kj0=g[jy<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|(jx+1)&nx];
					kj2=g[((jy+1)&ny)<<b|jx];
					kj3=g[jy<<b|(jx-1)&nx];
					kj4=g[((jy-1)&ny)<<b|jx];
					kj5=g[((jy-1)&ny)<<b|(jx+1)&nx]; }
				else {
					kj0=g[jy<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy+1)&ny)<<b|(jx-1)&nx];
					kj3=g[jy<<b|(jx-1)&nx];
					kj4=g[((jy-1)&ny)<<b|(jx-1)&nx];
					kj5=g[((jy-1)&ny)<<b|jx]; }
				de=-eps[RGTH(is)*spc+LFTH(ki0)];
				de-=eps[RDWH(is)*spc+LUPH(ki1)];
				de-=eps[LDWH(is)*spc+RUPH(ki2)];
				de-=eps[LFTH(is)*spc+RGTH(ki3)];
				de-=eps[LUPH(is)*spc+RDWH(ki4)];
				de-=eps[RUPH(is)*spc+LDWH(ki5)];
				de-=eps[RGTH(js)*spc+LFTH(kj0)];
				de-=eps[RDWH(js)*spc+LUPH(kj1)];
				de-=eps[LDWH(js)*spc+RUPH(kj2)];
				de-=eps[LFTH(js)*spc+RGTH(kj3)];
				de-=eps[LUPH(js)*spc+RDWH(kj4)];
				de-=eps[RUPH(js)*spc+LDWH(kj5)];
				g[iy<<b|ix]=is2=js?js&~7|(rnd>>4)%6:0;
				g[jy<<b|jx]=js2=is&~7|(rnd>>6)%6;
				if(iy&1) {
					ki0=g[iy<<b|(ix+1)&nx];
					ki1=g[((iy+1)&ny)<<b|(ix+1)&nx];
					ki2=g[((iy+1)&ny)<<b|ix];
					ki3=g[iy<<b|(ix-1)&nx];
					ki4=g[((iy-1)&ny)<<b|ix];
					ki5=g[((iy-1)&ny)<<b|(ix+1)&nx]; }
				else {
					ki0=g[iy<<b|(ix+1)&nx];
					ki1=g[((iy+1)&ny)<<b|ix];
					ki2=g[((iy+1)&ny)<<b|(ix-1)&nx];
					ki3=g[iy<<b|(ix-1)&nx];
					ki4=g[((iy-1)&ny)<<b|(ix-1)&nx];
					ki5=g[((iy-1)&ny)<<b|ix]; }
				if(jy&1) {
					kj0=g[jy<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|(jx+1)&nx];
					kj2=g[((jy+1)&ny)<<b|jx];
					kj3=g[jy<<b|(jx-1)&nx];
					kj4=g[((jy-1)&ny)<<b|jx];
					kj5=g[((jy-1)&ny)<<b|(jx+1)&nx]; }
				else {
					kj0=g[jy<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy+1)&ny)<<b|(jx-1)&nx];
					kj3=g[jy<<b|(jx-1)&nx];
					kj4=g[((jy-1)&ny)<<b|(jx-1)&nx];
					kj5=g[((jy-1)&ny)<<b|jx]; }
				de+=eps[RGTH(is2)*spc+LFTH(ki0)];
				de+=eps[RDWH(is2)*spc+LUPH(ki1)];
				de+=eps[LDWH(is2)*spc+RUPH(ki2)];
				de+=eps[LFTH(is2)*spc+RGTH(ki3)];
				de+=eps[LUPH(is2)*spc+RDWH(ki4)];
				de+=eps[RUPH(is2)*spc+LDWH(ki5)];
				de+=eps[RGTH(js2)*spc+LFTH(kj0)];
				de+=eps[RDWH(js2)*spc+LUPH(kj1)];
				de+=eps[LDWH(js2)*spc+RUPH(kj2)];
				de+=eps[LFTH(js2)*spc+RGTH(kj3)];
				de+=eps[LUPH(js2)*spc+RDWH(kj4)];
				de+=eps[RUPH(js2)*spc+LDWH(kj5)];
				if(de>0&&rand()>RAND_MAX*exp(-de)) {
					g[iy<<b|ix]=is;
					g[jy<<b|jx]=js; }}}}
	else if(spc>1) {																// H, no rotation
		spc++;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			rnd=rand()&14|iy&1;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			js=g[jy<<b|jx];
			isspc=is*spc;
			jsspc=js*spc;
			de=-eps[isspc+g[iy<<b|(ix+1)&nx]];
			de-=eps[isspc+g[iy<<b|(ix-1)&nx]];
			de-=eps[isspc+g[((iy+1)&ny)<<b|ix]];
			de-=eps[isspc+g[((iy-1)&ny)<<b|ix]];
			de-=eps[isspc+g[((iy-1)&ny)<<b|(ix+(iy&1?1:-1))&nx]];
			de-=eps[isspc+g[((iy+1)&ny)<<b|(ix+(iy&1?1:-1))&nx]];
			de-=eps[jsspc+g[jy<<b|(jx+1)&nx]];
			de-=eps[jsspc+g[jy<<b|(jx-1)&nx]];
			de-=eps[jsspc+g[((jy+1)&ny)<<b|jx]];
			de-=eps[jsspc+g[((jy-1)&ny)<<b|jx]];
			de-=eps[jsspc+g[((jy-1)&ny)<<b|(jx+(jy&1?1:-1))&nx]];
			de-=eps[jsspc+g[((jy+1)&ny)<<b|(jx+(jy&1?1:-1))&nx]];
			g[iy<<b|ix]=js;
			g[jy<<b|jx]=is;
			de+=eps[isspc+g[jy<<b|(jx+1)&nx]];
			de+=eps[isspc+g[jy<<b|(jx-1)&nx]];
			de+=eps[isspc+g[((jy+1)&ny)<<b|jx]];
			de+=eps[isspc+g[((jy-1)&ny)<<b|jx]];
			de+=eps[isspc+g[((jy-1)&ny)<<b|(jx+(jy&1?1:-1))&nx]];
			de+=eps[isspc+g[((jy+1)&ny)<<b|(jx+(jy&1?1:-1))&nx]];
			de+=eps[jsspc+g[iy<<b|(ix+1)&nx]];
			de+=eps[jsspc+g[iy<<b|(ix-1)&nx]];
			de+=eps[jsspc+g[((iy+1)&ny)<<b|ix]];
			de+=eps[jsspc+g[((iy-1)&ny)<<b|ix]];
			de+=eps[jsspc+g[((iy-1)&ny)<<b|(ix+(iy&1?1:-1))&nx]];
			de+=eps[jsspc+g[((iy+1)&ny)<<b|(ix+(iy&1?1:-1))&nx]];
			if(de>0&&rand()>RAND_MAX*exp(-de)) {
				g[iy<<b|ix]=is;
				g[jy<<b|jx]=js; }}}
	else {																					// H, no rotation, no species
		for(db=-6;db<=6;db++) prob[db+6]=exp(-db**eps)<1?RAND_MAX*exp(-db**eps):RAND_MAX;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			rnd=rand()&14|iy&1;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			if(!g[jy<<b|jx]) {
				g[iy<<b|ix]=0;
				db =g[jy<<b|(jx+1)&nx];
				db-=g[iy<<b|(ix+1)&nx];
				db+=g[jy<<b|(jx-1)&nx];
				db-=g[iy<<b|(ix-1)&nx];
				db+=g[((jy+1)&ny)<<b|jx];
				db-=g[((iy+1)&ny)<<b|ix];
				db+=g[((jy-1)&ny)<<b|jx];
				db-=g[((iy-1)&ny)<<b|ix];
				db+=g[((jy+1)&ny)<<b|(jx+(jy&1?1:-1))&nx];
				db-=g[((iy+1)&ny)<<b|(ix+(iy&1?1:-1))&nx];
				db+=g[((jy-1)&ny)<<b|(jx+(jy&1?1:-1))&nx];
				db-=g[((iy-1)&ny)<<b|(ix+(iy&1?1:-1))&nx];
				if(prob[db+6]==RAND_MAX||rand()<=prob[db+6]) g[jy<<b|jx]=1;
				else g[iy<<b|ix]=1; }}}
	return; }


void MetropolisIsingT(Lattice lt,int itmax,float *eps) {
	static int hrz[16]={0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,1,1};
	static int vrt[16]={0,0,0,0, -1,-1,-1,-1, 1,1,1,1, -1,1,-1,1};
	int rnd,ix,iy,jx,jy,b,nx,ny,*g,small;
	int is,js,is2,js2,spc,isspc,jsspc,rot;
	int ki0,ki1,ki2,kj0,kj1,kj2;
	float de;
	int prob[7],db;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	if((nx*ny&-nx*ny)!=nx*ny) return;
	spc=lt->spc;
	rot=lt->rot;
	small=nx*ny<=RAND_MAX+1?nx*ny-1:0;
	nx--;
	ny--;
	for(b=0;nx>>b&1;b+=1);

	if(rot) {
		spc=(spc+1)*4;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			if((iy&3)==0) {
				ki0=g[((iy-1)&ny)<<b|ix];
				ki1=g[((iy+1)&ny)<<b|ix];
				ki2=g[((iy-1)&ny)<<b|(ix-1)&nx];
				rnd=rand()&~3; }
			else if((iy&3)==1) {
				ki0=g[((iy+1)&ny)<<b|ix];
				ki1=g[((iy+1)&ny)<<b|(ix-1)&nx];
				ki2=g[((iy-1)&ny)<<b|ix];
				rnd=rand()&~3|1; }
			else if((iy&3)==2) {
				ki0=g[((iy-1)&ny)<<b|(ix+1)&nx];
				ki1=g[((iy+1)&ny)<<b|ix];
				ki2=g[((iy-1)&ny)<<b|ix];
				rnd=rand()&~3|2; }
			else {
				ki0=g[((iy+1)&ny)<<b|(ix+1)&nx];
				ki1=g[((iy+1)&ny)<<b|ix];
				ki2=g[((iy-1)&ny)<<b|ix];
				rnd=rand()|3; }
			if(!(rnd&12)) {															// T, rot, no translation
				if(iy&1) {
					de=-eps[RGTT(is)*spc+LUPT(ki0)];
					de-=eps[LDWT(is)*spc+RGTT(ki1)];
					de-=eps[LUPT(is)*spc+LDWT(ki2)]; }
				else {
					de=-eps[RGTT(is)*spc+LDWT(ki0)];
					de-=eps[LDWT(is)*spc+LUPT(ki1)];
					de-=eps[LUPT(is)*spc+RGTT(ki2)]; }
				g[iy<<b|ix]=is2=is&~3|(rnd>>4)%3;
				if(iy&1) {
					de+=eps[RGTT(is2)*spc+LUPT(ki0)];
					de+=eps[LDWT(is2)*spc+RGTT(ki1)];
					de+=eps[LUPT(is2)*spc+LDWT(ki2)]; }
				else {
					de+=eps[RGTT(is2)*spc+LDWT(ki0)];
					de+=eps[LDWT(is2)*spc+LUPT(ki1)];
					de+=eps[LUPT(is2)*spc+RGTT(ki2)]; }
				if(de>0&&rand()>RAND_MAX*exp(-de))
					g[iy<<b|ix]=is; }
			else {																			// T, rot, with translation
				jx=(ix+hrz[rnd&15])&nx;
				jy=(iy+vrt[rnd&15])&ny;
				js=g[jy<<b|jx];
				if((jy&3)==0) {
					kj0=g[((jy-1)&ny)<<b|jx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|(jx-1)&nx]; }
				else if((jy&3)==1) {
					kj0=g[((jy+1)&ny)<<b|jx];
					kj1=g[((jy+1)&ny)<<b|(jx-1)&nx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				else if((jy&3)==2) {
					kj0=g[((jy-1)&ny)<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				else {
					kj0=g[((jy+1)&ny)<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				if(iy&1) {
					de=-eps[RGTT(is)*spc+LUPT(ki0)];
					de-=eps[LDWT(is)*spc+RGTT(ki1)];
					de-=eps[LUPT(is)*spc+LDWT(ki2)]; }
				else {
					de=-eps[RGTT(is)*spc+LDWT(ki0)];
					de-=eps[LDWT(is)*spc+LUPT(ki1)];
					de-=eps[LUPT(is)*spc+RGTT(ki2)]; }
				if(jy&1) {
					de-=eps[RGTT(js)*spc+LUPT(kj0)];
					de-=eps[LDWT(js)*spc+RGTT(kj1)];
					de-=eps[LUPT(js)*spc+LDWT(kj2)]; }
				else {
					de-=eps[RGTT(js)*spc+LDWT(kj0)];
					de-=eps[LDWT(js)*spc+LUPT(kj1)];
					de-=eps[LUPT(js)*spc+RGTT(kj2)]; }
				g[iy<<b|ix]=is2=js?js&~3|(rnd>>4)%3:0;
				g[jy<<b|jx]=js2=is&~3|(rnd>>6)%3;
				if((iy&3)==0) {
					ki0=g[((iy-1)&ny)<<b|ix];
					ki1=g[((iy+1)&ny)<<b|ix];
					ki2=g[((iy-1)&ny)<<b|(ix-1)&nx]; }
				else if((iy&3)==1) {
					ki0=g[((iy+1)&ny)<<b|ix];
					ki1=g[((iy+1)&ny)<<b|(ix-1)&nx];
					ki2=g[((iy-1)&ny)<<b|ix]; }
				else if((iy&3)==2) {
					ki0=g[((iy-1)&ny)<<b|(ix+1)&nx];
					ki1=g[((iy+1)&ny)<<b|ix];
					ki2=g[((iy-1)&ny)<<b|ix]; }
				else {
					ki0=g[((iy+1)&ny)<<b|(ix+1)&nx];
					ki1=g[((iy+1)&ny)<<b|ix];
					ki2=g[((iy-1)&ny)<<b|ix]; }
				if((jy&3)==0) {
					kj0=g[((jy-1)&ny)<<b|jx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|(jx-1)&nx]; }
				else if((jy&3)==1) {
					kj0=g[((jy+1)&ny)<<b|jx];
					kj1=g[((jy+1)&ny)<<b|(jx-1)&nx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				else if((jy&3)==2) {
					kj0=g[((jy-1)&ny)<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				else {
					kj0=g[((jy+1)&ny)<<b|(jx+1)&nx];
					kj1=g[((jy+1)&ny)<<b|jx];
					kj2=g[((jy-1)&ny)<<b|jx]; }
				if(iy&1) {
					de+=eps[RGTT(is2)*spc+LUPT(ki0)];
					de+=eps[LDWT(is2)*spc+RGTT(ki1)];
					de+=eps[LUPT(is2)*spc+LDWT(ki2)]; }
				else {
					de+=eps[RGTT(is2)*spc+LDWT(ki0)];
					de+=eps[LDWT(is2)*spc+LUPT(ki1)];
					de+=eps[LUPT(is2)*spc+RGTT(ki2)]; }
				if(jy&1) {
					de+=eps[RGTT(js2)*spc+LUPT(kj0)];
					de+=eps[LDWT(js2)*spc+RGTT(kj1)];
					de+=eps[LUPT(js2)*spc+LDWT(kj2)]; }
				else {
					de+=eps[RGTT(js2)*spc+LDWT(kj0)];
					de+=eps[LDWT(js2)*spc+LUPT(kj1)];
					de+=eps[LUPT(js2)*spc+RGTT(kj2)]; }
				if(de>0&&rand()>RAND_MAX*exp(-de)) {
					g[iy<<b|ix]=is;
					g[jy<<b|jx]=js; }}}}
	else if(spc>1) {																// T, no rotation
		spc++;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			is=g[iy<<b|ix];
			rnd=rand()&12|iy&3;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			js=g[jy<<b|jx];
			isspc=is*spc;
			jsspc=js*spc;
			de=-eps[isspc+g[((iy+1)&ny)<<b|ix]];
			de-=eps[isspc+g[((iy-1)&ny)<<b|ix]];
			de-=eps[isspc+g[((iy+(iy&1?1:-1))&ny)<<b|(ix+(iy&2?1:-1))&nx]];
			de-=eps[jsspc+g[((jy+1)&ny)<<b|jx]];
			de-=eps[jsspc+g[((jy-1)&ny)<<b|jx]];
			de-=eps[jsspc+g[((jy+(jy&1?1:-1))&ny)<<b|(jx+(jy&2?1:-1))&nx]];
			g[iy<<b|ix]=js;
			g[jy<<b|jx]=is;
			de+=eps[isspc+g[((jy+1)&ny)<<b|jx]];
			de+=eps[isspc+g[((jy-1)&ny)<<b|jx]];
			de+=eps[isspc+g[((jy+(jy&1?1:-1))&ny)<<b|(jx+(jy&2?1:-1))&nx]];
			de+=eps[jsspc+g[((iy+1)&ny)<<b|ix]];
			de+=eps[jsspc+g[((iy-1)&ny)<<b|ix]];
			de+=eps[jsspc+g[((iy+(iy&1?1:-1))&ny)<<b|(ix+(iy&2?1:-1))&nx]];
			if(de>0&&rand()>RAND_MAX*exp(-de)) {
				g[iy<<b|ix]=is;
				g[jy<<b|jx]=js; }}}
	else {																					// T, no rotation, no species
		for(db=-3;db<=3;db++) prob[db+3]=exp(-db**eps)<1?RAND_MAX*exp(-db**eps):RAND_MAX;
		while(itmax--) {
			if(small) {
				while(!g[ix=rand()&small]);
				iy=ix>>b;
				ix&=nx; }
			else while(!g[(iy=rand()&ny)<<b|(ix=(rand()&nx))]);
			rnd=rand()&12|iy&3;
			jx=(ix+hrz[rnd])&nx;
			jy=(iy+vrt[rnd])&ny;
			if(!g[jy<<b|jx]) {
				g[iy<<b|ix]=0;
				db =g[((jy+1)&ny)<<b|jx];
				db-=g[((iy+1)&ny)<<b|ix];
				db+=g[((jy-1)&ny)<<b|jx];
				db-=g[((iy-1)&ny)<<b|ix];
				db+=g[((jy+(jy&1?1:-1))&ny)<<b|(jx+(jy&2?1:-1))&nx];
				db-=g[((iy+(iy&1?1:-1))&ny)<<b|(ix+(iy&2?1:-1))&nx];
				if(prob[db+3]==RAND_MAX||rand()<=prob[db+3]) g[jy<<b|jx]=1;
				else g[iy<<b|ix]=1; }}}
	return; }


void MetropolisIsing(Lattice lt,int itmax,float *eps) {
	if(lt->shape=='S') MetropolisIsingS(lt,itmax,eps);
	else if(lt->shape=='H') MetropolisIsingH(lt,itmax,eps);
	else MetropolisIsingT(lt,itmax,eps);
	return; }


float EnergyAtIsingS(Lattice lt,float *eps,int ix,int iy,int spc1,int rot1) {
	float e;
	int nx,ny,bond,is2,spc,rot,*g;
	int k0,k1,k2,k3;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*4;
		is2=spc1<<2|rot1;
		k0=g[iy*nx+(ix+1)%nx];
		k1=g[(iy+1)%ny*nx+ix];
		k2=g[iy*nx+(ix-1+nx)%nx];
		k3=g[(iy-1+ny)%ny*nx+ix];
		e=eps[RGTS(is2)*spc+LFTS(k0)];
		e+=eps[DWNS(is2)*spc+UPPS(k1)];
		e+=eps[LFTS(is2)*spc+RGTS(k2)];
		e+=eps[UPPS(is2)*spc+DWNS(k3)]; }
	else if(spc>1) {
		spc++;
		is2=spc1*spc;
		e=eps[is2+g[iy*nx+(ix+1)%nx]];
		e+=eps[is2+g[(iy+1)%ny*nx+ix]];
		e+=eps[is2+g[iy*nx+(ix-1+nx)%nx]];
		e+=eps[is2+g[(iy-1+ny)%ny*nx+ix]]; }
	else if(spc1) {
		bond=g[iy*nx+(ix+1)%nx];
		bond+=g[(iy+1)%ny*nx+ix];
		bond+=g[iy*nx+(ix-1+nx)%nx];
		bond+=g[(iy-1+ny)%ny*nx+ix];
		e=*eps*bond; }
	else e=0;
	return e; }


float EnergyAtIsingH(Lattice lt,float *eps,int ix,int iy,int spc1,int rot1) {
	float e;
	int nx,ny,bond,is2,spc,rot,*g;
	int k0,k1,k2,k3,k4,k5;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*8;
		is2=spc1<<3|rot1;
		if(iy&1) {
			k0=g[iy*nx+(ix+1)%nx];
			k1=g[(iy+1)%ny*nx+(ix+1)%nx];
			k2=g[(iy+1)%ny*nx+ix];
			k3=g[iy*nx+(ix-1+nx)%nx];
			k4=g[(iy-1+ny)%ny*nx+ix];
			k5=g[(iy-1+ny)%ny*nx+(ix+1)%nx]; }
		else {
			k0=g[iy*nx+(ix+1)%nx];
			k1=g[(iy+1)%ny*nx+ix];
			k2=g[(iy+1)%ny*nx+(ix-1+nx)%nx];
			k3=g[iy*nx+(ix-1+nx)%nx];
			k4=g[(iy-1+ny)%ny*nx+(ix-1+nx)%nx];
			k5=g[(iy-1+ny)%ny*nx+ix]; }
		e=eps[RGTH(is2)*spc+LFTH(k0)];
		e+=eps[RDWH(is2)*spc+LUPH(k1)];
		e+=eps[LDWH(is2)*spc+RUPH(k2)];
		e+=eps[LFTH(is2)*spc+RGTH(k3)];
		e+=eps[LUPH(is2)*spc+RDWH(k4)];
		e+=eps[RUPH(is2)*spc+LDWH(k5)]; }
	else if(spc>1) {
		spc++;
		is2=spc1*spc;
		e=eps[is2+g[iy*nx+(ix+1)%nx]];
		e+=eps[is2+g[(iy+1)%ny*nx+ix]];
		e+=eps[is2+g[iy*nx+(ix-1+nx)%nx]];
		e+=eps[is2+g[(iy-1+ny)%ny*nx+ix]];
		e+=eps[is2+g[(iy-1+ny)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx]];
		e+=eps[is2+g[(iy+1)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx]]; }
	else if(spc1) {
		bond=g[iy*nx+(ix+1)%nx];
		bond+=g[(iy+1)%ny*nx+ix];
		bond+=g[iy*nx+(ix-1+nx)%nx];
		bond+=g[(iy-1+ny)%ny*nx+ix];
		bond+=g[(iy-1+ny)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx];
		bond+=g[(iy+1)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx];
		e=*eps*bond; }
	else e=0;
	return e; }


float EnergyAtIsingT(Lattice lt,float *eps,int ix,int iy,int spc1,int rot1) {
	float e;
	int nx,ny,bond,is2,spc,rot,*g;
	int k0,k1,k2;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*4;
		is2=spc1<<2|rot1;
		if((iy&3)==0) {
			k0=g[(iy-1+ny)%ny*nx+ix];
			k1=g[(iy+1)%ny*nx+ix];
			k2=g[(iy-1+ny)%ny*nx+(ix-1+nx)%nx]; }
		else if((iy&3)==1) {
			k0=g[(iy+1)%ny*nx+ix];
			k1=g[(iy+1)%ny*nx+(ix-1+nx)%nx];
			k2=g[(iy-1+ny)%ny*nx+ix]; }
		else if((iy&3)==2) {
			k0=g[(iy-1+ny)%ny*nx+(ix+1)%nx];
			k1=g[(iy+1)%ny*nx+ix];
			k2=g[(iy-1+ny)%ny*nx+ix]; }
		else {
			k0=g[(iy+1)%ny*nx+(ix+1)%nx];
			k1=g[(iy+1)%ny*nx+ix];
			k2=g[(iy-1+ny)%ny*nx+ix]; }
		if(iy&1) {
			e=eps[RGTT(is2)*spc+LUPT(k0)];
			e+=eps[LDWT(is2)*spc+RGTT(k1)];
			e+=eps[LUPT(is2)*spc+LDWT(k2)]; }
		else {
			e=eps[RGTT(is2)*spc+LDWT(k0)];
			e+=eps[LDWT(is2)*spc+LUPT(k1)];
			e+=eps[LUPT(is2)*spc+RGTT(k2)]; }}
	else if(spc>1) {
		spc++;
		is2=spc1*spc;
		e=eps[is2+g[(iy-1+ny)%ny*nx+ix]];
		e+=eps[is2+g[(iy+1)%ny*nx+ix]];
		e+=eps[is2+g[(iy+(iy&1?1:-1)+ny)%ny*nx+(ix+(iy&2?1:-1)+nx)%nx]]; }
	else if(spc1) {
		bond=g[(iy-1+ny)%ny*nx+ix];
		bond+=g[(iy+1)%ny*nx+ix];
		bond+=g[(iy+(iy&1?1:-1)+ny)%ny*nx+(ix+(iy&2?1:-1)+nx)%nx];
		e=*eps*bond; }
	else e=0;
	return e; }


float EnergyAtIsing(Lattice lt,float *eps,int ix,int iy,int spc1,int rot1) {
	if(lt->shape=='S') return EnergyAtIsingS(lt,eps,ix,iy,spc1,rot1);
	else if(lt->shape=='H') return EnergyAtIsingH(lt,eps,ix,iy,spc1,rot1);
	else return EnergyAtIsingT(lt,eps,ix,iy,spc1,rot1); }


float EnergyIsingS(Lattice lt,float *eps) {
	float e;
	int ix,iy,nx,ny,bond,is2,spc,rot,*g;
	int k0,k1;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*4;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++) {
				is2=g[iy*nx+ix];
				k0=g[iy*nx+(ix+1)%nx];
				k1=g[(iy+1)%ny*nx+ix];
				e+=eps[RGTS(is2)*spc+LFTS(k0)];
				e+=eps[DWNS(is2)*spc+UPPS(k1)]; }}
	else if(spc>1) {
		spc++;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++)	{
				is2=g[iy*nx+ix]*spc;
				e+=eps[is2+g[iy*nx+(ix+1)%nx]];
				e+=eps[is2+g[(iy+1)%ny*nx+ix]]; }}
	else {
		bond=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++)
				if(g[iy*nx+ix]) {
					bond+=g[iy*nx+(ix+1)%nx];
					bond+=g[(iy+1)%ny*nx+ix]; }
		e=*eps*bond; }
	return e; }


float EnergyIsingH(Lattice lt,float *eps) {
	float e;
	int ix,iy,nx,ny,bond,is2,spc,rot,*g;
	int k0,k1,k2;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*8;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++) {
				is2=g[iy*nx+ix];
				if(iy&1) {
					k0=g[iy*nx+(ix+1)%nx];
					k1=g[(iy+1)%ny*nx+(ix+1)%nx];
					k2=g[(iy+1)%ny*nx+ix]; }
				else {
					k0=g[iy*nx+(ix+1)%nx];
					k1=g[(iy+1)%ny*nx+ix];
					k2=g[(iy+1)%ny*nx+(ix-1+nx)%nx]; }
				e+=eps[RGTH(is2)*spc+LFTH(k0)];
				e+=eps[RDWH(is2)*spc+LUPH(k1)];
				e+=eps[LDWH(is2)*spc+RUPH(k2)]; }}
	else if(spc>1) {
		spc++;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++) {
				is2=g[iy*nx+ix]*spc;
				e+=eps[is2+g[iy*nx+(ix+1)%nx]];
				e+=eps[is2+g[(iy+1)%ny*nx+ix]];
				e+=eps[is2+g[(iy+1)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx]]; }}
	else {
		bond=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++)
				if(g[iy*nx+ix]) {
					bond+=g[iy*nx+(ix+1)%nx];
					bond+=g[(iy+1)%ny*nx+ix];
					bond+=g[(iy+1)%ny*nx+(ix+(iy&1?1:-1)+nx)%nx]; }
		e=*eps*bond; }
	return e; }


float EnergyIsingT(Lattice lt,float *eps) {
	float e;
	int ix,iy,nx,ny,bond,is2,spc,rot,*g;
	int k0,k1,k2;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	rot=lt->rot;

	if(rot) {
		spc=(spc+1)*4;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++) {
				is2=g[iy*nx+ix];
				if((iy&3)==0) {
					k0=g[(iy-1+ny)%ny*nx+ix];
					k1=g[(iy+1)%ny*nx+ix];
					k2=g[(iy-1+ny)%ny*nx+(ix-1+nx)%nx]; }
				else if((iy&3)==1) {
					k0=g[(iy+1)%ny*nx+ix];
					k1=g[(iy+1)%ny*nx+(ix-1+nx)%nx];
					k2=g[(iy-1+ny)%ny*nx+ix]; }
				else if((iy&3)==2) {
					k0=g[(iy-1+ny)%ny*nx+(ix+1)%nx];
					k1=g[(iy+1)%ny*nx+ix];
					k2=g[(iy-1+ny)%ny*nx+ix]; }
				else {
					k0=g[(iy+1)%ny*nx+(ix+1)%nx];
					k1=g[(iy+1)%ny*nx+ix];
					k2=g[(iy-1+ny)%ny*nx+ix]; }
				if(iy&1) {
					e+=eps[RGTT(is2)*spc+LUPT(k0)];
					e+=eps[LDWT(is2)*spc+RGTT(k1)];
					e+=eps[LUPT(is2)*spc+LDWT(k2)]; }
				else {
					e+=eps[RGTT(is2)*spc+LDWT(k0)];
					e+=eps[LDWT(is2)*spc+LUPT(k1)];
					e+=eps[LUPT(is2)*spc+RGTT(k2)]; }}}
	else if(spc>1) {
		spc++;
		e=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++) {
				is2=g[iy*nx+ix]*spc;
				e+=eps[is2+g[(iy-1+ny)%ny*nx+ix]];
				e+=eps[is2+g[(iy+1)%ny*nx+ix]];
				e+=eps[is2+g[(iy+(iy&1?1:-1)+ny)%ny*nx+(ix+(iy&2?1:-1)+nx)%nx]]; }}
	else {
		bond=0;
		for(iy=0;iy<ny;iy++)
			for(ix=0;ix<nx;ix++)
				if(g[iy*nx+ix]) {
					bond+=g[(iy-1+ny)%ny*nx+ix];
					bond+=g[(iy+1)%ny*nx+ix];
					bond+=g[(iy+(iy&1?1:-1)+ny)%ny*nx+(ix+(iy&2?1:-1)+nx)%nx]; }
		e=*eps*bond; }
	return e/2; }


float EnergyIsing(Lattice lt,float *eps) {
	if(lt->shape=='S') return EnergyIsingS(lt,eps);
	else if(lt->shape=='H') return EnergyIsingH(lt,eps);
	else return EnergyIsingT(lt,eps); }


int clsizeS(int *g,int nx,int ny,int ix,int iy) {
	int sum;

	if(g[iy*nx+ix]<1) return 0;
	g[iy*nx+ix]*=-1;
	sum=1;
	if(rand()&1) {
		sum+=clsizeS(g,nx,ny,(ix+1)%nx,iy);
		sum+=clsizeS(g,nx,ny,ix,(iy+1)%ny);
		sum+=clsizeS(g,nx,ny,(ix-1+nx)%nx,iy);
		sum+=clsizeS(g,nx,ny,ix,(iy-1+ny)%ny); }
	else {
		sum+=clsizeS(g,nx,ny,ix,(iy-1+ny)%ny);
		sum+=clsizeS(g,nx,ny,(ix-1+nx)%nx,iy);
		sum+=clsizeS(g,nx,ny,ix,(iy+1)%ny);
		sum+=clsizeS(g,nx,ny,(ix+1)%nx,iy); }
	return sum; }


int clsizeH(int *g,int nx,int ny,int ix,int iy) {
	int sum;

	if(g[iy*nx+ix]<1) return 0;
	g[iy*nx+ix]*=-1;
	sum=1;
	if(rand()&1) {
		sum+=clsizeH(g,nx,ny,(ix+1)%nx,iy);
		sum+=clsizeH(g,nx,ny,ix,(iy+1)%ny);
		sum+=clsizeH(g,nx,ny,(ix-1+nx)%nx,iy);
		sum+=clsizeH(g,nx,ny,ix,(iy-1+ny)%ny);
		sum+=clsizeH(g,nx,ny,(ix+(iy&1?1:-1)+nx)%nx,(iy+1)%ny);
		sum+=clsizeH(g,nx,ny,(ix+(iy&1?1:-1)+nx)%nx,(iy-1+ny)%ny); }
	else {
		sum+=clsizeH(g,nx,ny,(ix+(iy&1?1:-1)+nx)%nx,(iy-1+ny)%ny);
		sum+=clsizeH(g,nx,ny,(ix+(iy&1?1:-1)+nx)%nx,(iy+1)%ny);
		sum+=clsizeH(g,nx,ny,ix,(iy-1+ny)%ny);
		sum+=clsizeH(g,nx,ny,(ix-1+nx)%nx,iy);
		sum+=clsizeH(g,nx,ny,ix,(iy+1)%ny);
		sum+=clsizeH(g,nx,ny,(ix+1)%nx,iy); }
	return sum; }


int clsizeT(int *g,int nx,int ny,int ix,int iy) {
	int sum;

	if(g[iy*nx+ix]<1) return 0;
	g[iy*nx+ix]*=-1;
	sum=1;
	if(rand()&1) {
		sum+=clsizeT(g,nx,ny,ix,(iy+1)%ny);
		sum+=clsizeT(g,nx,ny,ix,(iy-1+ny)%ny);
		sum+=clsizeT(g,nx,ny,(ix+(iy&2?1:-1)+nx)%nx,(iy+(iy&1?1:-1)+ny)%ny); }
	else {
		sum+=clsizeT(g,nx,ny,(ix+(iy&2?1:-1)+nx)%nx,(iy+(iy&1?1:-1)+ny)%ny);
		sum+=clsizeT(g,nx,ny,ix,(iy-1+ny)%ny);
		sum+=clsizeT(g,nx,ny,ix,(iy+1)%ny); }
	return sum; }


int CtClustersIsing(Lattice lt,int *count,int ncount) {
	int i,max,ct,nx,ny,*g;
	char shape;

	g=lt->g;
	nx=lt->nx;
	ny=lt->ny;
	shape=lt->shape;

	max=0;
	for(i=0;i<ncount;i++) count[i]=0;
	for(i=0;i<nx*ny;i++)
		if(g[i]>0) {
			if(shape=='S') ct=clsizeS(g,nx,ny,i%nx,i/nx);
			else if(shape=='H') ct=clsizeH(g,nx,ny,i%nx,i/nx);
			else ct=clsizeT(g,nx,ny,i%nx,i/nx);
			if(ct<ncount) count[ct]++;
			if(ct>max) max=ct; }
	for(i=0;i<nx*ny;i++) g[i]*=-1;
	return max; }


void DisplayClustersIsing(Lattice lt) {
	int *count,max,i,nx,ny;

	nx=lt->nx;
	ny=lt->ny;
	count=(int*)calloc(nx*ny,sizeof(int));
	if(!count) return;
	max=CtClustersIsing(lt,count,nx*ny);
	printf("size\tnum\t\tconc.\n");
	for(i=1;i<=max;i++)
		if(count[i]) printf("%i\t\t%i\t\t%f\n",i,count[i],1.0*count[i]/(nx*ny));
	free(count);
	return; }

float MinDistIsing(Lattice lt,int ix,int iy,int jx,int jy) {
	float dist,distx,disty;
	int nx,ny,dx,dy;

	nx=lt->nx;
	ny=lt->ny;
	dx=jx-ix;
	dy=jy-iy;

	if(lt->shape=='S') {
		if(dx<-nx/2) dx+=nx;
		else if(dx>nx/2) dx-=nx;
		if(dy<-ny/2) dy+=ny;
		else if(dy>ny/2) dy-=ny;
		dist=sqrt(dx*dx+dy*dy); }
	else if(lt->shape=='H') {
		if((iy&1)==(jy&1)) distx=dx;
		else if(iy&1) distx=dx-0.5;
		else distx=dx+0.5;
		if(distx<-nx/2) distx+=nx;
		else if(distx>nx/2) distx-=nx;
		if(dy<-ny/2) dy+=ny;
		else if(dy>ny/2) dy-=ny;
		dist=sqrt(distx*distx+3.0/4.0*dy*dy); }
	else {
		if((iy&2)==(jy&2)) distx=sqrt(3)*dx;
		else if(iy&2) distx=sqrt(3)*(dx-0.5);
		else distx=sqrt(3)*(dx+0.5);
		if((iy&3)==(jy&3)) disty=3.0*dy/4.0;
		else if((iy&1)==(jy&1)) disty=1.5+3.0*floor(dy/4.0);
		else if((iy&1)&&(jy&3)==(iy+1&3)) disty=0.5+3.0*floor(dy/4.0);
		else if(iy&1) disty=2.0+3.0*floor(dy/4.0);
		else if((jy&3)==(iy+1&3)) disty=1.0+3.0*floor(dy/4.0);
		else disty=2.5+3.0*floor(dy/4.0);
		if(distx<-nx*sqrt(3)/2.0) distx+=nx*sqrt(3);
		else if(distx>nx*sqrt(3)/2.0) distx-=nx*sqrt(3);
		if(disty<-nx*3.0/8.0) disty+=nx*3.0/4.0;
		else if(disty>nx*3.0/8.0) disty-=nx*3.0/4.0;
		dist=sqrt(distx*distx+disty*disty); }
	return dist; }


void RadCorrFnIsing(Lattice lt,int spc1,int spc2,float *bins,int nbins,float binsize) {
	int i,j,nx,ny,obit,n1,n2;
	int *g;
	float d;
	char shape;

	nx=lt->nx;
	ny=lt->ny;
	g=lt->g;
	shape=lt->shape;
	for(i=0;i<nbins;i++) bins[i]=0;
	if(!lt->rot) obit=0;
	else obit=shape=='H'?3:2;
	for(i=0;i<nx*ny;i++)
		if(g[i]>>obit==spc1)
			for(j=i+1;j<nx*ny;j++)
				if(g[j]>>obit==spc2) {
					d=MinDistIsing(lt,i%nx,i/nx,j%nx,j/nx);
					if(d/binsize<nbins) bins[(int)(d/binsize)]+=2; }
	n1=n2=0;
	for(i=0;i<nx*ny;i++) {
		if(g[i]>>obit==spc1) n1++;
		if(g[i]>>obit==spc2) n2++; }
	if(spc1==spc2) bins[0]+=n1;
	if(!n1||!n2) return;
	if(shape=='S') d=nx*ny;
	else if(shape=='H') d=nx*ny*sqrt(3)/2.0;
	else d=nx*ny*sqrt(3)*3.0/4.0;
	for(i=0;i<nbins;i++)
		bins[i]/=n1*n2*3.1415926535*((i+1)*(i+1)-i*i)*binsize*binsize/d;
	return; }


void DisplayRCFIsing(Lattice lt,int spc1,int spc2,float binsize) {
	float *bins;
	int i,nbins,nx,ny;
	float dx,dy,d;
	char shape;
	
	shape=lt->shape;
	nx=lt->nx;
	ny=lt->ny;
	if(shape=='S') {dx=nx;dy=ny;}
	else if(shape=='H') {dx=nx;dy=sqrt(3)/2.0*ny;}
	else {dx=sqrt(3)*nx;dy=3.0/4.0*ny;}
	d=dx<dy?dx/2.0:dy/2.0;
	nbins=(int)(d/binsize);
	bins=(float*)calloc(nbins,sizeof(float));
	if(!bins) return;
	RadCorrFnIsing(lt,spc1,spc2,bins,nbins,binsize);
	printf("indx\trad\t\tg(r)\n");
	for(i=0;i<nbins;i++)
		printf("%i\t%f\t%f\n",i,binsize*(i+0.5),bins[i]);
	free(bins);
	return; }


float LatticeSiteDistH(float *xptr,float *yptr,int *ixptr,int *iyptr,float sp) {
	float x,y,spy,d;
	int xi,yi;

	x=*xptr;
	y=*yptr;
	spy=sp*0.86602540378;
	sp/=2.0;
	xi=(int)floor(x/sp);
	yi=(int)floor(y/spy);
	if((xi+yi)%2) {
		d=sqrt((x-(xi+1)*sp)*(x-(xi+1)*sp)+(y-yi*spy)*(y-yi*spy));
		if(d<sp) {*xptr=(xi+1)*sp;*yptr=yi*spy;*ixptr=(int)floor(0.5*(xi+1));*iyptr=yi;}
		else {*xptr=xi*sp;*yptr=(yi+1)*spy;d=2.0*sp-d;*ixptr=(int)floor(0.5*xi);*iyptr=yi+1;}}
	else {
		d=sqrt((x-xi*sp)*(x-xi*sp)+(y-yi*spy)*(y-yi*spy));
		if(d<sp) {*xptr=xi*sp;*yptr=yi*spy;*ixptr=(int)floor(0.5*xi);*iyptr=yi;}
		else {*xptr=(xi+1)*sp;*yptr=(yi+1)*spy;d=2.0*sp-d;*ixptr=(int)floor(0.5*(xi+1));*iyptr=yi+1;}}
	return d; }


/* ************************* OpenGL ************************ */
//#ifdef __gl_h_

#include <gl.h>
#include <glut.h>

void DisplayIsingGL(Lattice lt,float *colors) {
	int ix,iy,ic,nx,ny,spc;
	float x,y,color[4];
	int *g;

	nx=lt->nx;
	ny=lt->ny;
	spc=lt->spc;
	g=lt->g;
	if(spc>16) spc=16;
	if(nx<2||ny<2) return;

	if(lt->shape=='S'&&!lt->rot) {
		for(iy=0;iy<ny;iy++) {
			for(ix=0;ix<nx;ix++) {
				if(g[iy*nx+ix]) {
					for(ic=0;ic<4;ic++) color[ic]=colors[g[iy*nx+ix]*4+ic];
					glColor4fv(color);
					glRectf(ix-0.5,iy-0.5,ix+0.5,iy+0.5); }}}}
	else if(lt->shape=='S') {
		for(iy=0;iy<ny;iy++) {
			for(ix=0;ix<nx;ix++) {
				if(g[iy*nx+ix]) {
					for(ic=0;ic<4;ic++) color[ic]=colors[(g[iy*nx+ix]>>2)*4+ic];
					glColor4fv(color);
					glRectf(ix-0.5,iy-0.5,ix+0.5,iy+0.5); }}}}
	else if(lt->shape=='H'&&!lt->rot) {
		for(iy=0;iy<ny;iy++) {
			for(ix=0;ix<nx;ix++) {
				if(g[iy*nx+ix]) {
					x=(iy&1)?ix+0.5:ix;
					y=iy*0.5*sqrt(3);
					for(ic=0;ic<4;ic++) color[ic]=colors[g[iy*nx+ix]*4+ic];
					glColor4fv(color);
					glBegin(GL_POLYGON);
					glVertex2f(x,y-1.0/sqrt(3));
					glVertex2f(x-0.5,y-0.5/sqrt(3));
					glVertex2f(x-0.5,y+0.5/sqrt(3));
					glVertex2f(x,y+1.0/sqrt(3));
					glVertex2f(x+0.5,y+0.5/sqrt(3));
					glVertex2f(x+0.5,y-0.5/sqrt(3));
					glEnd(); }}}}
	else if(lt->shape=='H') {
		for(iy=0;iy<ny;iy++) {
			for(ix=0;ix<nx;ix++) {
				if(g[iy*nx+ix]) {
					x=(iy&1)?ix+0.5:ix;
					y=iy*0.5*sqrt(3);
					for(ic=0;ic<4;ic++) color[ic]=colors[(g[iy*nx+ix]>>3)*4+ic];
					glColor4fv(color);
					glBegin(GL_POLYGON);
					glVertex2f(x,y-1.0/sqrt(3));
					glVertex2f(x-0.5,y-0.5/sqrt(3));
					glVertex2f(x-0.5,y+0.5/sqrt(3));
					glVertex2f(x,y+1.0/sqrt(3));
					glVertex2f(x+0.5,y+0.5/sqrt(3));
					glVertex2f(x+0.5,y-0.5/sqrt(3));
					glEnd(); }}}}

	return; }


//#endif


