/*** Generated by Spin Version 5.2.4 -- 2 December 2009 ***/
/*** From source: task3.pml ***/

#ifdef SC
#define _FILE_OFFSET_BITS	64
#endif
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#if defined(WIN32) || defined(WIN64)
#include <time.h>
#else
#include <unistd.h>
#include <sys/times.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define Offsetof(X, Y)	((unsigned long)(&(((X *)0)->Y)))
#ifndef max
#define max(a,b) (((a)<(b)) ? (b) : (a))
#endif
#ifndef PRINTF
int Printf(const char *fmt, ...); /* prototype only */
#endif
#include "pan.h"
#ifdef LOOPSTATE
double cnt_loops;
#endif
State	A_Root;	/* seed-state for cycles */
State	now;	/* the full state-vector */
#undef C_States
#if defined(C_States) && defined(HAS_TRACK)
void
c_update(uchar *p_t_r)
{
#ifdef VERBOSE
	printf("c_update %u\n", p_t_r);
#endif
}
void
c_revert(uchar *p_t_r)
{
#ifdef VERBOSE
	printf("c_revert %u\n", p_t_r);
#endif
}
#endif
void
globinit(void)
{
}
void
locinit1(int h)
{
}
void
locinit0(int h)
{
}
#ifdef RANDOMIZE
	#define T_RAND  RANDOMIZE
#endif
#ifdef CNTRSTACK
	#define onstack_now()	(LL[trpt->j6] && LL[trpt->j7])
	#define onstack_put()	 LL[trpt->j6]++; LL[trpt->j7]++
	#define onstack_zap()	 LL[trpt->j6]--; LL[trpt->j7]--
#endif
#if !defined(SAFETY) && !defined(NOCOMP)
	#define V_A	(((now._a_t&1)?2:1) << (now._a_t&2))
	#define A_V	(((now._a_t&1)?1:2) << (now._a_t&2))
	int	S_A = 0;
#else
	#define V_A	0
	#define A_V	0
	#define S_A	0
#endif
#ifdef MA
#undef onstack_now
#undef onstack_put
#undef onstack_zap
#define onstack_put()	;
#define onstack_zap()	gstore((char *) &now, vsize, 4)
#else
#if defined(FULLSTACK) && !defined(BITSTATE)
#define onstack_put()	trpt->ostate = Lstate
#define onstack_zap()	{ \
	if (trpt->ostate) \
		trpt->ostate->tagged = \
		(S_A)? (trpt->ostate->tagged&~V_A) : 0; \
	}
#endif
#endif
#ifndef NO_V_PROVISO
#define V_PROVISO
#endif
#if !defined(NO_RESIZE) && !defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(SPACE) && NCORE==1
	#define AUTO_RESIZE
#endif

struct H_el {
	struct H_el *nxt;
#ifdef FULLSTACK
	unsigned int tagged;
	#if defined(BITSTATE) && !defined(NOREDUCE) && !defined(SAFETY)
	unsigned int proviso;
	#endif
#endif
#if defined(CHECK) || (defined(COLLAPSE) && !defined(FULLSTACK))
	unsigned long st_id;
#endif
#if !defined(SAFETY) || defined(REACH)
	unsigned int D;
#endif
#ifdef BCS
	#ifndef CONSERVATIVE
	#define CONSERVATIVE	1 /* good for up to 8 processes */
	#endif
	#ifdef CONSERVATIVE
		#if CONSERVATIVE <= 0 || CONSERVATIVE>32
		#error sensible values for CONSERVATIVE are 1..32 (256/8 = 32)
		#endif
	uchar ctx_pid[CONSERVATIVE];
	#endif
	uchar ctx_low;
#endif
#if NCORE>1
	/* could cost 1 extra word: 4 bytes if 32-bit and 8 bytes if 64-bit */
	#ifdef V_PROVISO
	uchar	cpu_id;		/* id of cpu that created the state */
	#endif
#endif
#ifdef COLLAPSE
	#if VECTORSZ<65536
	unsigned short ln;
	#else
	unsigned long ln;
	#endif
#endif
#if defined(AUTO_RESIZE) && !defined(BITSTATE)
	unsigned long m_K1;
#endif
	unsigned long state;
} **H_tab, **S_Tab;

typedef struct Trail {
	int   st;	/* current state */
	uchar pr;	/* process id */
	uchar tau;	/* 8 bit-flags */
	uchar o_pm;	/* 8 more bit-flags */
#if 0
	Meaning of bit-flags:
	tau&1   -> timeout enabled
	tau&2   -> request to enable timeout 1 level up (in claim)
	tau&4   -> current transition is a  claim move
	tau&8   -> current transition is an atomic move
	tau&16  -> last move was truncated on stack
	tau&32  -> current transition is a preselected move
	tau&64  -> at least one next state is not on the stack
	tau&128 -> current transition is a stutter move
	o_pm&1	-> the current pid moved -- implements else
	o_pm&2	-> this is an acceptance state
	o_pm&4	-> this is a  progress state
	o_pm&8	-> fairness alg rule 1 undo mark
	o_pm&16	-> fairness alg rule 3 undo mark
	o_pm&32	-> fairness alg rule 2 undo mark
	o_pm&64 -> the current proc applied rule2
	o_pm&128 -> a fairness, dummy move - all procs blocked
#endif
#ifdef NSUCC
	uchar n_succ;	/* nr of successor states */
#endif
#if defined(FULLSTACK) && defined(MA) && !defined(BFS)
	uchar proviso;
#endif
#ifndef BFS
	uchar  o_n, o_ot;	/* to save locals */
#endif
	uchar  o_m;
#ifdef EVENT_TRACE
#if nstates_event<256
	uchar o_event;
#else
	unsigned short o_event;
#endif
#endif
	int o_tt;
#ifndef BFS
	short o_To;
#ifdef T_RAND
	short oo_i;
#endif
#endif
#if defined(HAS_UNLESS) && !defined(BFS)
	int e_state;	/* if escape trans - state of origin */
#endif
#if (defined(FULLSTACK) && !defined(MA)) || defined(BFS) || (NCORE>1)
	struct H_el *ostate;	/* pointer to stored state */
#endif
#if defined(CNTRSTACK) && !defined(BFS)
	long	j6, j7;
#endif
	Trans *o_t;
#ifdef BCS
	/* bounded context switching option */
	unsigned short sched_limit;
	unsigned char  bcs; /* phase 1 or 2, or forced 4 */
	unsigned char  b_pno; /* preferred pid */
#endif
#ifdef P_RAND
	short p_skip;	/* to find starting point in list */
	unsigned char p_left;	/* nr of procs left to explore */
#endif
#ifdef HAS_SORTED
	short ipt;
#endif
	union {
		int oval;
		int *ovals;
	} bup;
} Trail;
Trail	*trail, *trpt;
FILE	*efd;
uchar	*this;
long	maxdepth=10000;
long	omaxdepth=10000;
#ifdef BCS
	/* bitflags in trpt->bcs */
	#define B_PHASE1	1
	#define B_PHASE2	2
	#define B_FORCED	4
int	sched_max = 0;
#endif
#ifdef PERMUTED
	uchar	permuted = 1;
#else
	uchar	permuted = 0;
#endif
double	quota;	/* time limit */
#if NCORE>1
long	z_handoff = -1;
#endif
#ifdef SC
char	*stackfile;
#endif
uchar	*SS, *LL;
uchar	HASH_NR = 0;

double memcnt = (double) 0;
double memlim = (double) (1<<30); /* 1 GB */
#if NCORE>1
double mem_reserved = (double) 0;
#endif

/* for emalloc: */
static char *have;
static long left = 0L;
static double fragment = (double) 0;
static unsigned long grow;

unsigned int HASH_CONST[] = {
	/* asuming 4 bytes per int */
	0x100d4e63,	0x0fc22f87,
	0x3ff0c3ff,	0x38e84cd7,
	0x02b148e9,	0x98b2e49d,
	0xb616d379,	0xa5247fd9,
	0xbae92a15,	0xb91c8bc5,
	0x8e5880f3,	0xacd7c069,
	0xb4c44bb3,	0x2ead1fb7,
	0x8e428171,	0xdbebd459,
	0x00400007,	0x04c11db7,
	0x828ae611,	0x6cb25933,
	0x86cdd651,	0x9e8f5f21,
	0xd5f8d8e7,	0x9c4e956f,
	0xb5cf2c71,	0x2e805a6d,
	0x33fc3a55,	0xaf203ed1,
	0xe31f5909,	0x5276db35,
	0x0c565ef7,	0x273d1aa5,
	0x8923b1dd,	0xa9acaac5,
	0xd1f69207,	0xedfd944b,
	0x9a68e46b,	0x5355e13f,
	0x7eeb44f9,	0x932beea9,
	0x330c4cd3,	0x87f34e5f,
	0x1b5851b7,	0xb9ca6447,
	0x58f96a8f,	0x1b3b5307,
	0x31c387b3,	0xf35f0f35,
	0xa0acc4df,	0xf3140303,
	0x2446245d,	0xe4b8f4ef,
	0x5c007383,	0x68e648af,
	0x1814fba7,	0xcdf731b5,
	0xd09ccb4b,	0xb92d0eff,
	0xcc3c6b67,	0xd3af6a57,
	0xf44fc3f5,	0x5bb67623,
	0xaeb9c953,	0x5e0ac739,
	0x3a7fda09,	0x5edf39eb,
	0x661eefd9,	0x6423f0d1,
	0x910fe413,	0x9ec92297,
	0x4bd8159d,	0xa7b16ee1,
	0x89d484e9,	0x7f305cb3,
	0xc5f303e7,	0x415deeef,
	0x09986f89,	0x7e9c4117,
	0x0b7cbedb,	0xf9ed7561,
	0x7a20ac99,	0xf05adef3,
	0x5893d75b,	0x44d73327,
	0xb583c873,	0x324d2145,
	0x7fa3829b,	0xe4b47a23,
	0xe256d94f,	0xb1fd8959,
	0xe561a321,	0x1435ac09,
	0xdd62408b,	0x02ec0bcb,
	0x5469b785,	0x2f4f50bb,
	0x20f19395,	0xf96ba085,
	0x2381f937,	0x768e2a11,
	0
};
#if NCORE>1
extern int core_id;
#endif
long	mreached=0;
int done=0, errors=0, Nrun=1;
int	c_init_done=0;
char	*c_stack_start = (char *) 0;
double	nstates=0, nlinks=0, truncs=0, truncs2=0;
double	nlost=0, nShadow=0, hcmp=0, ngrabs=0;
#ifdef PUTPID
char *progname;
#endif
#if defined(ZAPH) && defined(BITSTATE)
double zstates = 0;
#endif
int	c_init_run;
#ifdef BFS
double midrv=0, failedrv=0, revrv=0;
#endif
unsigned long	nr_states=0; /* nodes in DFA */
long	Fa=0, Fh=0, Zh=0, Zn=0;
long	PUT=0, PROBE=0, ZAPS=0;
long	Ccheck=0, Cholds=0;
int	a_cycles=0, upto=1, strict=0, verbose = 0, signoff = 0;
#ifdef HAS_CODE
int	gui = 0, coltrace = 0, readtrail = 0;
int	whichtrail = 0, onlyproc = -1, silent = 0;
#endif
int	state_tables=0, fairness=0, no_rck=0, Nr_Trails=0;
char	simvals[128];
#ifndef INLINE
int	TstOnly=0;
#endif
unsigned long mask, nmask;
#ifdef BITSTATE
int	ssize=23;	/* 1 Mb */
#else
int	ssize=19;	/* 512K slots */
#endif
int	hmax=0, svmax=0, smax=0;
int	Maxbody=0, XX;
uchar	*noptr;	/* used by macro Pptr(x) */
#ifdef VAR_RANGES
void logval(char *, int);
void dumpranges(void);
#endif
#ifdef MA
#define INLINE_REV
extern void dfa_init(unsigned short);
extern int  dfa_member(unsigned long);
extern int  dfa_store(uchar *);
unsigned int	maxgs = 0;
#endif

#ifdef ALIGNED
	State	comp_now __attribute__ ((aligned (8)));
	/* gcc 64-bit aligned for Itanium2 systems */
	/* MAJOR runtime penalty if not used on those systems */
#else
	State	comp_now;	/* compressed state vector */
#endif

State	comp_msk;
uchar	*Mask = (uchar *) &comp_msk;
#ifdef COLLAPSE
State	comp_tmp;
static char	*scratch = (char *) &comp_tmp;
#endif
Stack	*stack; 	/* for queues, processes */
Svtack	*svtack;	/* for old state vectors */
#ifdef BITSTATE
static unsigned int hfns = 3;	/* new default */
#endif
static unsigned long j1_spin; /* 5.2.1: avoid nameclash with math.h */
static unsigned long K1, K2;
static unsigned long j2, j3, j4;
#ifdef BITSTATE
static long udmem;
#endif
static long	A_depth = 0;
long	depth = 0;
#if NCORE>1
long nr_handoffs = 0;
#endif
static uchar	warned = 0, iterative = 0, exclusive = 0, like_java = 0, every_error = 0;
static uchar	noasserts = 0, noends = 0, bounded = 0;
#if defined(T_RAND) || defined(P_RAND) || defined(RANDSTOR)
unsigned int s_rand = 123;	/* default seed */
#endif
#if SYNC>0 && ASYNC==0
void set_recvs(void);
int  no_recvs(int);
#endif
#if SYNC
#define IfNotBlocked	if (boq != -1) continue;
#define UnBlock     	boq = -1
#else
#define IfNotBlocked	/* cannot block */
#define UnBlock     	/* don't bother */
#endif

#ifdef BITSTATE
int (*bstore)(char *, int);
int bstore_reg(char *, int);
int bstore_mod(char *, int);
#endif
void active_procs(void);
void cleanup(void);
void do_the_search(void);
void find_shorter(int);
void iniglobals(void);
void stopped(int);
void wrapup(void);
int *grab_ints(int);
void ungrab_ints(int *, int);
#ifndef NOBOUNDCHECK
#define Index(x, y)	Boundcheck(x, y, II, tt, t)
#else
#define Index(x, y)	x
#endif
short Air[] = {  (short) Air0, (short) Air1, (short) Air2 };
int
addproc(int n)
{	int j, h = now._nr_pr;
#ifndef NOCOMP
	int k;
#endif
	uchar *o_this = this;

#ifndef INLINE
	if (TstOnly) return (h < MAXPROC);
#endif
#ifndef NOBOUNDCHECK
/* redefine Index only within this procedure */
#undef Index
#define Index(x, y)	Boundcheck(x, y, 0, 0, 0)
#endif
	if (h >= MAXPROC)
		Uerror("too many processes");
	switch (n) {
	case 0: j = sizeof(P0); break;
	case 1: j = sizeof(P1); break;
	case 2: j = sizeof(P2); break;
	default: Uerror("bad proc - addproc");
	}
	if (vsize%WS)
		proc_skip[h] = WS-(vsize%WS);
	else
		proc_skip[h] = 0;
#ifndef NOCOMP
	for (k = vsize + (int) proc_skip[h]; k > vsize; k--)
		Mask[k-1] = 1; /* align */
#endif
	vsize += (int) proc_skip[h];
	proc_offset[h] = vsize;
#if defined(SVDUMP) && defined(VERBOSE)
	if (vprefix > 0)
	{	int dummy = 0;
		write(svfd, (uchar *) &dummy, sizeof(int)); /* mark */
		write(svfd, (uchar *) &h, sizeof(int));
		write(svfd, (uchar *) &n, sizeof(int));
#if VECTORSZ>32000
		write(svfd, (uchar *) &proc_offset[h], sizeof(int));
		write(svfd, (uchar *) &now, vprefix-4*sizeof(int)); /* padd */
#else
		write(svfd, (uchar *) &proc_offset[h], sizeof(short));
		write(svfd, (uchar *) &now, vprefix-3*sizeof(int)-sizeof(short)); /* padd */
#endif
	}
#endif
	now._nr_pr += 1;
#if defined(BCS) && defined(CONSERVATIVE)
	if (now._nr_pr >= CONSERVATIVE*8)
	{	printf("pan: error: too many processes -- recompile with ");
		printf("-DCONSERVATIVE=%d\n", CONSERVATIVE+1);
		pan_exit(1);
	}
#endif
	if (fairness && ((int) now._nr_pr + 1 >= (8*NFAIR)/2))
	{	printf("pan: error: too many processes -- current");
		printf(" max is %d procs (-DNFAIR=%d)\n",
			(8*NFAIR)/2 - 2, NFAIR);
		printf("\trecompile with -DNFAIR=%d\n",
			NFAIR+1);
		pan_exit(1);
	}
	vsize += j;
#ifndef NOVSZ
	now._vsz = vsize;
#endif
#ifndef NOCOMP
	for (k = 1; k <= Air[n]; k++)
		Mask[vsize - k] = 1; /* pad */
	Mask[vsize-j] = 1; /* _pid */
#endif
	hmax = max(hmax, vsize);
	if (vsize >= VECTORSZ)
	{	printf("pan: error, VECTORSZ too small, recompile pan.c");
		printf(" with -DVECTORSZ=N with N>%d\n", (int) vsize);
		Uerror("aborting");
	}
	memset((char *)pptr(h), 0, j);
	this = pptr(h);
	if (BASE > 0 && h > 0)
		((P0 *)this)->_pid = h-BASE;
	else
		((P0 *)this)->_pid = h;
	switch (n) {
	case 2:	/* np_ */
		((P2 *)pptr(h))->_t = 2;
		((P2 *)pptr(h))->_p = 0;
		reached2[0] = 1;
		accpstate[2][1] = 1;
		break;
	case 1:	/* pcbit_receive */
		((P1 *)pptr(h))->_t = 1;
		((P1 *)pptr(h))->_p = 19; reached1[19]=1;
		/* params: */
		/* locals: */
		((P1 *)pptr(h))->result = 0;
#ifdef VAR_RANGES
		logval("pcbit_receive:result", ((P1 *)pptr(h))->result);
#endif
#ifdef HAS_CODE
		locinit1(h);
#endif
		break;
	case 0:	/* memory_manager */
		((P0 *)pptr(h))->_t = 0;
		((P0 *)pptr(h))->_p = 9; reached0[9]=1;
		/* params: */
		/* locals: */
		((P0 *)pptr(h))->who = 0;
#ifdef VAR_RANGES
		logval("memory_manager:who", ((P0 *)pptr(h))->who);
#endif
#ifdef HAS_CODE
		locinit0(h);
#endif
		break;
	}
	this = o_this;
	return h-BASE;
#ifndef NOBOUNDCHECK
#undef Index
#define Index(x, y)	Boundcheck(x, y, II, tt, t)
#endif
}

#if defined(BITSTATE) && defined(COLLAPSE)
/* just to allow compilation, to generate the error */
long col_p(int i, char *z) { return 0; }
long col_q(int i, char *z) { return 0; }
#endif
#ifndef BITSTATE
#ifdef COLLAPSE
long
col_p(int i, char *z)
{	int j, k; unsigned long ordinal(char *, long, short);
	char *x, *y;
	P0 *ptr = (P0 *) pptr(i);
	switch (ptr->_t) {
	case 0: j = sizeof(P0); break;
	case 1: j = sizeof(P1); break;
	case 2: j = sizeof(P2); break;
	default: Uerror("bad proctype - collapse");
	}
	if (z) x = z; else x = scratch;
	y = (char *) ptr; k = proc_offset[i];
	for ( ; j > 0; j--, y++)
		if (!Mask[k++]) *x++ = *y;
	for (j = 0; j < WS-1; j++)
		*x++ = 0;
	x -= j;
	if (z) return (long) (x - z);
	return ordinal(scratch, x-scratch, (short) (2+ptr->_t));
}
#endif
#endif
void
run(void)
{	/* int i; */
	memset((char *)&now, 0, sizeof(State));
	vsize = (unsigned long) (sizeof(State) - VECTORSZ);
#ifndef NOVSZ
	now._vsz = vsize;
#endif
/* optional provisioning statements, e.g. to */
/* set hidden variables, used as constants */
#ifdef PROV
#include PROV
#endif
	settable();
	Maxbody = max(Maxbody, ((int) sizeof(P0)));
	Maxbody = max(Maxbody, ((int) sizeof(P1)));
	Maxbody = max(Maxbody, ((int) sizeof(P2)));
	reached[0] = reached0;
	reached[1] = reached1;
	reached[2] = reached2;
	accpstate[0] = (uchar *) emalloc(nstates0);
	accpstate[1] = (uchar *) emalloc(nstates1);
	accpstate[2] = (uchar *) emalloc(nstates2);
	progstate[0] = (uchar *) emalloc(nstates0);
	progstate[1] = (uchar *) emalloc(nstates1);
	progstate[2] = (uchar *) emalloc(nstates2);
	loopstate0 = loopstate[0] = (uchar *) emalloc(nstates0);
	loopstate1 = loopstate[1] = (uchar *) emalloc(nstates1);
	loopstate2 = loopstate[2] = (uchar *) emalloc(nstates2);
	stopstate[0] = (uchar *) emalloc(nstates0);
	stopstate[1] = (uchar *) emalloc(nstates1);
	stopstate[2] = (uchar *) emalloc(nstates2);
	visstate[0] = (uchar *) emalloc(nstates0);
	visstate[1] = (uchar *) emalloc(nstates1);
	visstate[2] = (uchar *) emalloc(nstates2);
	mapstate[0] = (short *) emalloc(nstates0 * sizeof(short));
	mapstate[1] = (short *) emalloc(nstates1 * sizeof(short));
	mapstate[2] = (short *) emalloc(nstates2 * sizeof(short));
#ifdef HAS_CODE
#ifdef HAS_CODE
#ifdef HAS_CODE
	NrStates[0] = nstates0;
	NrStates[1] = nstates1;
	NrStates[2] = nstates2;
#endif
#endif
#endif
	stopstate[0][endstate0] = 1;
	stopstate[1][endstate1] = 1;
	stopstate[2][endstate2] = 1;
	retrans(0, nstates0, start0, src_ln0, reached0, loopstate0);
	retrans(1, nstates1, start1, src_ln1, reached1, loopstate1);
	if (state_tables)
	{ printf("\nTransition Type: ");
	  printf("A=atomic; D=d_step; L=local; G=global\n");
	  printf("Source-State Labels: ");
	  printf("p=progress; e=end; a=accept;\n");
#ifdef MERGED
	  printf("Note: statement merging was used. Only the first\n");
	  printf("      stmnt executed in each merge sequence is shown\n");
	  printf("      (use spin -a -o3 to disable statement merging)\n");
#endif
	  pan_exit(0);
	}
	iniglobals();
#if defined(VERI) && !defined(NOREDUCE) && !defined(NP)
	if (!state_tables
#ifdef HAS_CODE
	&& !readtrail
#endif
#if NCORE>1
	&& core_id == 0
#endif
	)
	{ printf("warning: for p.o. reduction to be valid ");
	  printf("the never claim must be stutter-invariant\n");
	  printf("(never claims generated from LTL ");
	  printf("formulae are stutter-invariant)\n");
	}
#endif
	UnBlock;	/* disable rendez-vous */
#ifdef BITSTATE
	if (udmem)
	{	udmem *= 1024L*1024L;
	#if NCORE>1
		if (!readtrail)
		{	void init_SS(unsigned long);
			init_SS((unsigned long) udmem);
		} else
	#endif
		SS = (uchar *) emalloc(udmem);
		bstore = bstore_mod;
	} else
	#if NCORE>1
		{ void init_SS(unsigned long);
		  init_SS(ONE_L<<(ssize-3));
		}
	#else
		SS = (uchar *) emalloc(ONE_L<<(ssize-3));
	#endif
#else
	hinit();
#endif
#if defined(FULLSTACK) && defined(BITSTATE)
	onstack_init();
#endif
#if defined(CNTRSTACK) && !defined(BFS)
	LL = (uchar *) emalloc(ONE_L<<(ssize-3));
#endif
	stack	= ( Stack *) emalloc(sizeof(Stack));
	svtack	= (Svtack *) emalloc(sizeof(Svtack));
	/* a place to point for Pptr of non-running procs: */
	noptr	= (uchar *) emalloc(Maxbody * sizeof(char));
#if defined(SVDUMP) && defined(VERBOSE)
	if (vprefix > 0)
		(void) write(svfd, (uchar *) &vprefix, sizeof(int));
#endif
#ifdef VERI
	Addproc(VERI);	/* never - pid = 0 */
#endif
	active_procs();	/* started after never */
#ifdef EVENT_TRACE
	now._event = start_event;
	reached[EVENT_TRACE][start_event] = 1;
#endif
#ifdef HAS_CODE
	globinit();
#endif
#ifdef BITSTATE
go_again:
#endif
	do_the_search();
#ifdef BITSTATE
	if (--Nrun > 0 && HASH_CONST[++HASH_NR])
	{	printf("Run %d:\n", HASH_NR);
		wrap_stats();
		printf("\n");
		if (udmem)	/* Dillinger 3/2/09 */
		{	memset(SS, 0, udmem);
		} else
		{	memset(SS, 0, ONE_L<<(ssize-3));
		}
#ifdef CNTRSTACK
		memset(LL, 0, ONE_L<<(ssize-3));
#endif
#ifdef FULLSTACK
		memset((uchar *) S_Tab, 0, 
		maxdepth*sizeof(struct H_el *));
#endif
		nstates=nlinks=truncs=truncs2=ngrabs = 0;
		nlost=nShadow=hcmp = 0;
		Fa=Fh=Zh=Zn = 0;
		PUT=PROBE=ZAPS=Ccheck=Cholds = 0;
		goto go_again;
	}
#endif
}
#ifdef HAS_PROVIDED
int provided(int, uchar, int, Trans *);
#endif
#if NCORE>1
#define GLOBAL_LOCK	(0)
#ifndef CS_N
#define CS_N		(256*NCORE)
#endif
#ifdef NGQ
#define NR_QS		(NCORE)
#define CS_NR		(CS_N+1)	/* 2^N + 1, nr critical sections */
#define GQ_RD		GLOBAL_LOCK
#define GQ_WR		GLOBAL_LOCK
#define CS_ID		(1 + (int) (j1_spin & (CS_N-1))) /* mask: 2^N - 1, zero reserved */
#define QLOCK(n)	(1+n)
#else
#define NR_QS		(NCORE+1)
#define CS_NR		(CS_N+3)
#define GQ_RD		(1)
#define GQ_WR		(2)
#define CS_ID		(3 + (int) (j1_spin & (CS_N-1)))
#define QLOCK(n)	(3+n)
#endif

void e_critical(int);
void x_critical(int);

#ifndef SEP_STATE
	#define enter_critical(w)	e_critical(w)
	#define leave_critical(w)	x_critical(w)
#else
	#ifdef NGQ
	#define enter_critical(w)	{ if (w < 1+NCORE) e_critical(w); }
	#define leave_critical(w)	{ if (w < 1+NCORE) x_critical(w); }
	#else
	#define enter_critical(w)	{ if (w < 3+NCORE) e_critical(w); }
	#define leave_critical(w)	{ if (w < 3+NCORE) x_critical(w); }
	#endif
#endif

int
cpu_printf(const char *fmt, ...)
{	va_list args;
	enter_critical(GLOBAL_LOCK);	/* printing */
	printf("cpu%d: ", core_id);
	fflush(stdout);
	va_start(args, fmt);
	vprintf(fmt, args);
	va_end(args);
	fflush(stdout);
	leave_critical(GLOBAL_LOCK);
	return 1;
}
#else
int
cpu_printf(const char *fmt, ...)
{	va_list args;
	va_start(args, fmt);
	vprintf(fmt, args);
	va_end(args);
	return 1;
}
#endif
int
Printf(const char *fmt, ...)
{	/* Make sure the args to Printf
	 * are always evaluated (e.g., they
	 * could contain a run stmnt)
	 * but do not generate the output
	 * during verification runs
	 * unless explicitly wanted
	 * If this fails on your system
	 * compile SPIN itself -DPRINTF
	 * and this code is not generated
	 */
#ifdef HAS_CODE
	if (readtrail)
	{	va_list args;
		va_start(args, fmt);
		vprintf(fmt, args);
		va_end(args);
		return 1;
	}
#endif
#ifdef PRINTF
	va_list args;
	va_start(args, fmt);
	vprintf(fmt, args);
	va_end(args);
#endif
	return 1;
}
extern void printm(int);
#ifndef SC
#define getframe(i)	&trail[i];
#else
static long HHH, DDD, hiwater;
static long CNT1, CNT2;
static int stackwrite;
static int stackread;
static Trail frameptr;
Trail *
getframe(int d)
{
	if (CNT1 == CNT2)
		return &trail[d];

	if (d >= (CNT1-CNT2)*DDD)
		return &trail[d - (CNT1-CNT2)*DDD];

	if (!stackread
	&&  (stackread = open(stackfile, 0)) < 0)
	{	printf("getframe: cannot open %s\n", stackfile);
		wrapup();
	}
	if (lseek(stackread, d* (off_t) sizeof(Trail), SEEK_SET) == -1
	|| read(stackread, &frameptr, sizeof(Trail)) != sizeof(Trail))
	{	printf("getframe: frame read error\n");
		wrapup();
	}
	return &frameptr;
}
#endif
#if !defined(SAFETY) && !defined(BITSTATE)
#if !defined(FULLSTACK) || defined(MA)
#define depth_of(x)	A_depth /* an estimate */
#else
int
depth_of(struct H_el *s)
{	Trail *t; int d;
	for (d = 0; d <= A_depth; d++)
	{	t = getframe(d);
		if (s == t->ostate)
			return d;
	}
	printf("pan: cannot happen, depth_of\n");
	return depthfound;
}
#endif
#endif
#if NCORE>1
extern void cleanup_shm(int);
volatile unsigned int	*search_terminated; /* to signal early termination */
#endif
void
pan_exit(int val)
{	void stop_timer(void);
	if (signoff)
	{	printf("--end of output--\n");
	}
#if NCORE>1
	if (search_terminated != NULL)
	{	*search_terminated |= 1;	/* pan_exit */
	}
#ifdef USE_DISK
	{ void	dsk_stats(void);
		dsk_stats();
	}
#endif
	if (!state_tables && !readtrail)
	{	cleanup_shm(1);
	}
#endif
	if (val == 2)
	{	val = 0;
	} else
	{	stop_timer();
	}

#ifdef C_EXIT
	C_EXIT; /* trust that it defines a fct */
#endif
	exit(val);
}
#ifdef HAS_CODE
char *
transmognify(char *s)
{	char *v, *w;
	static char buf[2][2048];
	int i, toggle = 0;
	if (!s || strlen(s) > 2047) return s;
	memset(buf[0], 0, 2048);
	memset(buf[1], 0, 2048);
	strcpy(buf[toggle], s);
	while ((v = strstr(buf[toggle], "{c_code")))
	{	*v = '\0'; v++;
		strcpy(buf[1-toggle], buf[toggle]);
		for (w = v; *w != '}' && *w != '\0'; w++) /* skip */;
		if (*w != '}') return s;
		*w = '\0'; w++;
		for (i = 0; code_lookup[i].c; i++)
			if (strcmp(v, code_lookup[i].c) == 0
			&&  strlen(v) == strlen(code_lookup[i].c))
			{	if (strlen(buf[1-toggle])
				 +  strlen(code_lookup[i].t)
				 +  strlen(w) > 2047)
					return s;
				strcat(buf[1-toggle], code_lookup[i].t);
				break;
			}
		strcat(buf[1-toggle], w);
		toggle = 1 - toggle;
	}
	buf[toggle][2047] = '\0';
	return buf[toggle];
}
#else
char * transmognify(char *s) { return s; }
#endif
#ifdef HAS_CODE
void
add_src_txt(int ot, int tt)
{	Trans *t;
	char *q;

	for (t = trans[ot][tt]; t; t = t->nxt)
	{	printf("\t\t");
		q = transmognify(t->tp);
		for ( ; q && *q; q++)
			if (*q == '\n')
				printf("\\n");
			else
				putchar(*q);
		printf("\n");
	}
}
void
wrap_trail(void)
{	static int wrap_in_progress = 0;
	int i; short II;
	P0 *z;

	if (wrap_in_progress++) return;

	printf("spin: trail ends after %ld steps\n", depth);
	if (onlyproc >= 0)
	{	if (onlyproc >= now._nr_pr) { pan_exit(0); }
		II = onlyproc;
		z = (P0 *)pptr(II);
		printf("%3ld:	proc %d (%s) ",
			depth, II, procname[z->_t]);
		for (i = 0; src_all[i].src; i++)
			if (src_all[i].tp == (int) z->_t)
			{	printf(" line %3d",
					src_all[i].src[z->_p]);
				break;
			}
		printf(" (state %2d)", z->_p);
		if (!stopstate[z->_t][z->_p])
			printf(" (invalid end state)");
		printf("\n");
		add_src_txt(z->_t, z->_p);
		pan_exit(0);
	}
	printf("#processes %d:\n", now._nr_pr);
	if (depth < 0) depth = 0;
	for (II = 0; II < now._nr_pr; II++)
	{	z = (P0 *)pptr(II);
		printf("%3ld:	proc %d (%s) ",
			depth, II, procname[z->_t]);
		for (i = 0; src_all[i].src; i++)
			if (src_all[i].tp == (int) z->_t)
			{	printf(" line %3d",
					src_all[i].src[z->_p]);
				break;
			}
		printf(" (state %2d)", z->_p);
		if (!stopstate[z->_t][z->_p])
			printf(" (invalid end state)");
		printf("\n");
		add_src_txt(z->_t, z->_p);
	}
	c_globals();
	for (II = 0; II < now._nr_pr; II++)
	{	z = (P0 *)pptr(II);
		c_locals(II, z->_t);
	}
#ifdef ON_EXIT
	ON_EXIT;
#endif
	pan_exit(0);
}
FILE *
findtrail(void)
{	FILE *fd;
	char fnm[512], *q;
	char MyFile[512];
	char MySuffix[16];
	int  try_core;
	int  candidate_files;

	if (trailfilename != NULL)
	{	fd = fopen(trailfilename, "r");
		if (fd == NULL)
		{	printf("pan: cannot find %s\n", trailfilename);
			pan_exit(1);
		} /* else */
		goto success;
	}
talk:
	try_core = 1;
	candidate_files = 0;
	tprefix = "trail";
	strcpy(MyFile, TrailFile);
	do { /* see if there's more than one possible trailfile */
		if (whichtrail)
		{	sprintf(fnm, "%s%d.%s",
				MyFile, whichtrail, tprefix);
			fd = fopen(fnm, "r");
			if (fd != NULL)
			{	candidate_files++;
				if (verbose==100)
					printf("trail%d: %s\n",
						candidate_files, fnm);
				fclose(fd);
			}
			if ((q = strchr(MyFile, '.')) != NULL)
			{	*q = '\0';
				sprintf(fnm, "%s%d.%s",
					MyFile, whichtrail, tprefix);
				*q = '.';
				fd = fopen(fnm, "r");
				if (fd != NULL)
				{	candidate_files++;
					if (verbose==100)
						printf("trail%d: %s\n",
							candidate_files, fnm);
					fclose(fd);
			}	}
		} else
		{	sprintf(fnm, "%s.%s", MyFile, tprefix);
			fd = fopen(fnm, "r");
			if (fd != NULL)
			{	candidate_files++;
				if (verbose==100)
					printf("trail%d: %s\n",
						candidate_files, fnm);
				fclose(fd);
			}
			if ((q = strchr(MyFile, '.')) != NULL)
			{	*q = '\0';
				sprintf(fnm, "%s.%s", MyFile, tprefix);
				*q = '.';
				fd = fopen(fnm, "r");
				if (fd != NULL)
				{	candidate_files++;
					if (verbose==100)
						printf("trail%d: %s\n",
							candidate_files, fnm);
					fclose(fd);
		}	}	}
		tprefix = MySuffix;
		sprintf(tprefix, "cpu%d_trail", try_core++);
	} while (try_core <= NCORE);

	if (candidate_files != 1)
	{	if (verbose != 100)
		{	printf("error: there are %d trail files:\n",
				candidate_files);
			verbose = 100;
			goto talk;
		} else
		{	printf("pan: rm or mv all except one\n");
			exit(1);
	}	}
	try_core = 1;
	strcpy(MyFile, TrailFile); /* restore */
	tprefix = "trail";
try_again:
	if (whichtrail)
	{	sprintf(fnm, "%s%d.%s", MyFile, whichtrail, tprefix);
		fd = fopen(fnm, "r");
		if (fd == NULL && (q = strchr(MyFile, '.')))
		{	*q = '\0';
			sprintf(fnm, "%s%d.%s",
				MyFile, whichtrail, tprefix);
			*q = '.';
			fd = fopen(fnm, "r");
		}
	} else
	{	sprintf(fnm, "%s.%s", MyFile, tprefix);
		fd = fopen(fnm, "r");
		if (fd == NULL && (q = strchr(MyFile, '.')))
		{	*q = '\0';
			sprintf(fnm, "%s.%s", MyFile, tprefix);
			*q = '.';
			fd = fopen(fnm, "r");
	}	}
	if (fd == NULL)
	{	if (try_core < NCORE)
		{	tprefix = MySuffix;
			sprintf(tprefix, "cpu%d_trail", try_core++);
			goto try_again;
		}
		printf("pan: cannot find trailfile %s\n", fnm);
		pan_exit(1);
	}
success:
#if NCORE>1 && defined(SEP_STATE)
	{	void set_root(void); /* for partial traces from local root */
		set_root();
	}
#endif
	return fd;
}

uchar do_transit(Trans *, short);

void
getrail(void)
{	FILE *fd;
	char *q;
	int i, t_id, lastnever=-1; short II;
	Trans *t;
	P0 *z;

	fd = findtrail();	/* exits if unsuccessful */
	while (fscanf(fd, "%ld:%d:%d\n", &depth, &i, &t_id) == 3)
	{	if (depth == -1)
			printf("<<<<<START OF CYCLE>>>>>\n");
		if (depth < 0)
			continue;
		if (i > now._nr_pr)
		{	printf("pan: Error, proc %d invalid pid ", i);
			printf("transition %d\n", t_id);
			break;
		}
		II = i;
		z = (P0 *)pptr(II);
		for (t = trans[z->_t][z->_p]; t; t = t->nxt)
			if (t->t_id == (T_ID) t_id)
				break;
		if (!t)
		{	for (i = 0; i < NrStates[z->_t]; i++)
			{	t = trans[z->_t][i];
				if (t && t->t_id == (T_ID) t_id)
				{	printf("\tRecovered at state %d\n", i);
					z->_p = i;
					goto recovered;
			}	}
			printf("pan: Error, proc %d type %d state %d: ",
				II, z->_t, z->_p);
			printf("transition %d not found\n", t_id);
			printf("pan: list of possible transitions in this process:\n");
			if (z->_t >= 0 && z->_t <= _NP_)
			for (t = trans[z->_t][z->_p]; t; t = t->nxt)
				printf("	t_id %d -- case %d, [%s]\n",
					t->t_id, t->forw, t->tp);
			break; /* pan_exit(1); */
		}
recovered:
		q = transmognify(t->tp);
		if (gui) simvals[0] = '\0';
		this = pptr(II);
		trpt->tau |= 1;
		if (!do_transit(t, II))
		{	if (onlyproc >= 0 && II != onlyproc)
				goto moveon;
			printf("pan: error, next transition UNEXECUTABLE on replay\n");
			printf("     most likely causes: missing c_track statements\n");
			printf("       or illegal side-effects in c_expr statements\n");
		}
		if (onlyproc >= 0 && II != onlyproc)
			goto moveon;
		if (verbose)
		{	printf("%3ld: proc %2d (%s) ", depth, II, procname[z->_t]);
			for (i = 0; src_all[i].src; i++)
				if (src_all[i].tp == (int) z->_t)
				{	printf(" line %3d \"%s\" ",
						src_all[i].src[z->_p], PanSource);
					break;
				}
			printf("(state %d) trans {%d,%d} [%s]\n",
				z->_p, t_id, t->forw, q?q:"");
			c_globals();
			for (i = 0; i < now._nr_pr; i++)
			{	c_locals(i, ((P0 *)pptr(i))->_t);
			}
		} else
		if (strcmp(procname[z->_t], ":never:") == 0)
		{	if (lastnever != (int) z->_p)
			{	for (i = 0; src_all[i].src; i++)
					if (src_all[i].tp == (int) z->_t)
					{	printf("MSC: ~G %d\n",
							src_all[i].src[z->_p]);
						break;
					}
				if (!src_all[i].src)
					printf("MSC: ~R %d\n", z->_p);
			}
			lastnever = z->_p;
			goto sameas;
		} else
		if (strcmp(procname[z->_t], ":np_:") != 0)
		{
sameas:		if (no_rck) goto moveon;
			if (coltrace)
			{	printf("%ld: ", depth);
				for (i = 0; i < II; i++)
					printf("\t\t");
				printf("%s(%d):", procname[z->_t], II);
				printf("[%s]\n", q?q:"");
			} else if (!silent)
			{	if (strlen(simvals) > 0) {
				printf("%3ld:	proc %2d (%s)", 
					depth, II, procname[z->_t]);
				for (i = 0; src_all[i].src; i++)
					if (src_all[i].tp == (int) z->_t)
					{	printf(" line %3d \"%s\" ",
							src_all[i].src[z->_p], PanSource);
						break;
					}
				printf("(state %d)	[values: %s]\n", z->_p, simvals);
				}
				printf("%3ld:	proc %2d (%s)", 
					depth, II, procname[z->_t]);
				for (i = 0; src_all[i].src; i++)
					if (src_all[i].tp == (int) z->_t)
					{	printf(" line %3d \"%s\" ",
							src_all[i].src[z->_p], PanSource);
						break;
					}
				printf("(state %d)	[%s]\n", z->_p, q?q:"");
			/*	printf("\n");	*/
		}	}
moveon:	z->_p = t->st;
	}
	wrap_trail();
}
#endif
int
f_pid(int pt)
{	int i;
	P0 *z;
	for (i = 0; i < now._nr_pr; i++)
	{	z = (P0 *)pptr(i);
		if (z->_t == (unsigned) pt)
			return BASE+z->_pid;
	}
	return -1;
}
#ifdef VERI
void check_claim(int);
#endif

#if !defined(HASH64) && !defined(HASH32)
	#if WS>4
		#define HASH64
	#else
		#define HASH32
	#endif
#endif
#if defined(HASH32) && defined(SAFETY) && !defined(SFH) && !defined(SPACE)
	#define SFH
#endif
#if defined(SFH) && (defined(BITSTATE) || defined(COLLAPSE) || defined(HC) || defined(HASH64) || defined(MA))
	#undef SFH
#endif
#if defined(SFH) && !defined(NOCOMP)
	#define NOCOMP	/* go for speed */
#endif
#if NCORE>1 && !defined(GLOB_HEAP)
	#define SEP_HEAP /* version 5.1.2 */
#endif

#ifdef BITSTATE
int
bstore_mod(char *v, int n)	/* hasharray size not a power of two */
{	unsigned long x, y;
	unsigned int i = 1;

	d_hash((uchar *) v, n); /* sets j3, j4, K1, K2 */
	x = K1; y = j3;
	for (;;)
	{	if (!(SS[x%udmem]&(1<<y))) break;
		if (i == hfns) {
#ifdef DEBUG
			printf("Old bitstate\n");
#endif
			return 1;
		}
		x = (x + K2 + i);
		y = (y + j4) & 7;
		i++;
	}
#ifdef RANDSTOR
	if (rand()%100 > RANDSTOR) return 0;
#endif
	for (;;)
	{	SS[x%udmem] |= (1<<y);
		if (i == hfns) break;
		x = (x + K2 + i);
		y = (y + j4) & 7;
		i++;
	}
#ifdef DEBUG
	printf("New bitstate\n");
#endif
	if (now._a_t&1)
	{	nShadow++;
	}
	return 0;
}
int
bstore_reg(char *v, int n)	/* extended hashing, Peter Dillinger, 2004 */
{	unsigned long x, y;
	unsigned int i = 1;

	d_hash((uchar *) v, n); /* sets j1-j4 */
	x = j2; y = j3;
	for (;;)
	{	if (!(SS[x]&(1<<y))) break;
		if (i == hfns) {
#ifdef DEBUG
			printf("Old bitstate\n");
#endif
			return 1;
		}
		x = (x + j1_spin + i) & nmask;
		y = (y + j4) & 7;
		i++;
	}
#ifdef RANDSTOR
	if (rand()%100 > RANDSTOR) return 0;
#endif
	for (;;)
	{	SS[x] |= (1<<y);
		if (i == hfns) break;
		x = (x + j1_spin + i) & nmask;
		y = (y + j4) & 7;
		i++;
	}
#ifdef DEBUG
	printf("New bitstate\n");
#endif
	if (now._a_t&1)
	{	nShadow++;
	}
	return 0;
}
#endif
unsigned long TMODE = 0666; /* file permission bits for trail files */

int trcnt=1;
char snap[64], fnm[512];

int
make_trail(void)
{	int fd;
	char *q;
	char MyFile[512];
	int w_flags = O_CREAT|O_WRONLY|O_TRUNC;

	if (exclusive == 1 && iterative == 0)
	{	w_flags |= O_EXCL;
	}

	q = strrchr(TrailFile, '/');
	if (q == NULL) q = TrailFile; else q++;
	strcpy(MyFile, q); /* TrailFile is not a writable string */

	if (iterative == 0 && Nr_Trails++ > 0)
	{	sprintf(fnm, "%s%d.%s",
			MyFile, Nr_Trails-1, tprefix);
	} else
	{
#ifdef PUTPID
		sprintf(fnm, "%s_%s_%d.%s", MyFile, progname, getpid(), tprefix);
#else
		sprintf(fnm, "%s.%s", MyFile, tprefix);
#endif
	}
	if ((fd = open(fnm, w_flags, TMODE)) < 0)
	{	if ((q = strchr(MyFile, '.')))
		{	*q = '\0';
			if (iterative == 0 && Nr_Trails-1 > 0)
				sprintf(fnm, "%s%d.%s",
					MyFile, Nr_Trails-1, tprefix);
			else
				sprintf(fnm, "%s.%s", MyFile, tprefix);
			*q = '.';
			fd = open(fnm, w_flags, TMODE);
	}	}
	if (fd < 0)
	{	printf("pan: cannot create %s\n", fnm);
		perror("cause");
	} else
	{
#if NCORE>1 && (defined(SEP_STATE) || !defined(FULL_TRAIL))
	void write_root(void); 
	write_root();
#else
		printf("pan: wrote %s\n", fnm);
#endif
	}
	return fd;
}

#ifndef FREQ
#define FREQ	(1000000)
#endif
#ifdef BFS
#define Q_PROVISO
#ifndef INLINE_REV
#define INLINE_REV
#endif

typedef struct SV_Hold {
	State *sv;
	int  sz;
	struct SV_Hold *nxt;
} SV_Hold;

typedef struct EV_Hold {
	char *sv;
	int  sz;
	int nrpr;
	int nrqs;
	char *po;
	char *qo;
	char *ps, *qs;
	struct EV_Hold *nxt;
} EV_Hold;

typedef struct BFS_Trail {
	Trail	*frame;
	SV_Hold *onow;
	EV_Hold *omask;
#ifdef Q_PROVISO
	struct H_el *lstate;
#endif
	short boq;
	struct BFS_Trail *nxt;
} BFS_Trail;

BFS_Trail *bfs_trail, *bfs_bot, *bfs_free;

SV_Hold *svhold, *svfree;

#ifdef BFS_DISK
#ifndef BFS_LIMIT
	#define BFS_LIMIT	100000
#endif
#ifndef BFS_DSK_LIMIT
	#define BFS_DSK_LIMIT	1000000
#endif
#if defined(WIN32) || defined(WIN64)
	#define RFLAGS	(O_RDONLY|O_BINARY)
	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)
#else
	#define RFLAGS	(O_RDONLY)
	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC)
#endif
long bfs_size_limit;
int bfs_dsk_write = -1;
int bfs_dsk_read = -1;
long bfs_dsk_writes, bfs_dsk_reads;
int bfs_dsk_seqno_w, bfs_dsk_seqno_r;
#endif

uchar do_reverse(Trans *, short, uchar);
void snapshot(void);

SV_Hold *
getsv(int n)
{	SV_Hold *h = (SV_Hold *) 0, *oh;

	oh = (SV_Hold *) 0;
	for (h = svfree; h; oh = h, h = h->nxt)
	{	if (n == h->sz)
		{	if (!oh)
				svfree = h->nxt;
			else
				oh->nxt = h->nxt;
			h->nxt = (SV_Hold *) 0;
			break;
		}
		if (n < h->sz)
		{	h = (SV_Hold *) 0;
			break;
		}
		/* else continue */
	}

	if (!h)
	{	h = (SV_Hold *) emalloc(sizeof(SV_Hold));
		h->sz = n;
#ifdef BFS_DISK
		if (bfs_size_limit >= BFS_LIMIT)
		{	h->sv = (State *) 0;	/* means: read disk */
			bfs_dsk_writes++;	/* count */
			if (bfs_dsk_write < 0	/* file descriptor */
			||  bfs_dsk_writes%BFS_DSK_LIMIT == 0)
			{	char dsk_nm[32];
				if (bfs_dsk_write >= 0)
				{	(void) close(bfs_dsk_write);
				}
				sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_w++);
				bfs_dsk_write = open(dsk_nm, WFLAGS, 0644);
				if (bfs_dsk_write < 0)
				{	Uerror("could not create tmp disk file");
				}
				printf("pan: created disk file %s\n", dsk_nm);
			}
			if (write(bfs_dsk_write, (char *) &now, n) != n)
			{	Uerror("aborting -- disk write failed (disk full?)");
			}
			return h; /* no memcpy */
		}
		bfs_size_limit++;
#endif
		h->sv = (State *) emalloc(sizeof(State) - VECTORSZ + n);
	}

	memcpy((char *)h->sv, (char *)&now, n);
	return h;
}

EV_Hold *
getsv_mask(int n)
{	EV_Hold *h;
	static EV_Hold *kept = (EV_Hold *) 0;

	for (h = kept; h; h = h->nxt)
		if (n == h->sz
		&&  (memcmp((char *) Mask, (char *) h->sv, n) == 0)
		&&  (now._nr_pr == h->nrpr)
		&&  (now._nr_qs == h->nrqs)
#if VECTORSZ>32000
		&&  (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(int)) == 0)
		&&  (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(int)) == 0)
#else
		&&  (memcmp((char *) proc_offset, (char *) h->po, now._nr_pr * sizeof(short)) == 0)
		&&  (memcmp((char *) q_offset, (char *) h->qo, now._nr_qs * sizeof(short)) == 0)
#endif
		&&  (memcmp((char *) proc_skip, (char *) h->ps, now._nr_pr * sizeof(uchar)) == 0)
		&&  (memcmp((char *) q_skip,    (char *) h->qs, now._nr_qs * sizeof(uchar)) == 0))
			break;
	if (!h)
	{	h = (EV_Hold *) emalloc(sizeof(EV_Hold));
		h->sz = n;
		h->nrpr = now._nr_pr;
		h->nrqs = now._nr_qs;

		h->sv = (char *) emalloc(n * sizeof(char));
		memcpy((char *) h->sv, (char *) Mask, n);

		if (now._nr_pr > 0)
		{	h->ps = (char *) emalloc(now._nr_pr * sizeof(int));
			memcpy((char *) h->ps, (char *) proc_skip,   now._nr_pr * sizeof(uchar));
#if VECTORSZ>32000
			h->po = (char *) emalloc(now._nr_pr * sizeof(int));
			memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(int));
#else
			h->po = (char *) emalloc(now._nr_pr * sizeof(short));
			memcpy((char *) h->po, (char *) proc_offset, now._nr_pr * sizeof(short));
#endif
		}
		if (now._nr_qs > 0)
		{	h->qs = (char *) emalloc(now._nr_qs * sizeof(int));
			memcpy((char *) h->qs, (char *) q_skip,   now._nr_qs * sizeof(uchar));
#if VECTORSZ>32000
			h->qo = (char *) emalloc(now._nr_qs * sizeof(int));
			memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(int));
#else
			h->qo = (char *) emalloc(now._nr_qs * sizeof(short));
			memcpy((char *) h->qo, (char *) q_offset, now._nr_qs * sizeof(short));
#endif
		}

		h->nxt = kept;
		kept = h;
	}
	return h;
}

void
freesv(SV_Hold *p)
{	SV_Hold *h, *oh;

	oh = (SV_Hold *) 0;
	for (h = svfree; h; oh = h, h = h->nxt)
		if (h->sz >= p->sz)
			break;

	if (!oh)
	{	p->nxt = svfree;
		svfree = p;
	} else
	{	p->nxt = h;
		oh->nxt = p;
	}
}

BFS_Trail *
get_bfs_frame(void)
{	BFS_Trail *t;

	if (bfs_free)
	{	t = bfs_free;
		bfs_free = bfs_free->nxt;
		t->nxt = (BFS_Trail *) 0;
	} else
	{	t = (BFS_Trail *) emalloc(sizeof(BFS_Trail));
	}
	t->frame = (Trail *) emalloc(sizeof(Trail));
	return t;
}

void
push_bfs(Trail *f, int d)
{	BFS_Trail *t;

	t = get_bfs_frame();
	memcpy((char *)t->frame, (char *)f, sizeof(Trail));
	t->frame->o_tt = d;	/* depth */

	t->boq = boq;
	t->onow = getsv(vsize);
	t->omask = getsv_mask(vsize);
#if defined(FULLSTACK) && defined(Q_PROVISO)
	t->lstate = Lstate;
#endif
	if (!bfs_bot)
	{	bfs_bot = bfs_trail = t;
	} else
	{	bfs_bot->nxt = t;
		bfs_bot = t;
	}
#ifdef CHECK
	printf("PUSH %u (%d)\n", t->frame, d);
#endif
}

Trail *
pop_bfs(void)
{	BFS_Trail *t;

	if (!bfs_trail)
		return (Trail *) 0;

	t = bfs_trail;
	bfs_trail = t->nxt;
	if (!bfs_trail)
		bfs_bot = (BFS_Trail *) 0;
#if defined(Q_PROVISO) && !defined(BITSTATE) && !defined(NOREDUCE)
	if (t->lstate) t->lstate->tagged = 0;
#endif

	t->nxt = bfs_free;
	bfs_free = t;

	vsize = t->onow->sz;
	boq = t->boq;
#ifdef BFS_DISK
	if (t->onow->sv == (State *) 0)
	{	char dsk_nm[32];
		bfs_dsk_reads++;	/* count */
		if (bfs_dsk_read >= 0	/* file descriptor */
		&&  bfs_dsk_reads%BFS_DSK_LIMIT == 0)
		{	(void) close(bfs_dsk_read);
			sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r-1);
			(void) unlink(dsk_nm);
			bfs_dsk_read = -1;
		}
		if (bfs_dsk_read < 0)
		{	sprintf(dsk_nm, "pan_bfs_%d.tmp", bfs_dsk_seqno_r++);
			bfs_dsk_read = open(dsk_nm, RFLAGS);
			if (bfs_dsk_read < 0)
			{	Uerror("could not open temp disk file");
		}	}
		if (read(bfs_dsk_read, (char *) &now, vsize) != vsize)
		{	Uerror("bad bfs disk file read");
		}
#ifndef NOVSZ
		if (now._vsz != vsize)
		{	Uerror("disk read vsz mismatch");
		}
#endif
	} else
#endif
		memcpy((uchar *) &now, (uchar *) t->onow->sv, vsize);
	memcpy((uchar *) Mask, (uchar *) t->omask->sv, vsize);
	if (now._nr_pr > 0)
#if VECTORSZ>32000
	{	memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(int));
#else
	{	memcpy((char *)proc_offset, (char *)t->omask->po, now._nr_pr * sizeof(short));
#endif
		memcpy((char *)proc_skip,   (char *)t->omask->ps, now._nr_pr * sizeof(uchar));
	}
	if (now._nr_qs > 0)
#if VECTORSZ>32000
	{	memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(int));
#else
	{	memcpy((uchar *)q_offset, (uchar *)t->omask->qo, now._nr_qs * sizeof(short));
#endif
		memcpy((uchar *)q_skip,   (uchar *)t->omask->qs, now._nr_qs * sizeof(uchar));
	}
#ifdef BFS_DISK
	if (t->onow->sv != (State *) 0)
#endif
		freesv(t->onow);	/* omask not freed */
#ifdef CHECK
	printf("POP %u (%d)\n", t->frame, t->frame->o_tt);
#endif
	return t->frame;
}

void
store_state(Trail *ntrpt, int shortcut, short oboq)
{
#ifdef VERI
	Trans *t2 = (Trans *) 0;
	uchar ot; int tt, E_state;
	uchar o_opm = trpt->o_pm, *othis = this;

	if (shortcut)
	{
#ifdef VERBOSE
		printf("claim: shortcut\n");
#endif
		goto store_it;	/* no claim move */
	}

	this  = (((uchar *)&now)+proc_offset[0]); /* 0 = never claim */
	trpt->o_pm = 0;

	tt    = (int)   ((P0 *)this)->_p;
	ot    = (uchar) ((P0 *)this)->_t;

#ifdef HAS_UNLESS
	E_state = 0;
#endif
	for (t2 = trans[ot][tt]; t2; t2 = t2?t2->nxt:(Trans *)0)
	{
#ifdef HAS_UNLESS
		if (E_state > 0
		&&  E_state != t2->e_trans)
			break;
#endif
		if (do_transit(t2, 0))
		{
#ifdef VERBOSE
			if (!reached[ot][t2->st])
			printf("depth: %d -- claim move from %d -> %d\n",
				trpt->o_tt, ((P0 *)this)->_p, t2->st);
#endif
#ifdef HAS_UNLESS
			E_state = t2->e_trans;
#endif
			if (t2->st > 0)
			{	((P0 *)this)->_p = t2->st;
				reached[ot][t2->st] = 1;
#ifndef NOCLAIM
				check_claim(t2->st);
#endif
			}
			if (now._nr_pr == 0)	/* claim terminated */
				uerror("end state in claim reached");

#ifdef PEG
			peg[t2->forw]++;
#endif
			trpt->o_pm |= 1;
			if (t2->atom&2)
			Uerror("atomic in claim not supported in BFS mode");
store_it:

#endif

#ifdef BITSTATE
			if (!bstore((char *)&now, vsize))
#else
#ifdef MA
			if (!gstore((char *)&now, vsize, 0))
#else
			if (!hstore((char *)&now, vsize))
#endif
#endif
			{	static long sdone = (long) 0; long ndone;
				nstates++;
#ifndef NOREDUCE
				trpt->tau |= 64;
#endif
				ndone = (unsigned long) (nstates/((double) FREQ));
				if (ndone != sdone && mreached%10 != 0)
				{	snapshot();
					sdone = ndone;
#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
					if (nstates > ((double)(1<<(ssize+1))))
					{	void resize_hashtable(void);
						resize_hashtable();
					}
#endif
				}
#if SYNC
				if (boq != -1)
					midrv++;
				else if (oboq != -1)
				{	Trail *x;
					x = (Trail *) trpt->ostate; /* pre-rv state */
					if (x) x->o_pm |= 4; /* mark success */
				}
#endif
				push_bfs(ntrpt, trpt->o_tt+1);
			} else
			{	truncs++;
#if !defined(NOREDUCE) && defined(FULLSTACK) && defined(Q_PROVISO)
#if !defined(BITSTATE)
				if (Lstate && Lstate->tagged) trpt->tau |= 64;
#else
				if (trpt->tau&32)
				{  BFS_Trail *tprov;
				   for (tprov = bfs_trail; tprov; tprov = tprov->nxt)
					if (tprov->onow->sv != (State *) 0
					&&  memcmp((uchar *)&now, (uchar *)tprov->onow->sv, vsize) == 0)
					{	trpt->tau |= 64;
						break;	/* state is in queue */
				}	}
#endif
#endif
			}
#ifdef VERI
			((P0 *)this)->_p = tt;	/* reset claim */
			if (t2)
				do_reverse(t2, 0, 0);
			else
				break;
	}	}
	this = othis;
	trpt->o_pm = o_opm;
#endif
}

Trail *ntrpt;

void
bfs(void)
{	Trans *t; Trail *otrpt, *x;
	uchar _n, _m, ot, nps = 0;
	int tt, E_state;
	short II, From = (short) (now._nr_pr-1), To = BASE;
	short oboq = boq;

	ntrpt = (Trail *) emalloc(sizeof(Trail));
	trpt->ostate = (struct H_el *) 0;
	trpt->tau = 0;

	trpt->o_tt = -1;
	store_state(ntrpt, 0, oboq);	/* initial state */

	while ((otrpt = pop_bfs()))	/* also restores now */
	{	memcpy((char *) trpt, (char *) otrpt, sizeof(Trail));
#if defined(C_States) && (HAS_TRACK==1)
		c_revert((uchar *) &(now.c_state[0]));
#endif
		if (trpt->o_pm & 4)
		{
#ifdef VERBOSE
			printf("Revisit of atomic not needed (%d)\n",
				trpt->o_pm);
#endif
			continue;
		}
#ifndef NOREDUCE
		nps = 0;
#endif
		if (trpt->o_pm == 8)
		{	revrv++;
			if (trpt->tau&8)
			{
#ifdef VERBOSE
				printf("Break atomic (pm:%d,tau:%d)\n",
					trpt->o_pm, trpt->tau);
#endif
				trpt->tau &= ~8;
			}
#ifndef NOREDUCE
			else if (trpt->tau&32)
			{
#ifdef VERBOSE
				printf("Void preselection (pm:%d,tau:%d)\n",
					trpt->o_pm, trpt->tau);
#endif
				trpt->tau &= ~32;
				nps = 1; /* no preselection in repeat */
			}
#endif
		}
		trpt->o_pm &= ~(4|8);
		if (trpt->o_tt > mreached)
		{	mreached = trpt->o_tt;
			if (mreached%10 == 0)
			{	snapshot();
		}	}
		depth = trpt->o_tt;
		if (depth >= maxdepth)
		{
#if SYNC
			Trail *x;
			if (boq != -1)
			{	x = (Trail *) trpt->ostate;
				if (x) x->o_pm |= 4; /* not failing */
			}
#endif
			truncs++;
			if (!warned)
			{	warned = 1;
		  		printf("error: max search depth too small\n");
			}
			if (bounded)
				uerror("depth limit reached");
			continue;
		}
#ifndef NOREDUCE
		if (boq == -1 && !(trpt->tau&8) && nps == 0)
		for (II = now._nr_pr-1; II >= BASE; II -= 1)
		{
Pickup:			this = pptr(II);
			tt = (int) ((P0 *)this)->_p;
			ot = (uchar) ((P0 *)this)->_t;
			if (trans[ot][tt]->atom & 8)
			{	t = trans[ot][tt];
				if (t->qu[0] != 0)
				{	Ccheck++;
					if (!q_cond(II, t))
						continue;
					Cholds++;
				}
				From = To = II;
				trpt->tau |= 32; /* preselect marker */
#ifdef DEBUG
				printf("%3d: proc %d PreSelected (tau=%d)\n", 
					depth, II, trpt->tau);
#endif
				goto MainLoop;
		}	}
		trpt->tau &= ~32;
#endif
Repeat:
		if (trpt->tau&8)		/* atomic */
		{	From = To = (short ) trpt->pr;
			nlinks++;
		} else
		{	From = now._nr_pr-1;
			To = BASE;
		}
MainLoop:
		_n = _m = 0;
		for (II = From; II >= To; II -= 1)
		{
			this = (((uchar *)&now)+proc_offset[II]);
			tt = (int) ((P0 *)this)->_p;
			ot = (uchar) ((P0 *)this)->_t;
#if SYNC
			/* no rendezvous with same proc */
			if (boq != -1 && trpt->pr == II) continue;
#endif
			ntrpt->pr = (uchar) II;
			ntrpt->st = tt;	
			trpt->o_pm &= ~1;		/* no move yet */
#ifdef EVENT_TRACE
			trpt->o_event = now._event;
#endif
#ifdef HAS_PROVIDED
			if (!provided(II, ot, tt, t)) continue;
#endif
#ifdef HAS_UNLESS
			E_state = 0;
#endif
			for (t = trans[ot][tt]; t; t = t->nxt)
			{
#ifdef HAS_UNLESS
				if (E_state > 0
				&&  E_state != t->e_trans)
					break;
#endif
				ntrpt->o_t = t;

				oboq = boq;

				if (!(_m = do_transit(t, II)))
					continue;

				trpt->o_pm |= 1;	/* we moved */
				(trpt+1)->o_m = _m;	/* for unsend */
#ifdef PEG
				peg[t->forw]++;
#endif
#ifdef CHECK
				printf("%3d: proc %d exec %d, ",
					depth, II, t->forw);
				printf("%d to %d, %s %s %s",
					tt, t->st, t->tp,
					(t->atom&2)?"atomic":"",
					(boq != -1)?"rendez-vous":"");
#ifdef HAS_UNLESS
				if (t->e_trans)
					printf(" (escapes to state %d)", t->st);
#endif
				printf(" %saccepting [tau=%d]\n",
					(trpt->o_pm&2)?"":"non-", trpt->tau);
#endif
#ifdef HAS_UNLESS
				E_state = t->e_trans;
#if SYNC>0
				if (t->e_trans > 0 && (boq != -1 /* || oboq != -1 */))
				{ fprintf(efd, "error:	the use of rendezvous stmnt in the escape clause\n");
				  fprintf(efd, "	of an unless stmnt is not compatible with -DBFS\n");
				  pan_exit(1);
				}
#endif
#endif
				if (t->st > 0) ((P0 *)this)->_p = t->st;

	/* ptr to pred: */	ntrpt->ostate = (struct H_el *) otrpt;
				ntrpt->st = tt;
				if (boq == -1 && (t->atom&2))	/* atomic */
					ntrpt->tau = 8;	/* record for next move */
				else
					ntrpt->tau = 0;

				store_state(ntrpt, (boq != -1 || (t->atom&2)), oboq);
#ifdef EVENT_TRACE
				now._event = trpt->o_event;
#endif

				/* undo move and continue */
				trpt++;	/* this is where ovals and ipt are set */
				do_reverse(t, II, _m);	/* restore now. */
				trpt--;
#ifdef CHECK
	#if NCORE>1
				enter_critical(GLOBAL_LOCK);	/* in verbose mode only */
				printf("cpu%d: ", core_id);
	#endif
				printf("%3d: proc %d ", depth, II);
				printf("reverses %d, %d to %d,",
					t->forw, tt, t->st);
				printf(" %s [abit=%d,adepth=%d,",
					t->tp, now._a_t, A_depth);
				printf("tau=%d,%d]\n",
					trpt->tau, (trpt-1)->tau);
	#if NCORE>1
				leave_critical(GLOBAL_LOCK);
	#endif
#endif
				reached[ot][t->st] = 1;
				reached[ot][tt] = 1;

				((P0 *)this)->_p = tt;
				_n |= _m;
		}	}
#ifndef NOREDUCE
		/* preselected - no succ definitely outside stack */
		if ((trpt->tau&32) && !(trpt->tau&64))
		{	From = now._nr_pr-1; To = BASE;
#ifdef DEBUG
			cpu_printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", 
				depth, II+1, (int) _n, trpt->tau);
#endif
			_n = 0; trpt->tau &= ~32;
			if (II >= BASE)
				goto Pickup;
			goto MainLoop;
		}
		trpt->tau &= ~(32|64);
#endif
		if (_n != 0)
			continue;
#ifdef DEBUG
		printf("%3d: no move [II=%d, tau=%d, boq=%d, _nr_pr=%d]\n",
			depth, II, trpt->tau, boq, now._nr_pr);
#endif
		if (boq != -1)
		{	failedrv++;
			x = (Trail *) trpt->ostate; /* pre-rv state */
			if (!x) continue; /* root state */
			if ((x->tau&8) || (x->tau&32)) /* break atomic or preselect at parent */
			{	x->o_pm |= 8; /* mark failure */
				this = (((uchar *)&now)+proc_offset[otrpt->pr]);
#ifdef VERBOSE
				printf("\treset state of %d from %d to %d\n",
					otrpt->pr, ((P0 *)this)->_p, otrpt->st);
#endif
				((P0 *)this)->_p = otrpt->st;
				unsend(boq);	/* retract rv offer */
				boq = -1;
				push_bfs(x, x->o_tt);
#ifdef VERBOSE
				printf("failed rv, repush with %d\n", x->o_pm);
#endif
			}
#ifdef VERBOSE
			else printf("failed rv, tau at parent: %d\n", x->tau);
#endif
		} else if (now._nr_pr > 0)
		{
			if ((trpt->tau&8))		/* atomic */
			{	trpt->tau &= ~(1|8);	/* 1=timeout, 8=atomic */
#ifdef DEBUG
				printf("%3d: atomic step proc %d blocks\n",
					depth, II+1);
#endif
				goto Repeat;
			}

			if (!(trpt->tau&1)) /* didn't try timeout yet */
			{	trpt->tau |=  1;
#ifdef DEBUG
				printf("%d: timeout\n", depth);
#endif
				goto MainLoop;
			}
#ifndef VERI
			if (!noends && !a_cycles && !endstate())
				uerror("invalid end state");
#endif
	}	}
}

void
putter(Trail *trpt, int fd)
{	long j;

	if (!trpt) return;

	if (trpt != (Trail *) trpt->ostate)
		putter((Trail *) trpt->ostate, fd);

	if (trpt->o_t)
	{	sprintf(snap, "%d:%d:%d\n",
			trcnt++, trpt->pr, trpt->o_t->t_id);
		j = strlen(snap);
		if (write(fd, snap, j) != j)
		{       printf("pan: error writing %s\n", fnm);
			pan_exit(1);
	}	}
}

void
nuerror(char *str)
{	int fd = make_trail();
	int j;

	if (fd < 0) return;
#ifdef VERI
	sprintf(snap, "-2:%d:-2\n", VERI);
	(void) write(fd, snap, strlen(snap));
#endif
#ifdef MERGED
	sprintf(snap, "-4:-4:-4\n");
	(void) write(fd, snap, strlen(snap));
#endif
	trcnt = 1;
	putter(trpt, fd);
	if (ntrpt->o_t)
	{	sprintf(snap, "%d:%d:%d\n",
			trcnt++, ntrpt->pr, ntrpt->o_t->t_id);
		j = strlen(snap);
		if (write(fd, snap, j) != j)
		{       printf("pan: error writing %s\n", fnm);
			pan_exit(1);
	}	}
	close(fd);
	if (errors >= upto && upto != 0)
	{	wrapup();
	}
}
#endif
#if NCORE>1
#if defined(WIN32) || defined(WIN64)
#ifndef _CONSOLE
	#define _CONSOLE
#endif
	#ifdef WIN64
#undef long
	#endif
#include <windows.h>

	#ifdef WIN64
	#define long	long long
	#endif
#else
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#endif

/* code common to cygwin/linux and win32/win64: */

#ifdef VERBOSE
	#define VVERBOSE	(1)
#else
	#define VVERBOSE	(0)
#endif

/* the following values must be larger than 256 and must fit in an int */
#define QUIT		1024	/* terminate now command */
#define QUERY		 512	/* termination status query message */
#define QUERY_F	 513	/* query failed, cannot quit */

#define GN_FRAMES	(int) (GWQ_SIZE / (double) sizeof(SM_frame))
#define LN_FRAMES	(int) (LWQ_SIZE / (double) sizeof(SM_frame))

#ifndef VMAX
	#define VMAX	VECTORSZ
#endif
#ifndef PMAX
	#define PMAX	64
#endif
#ifndef QMAX
	#define QMAX	64
#endif

#if VECTORSZ>32000
	#define OFFT	int
#else
	#define OFFT	short
#endif

#ifdef SET_SEG_SIZE
	/* no longer usefule -- being recomputed for local heap size anyway */
	double SEG_SIZE = (((double) SET_SEG_SIZE) * 1048576.);
#else
	double SEG_SIZE = (1048576.*1024.);	/* 1GB default shared memory pool segments */
#endif

double LWQ_SIZE = 0.; /* initialized in main */

#ifdef SET_WQ_SIZE
	#ifdef NGQ
	#warning SET_WQ_SIZE applies to global queue -- ignored
	double GWQ_SIZE = 0.;
	#else
	double GWQ_SIZE = (((double) SET_WQ_SIZE) * 1048576.);
	/* must match the value in pan_proxy.c, if used */
	#endif
#else
	#ifdef NGQ
	double GWQ_SIZE = 0.;
	#else
	double GWQ_SIZE = (128.*1048576.);	/* 128 MB default queue sizes */
	#endif
#endif

/* Crash Detection Parameters */
#ifndef ONESECOND
	#define ONESECOND	(1<<25)
#endif
#ifndef SHORT_T
	#define SHORT_T	(0.1)
#endif
#ifndef LONG_T
	#define LONG_T	(600)
#endif

double OneSecond   = (double) (ONESECOND); /* waiting for a free slot -- checks crash */
double TenSeconds  = 10. * (ONESECOND);    /* waiting for a lock -- check for a crash */

/* Termination Detection Params -- waiting for new state input in Get_Full_Frame */
double Delay       = ((double) SHORT_T) * (ONESECOND);	/* termination detection trigger */
double OneHour     = ((double) LONG_T) * (ONESECOND);	/* timeout termination detection */

typedef struct SM_frame     SM_frame;
typedef struct SM_results   SM_results;
typedef struct sh_Allocater sh_Allocater;

struct SM_frame {			/* about 6K per slot */
	volatile int	m_vsize;	/* 0 means free slot */
	volatile int	m_boq;		/* >500 is a control message */
#ifdef FULL_TRAIL
	volatile struct Stack_Tree *m_stack;	/* ptr to previous state */
#endif
	volatile uchar	m_tau;
	volatile uchar	m_o_pm;
	volatile int	nr_handoffs;	/* to compute real_depth */
	volatile char	m_now     [VMAX];
	volatile char	m_Mask    [(VMAX + 7)/8];
	volatile OFFT	m_p_offset[PMAX];
	volatile OFFT	m_q_offset[QMAX];
	volatile uchar	m_p_skip  [PMAX];
	volatile uchar	m_q_skip  [QMAX];
#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
	volatile uchar	m_c_stack [StackSize];
#endif
};

int	proxy_pid;		/* id of proxy if nonzero -- receive half */
int	store_proxy_pid;
short	remote_party;
int	proxy_pid_snd;		/* id of proxy if nonzero -- send half */
char	o_cmdline[512];		/* to pass options to children */

int	iamin[CS_NR+NCORE];		/* non-shared */

#if defined(WIN32) || defined(WIN64)
int tas(volatile LONG *);

HANDLE		proxy_handle_snd;	/* for Windows Create and Terminate */

struct sh_Allocater {			/* shared memory for states */
	volatile char	*dc_arena;	/* to allocate states from */
	volatile long	 pattern;	/* to detect overruns */
	volatile long	 dc_size;	/* nr of bytes left */
	volatile void	*dc_start;	/* where memory segment starts */
	volatile void	*dc_id;		/* to attach, detach, remove shared memory segments */
	volatile sh_Allocater *nxt;	/* linked list of pools */
};
DWORD		worker_pids[NCORE];	/* root mem of pids of all workers created */
HANDLE		worker_handles[NCORE];	/* for windows Create and Terminate */
void *		shmid      [NR_QS];	/* return value from CreateFileMapping */
void *		shmid_M;		/* shared mem for state allocation in hashtable */

#ifdef SEP_STATE
	void *shmid_X;
#else
	void *shmid_S;			/* shared bitstate arena or hashtable */
#endif
#else
int tas(volatile int *);

struct sh_Allocater {			/* shared memory for states */
	volatile char	*dc_arena;	/* to allocate states from */
	volatile long	 pattern;	/* to detect overruns */
	volatile long	 dc_size;	/* nr of bytes left */
	volatile char	*dc_start;	/* where memory segment starts */
	volatile int	dc_id;		/* to attach, detach, remove shared memory segments */
	volatile sh_Allocater *nxt;	/* linked list of pools */
};

int	worker_pids[NCORE];	/* root mem of pids of all workers created */
int	shmid      [NR_QS];	/* return value from shmget */
int	nibis = 0;		/* set after shared mem has been released */
int	shmid_M;		/* shared mem for state allocation in hashtable */
#ifdef SEP_STATE
	long	shmid_X;
#else
	int	shmid_S;	/* shared bitstate arena or hashtable */
	volatile sh_Allocater	*first_pool;	/* of shared state memory */
	volatile sh_Allocater	*last_pool;
#endif
#endif

struct SM_results {			/* for shuttling back final stats */
	volatile int	m_vsize;	/* avoid conflicts with frames */
	volatile int	m_boq;		/* these 2 fields are not written in record_info */
	/* probably not all fields really need to be volatile */
	volatile double	m_memcnt;
	volatile double	m_nstates;
	volatile double	m_truncs;
	volatile double	m_truncs2;
	volatile double	m_nShadow;
	volatile double	m_nlinks;
	volatile double	m_ngrabs;
	volatile double	m_nlost;
	volatile double	m_hcmp;
	volatile double	m_frame_wait;
	volatile int	m_hmax;
	volatile int	m_svmax;
	volatile int	m_smax;
	volatile int	m_mreached;
	volatile int	m_errors;
	volatile int	m_VMAX;
	volatile short	m_PMAX;
	volatile short	m_QMAX;
	volatile uchar	m_R;		/* reached info for all proctypes */
};

int		core_id = 0;		/* internal process nr, to know which q to use */
unsigned long	nstates_put = 0;	/* statistics */
unsigned long	nstates_get = 0;
int		query_in_progress = 0;	/* termination detection */

double		free_wait  = 0.;	/* waiting for a free frame */
double		frame_wait = 0.;	/* waiting for a full frame */
double		lock_wait  = 0.;	/* waiting for access to cs */
double		glock_wait[3];	/* waiting for access to global lock */

char		*sprefix = "rst";
uchar		was_interrupted, issued_kill, writing_trail;

static SM_frame cur_Root;		/* current root, to be safe with error trails */

SM_frame	*m_workq   [NR_QS];	/* per cpu work queues + global q */
char		*shared_mem[NR_QS];	/* return value from shmat */
#ifdef SEP_HEAP
char		*my_heap;
long		 my_size;
#endif
volatile sh_Allocater	*dc_shared;	/* assigned at initialization */

static int	vmax_seen, pmax_seen, qmax_seen;
static double	gq_tries, gq_hasroom, gq_hasnoroom;

volatile int *prfree;
volatile int *prfull;
volatile int *prcnt;
volatile int *prmax;

volatile int	*sh_lock;	/* mutual exclusion locks - in shared memory */
volatile double *is_alive;	/* to detect when processes crash */
volatile int    *grfree, *grfull, *grcnt, *grmax;	/* access to shared global q */
volatile double *gr_readmiss, *gr_writemiss;
static   int	lrfree;		/* used for temporary recording of slot */
static   int dfs_phase2;

void mem_put(int);		/* handoff state to other cpu */
void mem_put_acc(void);	/* liveness mode */
void mem_get(void);		/* get state from work queue  */
void sudden_stop(char *);
#if 0
void enter_critical(int);
void leave_critical(int);
#endif

void
record_info(SM_results *r)
{	int i;
	uchar *ptr;

#ifdef SEP_STATE
	if (0)
	{	cpu_printf("nstates %g nshadow %g -- memory %-6.3f Mb\n",
			nstates, nShadow, memcnt/(1048576.));
	}
	r->m_memcnt = 0;
#else
	#ifdef BITSTATE
	r->m_memcnt = 0; /* it's shared */
	#endif
	r->m_memcnt = memcnt;
#endif
	if (a_cycles && core_id == 1)
	{	r->m_nstates  = nstates;
		r->m_nShadow  = nstates;
	} else
	{	r->m_nstates  = nstates;
		r->m_nShadow  = nShadow;
	}
	r->m_truncs   = truncs;
	r->m_truncs2  = truncs2;
	r->m_nlinks   = nlinks;
	r->m_ngrabs   = ngrabs;
	r->m_nlost    = nlost;
	r->m_hcmp     = hcmp;
	r->m_frame_wait = frame_wait;
	r->m_hmax     = hmax;
	r->m_svmax    = svmax;
	r->m_smax     = smax;
	r->m_mreached = mreached;
	r->m_errors   = errors;
	r->m_VMAX     = vmax_seen;
	r->m_PMAX     = (short) pmax_seen;
	r->m_QMAX     = (short) qmax_seen;
	ptr = (uchar *) &(r->m_R);
	for (i = 0; i <= _NP_; i++)	/* all proctypes */
	{	memcpy(ptr, reached[i], NrStates[i]*sizeof(uchar));
		ptr += NrStates[i]*sizeof(uchar);
	}
	if (verbose>1)
	{	cpu_printf("Put Results nstates %g (sz %d)\n", nstates, ptr - &(r->m_R));
	}
}

void snapshot(void);

void
retrieve_info(SM_results *r)
{	int i, j;
	volatile uchar *ptr;

	snapshot();	/* for a final report */

	enter_critical(GLOBAL_LOCK);
#ifdef SEP_HEAP
	if (verbose)
	{	printf("cpu%d: local heap-left %ld KB (%d MB)\n",
			core_id, (int) (my_size/1024), (int) (my_size/1048576));
	}
#endif
	if (verbose && core_id == 0)
	{	printf("qmax: ");
		for (i = 0; i < NCORE; i++)
		{	printf("%d ", prmax[i]);
		}
#ifndef NGQ
		printf("G: %d", *grmax);
#endif
		printf("\n");
	}
	leave_critical(GLOBAL_LOCK);

	memcnt  += r->m_memcnt;
	nstates += r->m_nstates;
	nShadow += r->m_nShadow;
	truncs  += r->m_truncs;
	truncs2 += r->m_truncs2;
	nlinks  += r->m_nlinks;
	ngrabs  += r->m_ngrabs;
	nlost   += r->m_nlost;
	hcmp    += r->m_hcmp;
	/* frame_wait += r->m_frame_wait; */
	errors  += r->m_errors;

	if (hmax  < r->m_hmax)  hmax  = r->m_hmax;
	if (svmax < r->m_svmax) svmax = r->m_svmax;
	if (smax  < r->m_smax)  smax  = r->m_smax;
	if (mreached < r->m_mreached) mreached = r->m_mreached;

	if (vmax_seen < r->m_VMAX) vmax_seen = r->m_VMAX;
	if (pmax_seen < (int) r->m_PMAX) pmax_seen = (int) r->m_PMAX;
	if (qmax_seen < (int) r->m_QMAX) qmax_seen = (int) r->m_QMAX;

	ptr = &(r->m_R);
	for (i = 0; i <= _NP_; i++)	/* all proctypes */
	{	for (j = 0; j < NrStates[i]; j++)
		{	if (*(ptr + j) != 0)
			{	reached[i][j] = 1;
		}	}
		ptr += NrStates[i]*sizeof(uchar);
	}
	if (verbose>1)
	{	cpu_printf("Got Results (%d)\n", ptr - &(r->m_R));
		snapshot();
	}
}

#if !defined(WIN32) && !defined(WIN64)
static void
rm_shared_segments(void)
{	int m;
	volatile sh_Allocater *nxt_pool;
	/*
	 * mark all shared memory segments for removal 
	 * the actual removes wont happen intil last process dies or detaches
	 * the shmctl calls can return -1 if not all procs have detached yet
	 */
	for (m = 0; m < NR_QS; m++)	/* +1 for global q */
	{	if (shmid[m] != -1)
		{	(void) shmctl(shmid[m], IPC_RMID, NULL);
	}	}
#ifdef SEP_STATE
	if (shmid_M != -1)
	{	(void) shmctl(shmid_M, IPC_RMID, NULL);
	}
#else
	if (shmid_S != -1)
	{	(void) shmctl(shmid_S, IPC_RMID, NULL);
	}
	for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)
	{	shmid_M = (int) (last_pool->dc_id);
		nxt_pool = last_pool->nxt;	/* as a pre-caution only */
		if (shmid_M != -1)
		{	(void) shmctl(shmid_M, IPC_RMID, NULL);
	}	}
#endif
}
#endif

void
sudden_stop(char *s)
{	char b[64];
	int i;

	printf("cpu%d: stop - %s\n", core_id, s);
#if !defined(WIN32) && !defined(WIN64)
	if (proxy_pid != 0)
	{	rm_shared_segments();
	}
#endif
	if (search_terminated != NULL)
	{	if (*search_terminated != 0)
		{	if (verbose)
			{	printf("cpu%d: termination initiated (%d)\n",
					core_id, *search_terminated);
			}
		} else
		{	if (verbose)
			{	printf("cpu%d: initiated termination\n", core_id);
			}
			*search_terminated |= 8;	/* sudden_stop */
		}
		if (core_id == 0)
		{	if (((*search_terminated) & 4)	/* uerror in one of the cpus */
			&& !((*search_terminated) & (8|32|128|256))) /* abnormal stop */
			{	if (errors == 0) errors++; /* we know there is at least 1 */
			}
			wrapup(); /* incomplete stats, but at least something */
		}
		return;
	} /* else: should rarely happen, take more drastic measures */

	if (core_id == 0)	/* local root process */
	{	for (i = 1; i < NCORE; i++)	/* not for 0 of course */
		{
#if defined(WIN32) || defined(WIN64)
				DWORD dwExitCode = 0;
				GetExitCodeProcess(worker_handles[i], &dwExitCode);
				if (dwExitCode == STILL_ACTIVE)
				{	TerminateProcess(worker_handles[i], 0);
				}
				printf("cpu0: terminate %d %d\n",
					worker_pids[i], (dwExitCode == STILL_ACTIVE));
#else
				sprintf(b, "kill -%d %d", SIGKILL, worker_pids[i]);
				system(b);	/* if this is a proxy: receive half */
				printf("cpu0: %s\n", b);
#endif
		}
		issued_kill++;
	} else
	{	/* on WIN32/WIN64 -- these merely kills the root process... */
		if (was_interrupted == 0)
		{	sprintf(b, "kill -%d %d", SIGINT, worker_pids[0]);
			system(b);	/* warn the root process */
			printf("cpu%d: %s\n", core_id, b);
			issued_kill++;
	}	}
}

#define iam_alive()	is_alive[core_id]++

extern int crash_test(double);
extern void crash_reset(void);

int
someone_crashed(int wait_type)
{	static double last_value = 0.0;
	static int count = 0;

	if (search_terminated == NULL
	|| *search_terminated != 0)
	{
		if (!(*search_terminated & (8|32|128|256)))
		{	if (count++ < 100*NCORE)
			{	return 0;
		}	}
		return 1;
	}
	/* check left neighbor only */
	if (last_value == is_alive[(core_id + NCORE - 1) % NCORE])
	{	if (count++ >= 100)	/* to avoid unnecessary checks */
		{	return 1;
		}
		return 0;
	}
	last_value = is_alive[(core_id + NCORE - 1) % NCORE];
	count = 0;
	crash_reset();
	return 0;
}

void
sleep_report(void)
{
	enter_critical(GLOBAL_LOCK);
	if (verbose)
	{
#ifdef NGQ
		printf("cpu%d: locks: global %g\tother %g\t",
			core_id, glock_wait[0], lock_wait - glock_wait[0]);
#else
		printf("cpu%d: locks: GL %g, RQ %g, WQ %g, HT %g\t",
			core_id, glock_wait[0], glock_wait[1], glock_wait[2],
			lock_wait - glock_wait[0] - glock_wait[1] - glock_wait[2]);
#endif
		printf("waits: states %g slots %g\n", frame_wait, free_wait);
#ifndef NGQ
		printf("cpu%d: gq [tries %g, room %g, noroom %g]\n", core_id, gq_tries, gq_hasroom, gq_hasnoroom);
		if (core_id == 0 && (*gr_readmiss >= 1.0 || *gr_readmiss >= 1.0 || *grcnt != 0))
		printf("cpu0: gq [readmiss: %g, writemiss: %g cnt %d]\n", *gr_readmiss, *gr_writemiss, *grcnt);
#endif
	}
	if (free_wait > 1000000.)
	#ifndef NGQ
	if (!a_cycles)
	{	printf("hint: this search may be faster with a larger work-queue\n");
		printf("	(-DSET_WQ_SIZE=N with N>%g), and/or with -DUSE_DISK\n",
			GWQ_SIZE/sizeof(SM_frame));
		printf("      or with a larger value for -zN (N>%d)\n", z_handoff);
	#else
	{	printf("hint: this search may be faster if compiled without -DNGQ, with -DUSE_DISK, ");
		printf("or with a larger -zN (N>%d)\n", z_handoff);
	#endif
	}
	leave_critical(GLOBAL_LOCK);
}

#ifndef MAX_DSK_FILE
	#define MAX_DSK_FILE	1000000	/* default is max 1M states per file */
#endif

void
multi_usage(FILE *fd)
{	static int warned = 0;
	if (warned > 0) { return; } else { warned++; }
	fprintf(fd, "\n");
	fprintf(fd, "Defining multi-core mode:\n\n");
	fprintf(fd, "        -DDUAL_CORE --> same as -DNCORE=2\n");
	fprintf(fd, "        -DQUAD_CORE --> same as -DNCORE=4\n");
	fprintf(fd, "        -DNCORE=N   --> enables multi_core verification if N>1\n");
	fprintf(fd, "\n");
	fprintf(fd, "Additional directives supported in multi-core mode:\n\n");
	fprintf(fd, "        -DSEP_STATE --> forces separate statespaces instead of a single shared state space\n");
	fprintf(fd, "        -DNUSE_DISK --> use disk for storing states when a work queue overflows\n");
	fprintf(fd, "        -DMAX_DSK_FILE --> max nr of states per diskfile (%d)\n", MAX_DSK_FILE);
	fprintf(fd, "        -DFULL_TRAIL --> support full error trails (increases memory use)\n");
	fprintf(fd, "\n");
	fprintf(fd, "More advanced use (should rarely need changing):\n\n");
	fprintf(fd, "     To change the nr of states that can be stored in the global queue\n");
	fprintf(fd, "     (lower numbers allow for more states to be stored, prefer multiples of 8):\n");
	fprintf(fd, "        -DVMAX=N    --> upperbound on statevector for handoffs (N=%d)\n", VMAX);
	fprintf(fd, "        -DPMAX=N    --> upperbound on nr of procs (default: N=%d)\n", PMAX);
	fprintf(fd, "        -DQMAX=N    --> upperbound on nr of channels (default: N=%d)\n", QMAX);
	fprintf(fd, "\n");
	fprintf(fd, "     To set the total amount of memory reserved for the global workqueue:\n");
	fprintf(fd, "        -DSET_WQ_SIZE=N --> default: N=128 (defined in MBytes)\n\n");
	fprintf(fd, "     To force the use of a single global heap, instead of separate heaps:\n");
	fprintf(fd, "        -DGLOB_HEAP\n");
	fprintf(fd, "\n");
	fprintf(fd, "     To define a fct to initialize data before spawning processes (use quotes):\n");
	fprintf(fd, "        \"-DC_INIT=fct()\"\n");
	fprintf(fd, "\n");
	fprintf(fd, "     Timer settings for termination and crash detection:\n");
	fprintf(fd, "        -DSHORT_T=N --> timeout for termination detection trigger (N=%g)\n", (double) SHORT_T);
	fprintf(fd, "        -DLONG_T=N  --> timeout for giving up on termination detection (N=%g)\n", (double) LONG_T);
	fprintf(fd, "        -DONESECOND --> (1<<29) --> timeout waiting for a free slot -- to check for crash\n");
	fprintf(fd, "        -DT_ALERT   --> collect stats on crash alert timeouts\n\n");
	fprintf(fd, "Help with Linux/Windows/Cygwin configuration for multi-core:\n");
	fprintf(fd, "	http://spinroot.com/spin/multicore/V5_Readme.html\n");
	fprintf(fd, "\n");
}
#if NCORE>1 && defined(FULL_TRAIL)
typedef struct Stack_Tree {
	uchar	      pr;	/* process that made transition */
	T_ID	    t_id;	/* id of transition */
	volatile struct Stack_Tree *prv; /* backward link towards root */
} Stack_Tree;

struct H_el *grab_shared(int);
volatile Stack_Tree **stack_last; /* in shared memory */
char *stack_cache = NULL;	/* local */
int  nr_cached = 0;		/* local */

#ifndef CACHE_NR
	#define CACHE_NR	1024
#endif

volatile Stack_Tree *
stack_prefetch(void)
{	volatile Stack_Tree *st;

	if (nr_cached == 0)
	{	stack_cache = (char *) grab_shared(CACHE_NR * sizeof(Stack_Tree));
		nr_cached = CACHE_NR;
	}
	st = (volatile Stack_Tree *) stack_cache;
	stack_cache += sizeof(Stack_Tree);
	nr_cached--;
	return st;
}

void
Push_Stack_Tree(short II, T_ID t_id)
{	volatile Stack_Tree *st;

	st = (volatile Stack_Tree *) stack_prefetch();
	st->pr = II;
	st->t_id = t_id;
	st->prv = (Stack_Tree *) stack_last[core_id];
	stack_last[core_id] = st;
}

void
Pop_Stack_Tree(void)
{	volatile Stack_Tree *cf = stack_last[core_id];

	if (cf)
	{	stack_last[core_id] = cf->prv;
	} else if (nr_handoffs * z_handoff + depth > 0)
	{	printf("cpu%d: error pop_stack_tree (depth %d)\n",
			core_id, depth);
	}
}
#endif

void
e_critical(int which)
{	double cnt_start;

	if (readtrail || iamin[which] > 0)
	{	if (!readtrail && verbose)
		{	printf("cpu%d: Double Lock on %d (now %d)\n",
				core_id, which, iamin[which]+1);
			fflush(stdout);
		}
		iamin[which]++;	/* local variable */
		return;
	}

	cnt_start = lock_wait;

	while (sh_lock != NULL)	/* as long as we have shared memory */
	{	int r = tas(&sh_lock[which]);
		if (r == 0)
		{	iamin[which] = 1;
			return;		/* locked */
		}

		lock_wait++;
#ifndef NGQ
		if (which < 3) { glock_wait[which]++; }
#else
		if (which == 0) { glock_wait[which]++; }
#endif
		iam_alive();

		if (lock_wait - cnt_start > TenSeconds)
		{	printf("cpu%d: lock timeout on %d\n", core_id, which);
			cnt_start = lock_wait;
			if (someone_crashed(1))
			{	sudden_stop("lock timeout");
				pan_exit(1);
	}	}	}
}

void
x_critical(int which)
{
	if (iamin[which] != 1)
	{	if (iamin[which] > 1)
		{	iamin[which]--;	/* this is thread-local - no races on this one */
			if (!readtrail && verbose)
			{	printf("cpu%d: Partial Unlock on %d (%d more needed)\n",
					core_id, which, iamin[which]);
				fflush(stdout);
			}
			return;
		} else /* iamin[which] <= 0 */
		{	if (!readtrail)
			{	printf("cpu%d: Invalid Unlock iamin[%d] = %d\n",
					core_id, which, iamin[which]);
				fflush(stdout);
			}
			return;
	}	}

	if (sh_lock != NULL)
	{	iamin[which]   = 0;
		sh_lock[which] = 0;	/* unlock */
	}
}

void
#if defined(WIN32) || defined(WIN64)
start_proxy(char *s, DWORD r_pid)
#else
start_proxy(char *s, int r_pid)
#endif
{	char  Q_arg[16], Z_arg[16], Y_arg[16];
	char *args[32], *ptr;
	int   argcnt = 0;

	sprintf(Q_arg, "-Q%d", getpid());
	sprintf(Y_arg, "-Y%d", r_pid);
	sprintf(Z_arg, "-Z%d", proxy_pid /* core_id */);

	args[argcnt++] = "proxy";
	args[argcnt++] = s; /* -r or -s */
	args[argcnt++] = Q_arg;
	args[argcnt++] = Z_arg;
	args[argcnt++] = Y_arg;

	if (strlen(o_cmdline) > 0)
	{	ptr = o_cmdline; /* assume args separated by spaces */
		do {	args[argcnt++] = ptr++;
			if ((ptr = strchr(ptr, ' ')) != NULL)
			{	while (*ptr == ' ')
				{	*ptr++ = '\0';
				}
			} else
			{	break;
			}
		} while (argcnt < 31);
	}
	args[argcnt] = NULL;
#if defined(WIN32) || defined(WIN64)
	execvp("pan_proxy", args); /* no return */
#else
	execvp("./pan_proxy", args); /* no return */
#endif
	Uerror("pan_proxy exec failed");
}
/*** end of common code fragment ***/

#if !defined(WIN32) && !defined(WIN64)
void
init_shm(void)		/* initialize shared work-queues - linux/cygwin */
{	key_t	key[NR_QS];
	int	n, m;
	int	must_exit = 0;

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 3: allocate shared workqueues %g MB\n",
			((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.) );
	}
	for (m = 0; m < NR_QS; m++)		/* last q is the global q */
	{	double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;
		key[m] = ftok(PanSource, m+1);
		if (key[m] == -1)
		{	perror("ftok shared queues"); must_exit = 1; break;
		}

		if (core_id == 0)	/* root creates */
		{	/* check for stale copy */
			shmid[m] = shmget(key[m], (size_t) qsize, 0600);
			if (shmid[m] != -1)	/* yes there is one; remove it */
			{	printf("cpu0: removing stale q%d, status: %d\n",
					m, shmctl(shmid[m], IPC_RMID, NULL));
			}
			shmid[m] = shmget(key[m], (size_t) qsize, 0600|IPC_CREAT|IPC_EXCL);
			memcnt += qsize;
		} else			/* workers attach */
		{	shmid[m] = shmget(key[m], (size_t) qsize, 0600);
			/* never called, since we create shm *before* we fork */
		}
		if (shmid[m] == -1)
		{	perror("shmget shared queues"); must_exit = 1; break;
		}

		shared_mem[m] = (char *) shmat(shmid[m], (void *) 0, 0);	/* attach */
		if (shared_mem[m] == (char *) -1)
		{ fprintf(stderr, "error: cannot attach shared wq %d (%d Mb)\n",
				m+1, (int) (qsize/(1048576.)));
		  perror("shmat shared queues"); must_exit = 1; break;
		}

		m_workq[m] = (SM_frame *) shared_mem[m];
		if (core_id == 0)
		{	int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;
			for (n = 0; n < nframes; n++)
			{	m_workq[m][n].m_vsize = 0;
				m_workq[m][n].m_boq = 0;
	}	}	}

	if (must_exit)
	{	rm_shared_segments();
		fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);	/* calls cleanup_shm */
	}
}

static uchar *
prep_shmid_S(size_t n)		/* either sets SS or H_tab, linux/cygwin */
{	char	*rval;
#ifndef SEP_STATE
	key_t	key;

	if (verbose && core_id == 0)
	{
	#ifdef BITSTATE
		printf("cpu0: step 1: allocate shared bitstate %g Mb\n",
			(double) n / (1048576.));
	#else
		printf("cpu0: step 1: allocate shared hastable %g Mb\n",
			(double) n / (1048576.));
	#endif
	}
	#ifdef MEMLIM
	if (memcnt + (double) n > memlim)
	{	printf("cpu0: S %8g + %d Kb exceeds memory limit of %8g Mb\n",
			memcnt/1024., n/1024, memlim/(1048576.));
		printf("cpu0: insufficient memory -- aborting\n");
		exit(1);
	}
	#endif

	key = ftok(PanSource, NCORE+2);	/* different from queues */
	if (key == -1)
	{	perror("ftok shared bitstate or hashtable");
		fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);
	}

	if (core_id == 0)	/* root */
	{	shmid_S = shmget(key, n, 0600);
		if (shmid_S != -1)
		{	printf("cpu0: removing stale segment, status: %d\n",
				shmctl(shmid_S, IPC_RMID, NULL));
		}
		shmid_S = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);
		memcnt += (double) n;
	} else			/* worker */
	{	shmid_S = shmget(key, n, 0600);
	}
	if (shmid_S == -1)
	{	perror("shmget shared bitstate or hashtable too large?");
		fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);
	}

	rval = (char *) shmat(shmid_S, (void *) 0, 0);	/* attach */
	if ((char *) rval == (char *) -1)
	{	perror("shmat shared bitstate or hashtable");
		fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);
	}
#else
	rval = (char *) emalloc(n);
#endif
	return (uchar *) rval;
}

#define TRY_AGAIN	1
#define NOT_AGAIN	0

static char shm_prep_result;

static uchar *
prep_state_mem(size_t n)		/* sets memory arena for states linux/cygwin */
{	char	*rval;
	key_t	key;
	static int cnt = 3;		/* start larger than earlier ftok calls */

	shm_prep_result = NOT_AGAIN;	/* default */
	if (verbose && core_id == 0)
	{	printf("cpu0: step 2+: pre-allocate memory arena %d of %6.2g Mb\n",
			cnt-3, (double) n / (1048576.));
	}
	#ifdef MEMLIM
	if (memcnt + (double) n > memlim)
	{	printf("cpu0: error: M %.0f + %.0f Kb exceeds memory limit of %.0f Mb\n",
			memcnt/1024.0, (double) n/1024.0, memlim/(1048576.));
		return NULL;
	}
	#endif

	key = ftok(PanSource, NCORE+cnt); cnt++;
	if (key == -1)
	{	perror("ftok T");
		printf("pan: check './pan --' for usage details\n");
		pan_exit(1);
	}

	if (core_id == 0)
	{	shmid_M = shmget(key, n, 0600);
		if (shmid_M != -1)
		{	printf("cpu0: removing stale memory segment %d, status: %d\n",
				cnt-3, shmctl(shmid_M, IPC_RMID, NULL));
		}
		shmid_M = shmget(key, n, 0600 | IPC_CREAT | IPC_EXCL);
		/* memcnt += (double) n; -- only amount actually used is counted */
	} else
	{	shmid_M = shmget(key, n, 0600);
	
	}
	if (shmid_M == -1)
	{	if (verbose)
		{	printf("error: failed to get pool of shared memory %d of %.0f Mb\n",
				cnt-3, ((double)n)/(1048576.));
			perror("state mem");
			printf("pan: check './pan --' for usage details\n");
		}
		shm_prep_result = TRY_AGAIN;
		return NULL;
	}
	rval = (char *) shmat(shmid_M, (void *) 0, 0);	/* attach */

	if ((char *) rval == (char *) -1)
	{	printf("cpu%d error: failed to attach pool of shared memory %d of %.0f Mb\n",
			 core_id, cnt-3, ((double)n)/(1048576.));
		perror("state mem");
		return NULL;
	}
	return (uchar *) rval;
}

void
init_HT(unsigned long n)	/* cygwin/linux version */
{	volatile char	*x;
	double  get_mem;
#ifndef SEP_STATE
	volatile char	*dc_mem_start;
	double  need_mem, got_mem = 0.;
#endif

#ifdef SEP_STATE
 #ifndef MEMLIM
	if (verbose)
	{	printf("cpu0: steps 0,1: no -DMEMLIM set\n");
	}
 #else
	if (verbose)
	{	printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n",
		MEMLIM, ((double)n/(1048576.)), (((double) NCORE * LWQ_SIZE) + GWQ_SIZE) /(1048576.) );
	}
 #endif
	get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *) + 4*sizeof(void *) + 2*sizeof(double);
	/* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */
	get_mem += 4 * NCORE * sizeof(void *); /* prfree, prfull, prcnt, prmax */
 #ifdef FULL_TRAIL
	get_mem += (NCORE) * sizeof(Stack_Tree *); /* NCORE * stack_last */
 #endif
	x = (volatile char *) prep_state_mem((size_t) get_mem); /* work queues and basic structs */
	shmid_X = (long) x;
	if (x == NULL)
	{	printf("cpu0: could not allocate shared memory, see ./pan --\n");
		exit(1);
	}
	search_terminated = (volatile unsigned int *) x; /* comes first */
	x += sizeof(void *); /* maintain alignment */

	is_alive   = (volatile double *) x;
	x += NCORE * sizeof(double);

	sh_lock   = (volatile int *) x;
	x += CS_NR * sizeof(void *);

	grfree    = (volatile int *) x;
	x += sizeof(void *);
	grfull    = (volatile int *) x;
	x += sizeof(void *);
	grcnt    = (volatile int *) x;
	x += sizeof(void *);
	grmax    = (volatile int *) x;
	x += sizeof(void *);
	prfree = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prfull = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prcnt = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prmax = (volatile int *) x;
	x += NCORE * sizeof(void *);
	gr_readmiss    = (volatile double *) x;
	x += sizeof(double);
	gr_writemiss    = (volatile double *) x;
	x += sizeof(double);

	#ifdef FULL_TRAIL
		stack_last = (volatile Stack_Tree **) x;
		x += NCORE * sizeof(Stack_Tree *);
	#endif

	#ifndef BITSTATE
		H_tab = (struct H_el **) emalloc(n);
	#endif
#else
	#ifndef MEMLIM
		#warning MEMLIM not set
		#define MEMLIM	(2048)
	#endif

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 0: -DMEMLIM=%d Mb minus hashtable+workqs (%g + %g Mb) leaves %g Mb\n",
			MEMLIM, ((double)n/(1048576.)), (NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),
			(memlim - memcnt - (double) n - (NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));
	}
	#ifndef BITSTATE
		H_tab = (struct H_el **) prep_shmid_S((size_t) n);	/* hash_table */
	#endif
	need_mem = memlim - memcnt - ((double) NCORE * LWQ_SIZE) - GWQ_SIZE;
	if (need_mem <= 0.)
	{	Uerror("internal error -- shared state memory");
	}

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 2: pre-allocate shared state memory %g Mb\n",
			need_mem/(1048576.));
	}
#ifdef SEP_HEAP
	SEG_SIZE = need_mem / NCORE;
	if (verbose && core_id == 0)
	{	printf("cpu0: setting segsize to %6g MB\n",
			SEG_SIZE/(1048576.));
	}
	#if defined(CYGWIN) || defined(__CYGWIN__)
	if (SEG_SIZE > 512.*1024.*1024.)
	{	printf("warning: reducing SEG_SIZE of %g MB to 512MB (exceeds max for Cygwin)\n",
			SEG_SIZE/(1024.*1024.));
		SEG_SIZE = 512.*1024.*1024.;
	}
	#endif
#endif
	mem_reserved = need_mem;
	while (need_mem > 1024.)
	{	get_mem = need_mem;
shm_more:
		if (get_mem > (double) SEG_SIZE)
		{	get_mem = (double) SEG_SIZE;
		}
		if (get_mem <= 0.0) break;

		/* for allocating states: */
		x = dc_mem_start = (volatile char *) prep_state_mem((size_t) get_mem);
		if (x == NULL)
		{	if (shm_prep_result == NOT_AGAIN
			||  first_pool != NULL
			||  SEG_SIZE < (16. * 1048576.))
			{	break;
			}
			SEG_SIZE /= 2.;
			if (verbose)
			{	printf("pan: lowered segsize to 0.000000\n", SEG_SIZE);
			}
			if (SEG_SIZE >= 1024.)
			{	goto shm_more;
			}
			break;
		}

		need_mem -= get_mem;
		got_mem  += get_mem;
		if (first_pool == NULL)
		{	search_terminated = (volatile unsigned int *) x; /* comes first */
			x += sizeof(void *); /* maintain alignment */

			is_alive   = (volatile double *) x;
			x += NCORE * sizeof(double);

			sh_lock   = (volatile int *) x;
			x += CS_NR * sizeof(void *);

			grfree    = (volatile int *) x;
			x += sizeof(void *);
			grfull    = (volatile int *) x;
			x += sizeof(void *);
			grcnt    = (volatile int *) x;
			x += sizeof(void *);
			grmax    = (volatile int *) x;
			x += sizeof(void *);
			prfree = (volatile int *) x;
			x += NCORE * sizeof(void *);
			prfull = (volatile int *) x;
			x += NCORE * sizeof(void *);
			prcnt = (volatile int *) x;
			x += NCORE * sizeof(void *);
			prmax = (volatile int *) x;
			x += NCORE * sizeof(void *);
			gr_readmiss  = (volatile double *) x;
			x += sizeof(double);
			gr_writemiss = (volatile double *) x;
			x += sizeof(double);
 #ifdef FULL_TRAIL
			stack_last = (volatile Stack_Tree **) x;
			x += NCORE * sizeof(Stack_Tree *);
 #endif
			if (((long)x)&(sizeof(void *)-1)) /* 64-bit word alignment */
			{	x += sizeof(void *)-(((long)x)&(sizeof(void *)-1));
			}

			#ifdef COLLAPSE
			ncomps = (unsigned long *) x;
			x += (256+2) * sizeof(unsigned long);
			#endif
		}

		dc_shared = (sh_Allocater *) x; /* must be in shared memory */
		x += sizeof(sh_Allocater);

		if (core_id == 0)	/* root only */
		{	dc_shared->dc_id     = shmid_M;
			dc_shared->dc_start  = dc_mem_start;
			dc_shared->dc_arena  = x;
			dc_shared->pattern   = 1234567; /* protection */
			dc_shared->dc_size   = (long) get_mem - (long) (x - dc_mem_start);
			dc_shared->nxt       = (long) 0;

			if (last_pool == NULL)
			{	first_pool = last_pool = dc_shared;
			} else
			{	last_pool->nxt = dc_shared;
				last_pool = dc_shared;
			}
		} else if (first_pool == NULL)
		{	first_pool = dc_shared;
	}	}

	if (need_mem > 1024.)
	{	printf("cpu0: could allocate only %g Mb of shared memory (wanted %g more)\n",
			got_mem/(1048576.), need_mem/(1048576.));
	}

	if (!first_pool)
	{	printf("cpu0: insufficient memory -- aborting.\n");
		exit(1);
	}
	/* we are still single-threaded at this point, with core_id 0 */
	dc_shared = first_pool;

#endif
}

	/* Test and Set assembly code */

	#if defined(i386) || defined(__i386__) || defined(__x86_64__)
		int
		tas(volatile int *s)	/* tested */
		{	int r;
			__asm__ __volatile__(
				"xchgl %0, %1 \n\t"
		       		: "=r"(r), "=m"(*s)
				: "0"(1), "m"(*s)
				: "memory");
		
			return r;
		}
	#elif defined(__arm__)
		int
		tas(volatile int *s)	/* not tested */
		{	int r = 1;
			__asm__ __volatile__(
				"swpb %0, %0, [%3] \n"
				: "=r"(r), "=m"(*s)
				: "0"(r), "r"(s));

			return r;
		}
	#elif defined(sparc) || defined(__sparc__)
		int
		tas(volatile int *s)	/* not tested */
		{	int r = 1;
			__asm__ __volatile__(
				" ldstub [%2], %0 \n"
				: "=r"(r), "=m"(*s)
				: "r"(s));

			return r;
		}
	#elif defined(ia64) || defined(__ia64__)
		/* Intel Itanium */
		int
		tas(volatile int *s)	/* tested */
		{	long int r;
			__asm__ __volatile__(
				"	xchg4 	%0=%1,%2	\n"
		:		"=r"(r), "+m"(*s)
		:		"r"(1)
		:		"memory");
			return (int) r;
		}
	#else
		#error missing definition of test and set operation for this platform
	#endif

void
cleanup_shm(int val)
{	volatile sh_Allocater *nxt_pool;
	unsigned long cnt = 0;
	int m;

	if (nibis != 0)
	{	printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val);
		return;
	} else
	{	nibis = 1;
	}
	if (search_terminated != NULL)
	{	*search_terminated |= 16; /* cleanup_shm */
	}

	for (m = 0; m < NR_QS; m++)
	{	if (shmdt((void *) shared_mem[m]) > 0)
		{	perror("shmdt detaching from shared queues");
	}	}

#ifdef SEP_STATE
	if (shmdt((void *) shmid_X) != 0)
	{	perror("shmdt detaching from shared state memory");
	}
#else
	#ifdef BITSTATE
		if (SS > 0 && shmdt((void *) SS) != 0)
		{	if (verbose)
			{	perror("shmdt detaching from shared bitstate arena");
		}	}
	#else
		if (core_id == 0)
		{	/* before detaching: */
			for (nxt_pool = dc_shared; nxt_pool != NULL; nxt_pool = nxt_pool->nxt)
			{	cnt += nxt_pool->dc_size;
			}
			if (verbose)
			{	printf("cpu0: done, %ld Mb of shared state memory left\n",
					cnt / (long)(1048576));
		}	}

		if (shmdt((void *) H_tab) != 0)
		{	perror("shmdt detaching from shared hashtable");
		}

		for (last_pool = first_pool; last_pool != NULL; last_pool = nxt_pool)
		{	nxt_pool = last_pool->nxt;
			if (shmdt((void *) last_pool->dc_start) != 0)
			{	perror("shmdt detaching from shared state memory");
		}	}
		first_pool = last_pool = NULL;	/* precaution */
	#endif
#endif
	/* detached from shared memory - so cannot use cpu_printf */
	if (verbose)
	{	printf("cpu%d: done -- got %d states from queue\n",
			core_id, nstates_get);
	}
}

extern void give_up(int);
extern void Read_Queue(int);

void
mem_get(void)
{	SM_frame   *f;
	int is_parent;

#if defined(MA) && !defined(SEP_STATE)
	#error MA without SEP_STATE is not supported with multi-core
#endif
#ifdef BFS
	#error BFS is not supported with multi-core
#endif
#ifdef SC
	#error SC is not supported with multi-core
#endif
	init_shm();	/* we are single threaded when this starts */

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 4: calling fork()\n");
	}
	fflush(stdout);

/*	if NCORE > 1 the child or the parent should fork N-1 more times
 *	the parent is the only process with core_id == 0 and is_parent > 0
 *	the workers have is_parent = 0 and core_id = 1..NCORE-1
 */
	if (core_id == 0)
	{	worker_pids[0] = getpid();	/* for completeness */
		while (++core_id < NCORE)	/* first worker sees core_id = 1 */
		{	is_parent = fork();
			if (is_parent == -1)
			{	Uerror("fork failed");
			}
			if (is_parent == 0)	/* this is a worker process */
			{	if (proxy_pid == core_id)	/* always non-zero */
				{	start_proxy("-r", 0);	/* no return */
				}
				goto adapt;	/* root process continues spawning */
			}
			worker_pids[core_id] = is_parent;
		}
		/* note that core_id is now NCORE */
		if (proxy_pid > 0 && proxy_pid < NCORE)
		{	proxy_pid_snd = fork();
			if (proxy_pid_snd == -1)
			{	Uerror("proxy fork failed");
			}
			if (proxy_pid_snd == 0)
			{	start_proxy("-s", worker_pids[proxy_pid]); /* no return */
		}	} /* else continue */
		if (is_parent > 0)
		{	core_id = 0;	/* reset core_id for root process */
		}
	} else	/* worker */
	{	static char db0[16];	/* good for up to 10^6 cores */
		static char db1[16];
adapt:		tprefix = db0; sprefix = db1;
		sprintf(tprefix, "cpu%d_trail", core_id);
		sprintf(sprefix, "cpu%d_rst", core_id);
		memcnt = 0;	/* count only additionally allocated memory */
	}
	signal(SIGINT, give_up);

	if (proxy_pid == 0)		/* not in a cluster setup, pan_proxy must attach */
	{	rm_shared_segments();	/* mark all shared segments for removal on exit */
	}
	if (verbose)
	{	cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid());
	}
#if defined(SEP_HEAP) && !defined(SEP_STATE)
	{	int i;
		volatile sh_Allocater *ptr;
		ptr = first_pool;
		for (i = 0; i < NCORE  && ptr != NULL; i++)
		{	if (i == core_id)
			{	my_heap = (char *) ptr->dc_arena;
				my_size = (long) ptr->dc_size;
				if (verbose)
				cpu_printf("local heap %ld MB\n", my_size/(1048576));
				break;
			}
			ptr = ptr->nxt; /* local */
		}
		if (my_heap == NULL)
		{	printf("cpu%d: no local heap\n", core_id);
			pan_exit(1);
		} /* else */
	#if defined(CYGWIN) || defined(__CYGWIN__)
		ptr = first_pool;
		for (i = 0; i < NCORE  && ptr != NULL; i++)
		{	ptr = ptr->nxt; /* local */
		}
		dc_shared = ptr; /* any remainder */
	#else
		dc_shared = NULL; /* used all mem for local heaps */
	#endif
	}
#endif
	if (core_id == 0 && !remote_party)
	{	new_state();		/* cpu0 explores root */
		if (verbose)
		cpu_printf("done with 1st dfs, nstates %g (put %d states), read q\n",
			nstates, nstates_put);
		dfs_phase2 = 1;
	}
	Read_Queue(core_id);	/* all cores */

	if (verbose)
	{	cpu_printf("put %6d states into queue -- got %6d\n",
			nstates_put, nstates_get);
	}
	if (proxy_pid != 0)
	{	rm_shared_segments();
	}
	done = 1;
	wrapup();
	exit(0);
}

#else
int unpack_state(SM_frame *, int);
#endif

struct H_el *
grab_shared(int n)
{
#ifndef SEP_STATE
	char *rval = (char *) 0;

	if (n == 0)
	{	printf("cpu%d: grab shared zero\n", core_id); fflush(stdout);
		return (struct H_el *) rval;
	} else if (n&(sizeof(void *)-1))
	{	n += sizeof(void *)-(n&(sizeof(void *)-1)); /* alignment */
	}

#ifdef SEP_HEAP
	/* no locking */
	if (my_heap != NULL && my_size > n)
	{	rval = my_heap;
		my_heap += n;
		my_size -= n;
		goto done;
	}
#endif

	if (!dc_shared)
	{	sudden_stop("pan: out of memory");
	}

	/* another lock is always already in effect when this is called */
	/* but not always the same lock -- i.e., on different parts of the hashtable */
	enter_critical(GLOBAL_LOCK);	/* this must be independently mutex */
#if defined(SEP_HEAP) && !defined(WIN32) && !defined(WIN64)
	{	static int noted = 0;
		if (!noted)
		{	noted = 1;
			printf("cpu%d: global heap has %ld bytes left, needed %d\n",
				core_id, dc_shared?dc_shared->dc_size:0, n);
	}	}
#endif
#if 0
		if (dc_shared->pattern != 1234567)
		{	leave_critical(GLOBAL_LOCK);
			Uerror("overrun -- memory corruption");
		}
#endif
		if (dc_shared->dc_size < n)
		{	if (verbose)
			{ printf("Next Pool %g Mb + %d\n", memcnt/(1048576.), n);
			}
			if (dc_shared->nxt == NULL
			||  dc_shared->nxt->dc_arena == NULL
			||  dc_shared->nxt->dc_size < n)
			{	printf("cpu%d: memcnt %g Mb + wanted %d bytes more\n",
					core_id, memcnt / (1048576.), n);
				leave_critical(GLOBAL_LOCK);
				sudden_stop("out of memory -- aborting");
				wrapup();	/* exits */
			} else
			{	dc_shared = (sh_Allocater *) dc_shared->nxt;
		}	}

		rval = (char *) dc_shared->dc_arena;
		dc_shared->dc_arena += n;
		dc_shared->dc_size  -= (long) n;
#if 0
		if (VVERBOSE)
		printf("cpu%d grab shared (%d bytes) -- %ld left\n",
			core_id, n, dc_shared->dc_size);
#endif
	leave_critical(GLOBAL_LOCK);
done:
	memset(rval, 0, n);
	memcnt += (double) n;

	return (struct H_el *) rval;
#else
	return (struct H_el *) emalloc(n);
#endif
}

SM_frame *
Get_Full_Frame(int n)
{	SM_frame *f;
	double cnt_start = frame_wait;

	f = &m_workq[n][prfull[n]];
	while (f->m_vsize == 0)	/* await full slot LOCK : full frame */
	{	iam_alive();
#ifndef NGQ
	#ifndef SAFETY
		if (!a_cycles || core_id != 0)
	#endif
		if (*grcnt > 0)	/* accessed outside lock, but safe even if wrong */
		{	enter_critical(GQ_RD);	/* gq - read access */
			if (*grcnt > 0)		/* could have changed */
			{	f = &m_workq[NCORE][*grfull];	/* global q */
				if (f->m_vsize == 0)
				{	/* writer is still filling the slot */
					*gr_writemiss++;
					f = &m_workq[n][prfull[n]]; /* reset */
				} else
				{	*grfull = (*grfull+1) % (GN_FRAMES);
						enter_critical(GQ_WR);
						*grcnt = *grcnt - 1;
						leave_critical(GQ_WR);
					leave_critical(GQ_RD);
					return f;
			}	}
			leave_critical(GQ_RD);
		}
#endif
		if (frame_wait++ - cnt_start > Delay)
		{	if (0)
			{	cpu_printf("timeout on q%d -- %u -- query %d\n",
					n, f, query_in_progress);
			}
			return (SM_frame *) 0;	/* timeout */
	}	}
	iam_alive();
	if (VVERBOSE) cpu_printf("got frame from q%d\n", n);
	prfull[n] = (prfull[n] + 1) % (LN_FRAMES);
	enter_critical(QLOCK(n));
		prcnt[n]--; /* lock out increments */
	leave_critical(QLOCK(n));
	return f;
}

SM_frame *
Get_Free_Frame(int n)
{	SM_frame *f;
	double cnt_start = free_wait;

	if (VVERBOSE) { cpu_printf("get free frame from q%d\n", n); }

	if (n == NCORE)	/* global q */
	{	f = &(m_workq[n][lrfree]);
	} else
	{	f = &(m_workq[n][prfree[n]]);
	}
	while (f->m_vsize != 0)	/* await free slot LOCK : free slot */
	{	iam_alive();
		if (free_wait++ - cnt_start > OneSecond)
		{	if (verbose)
			{	cpu_printf("timeout waiting for free slot q%d\n", n);
			}
			cnt_start = free_wait;
			if (someone_crashed(1))
			{	printf("cpu%d: search terminated\n", core_id);
				sudden_stop("get free frame");
				pan_exit(1);
	}	}	}
	if (n != NCORE)
	{	prfree[n] = (prfree[n] + 1) % (LN_FRAMES);
		enter_critical(QLOCK(n));
			prcnt[n]++; /* lock out decrements */
			if (prmax[n] < prcnt[n])
			{	prmax[n] = prcnt[n];
			}
		leave_critical(QLOCK(n));
	}
	return f;
}

#ifndef NGQ
int
GlobalQ_HasRoom(void)
{	int rval = 0;

	gq_tries++;
	if (*grcnt < GN_FRAMES) /* there seems to be room */
	{	enter_critical(GQ_WR);	/* gq write access */
		if (*grcnt < GN_FRAMES)
		{	if (m_workq[NCORE][*grfree].m_vsize != 0)
			{	/* can happen if reader is slow emptying slot */
				*gr_readmiss++;
				goto out; /* dont wait: release lock and return */
			}
			lrfree = *grfree;	/* Get_Free_Frame use lrfree in this mode */
			*grfree = (*grfree + 1) % GN_FRAMES;
			*grcnt = *grcnt + 1;	/* count nr of slots filled -- no additional lock needed */
			if (*grmax < *grcnt) *grmax = *grcnt;
			leave_critical(GQ_WR);	/* for short lock duration */
			gq_hasroom++;
			mem_put(NCORE);		/* copy state into reserved slot */
			rval = 1;		/* successfull handoff */
		} else
		{	gq_hasnoroom++;
out:			leave_critical(GQ_WR);
	}	}
	return rval;
}
#endif

int
unpack_state(SM_frame *f, int from_q)
{	int i, j;
	static struct H_el D_State;

	if (f->m_vsize > 0)
	{	boq   = f->m_boq;
		if (boq > 256)
		{	cpu_printf("saw control %d, expected state\n", boq);
			return 0;
		}
		vsize = f->m_vsize;
correct:
		memcpy((uchar *) &now, (uchar *) f->m_now, vsize);
		for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
		{	Mask[i] = (f->m_Mask[i/8] & (1<<j)) ? 1 : 0;
		}
		if (now._nr_pr > 0)
		{	memcpy((uchar *) proc_offset, (uchar *) f->m_p_offset, now._nr_pr * sizeof(OFFT));
			memcpy((uchar *) proc_skip,   (uchar *) f->m_p_skip,   now._nr_pr * sizeof(uchar));
		}
		if (now._nr_qs > 0)
		{	memcpy((uchar *) q_offset,    (uchar *) f->m_q_offset, now._nr_qs * sizeof(OFFT));
			memcpy((uchar *) q_skip,      (uchar *) f->m_q_skip,   now._nr_qs * sizeof(uchar));
		}
#ifndef NOVSZ
		if (vsize != now._vsz)
		{	cpu_printf("vsize %d != now._vsz %d (type %d) %d\n",
				vsize, now._vsz, f->m_boq, f->m_vsize);
			vsize = now._vsz;
			goto correct;	/* rare event: a race */
		}
#endif
		hmax = max(hmax, vsize);

		if (f != &cur_Root)
		{	memcpy((uchar *) &cur_Root, (uchar *) f, sizeof(SM_frame));
		}

		if (((now._a_t) & 1) == 1)	/* i.e., when starting nested DFS */
		{	A_depth = depthfound = 0;
			memcpy((uchar *)&A_Root, (uchar *)&now, vsize);
		}
		nr_handoffs = f->nr_handoffs;
	} else
	{	cpu_printf("pan: state empty\n");
	}

	depth = 0;
	trpt = &trail[1];
	trpt->tau    = f->m_tau;
	trpt->o_pm   = f->m_o_pm;

	(trpt-1)->ostate = &D_State; /* stub */
	trpt->ostate = &D_State;

#ifdef FULL_TRAIL
	if (upto > 0)
	{	stack_last[core_id] = (Stack_Tree *) f->m_stack;
	}
	#if defined(VERBOSE)
	if (stack_last[core_id])
	{	cpu_printf("%d: UNPACK -- SET m_stack %u (%d,%d)\n",
			depth, stack_last[core_id], stack_last[core_id]->pr,
			stack_last[core_id]->t_id);
	}
	#endif
#endif

	if (!trpt->o_t)
	{	static Trans D_Trans;
		trpt->o_t = &D_Trans;
	}

	#ifdef VERI
	if ((trpt->tau & 4) != 4)
	{	trpt->tau |= 4;	/* the claim moves first */
		cpu_printf("warning: trpt was not up to date\n");
	}
	#endif

	for (i = 0; i < (int) now._nr_pr; i++)
	{	P0 *ptr = (P0 *) pptr(i);
	#ifndef NP
		if (accpstate[ptr->_t][ptr->_p])
		{	trpt->o_pm |= 2;
		}
	#else
		if (progstate[ptr->_t][ptr->_p])
		{	trpt->o_pm |= 4;
		}
	#endif
	}

	#ifdef EVENT_TRACE
		#ifndef NP
			if (accpstate[EVENT_TRACE][now._event])
			{	trpt->o_pm |= 2;
			}
		#else
			if (progstate[EVENT_TRACE][now._event])
			{	trpt->o_pm |= 4;
			}
		#endif
	#endif

	#if defined(C_States) && (HAS_TRACK==1)
		/* restore state of tracked C objects */
		c_revert((uchar *) &(now.c_state[0]));
		#if (HAS_STACK==1)
		c_unstack((uchar *) f->m_c_stack); /* unmatched tracked data */
		#endif
	#endif
	return 1;
}

void
write_root(void)	/* for trail file */
{	int fd;

	if (iterative == 0 && Nr_Trails > 1)
		sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix);
	else
		sprintf(fnm, "%s.%s", TrailFile, sprefix);

	if (cur_Root.m_vsize == 0)
	{	(void) unlink(fnm); /* remove possible old copy */
		return;	/* its the default initial state */
	}

	if ((fd = creat(fnm, TMODE)) < 0)
	{	char *q;
		if ((q = strchr(TrailFile, '.')))
		{	*q = '\0';		/* strip .pml */
			if (iterative == 0 && Nr_Trails-1 > 0)
				sprintf(fnm, "%s%d.%s", TrailFile, Nr_Trails-1, sprefix);
			else
				sprintf(fnm, "%s.%s", TrailFile, sprefix);
			*q = '.';
			fd = creat(fnm, TMODE);
		}
		if (fd < 0)
		{	cpu_printf("pan: cannot create %s\n", fnm);
			perror("cause");
			return;
	}	}

	if (write(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))
	{	cpu_printf("pan: error writing %s\n", fnm);
	} else
	{	cpu_printf("pan: wrote %s\n", fnm);
	}
	close(fd);
}

void
set_root(void)
{	int fd;
	char *q;
	char MyFile[512];
	char MySuffix[16];
	char *ssuffix = "rst";
	int  try_core = 1;

	strcpy(MyFile, TrailFile);
try_again:
	if (whichtrail > 0)
	{	sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix);
		fd = open(fnm, O_RDONLY, 0);
		if (fd < 0 && (q = strchr(MyFile, '.')))
		{	*q = '\0';	/* strip .pml */
			sprintf(fnm, "%s%d.%s", MyFile, whichtrail, ssuffix);
			*q = '.';
			fd = open(fnm, O_RDONLY, 0);
		}
	} else
	{	sprintf(fnm, "%s.%s", MyFile, ssuffix);
		fd = open(fnm, O_RDONLY, 0);
		if (fd < 0 && (q = strchr(MyFile, '.')))
		{	*q = '\0';	/* strip .pml */
			sprintf(fnm, "%s.%s", MyFile, ssuffix);
			*q = '.';
			fd = open(fnm, O_RDONLY, 0);
	}	}

	if (fd < 0)
	{	if (try_core < NCORE)
		{	ssuffix = MySuffix;
			sprintf(ssuffix, "cpu%d_rst", try_core++);
			goto try_again;
		}
		cpu_printf("no file '%s.rst' or '%s' (not an error)\n", MyFile, fnm);
	} else
	{	if (read(fd, &cur_Root, sizeof(SM_frame)) != sizeof(SM_frame))
		{	cpu_printf("read error %s\n", fnm);
			close(fd);
			pan_exit(1);
		}
		close(fd);
		(void) unpack_state(&cur_Root, -2);
#ifdef SEP_STATE
		cpu_printf("partial trail -- last few steps only\n");
#endif
		cpu_printf("restored root from '%s'\n", fnm);
		printf("=====State:=====\n");
		{	int i, j; P0 *z;
			for (i = 0; i < now._nr_pr; i++)
			{	z = (P0 *)pptr(i);
				printf("proc %2d (%s) ", i, procname[z->_t]);
				for (j = 0; src_all[j].src; j++)
				if (src_all[j].tp == (int) z->_t)
				{	printf(" line %3d \"%s\" ",
						src_all[j].src[z->_p], PanSource);
					break;
				}
				printf("(state %d)\n", z->_p);
				c_locals(i, z->_t);
			}
			c_globals();
		}
		printf("================\n");
	}
}

#ifdef USE_DISK
unsigned long dsk_written, dsk_drained;
void mem_drain(void);
#endif

void
m_clear_frame(SM_frame *f)
{	int i, clr_sz = sizeof(SM_results);

	for (i = 0; i <= _NP_; i++)	/* all proctypes */
	{	clr_sz += NrStates[i]*sizeof(uchar);
	}
	memset(f, 0, clr_sz);
	/* caution if sizeof(SM_results) > sizeof(SM_frame) */
}

#define TargetQ_Full(n)	(m_workq[n][prfree[n]].m_vsize != 0)
#define TargetQ_NotFull(n)	(m_workq[n][prfree[n]].m_vsize == 0)

int
AllQueuesEmpty(void)
{	int q;
#ifndef NGQ
	if (*grcnt != 0)
	{	return 0;
	}
#endif
	for (q = 0; q < NCORE; q++)
	{	if (prcnt[q] != 0)
		{	return 0;
	}	}
	return 1;
}

void
Read_Queue(int q)
{	SM_frame   *f, *of;
	int	remember, target_q;
	SM_results *r;
	double patience = 0.0;

	target_q = (q + 1) % NCORE;

	for (;;)
	{	f = Get_Full_Frame(q);
		if (!f)	/* 1 second timeout -- and trigger for Query */
		{	if (someone_crashed(2))
			{	printf("cpu%d: search terminated [code %d]\n",
					core_id, search_terminated?*search_terminated:-1);
				sudden_stop("");
				pan_exit(1);
			}
#ifdef TESTING
	/* to profile with cc -pg and gprof pan.exe -- set handoff depth beyond maxdepth */
			exit(0);
#endif
			remember = *grfree;
			if (core_id == 0		/* root can initiate termination */
			&& remote_party == 0		/* and only the original root */
			&& query_in_progress == 0	/* unless its already in progress */
			&& AllQueuesEmpty())
			{	f = Get_Free_Frame(target_q);
				query_in_progress = 1;	/* only root process can do this */
				if (!f) { Uerror("Fatal1: no free slot"); }
				f->m_boq = QUERY;	/* initiate Query */
				if (verbose)
				{  cpu_printf("snd QUERY to q%d (%d) into slot %d\n",
					target_q, nstates_get + 1, prfree[target_q]-1);
				}
				f->m_vsize = remember + 1;
				/* number will not change unless we receive more states */
			} else if (patience++ > OneHour) /* one hour watchdog timer */
			{	cpu_printf("timeout -- giving up\n");
				sudden_stop("queue timeout");
				pan_exit(1);
			}
			if (0) cpu_printf("timed out -- try again\n");
			continue;	
		}
		patience = 0.0; /* reset watchdog */

		if (f->m_boq == QUERY)
		{	if (verbose)
			{	cpu_printf("got QUERY on q%d (%d <> %d) from slot %d\n",
					q, f->m_vsize, nstates_put + 1, prfull[q]-1);
				snapshot();
			}
			remember = f->m_vsize;
			f->m_vsize = 0;	/* release slot */

			if (core_id == 0 && remote_party == 0)	/* original root cpu0 */
			{	if (query_in_progress == 1	/* didn't send more states in the interim */
				&&  *grfree + 1 == remember)	/* no action on global queue meanwhile */
				{	if (verbose) cpu_printf("Termination detected\n");
					if (TargetQ_Full(target_q))
					{	if (verbose)
						cpu_printf("warning: target q is full\n");
					}
					f = Get_Free_Frame(target_q);
					if (!f) { Uerror("Fatal2: no free slot"); }
					m_clear_frame(f);
					f->m_boq = QUIT; /* send final Quit, collect stats */
					f->m_vsize = 111; /* anything non-zero will do */
					if (verbose)
					cpu_printf("put QUIT on q%d\n", target_q);
				} else
				{	if (verbose) cpu_printf("Stale Query\n");
#ifdef USE_DISK
					mem_drain();
#endif
				}
				query_in_progress = 0;
			} else
			{	if (TargetQ_Full(target_q))
				{	if (verbose)
					cpu_printf("warning: forward query - target q full\n");
				}
				f = Get_Free_Frame(target_q);
				if (verbose)
				cpu_printf("snd QUERY response to q%d (%d <> %d) in slot %d\n",
					target_q, remember, *grfree + 1, prfree[target_q]-1);
				if (!f) { Uerror("Fatal4: no free slot"); }

				if (*grfree + 1 == remember)	/* no action on global queue */
				{	f->m_boq = QUERY;	/* forward query, to root */
					f->m_vsize = remember;
				} else
				{	f->m_boq = QUERY_F;	/* no match -- busy */
					f->m_vsize = 112;	/* anything non-zero */
#ifdef USE_DISK
					if (dsk_written != dsk_drained)
					{	mem_drain();
					}
#endif
			}	}
			continue;
		}

		if (f->m_boq == QUERY_F)
		{	if (verbose)
			{	cpu_printf("got QUERY_F on q%d from slot %d\n", q, prfull[q]-1);
			}
			f->m_vsize = 0;	/* release slot */

			if (core_id == 0 && remote_party == 0)		/* original root cpu0 */
			{	if (verbose) cpu_printf("No Match on Query\n");
				query_in_progress = 0;
			} else
			{	if (TargetQ_Full(target_q))
				{	if (verbose) cpu_printf("warning: forwarding query_f, target queue full\n");
				}
				f = Get_Free_Frame(target_q);
				if (verbose) cpu_printf("forward QUERY_F to q%d into slot %d\n",
						target_q, prfree[target_q]-1);
				if (!f) { Uerror("Fatal5: no free slot"); }
				f->m_boq = QUERY_F;		/* cannot terminate yet */
				f->m_vsize = 113;		/* anything non-zero */
			}
#ifdef USE_DISK
			if (dsk_written != dsk_drained)
			{	mem_drain();
			}
#endif
			continue;
		}

		if (f->m_boq == QUIT)
		{	if (0) cpu_printf("done -- local memcnt %g Mb\n", memcnt/(1048576.));
			retrieve_info((SM_results *) f); /* collect and combine stats */
			if (verbose)
			{	cpu_printf("received Quit\n");
				snapshot();
			}
			f->m_vsize = 0;	/* release incoming slot */
			if (core_id != 0)
			{	f = Get_Free_Frame(target_q); /* new outgoing slot */
				if (!f) { Uerror("Fatal6: no free slot"); }
				m_clear_frame(f);	/* start with zeroed stats */
				record_info((SM_results *) f);
				f->m_boq = QUIT;	/* forward combined results */
				f->m_vsize = 114;	/* anything non-zero */
				if (verbose>1)
				cpu_printf("fwd Results to q%d\n", target_q);
			}
			break;			/* successful termination */
		}

		/* else: 0<= boq <= 255, means STATE transfer */
		if (unpack_state(f, q) != 0)
		{	nstates_get++;
			f->m_vsize = 0;	/* release slot */
			if (VVERBOSE) cpu_printf("Got state\n");

			if (search_terminated != NULL
			&&  *search_terminated == 0)
			{	new_state();	/* explore successors */
				memset((uchar *) &cur_Root, 0, sizeof(SM_frame));	/* avoid confusion */
			} else
			{	pan_exit(0);
			}
		} else
		{	pan_exit(0);
	}	}
	if (verbose) cpu_printf("done got %d put %d\n", nstates_get, nstates_put);
	sleep_report();
}

void
give_up(int unused_x)
{
	if (search_terminated != NULL)
	{	*search_terminated |= 32;	/* give_up */
	}
	if (!writing_trail)
	{	was_interrupted = 1;
		snapshot();
		cpu_printf("Give Up\n");
		sleep_report();
		pan_exit(1);
	} else /* we are already terminating */
	{	cpu_printf("SIGINT\n");
	}
}

void
check_overkill(void)
{
	vmax_seen = (vmax_seen + 7)/ 8;
	vmax_seen *= 8;	/* round up to a multiple of 8 */

	if (core_id == 0
	&&  !remote_party
	&&  nstates_put > 0
	&&  VMAX - vmax_seen > 8)
	{
#ifdef BITSTATE
		printf("cpu0: max VMAX value seen in this run: ");
#else
		printf("cpu0: recommend recompiling with ");
#endif
		printf("-DVMAX=%d\n", vmax_seen);
	}
}

void
mem_put(int q)	/* handoff state to other cpu, workq q */
{	SM_frame *f;
	int i, j;

	if (vsize > VMAX)
	{	vsize = (vsize + 7)/8; vsize *= 8; /* round up */
		printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize);
		Uerror("aborting");
	}
	if (now._nr_pr > PMAX)
	{	printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr);
		Uerror("aborting");
	}
	if (now._nr_qs > QMAX)
	{	printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs);
		Uerror("aborting");
	}
	if (vsize > vmax_seen) vmax_seen = vsize;
	if (now._nr_pr > pmax_seen) pmax_seen = now._nr_pr;
	if (now._nr_qs > qmax_seen) qmax_seen = now._nr_qs;

	f = Get_Free_Frame(q);	/* not called in likely deadlock states */
	if (!f) { Uerror("Fatal3: no free slot"); }

	if (VVERBOSE) cpu_printf("putting state into q%d\n", q);

	memcpy((uchar *) f->m_now,  (uchar *) &now, vsize);
	memset((uchar *) f->m_Mask, 0, (VMAX+7)/8 * sizeof(char));
	for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
	{	if (Mask[i])
		{	f->m_Mask[i/8] |= (1<<j);
	}	}

	if (now._nr_pr > 0)
	{ memcpy((uchar *) f->m_p_offset, (uchar *) proc_offset, now._nr_pr * sizeof(OFFT));
	  memcpy((uchar *) f->m_p_skip,   (uchar *) proc_skip,   now._nr_pr * sizeof(uchar));
	}
	if (now._nr_qs > 0)
	{ memcpy((uchar *) f->m_q_offset, (uchar *) q_offset, now._nr_qs * sizeof(OFFT));
	  memcpy((uchar *) f->m_q_skip,   (uchar *) q_skip,   now._nr_qs * sizeof(uchar));
	}
#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
	c_stack((uchar *) f->m_c_stack); /* save unmatched tracked data */
#endif
#ifdef FULL_TRAIL
	f->m_stack = stack_last[core_id];
#endif
	f->nr_handoffs = nr_handoffs+1;
	f->m_tau    = trpt->tau;
	f->m_o_pm   = trpt->o_pm;
	f->m_boq    = boq;
	f->m_vsize  = vsize;	/* must come last - now the other cpu can see it */

	if (query_in_progress == 1)
		query_in_progress = 2;	/* make sure we know, if a query makes the rounds */
	nstates_put++;
}

#ifdef USE_DISK
int Dsk_W_Nr, Dsk_R_Nr;
int dsk_file = -1, dsk_read = -1;
unsigned long dsk_written, dsk_drained;
char dsk_name[512];

#ifndef BFS_DISK
#if defined(WIN32) || defined(WIN64)
	#define RFLAGS	(O_RDONLY|O_BINARY)
	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC|O_BINARY)
#else
	#define RFLAGS	(O_RDONLY)
	#define WFLAGS	(O_CREAT|O_WRONLY|O_TRUNC)
#endif
#endif

void
dsk_stats(void)
{	int i;

	if (dsk_written > 0)
	{	cpu_printf("dsk_written %d states in %d files\ncpu%d: dsk_drained %6d states\n",
			dsk_written, Dsk_W_Nr, core_id, dsk_drained);
		close(dsk_read);
		close(dsk_file);
		for (i = 0; i < Dsk_W_Nr; i++)
		{	sprintf(dsk_name, "Q%.3d_%.3d.tmp", i, core_id);
			unlink(dsk_name);
	}	}
}

void
mem_drain(void)
{	SM_frame *f, g;
	int q = (core_id + 1) % NCORE;	/* target q */
	int sz;

	if (dsk_read < 0
	||  dsk_written <= dsk_drained)
	{	return;
	}

	while (dsk_written > dsk_drained
	&& TargetQ_NotFull(q))
	{	f = Get_Free_Frame(q);
		if (!f) { Uerror("Fatal: unhandled condition"); }

		if ((dsk_drained+1)%MAX_DSK_FILE == 0)	/* 100K states max per file */
		{	(void) close(dsk_read); 	/* close current read handle */
			sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr++, core_id);
			(void) unlink(dsk_name);	/* remove current file */
			sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_R_Nr, core_id);
			cpu_printf("reading %s\n", dsk_name);
			dsk_read = open(dsk_name, RFLAGS); /* open next file */
			if (dsk_read < 0)
			{	Uerror("could not open dsk file");
		}	}
		if (read(dsk_read, &g, sizeof(SM_frame)) != sizeof(SM_frame))
		{	Uerror("bad dsk file read");
		}
		sz = g.m_vsize;
		g.m_vsize = 0;
		memcpy(f, &g, sizeof(SM_frame));
		f->m_vsize = sz;	/* last */

		dsk_drained++;
	}
}

void
mem_file(void)
{	SM_frame f;
	int i, j, q = (core_id + 1) % NCORE;	/* target q */

	if (vsize > VMAX)
	{	printf("pan: recompile with -DVMAX=N with N >= %d\n", vsize);
		Uerror("aborting");
	}
	if (now._nr_pr > PMAX)
	{	printf("pan: recompile with -DPMAX=N with N >= %d\n", now._nr_pr);
		Uerror("aborting");
	}
	if (now._nr_qs > QMAX)
	{	printf("pan: recompile with -DQMAX=N with N >= %d\n", now._nr_qs);
		Uerror("aborting");
	}

	if (VVERBOSE) cpu_printf("filing state for q%d\n", q);

	memcpy((uchar *) f.m_now,  (uchar *) &now, vsize);
	memset((uchar *) f.m_Mask, 0, (VMAX+7)/8 * sizeof(char));
	for (i = j = 0; i < VMAX; i++, j = (j+1)%8)
	{	if (Mask[i])
		{	f.m_Mask[i/8] |= (1<<j);
	}	}

	if (now._nr_pr > 0)
	{	memcpy((uchar *)f.m_p_offset, (uchar *)proc_offset, now._nr_pr*sizeof(OFFT));
		memcpy((uchar *)f.m_p_skip,   (uchar *)proc_skip,   now._nr_pr*sizeof(uchar));
	}
	if (now._nr_qs > 0)
	{	memcpy((uchar *) f.m_q_offset, (uchar *) q_offset, now._nr_qs*sizeof(OFFT));
		memcpy((uchar *) f.m_q_skip,   (uchar *) q_skip,   now._nr_qs*sizeof(uchar));
	}
#if defined(C_States) && (HAS_TRACK==1) && (HAS_STACK==1)
	c_stack((uchar *) f.m_c_stack); /* save unmatched tracked data */
#endif
#ifdef FULL_TRAIL
	f.m_stack  = stack_last[core_id];
#endif
	f.nr_handoffs = nr_handoffs+1;
	f.m_tau    = trpt->tau;
	f.m_o_pm   = trpt->o_pm;
	f.m_boq    = boq;
	f.m_vsize  = vsize;

	if (query_in_progress == 1)
	{	query_in_progress = 2;
	}
	if (dsk_file < 0)
	{	sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr, core_id);
		dsk_file = open(dsk_name, WFLAGS, 0644);
		dsk_read = open(dsk_name, RFLAGS);
		if (dsk_file < 0 || dsk_read < 0)
		{	cpu_printf("File: <%s>\n", dsk_name);
			Uerror("cannot open diskfile");
		}
		Dsk_W_Nr++; /* nr of next file to open */
		cpu_printf("created temporary diskfile %s\n", dsk_name);
	} else if ((dsk_written+1)%MAX_DSK_FILE == 0)
	{	close(dsk_file); /* close write handle */
		sprintf(dsk_name, "Q%.3d_%.3d.tmp", Dsk_W_Nr++, core_id);
		dsk_file = open(dsk_name, WFLAGS, 0644);
		if (dsk_file < 0)
		{	cpu_printf("File: <%s>\n", dsk_name);
			Uerror("aborting: cannot open new diskfile");
		}
		cpu_printf("created temporary diskfile %s\n", dsk_name);
	}
	if (write(dsk_file, &f, sizeof(SM_frame)) != sizeof(SM_frame))
	{	Uerror("aborting -- disk write failed (disk full?)");
	}
	nstates_put++;
	dsk_written++;
}
#endif

int
mem_hand_off(void)
{
	if (search_terminated == NULL
	||  *search_terminated != 0)	/* not a full crash check */
	{	pan_exit(0);
	}
	iam_alive();		/* on every transition of Down */
#ifdef USE_DISK
	mem_drain();		/* maybe call this also on every Up */
#endif
	if (depth > z_handoff	/* above handoff limit */
#ifndef SAFETY
	&&  !a_cycles		/* not in liveness mode */
#endif
#if SYNC
	&&  boq == -1		/* not mid-rv */
#endif
#ifdef VERI
	&&  (trpt->tau&4)	 /* claim moves first  */
	&&  !((trpt-1)->tau&128) /* not a stutter move */
#endif
	&&  !(trpt->tau&8))	/* not an atomic move */
	{	int q = (core_id + 1) % NCORE;	/* circular handoff */
	#ifdef GENEROUS
		if (prcnt[q] < LN_FRAMES)
	#else
		if (TargetQ_NotFull(q)
		&& (dfs_phase2 == 0 || prcnt[core_id] > 0))
	#endif
		{	mem_put(q);
			return 1;
		}
		{	int rval;
	#ifndef NGQ
			rval = GlobalQ_HasRoom();
	#else
			rval = 0;
	#endif
	#ifdef USE_DISK
			if (rval == 0)
			{	void mem_file(void);
				mem_file();
				rval = 1;
			}
	#endif
			return rval;
		}
	}
	return 0; /* i.e., no handoff */
}

void
mem_put_acc(void)	/* liveness mode */
{	int q = (core_id + 1) % NCORE;

	if (search_terminated == NULL
	||  *search_terminated != 0)
	{	pan_exit(0);
	}
#ifdef USE_DISK
	mem_drain();
#endif
	/* some tortured use of preprocessing: */
#if !defined(NGQ) || defined(USE_DISK)
	if (TargetQ_Full(q))
	{
#endif
#ifndef NGQ
		if (GlobalQ_HasRoom())
		{	return;
		}
#endif
#ifdef USE_DISK
		mem_file();
	} else
#else
	#if !defined(NGQ) || defined(USE_DISK)
	}
	#endif
#endif
	{	mem_put(q);
	}
}

#if defined(WIN32) || defined(WIN64)
void
init_shm(void)		/* initialize shared work-queues */
{	char	key[512];
	int	n, m;
	int	must_exit = 0;

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 3: allocate shared work-queues %g Mb\n",
			((double) NCORE * LWQ_SIZE + GWQ_SIZE) / (1048576.));
	}
	for (m = 0; m < NR_QS; m++)	/* last q is global 1 */
	{	double qsize = (m == NCORE) ? GWQ_SIZE : LWQ_SIZE;
		sprintf(key, "Global\\pan_%s_%.3d", PanSource, m);
		if (core_id == 0)
		{	shmid[m] = CreateFileMapping(
				INVALID_HANDLE_VALUE,	/* use paging file */
				NULL,			/* default security */
				PAGE_READWRITE,		/* access permissions */
				0,			/* high-order 4 bytes */
				qsize,			/* low-order bytes, size in bytes */
				key);			/* name */
		} else			/* worker nodes just open these segments */
		{	shmid[m] = OpenFileMapping(
				FILE_MAP_ALL_ACCESS,	/* read/write access */
				FALSE,			/* children do not inherit handle */
				key);
		}
		if (shmid[m] == NULL)
		{	fprintf(stderr, "cpu%d: could not create or open shared queues\n",
				core_id);
			must_exit = 1;
			break;
		}
		/* attach: */
		shared_mem[m] = (char *) MapViewOfFile(shmid[m], FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if (shared_mem[m] == NULL)
		{ fprintf(stderr, "cpu%d: cannot attach shared q%d (%d Mb)\n",
			core_id, m+1, (int) (qsize/(1048576.)));
		  must_exit = 1;
		  break;
		}

		memcnt += qsize;

		m_workq[m] = (SM_frame *) shared_mem[m];
		if (core_id == 0)
		{	int nframes = (m == NCORE) ? GN_FRAMES : LN_FRAMES;
			for (n = 0; n < nframes; n++)
			{	m_workq[m][n].m_vsize = 0;
				m_workq[m][n].m_boq = 0;
	}	}	}

	if (must_exit)
	{	fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);	/* calls cleanup_shm */
	}
}

static uchar *
prep_shmid_S(size_t n)		/* either sets SS or H_tab, WIN32/WIN64 */
{	char	*rval;
#ifndef SEP_STATE
	char	key[512];

	if (verbose && core_id == 0)
	{
	#ifdef BITSTATE
		printf("cpu0: step 1: allocate shared bitstate %g Mb\n",
			(double) n / (1048576.));
	#else
		printf("cpu0: step 1: allocate shared hastable %g Mb\n",
			(double) n / (1048576.));
	#endif
	}
	#ifdef MEMLIM
	if (memcnt + (double) n > memlim)
	{	printf("cpu%d: S %8g + %d Kb exceeds memory limit of %8g Mb\n",
			core_id, memcnt/1024., n/1024, memlim/(1048576.));
		printf("cpu%d: insufficient memory -- aborting\n", core_id);
		exit(1);
	}
	#endif

	/* make key different from queues: */
	sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+2); /* different from qs */

	if (core_id == 0)	/* root */
	{	shmid_S = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
#ifdef WIN64
			PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);
#else
			PAGE_READWRITE, 0, n, key);
#endif
		memcnt += (double) n;
	} else			/* worker */
	{	shmid_S = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);
	}
	if (shmid_S == NULL)
	{
	#ifdef BITSTATE
		fprintf(stderr, "cpu%d: cannot %s shared bitstate",
			core_id, core_id?"open":"create");
	#else
		fprintf(stderr, "cpu%d: cannot %s shared hashtable",
			core_id, core_id?"open":"create");
	#endif
		fprintf(stderr, "pan: check './pan --' for usage details\n");
		pan_exit(1);
	}

	rval = (char *) MapViewOfFile(shmid_S, FILE_MAP_ALL_ACCESS, 0, 0, 0);	/* attach */
	if ((char *) rval == NULL)
	{ fprintf(stderr, "cpu%d: cannot attach shared bitstate or hashtable\n", core_id);
	  fprintf(stderr, "pan: check './pan --' for usage details\n");
	  pan_exit(1);
	}
#else
	rval = (char *) emalloc(n);
#endif
	return (uchar *) rval;
}

static uchar *
prep_state_mem(size_t n)		/* WIN32/WIN64 sets memory arena for states */
{	char	*rval;
	char	key[512];
	static int cnt = 3;		/* start larger than earlier ftok calls */

	if (verbose && core_id == 0)
	{	printf("cpu0: step 2+: pre-allocate memory arena %d of %g Mb\n",
			cnt-3, (double) n / (1048576.));
	}
	#ifdef MEMLIM
	if (memcnt + (double) n > memlim)
	{	printf("cpu%d: error: M %.0f + %.0f exceeds memory limit of %.0f Kb\n",
			core_id, memcnt/1024.0, (double) n/1024.0, memlim/1024.0);
		return NULL;
	}
	#endif

	sprintf(key, "Global\\pan_%s_%.3d", PanSource, NCORE+cnt); cnt++;

	if (core_id == 0)
	{	shmid_M = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
#ifdef WIN64
			PAGE_READWRITE, (n>>32), (n & 0xffffffff), key);
#else
			PAGE_READWRITE, 0, n, key);
#endif
	} else
	{	shmid_M = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, key);
	}
	if (shmid_M == NULL)
	{	printf("cpu%d: failed to get pool of shared memory nr %d of size %d\n",
			core_id, cnt-3, n);
		printf("pan: check './pan --' for usage details\n");
		return NULL;
	}
	rval = (char *) MapViewOfFile(shmid_M, FILE_MAP_ALL_ACCESS, 0, 0, 0);	/* attach */

	if (rval == NULL)
	{ printf("cpu%d: failed to attach pool of shared memory nr %d of size %d\n",
		core_id, cnt-3, n);
	  return NULL;
	}
	return (uchar *) rval;
}

void
init_HT(unsigned long n)	/* WIN32/WIN64 version */
{	volatile char	*x;
	double  get_mem;
#ifndef SEP_STATE
	char	*dc_mem_start;
#endif
	if (verbose) printf("cpu%d: initialization for Windows\n", core_id);

#ifdef SEP_STATE
 #ifndef MEMLIM
	if (verbose)
	{	printf("cpu0: steps 0,1: no -DMEMLIM set\n");
	}
 #else
	if (verbose)
	printf("cpu0: steps 0,1: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb)\n",
		MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.));
#endif
	get_mem = NCORE * sizeof(double) + (1 + CS_NR) * sizeof(void *)+ 4*sizeof(void *) + 2*sizeof(double);
	/* NCORE * is_alive + search_terminated + CS_NR * sh_lock + 6 gr vars */
	get_mem += 4 * NCORE * sizeof(void *);
 #ifdef FULL_TRAIL
	get_mem += (NCORE) * sizeof(Stack_Tree *);
	/* NCORE * stack_last */
 #endif
	x = (volatile char *) prep_state_mem((size_t) get_mem);
	shmid_X = (void *) x;
	if (x == NULL)
	{	printf("cpu0: could not allocate shared memory, see ./pan --\n");
		exit(1);
	}
	search_terminated = (volatile unsigned int *) x; /* comes first */
	x += sizeof(void *); /* maintain alignment */

	is_alive   = (volatile double *) x;
	x += NCORE * sizeof(double);

	sh_lock   = (volatile int *) x;
	x += CS_NR * sizeof(void *); /* allow 1 word per entry */

	grfree    = (volatile int *) x;
	x += sizeof(void *);
	grfull    = (volatile int *) x;
	x += sizeof(void *);
	grcnt    = (volatile int *) x;
	x += sizeof(void *);
	grmax    = (volatile int *) x;
	x += sizeof(void *);
	prfree = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prfull = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prcnt = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prmax = (volatile int *) x;
	x += NCORE * sizeof(void *);
	gr_readmiss    = (volatile double *) x;
	x += sizeof(double);
	gr_writemiss    = (volatile double *) x;
	x += sizeof(double);

	#ifdef FULL_TRAIL
		stack_last = (volatile Stack_Tree **) x;
		x += NCORE * sizeof(Stack_Tree *);
	#endif

	#ifndef BITSTATE
		H_tab = (struct H_el **) emalloc(n);
	#endif
#else
	#ifndef MEMLIM
		#warning MEMLIM not set
		#define MEMLIM	(2048)
	#endif

	if (core_id == 0 && verbose)
		printf("cpu0: step 0: -DMEMLIM=%d Mb - (hashtable %g Mb + workqueues %g Mb) = %g Mb for state storage\n",
		MEMLIM, ((double)n/(1048576.)), ((double) NCORE * LWQ_SIZE + GWQ_SIZE)/(1048576.),
		(memlim - memcnt - (double) n - ((double) NCORE * LWQ_SIZE + GWQ_SIZE))/(1048576.));
	#ifndef BITSTATE
		H_tab = (struct H_el **) prep_shmid_S((size_t) n);	/* hash_table */
	#endif
	get_mem = memlim - memcnt - ((double) NCORE) * LWQ_SIZE - GWQ_SIZE;
	if (get_mem <= 0)
	{	Uerror("internal error -- shared state memory");
	}

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 2: shared state memory %g Mb\n",
			get_mem/(1048576.));
	}
	x = dc_mem_start = (char *) prep_state_mem((size_t) get_mem);	/* for states */
	if (x == NULL)
	{	printf("cpu%d: insufficient memory -- aborting\n", core_id);
		exit(1);
	}

	search_terminated = (volatile unsigned int *) x; /* comes first */
	x += sizeof(void *); /* maintain alignment */

	is_alive   = (volatile double *) x;
	x += NCORE * sizeof(double);

	sh_lock   = (volatile int *) x;
	x += CS_NR * sizeof(int);

	grfree    = (volatile int *) x;
	x += sizeof(void *);
	grfull    = (volatile int *) x;
	x += sizeof(void *);
	grcnt    = (volatile int *) x;
	x += sizeof(void *);
	grmax    = (volatile int *) x;
	x += sizeof(void *);
	prfree = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prfull = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prcnt = (volatile int *) x;
	x += NCORE * sizeof(void *);
	prmax = (volatile int *) x;
	x += NCORE * sizeof(void *);
	gr_readmiss = (volatile double *) x;
	x += sizeof(double);
	gr_writemiss = (volatile double *) x;
	x += sizeof(double);

 #ifdef FULL_TRAIL
	stack_last = (volatile Stack_Tree **) x;
	x += NCORE * sizeof(Stack_Tree *);
 #endif
	if (((long)x)&(sizeof(void *)-1))	/* word alignment */
	{	x += sizeof(void *)-(((long)x)&(sizeof(void *)-1)); /* 64-bit align */
	}

	#ifdef COLLAPSE
	ncomps = (unsigned long *) x;
	x += (256+2) * sizeof(unsigned long);
	#endif

	dc_shared = (sh_Allocater *) x; /* in shared memory */
	x += sizeof(sh_Allocater);

	if (core_id == 0)	/* root only */
	{	dc_shared->dc_id     = shmid_M;
		dc_shared->dc_start  = (void *) dc_mem_start;
		dc_shared->dc_arena  = x;
		dc_shared->pattern   = 1234567;
		dc_shared->dc_size   = (long) get_mem - (long) (x - dc_mem_start);
		dc_shared->nxt       = NULL;
	}
#endif
}

#if defined(WIN32) || defined(WIN64) || defined(__i386__) || defined(__x86_64__)
extern BOOLEAN InterlockedBitTestAndSet(LONG volatile* Base, LONG Bit);
int
tas(volatile LONG *s)
{	return InterlockedBitTestAndSet(s, 1);
}
#else
	#error missing definition of test and set operation for this platform
#endif

void
cleanup_shm(int val)
{	int m;
	static int nibis = 0;

	if (nibis != 0)
	{	printf("cpu%d: Redundant call to cleanup_shm(%d)\n", core_id, val);
		return;
	} else
	{	nibis = 1;
	}
	if (search_terminated != NULL)
	{	*search_terminated |= 16; /* cleanup_shm */
	}

	for (m = 0; m < NR_QS; m++)
	{	if (shmid[m] != NULL)
		{	UnmapViewOfFile((char *) shared_mem[m]);
			CloseHandle(shmid[m]);
	}	}
#ifdef SEP_STATE
	UnmapViewOfFile((void *) shmid_X);
	CloseHandle((void *) shmid_M);
#else
	#ifdef BITSTATE
		if (shmid_S != NULL)
		{	UnmapViewOfFile(SS);
			CloseHandle(shmid_S);
		}
	#else
		if (core_id == 0 && verbose)
		{	printf("cpu0: done, %ld Mb of shared state memory left\n",
				dc_shared->dc_size / (long)(1048576));
		}
		if (shmid_S != NULL)
		{	UnmapViewOfFile(H_tab);
			CloseHandle(shmid_S);
		}
		shmid_M = (void *) (dc_shared->dc_id);
		UnmapViewOfFile((char *) dc_shared->dc_start);
		CloseHandle(shmid_M);
	#endif
#endif
	/* detached from shared memory - so cannot use cpu_printf */
	if (verbose)
	{	printf("cpu%d: done -- got %d states from queue\n",
			core_id, nstates_get);
	}
}

void
mem_get(void)
{	SM_frame   *f;
	int is_parent;

#if defined(MA) && !defined(SEP_STATE)
	#error MA requires SEP_STATE in multi-core mode
#endif
#ifdef BFS
	#error BFS is not supported in multi-core mode
#endif
#ifdef SC
	#error SC is not supported in multi-core mode
#endif
	init_shm();	/* we are single threaded when this starts */
	signal(SIGINT, give_up);	/* windows control-c interrupt */

	if (core_id == 0 && verbose)
	{	printf("cpu0: step 4: creating additional workers (proxy %d)\n",
			proxy_pid);
	}
#if 0
	if NCORE > 1 the child or the parent should fork N-1 more times
	the parent is the only process with core_id == 0 and is_parent > 0
	the others (workers) have is_parent = 0 and core_id = 1..NCORE-1
#endif
	if (core_id == 0)			/* root starts up the workers */
	{	worker_pids[0] = (DWORD) getpid();	/* for completeness */
		while (++core_id < NCORE)	/* first worker sees core_id = 1 */
		{	char cmdline[64];
			STARTUPINFO si = { sizeof(si) };
			PROCESS_INFORMATION pi;

			if (proxy_pid == core_id)	/* always non-zero */
			{	sprintf(cmdline, "pan_proxy.exe -r %s-Q%d -Z%d",
					o_cmdline, getpid(), core_id);
			} else
			{	sprintf(cmdline, "pan.exe %s-Q%d -Z%d",
					o_cmdline, getpid(), core_id);
			}
			if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline);

			is_parent = CreateProcess(0, cmdline, 0, 0, FALSE, 0, 0, 0, &si, &pi);
			if (is_parent == 0)
			{	Uerror("fork failed");
			}
			worker_pids[core_id] = pi.dwProcessId;
			worker_handles[core_id] = pi.hProcess;
			if (verbose)
			{	cpu_printf("created core %d, pid %d\n",
					core_id, pi.dwProcessId);
			}
			if (proxy_pid == core_id)	/* we just created the receive half */
			{	/* add proxy send, store pid in proxy_pid_snd */
				sprintf(cmdline, "pan_proxy.exe -s %s-Q%d -Z%d -Y%d",
					o_cmdline, getpid(), core_id, worker_pids[proxy_pid]);
				if (verbose) printf("cpu%d: spawn %s\n", core_id, cmdline);
				is_parent = CreateProcess(0, cmdline, 0,0, FALSE, 0,0,0, &si, &pi);
				if (is_parent == 0)
				{	Uerror("fork failed");
				}
				proxy_pid_snd = pi.dwProcessId;
				proxy_handle_snd = pi.hProcess;
				if (verbose)
				{	cpu_printf("created core %d, pid %d (send proxy)\n",
						core_id, pi.dwProcessId);
		}	}	}
		core_id = 0;		/* reset core_id for root process */
	} else	/* worker */
	{	static char db0[16];	/* good for up to 10^6 cores */
		static char db1[16];
		tprefix = db0; sprefix = db1;
		sprintf(tprefix, "cpu%d_trail", core_id);	/* avoid conflicts on file access */
		sprintf(sprefix, "cpu%d_rst", core_id);
		memcnt = 0;	/* count only additionally allocated memory */
	}
	if (verbose)
	{	cpu_printf("starting core_id %d -- pid %d\n", core_id, getpid());
	}
	if (core_id == 0 && !remote_party)
	{	new_state();	/* root starts the search */
		if (verbose)
		cpu_printf("done with 1st dfs, nstates %g (put %d states), start reading q\n",
			nstates, nstates_put);
		dfs_phase2 = 1;
	}
	Read_Queue(core_id);	/* all cores */

	if (verbose)
	{	cpu_printf("put %6d states into queue -- got %6d\n",
			nstates_put, nstates_get);
	}
	done = 1;
	wrapup();
	exit(0);
}
#endif

#ifdef BITSTATE
void
init_SS(unsigned long n)
{
	SS = (uchar *) prep_shmid_S((size_t) n);
	init_HT(0L);
}
#endif

#endif
clock_t start_time;
#if NCORE>1
clock_t crash_stamp;
#endif
#if !defined(WIN32) && !defined(WIN64)
struct tms start_tm;
#endif

void
start_timer(void)
{
#if defined(WIN32) || defined(WIN64)
	start_time = clock();
#else
	start_time = times(&start_tm);
#endif
}

void
stop_timer(void)
{	clock_t stop_time;
	double delta_time;
#if !defined(WIN32) && !defined(WIN64)
	struct tms stop_tm;
	stop_time = times(&stop_tm);
	delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));
#else
	stop_time = clock();
	delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);
#endif
	if (readtrail || delta_time < 0.00) return;
#if NCORE>1
	if (core_id == 0 && nstates > (double) 0)
	{	printf("\ncpu%d: elapsed time %.3g seconds (%g states visited)\n", core_id, delta_time, nstates);
		if (delta_time > 0.01)
		{	printf("cpu%d: rate %g states/second\n", core_id, nstates/delta_time);
		}
		{ void check_overkill(void);
	 	  check_overkill();
	}	}
#else
	printf("\npan: elapsed time %.3g seconds\n", delta_time);
	if (delta_time > 0.01)
	{	printf("pan: rate %9.8g states/second\n", nstates/delta_time);
		if (verbose)
		{	printf("pan: avg transition delay %.5g usec\n",
				delta_time/(nstates+truncs));
	}	}
#endif
}

#if NCORE>1
#ifdef T_ALERT
double t_alerts[17];

void
crash_report(void)
{	int i;
	printf("crash alert intervals:\n");
	for (i = 0; i < 17; i++)
	{	printf("%d\t%g\n", i, t_alerts[i]);
}	}
#endif

void
crash_reset(void)
{	/* false alarm */
	if (crash_stamp != (clock_t) 0)
	{
#ifdef T_ALERT
		double delta_time;
		int i;
#if defined(WIN32) || defined(WIN64)
		delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);
#else
		delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));
#endif
		for (i = 0; i < 16; i++)
		{	if (delta_time <= (i*30))
			{	t_alerts[i] = delta_time;
				break;
		}	}
		if (i == 16) t_alerts[i] = delta_time;
#endif
		if (verbose)
		printf("cpu%d: crash alert off\n", core_id);
	}
	crash_stamp = (clock_t) 0;
}

int
crash_test(double maxtime)
{	double delta_time;
	if (crash_stamp == (clock_t) 0)
	{	/* start timing */
#if defined(WIN32) || defined(WIN64)
		crash_stamp = clock();
#else
		crash_stamp = times(&start_tm);
#endif
		if (verbose)
		{	printf("cpu%d: crash detection\n", core_id);
		}
		return 0;
	}
#if defined(WIN32) || defined(WIN64)
	delta_time = ((double) (clock() - crash_stamp)) / ((double) CLOCKS_PER_SEC);
#else
	delta_time = ((double) (times(&start_tm) - crash_stamp)) / ((double) sysconf(_SC_CLK_TCK));
#endif
	return (delta_time >= maxtime);
}
#endif

void
do_the_search(void)
{	int i;
	depth = mreached = 0;
	trpt = &trail[0];
#ifdef VERI
	trpt->tau |= 4;	/* the claim moves first */
#endif
	for (i = 0; i < (int) now._nr_pr; i++)
	{	P0 *ptr = (P0 *) pptr(i);
#ifndef NP
		if (!(trpt->o_pm&2)
		&&  accpstate[ptr->_t][ptr->_p])
		{	trpt->o_pm |= 2;
		}
#else
		if (!(trpt->o_pm&4)
		&&  progstate[ptr->_t][ptr->_p])
		{	trpt->o_pm |= 4;
		}
#endif
	}
#ifdef EVENT_TRACE
#ifndef NP
	if (accpstate[EVENT_TRACE][now._event])
	{	trpt->o_pm |= 2;
	}
#else
	if (progstate[EVENT_TRACE][now._event])
	{	trpt->o_pm |= 4;
	}
#endif
#endif
#ifndef NOCOMP
	Mask[0] = Mask[1] = 1;	/* _nr_pr, _nr_qs */
	if (!a_cycles)
	{	i = &(now._a_t) - (uchar *) &now;
		Mask[i] = 1; /* _a_t */
	}
#ifndef NOFAIR
	if (!fairness)
	{	int j = 0;
		i = &(now._cnt[0]) - (uchar *) &now;
		while (j++ < NFAIR)
			Mask[i++] = 1; /* _cnt[] */
	}
#endif
#endif
#ifndef NOFAIR
	if (fairness
	&&  (a_cycles && (trpt->o_pm&2)))
	{	now._a_t = 2;	/* set the A-bit */
		now._cnt[0] = now._nr_pr + 1;
#ifdef VERBOSE
	printf("%3d: fairness Rule 1, cnt=%d, _a_t=%d\n",
		depth, now._cnt[now._a_t&1], now._a_t);
#endif
	}
#endif
	c_stack_start = (char *) &i; /* meant to be read-only */
#if defined(HAS_CODE) && defined (C_INIT)
	C_INIT; /* initialization of data that must precede fork() */
	c_init_done++;
#endif
#if defined(C_States) && (HAS_TRACK==1)
	/* capture initial state of tracked C objects */
	c_update((uchar *) &(now.c_state[0]));
#endif
#ifdef HAS_CODE
	if (readtrail) getrail(); /* no return */
#endif
	start_timer();
#ifdef BFS
	bfs();
#else
#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)
	/* initial state of tracked & unmatched objects */
	c_stack((uchar *) &(svtack->c_stack[0]));
#endif
#if defined(P_RAND) || defined(T_RAND)
	srand(s_rand);
#endif
#if NCORE>1
	mem_get();
#else
	new_state();	/* start 1st DFS */
#endif
#endif
}
#ifdef INLINE_REV
uchar
do_reverse(Trans *t, short II, uchar M)
{	uchar _m = M;
	int  tt = (int) ((P0 *)this)->_p;
#include REVERSE_MOVES
R999:	return _m;
}
#endif
#ifndef INLINE
#ifdef EVENT_TRACE
static char _tp = 'n'; static int _qid = 0;
#endif
uchar
do_transit(Trans *t, short II)
{	uchar _m = 0;
	int  tt = (int) ((P0 *)this)->_p;
#ifdef M_LOSS
	uchar delta_m = 0;
#endif
#ifdef EVENT_TRACE
	short oboq = boq;
	uchar ot = (uchar)  ((P0 *)this)->_t;
	if (ot == EVENT_TRACE) boq = -1;
#define continue	{ boq = oboq; return 0; }
#else
#define continue	return 0
#ifdef SEPARATE
	uchar ot = (uchar)  ((P0 *)this)->_t;
#endif
#endif
#include FORWARD_MOVES
P999:
#ifdef EVENT_TRACE
	if (ot == EVENT_TRACE) boq = oboq;
#endif
	return _m;
#undef continue
}
#ifdef EVENT_TRACE
void
require(char tp, int qid)
{	Trans *t;
	_tp = tp; _qid = qid;

	if (now._event != endevent)
	for (t = trans[EVENT_TRACE][now._event]; t; t = t->nxt)
	{	if (do_transit(t, EVENT_TRACE))
		{	now._event = t->st;
			reached[EVENT_TRACE][t->st] = 1;
#ifdef VERBOSE
	printf("	event_trace move to -> %d\n", t->st);
#endif
#ifndef BFS
#ifndef NP
			if (accpstate[EVENT_TRACE][now._event])
				(trpt+1)->o_pm |= 2;
#else
			if (progstate[EVENT_TRACE][now._event])
				(trpt+1)->o_pm |= 4;
#endif
#endif
#ifdef NEGATED_TRACE
			if (now._event == endevent)
			{
#ifndef BFS
				depth++; trpt++;
#endif
				uerror("event_trace error (all events matched)");
#ifndef BFS
				trpt--; depth--;
#endif
				break;
			}
#endif
			for (t = t->nxt; t; t = t->nxt)
			{	if (do_transit(t, EVENT_TRACE))
				 Uerror("non-determinism in event-trace");
			}
			return;
		}
#ifdef VERBOSE
		 else
	printf("	event_trace miss '%c' -- %d, %d, %d\n",
			tp, qid, now._event, t->forw);
#endif
	}
#ifdef NEGATED_TRACE
	now._event = endevent; /* only 1st try will count -- fixed 4.2.6 */
#else
#ifndef BFS
	depth++; trpt++;
#endif
	uerror("event_trace error (no matching event)");
#ifndef BFS
	trpt--; depth--;
#endif
#endif
}
#endif
int
enabled(int iam, int pid)
{	Trans *t; uchar *othis = this;
	int res = 0; int tt; uchar ot;
#ifdef VERI
	/* if (pid > 0) */ pid++;
#endif
	if (pid == iam)
		Uerror("used: enabled(pid=thisproc)");
	if (pid < 0 || pid >= (int) now._nr_pr)
		return 0;
	this = pptr(pid);
	TstOnly = 1;
	tt = (int) ((P0 *)this)->_p;
	ot = (uchar) ((P0 *)this)->_t;
	for (t = trans[ot][tt]; t; t = t->nxt)
		if (do_transit(t, (short) pid))
		{	res = 1;
			break;
		}
	TstOnly = 0;
	this = othis;
	return res;
}
#endif
void
snap_time(void)
{	clock_t stop_time;
	double delta_time;
#if !defined(WIN32) && !defined(WIN64)
	struct tms stop_tm;
	stop_time  = times(&stop_tm);
	delta_time = ((double) (stop_time - start_time)) / ((double) sysconf(_SC_CLK_TCK));
#else
	stop_time  = clock();
	delta_time = ((double) (stop_time - start_time)) / ((double) CLOCKS_PER_SEC);
#endif
	if (delta_time > 0.01)
	{	printf("t= %6.3g ", delta_time);
		printf("R= %7.0g", nstates/delta_time);
	}
	printf("\n");
	if (quota > 0.1 && delta_time > quota)
	{	printf("Time limit of %6.3g minutes exceeded\n", quota/60.0);
#if NCORE>1
		fflush(stdout);
		leave_critical(GLOBAL_LOCK);
		sudden_stop("time-limit");
		exit(1);
#endif
		wrapup();
	}
}
void
snapshot(void)
{
#if NCORE>1
	enter_critical(GLOBAL_LOCK);	/* snapshot */
	printf("cpu%d: ", core_id);
#endif
	printf("Depth= %7ld States= %8.3g ",
#if NCORE>1
		(long) (nr_handoffs * z_handoff) +
#endif
		mreached, nstates);
	printf("Transitions= %8.3g ", nstates+truncs);
#ifdef MA
	printf("Nodes= %7d ", nr_states);
#endif
	printf("Memory= %9.3f\t", memcnt/1048576.);
	snap_time();
	fflush(stdout);
#if NCORE>1
	leave_critical(GLOBAL_LOCK);
#endif
}
#ifdef SC
void
stack2disk(void)
{
	if (!stackwrite
	&&  (stackwrite = creat(stackfile, TMODE)) < 0)
		Uerror("cannot create stackfile");

	if (write(stackwrite, trail, DDD*sizeof(Trail))
	!=  DDD*sizeof(Trail))
		Uerror("stackfile write error -- disk is full?");

	memmove(trail, &trail[DDD], (HHH-DDD+2)*sizeof(Trail));
	memset(&trail[HHH-DDD+2], 0, (omaxdepth - HHH + DDD - 2)*sizeof(Trail));
	CNT1++;
}
void
disk2stack(void)
{	long have;

	CNT2++;
	memmove(&trail[DDD], trail, (HHH-DDD+2)*sizeof(Trail));

	if (!stackwrite
	||  lseek(stackwrite, -DDD* (off_t) sizeof(Trail), SEEK_CUR) == -1)
		Uerror("disk2stack lseek error");

	if (!stackread
	&&  (stackread = open(stackfile, 0)) < 0)
		Uerror("cannot open stackfile");

	if (lseek(stackread, (CNT1-CNT2)*DDD* (off_t) sizeof(Trail), SEEK_SET) == -1)
		Uerror("disk2stack lseek error");

	have = read(stackread, trail, DDD*sizeof(Trail));
	if (have !=  DDD*sizeof(Trail))
		Uerror("stackfile read error");
}
#endif
uchar *
Pptr(int x)
{	if (x < 0 || x >= MAXPROC || !proc_offset[x])
		return noptr;
	else
		return (uchar *) pptr(x);
}
int qs_empty(void);
/*
 * new_state() is the main DFS search routine in the verifier
 * it has a lot of code ifdef-ed together to support
 * different search modes, which makes it quite unreadable.
 * if you are studying the code, first use the C preprocessor
 * to generate a specific version from the pan.c source,
 * e.g. by saying:
 *	gcc -E -DNOREDUCE -DBITSTATE pan.c > ppan.c
 * and then study the resulting file, rather than this one
 */
#if !defined(BFS) && (!defined(BITSTATE) || !defined(MA))

#ifdef NSUCC
int N_succ[512];
void
tally_succ(int cnt)
{	if (cnt < 512) N_succ[cnt]++;
	else printf("tally_succ: cnt %d exceeds range\n", cnt);
}

void
dump_succ(void)
{	int i; double sum = 0.0;
	double w_avg = 0.0;
	printf("Successor counts:\n");
	for (i = 0; i < 512; i++)
	{	sum += (double) N_succ[i];
	}
	for (i = 0; i < 512; i++)
	{	if (N_succ[i] > 0)
		{	printf("%3d	%10d	(%.4g %% of total)\n",
				i, N_succ[i], (100.0 * (double) N_succ[i])/sum);
			w_avg += (double) i * (double) N_succ[i];
	}	}
	if (sum > N_succ[0])
	printf("mean %.4g (without 0: %.4g)\n", w_avg / sum, w_avg / (sum - (double) N_succ[0]));
}
#endif

#ifdef REVERSE
	#define FROM_P	(BASE)
	#define UPTO_P	(now._nr_pr-1)
	#define MORE_P	(II <= To)
	#define INI_P	(From-1)
	#define ALL_P	(II = From; II <= To; II++)
#else
	#define FROM_P	(now._nr_pr-1)
	#define UPTO_P	(BASE)
	#define MORE_P	(II >= BASE)
	#define INI_P	(From+1)
	#define ALL_P	(II = From; II >= To; II--)
#endif

void
new_state(void)
{	Trans *t;
	uchar _n, _m, ot;
#ifdef T_RAND
	short ooi, eoi;
#endif
#ifdef M_LOSS
	uchar delta_m = 0;
#endif
	short II, JJ = 0, kk;
	int tt;
	short From = FROM_P, To = UPTO_P;
#ifdef BCS
	trpt->sched_limit = 0; /* at depth=0 only */
#endif
Down:
#ifdef CHECK
	cpu_printf("%d: Down - %s %saccepting [pids %d-%d]\n",
		depth, (trpt->tau&4)?"claim":"program",
		(trpt->o_pm&2)?"":"non-", From, To);
#endif
#ifdef P_RAND
	trpt->p_skip = -1;
#endif
#ifdef SC
	if (depth > hiwater)
	{	stack2disk();
		maxdepth += DDD;
		hiwater += DDD;
		trpt -= DDD;
		if(verbose)
		printf("zap %d: %d (maxdepth now %d)\n",
			CNT1, hiwater, maxdepth);
	}
#endif
	trpt->tau &= ~(16|32|64); /* make sure these are off */
#if defined(FULLSTACK) && defined(MA)
	trpt->proviso = 0;
#endif
#ifdef NSUCC
	trpt->n_succ = 0;
#endif
#if NCORE>1
	if (mem_hand_off())
	{
#if SYNC
		(trpt+1)->o_n = 1;	/* not a deadlock: as below  */
#endif
#ifndef LOOPSTATE
		(trpt-1)->tau |= 16;	/* worstcase guess: as below */
#endif
#if NCORE>1 && defined(FULL_TRAIL)
		if (upto > 0)
		{	Pop_Stack_Tree();
		}
#endif
		goto Up;
	}
#endif
	if (depth >= maxdepth)
	{	if (!warned)
		{ warned = 1;
		  printf("error: max search depth too small\n");
		}
		if (bounded)
		{	uerror("depth limit reached");
		}
		truncs++;
#if SYNC
		(trpt+1)->o_n = 1; /* not a deadlock */
#endif
#ifndef LOOPSTATE
		(trpt-1)->tau |= 16;	/* worstcase guess */
#endif
#if NCORE>1 && defined(FULL_TRAIL)
		if (upto > 0)
		{	Pop_Stack_Tree();
		}
#endif
		goto Up;
	}
AllOver:
#if (defined(FULLSTACK) && !defined(MA)) || NCORE>1
	/* if atomic or rv move, carry forward previous state */
	trpt->ostate = (trpt-1)->ostate;
#endif
#ifdef VERI
	if ((trpt->tau&4) || ((trpt-1)->tau&128))
#endif
	if (boq == -1) {	/* if not mid-rv */
#ifndef SAFETY
		/* this check should now be redundant
		 * because the seed state also appears
		 * on the 1st dfs stack and would be
		 * matched in hstore below
		 */
		if ((now._a_t&1) && depth > A_depth)
		{	if (!memcmp((char *)&A_Root, 
				(char *)&now, vsize))
			{
				depthfound = A_depth;
#ifdef CHECK
			  printf("matches seed\n");
#endif
#ifdef NP
			  uerror("non-progress cycle");
#else
			  uerror("acceptance cycle");
#endif
#if NCORE>1 && defined(FULL_TRAIL)
			if (upto > 0)
			{	Pop_Stack_Tree();
			}
#endif
			  goto Up;
			}
#ifdef CHECK
			printf("not seed\n");
#endif
		}
#endif
		if (!(trpt->tau&8)) /* if no atomic move */
		{
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
			uchar was_last = now._last;
			now._last = 0;	/* value not stored */
#endif
#ifdef BITSTATE
#ifdef CNTRSTACK
		#if defined(BCS) && defined(STORE_CTX)
		{ int xj;
			for (xj = trpt->sched_limit; xj <= sched_max; xj++)
			{	now._ctx = xj;
				II = bstore((char *)&now, vsize);
				trpt->j6 = j1_spin; trpt->j7 = j2;
				JJ = LL[j1_spin] && LL[j2];
				if (II != 0) { break; }
			}
			now._ctx = 0; /* just in case */
		}
		#else
			II = bstore((char *)&now, vsize);
			trpt->j6 = j1_spin; trpt->j7 = j2;
			JJ = LL[j1_spin] && LL[j2];
		#endif
#else
		#ifdef FULLSTACK
		   #if defined(BCS) && defined(STORE_CTX)
		   { int xj;
			now._ctx = 0;
			JJ = onstack_now();
			for (xj = trpt->sched_limit; xj <= sched_max; xj++)
			{	now._ctx = xj;
				II = bstore((char *)&now, vsize);
				if (II != 0) { break; }
			}
			now._ctx = 0;
		   }
		   #else
			JJ = onstack_now();
			II = bstore((char *)&now, vsize);
		   #endif
		#else
		   #if defined(BCS) && defined(STORE_CTX)
		   { int xj;
			for (xj = trpt->sched_limit; xj <= sched_max; xj++)
			{	now._ctx = xj;
				II = bstore((char *)&now, vsize);
				JJ = II; /* worstcase guess for p.o. - order corrected in 5.2.1 */
				if (II != 0) { break; }
			}
			now._ctx = 0;
		   }
		   #else
			II = bstore((char *)&now, vsize);
			JJ = II; /* worstcase guess for p.o. - order corrected in 5.2.1 */
		   #endif
		#endif
#endif
#else
#ifdef MA
			II = gstore((char *)&now, vsize, 0);
#ifndef FULLSTACK
			JJ = II;
#else
			JJ = (II == 2)?1:0;
#endif
#else
			II = hstore((char *)&now, vsize);
#ifdef FULLSTACK
			JJ = (II == 2)?1:0;
#endif
#endif
#endif
			kk = (II == 1 || II == 2);
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
			now._last = was_last;	/* restore value */
#endif
#ifndef SAFETY
#if NCORE==1 || defined (SEP_STATE)
			if (II == 2 && ((trpt->o_pm&2) || ((trpt-1)->o_pm&2)))
		#ifndef NOFAIR
#if 0
			if (!fairness || ((now._a_t&1) && now._cnt[1] == 1)) /* 5.1.4 */
#else
			if (a_cycles && !fairness) /* 5.1.6 -- example by Hirofumi Watanabe */
#endif
		#endif
			{
				II = 3; /* Schwoon & Esparza 2005, Gastin&Moro 2004 */
#ifdef VERBOSE
				printf("state match on dfs stack\n");
#endif
				goto same_case;
			}
#endif
#if defined(FULLSTACK) && defined(BITSTATE)
			if (!JJ && (now._a_t&1) && depth > A_depth)
			{	int oj1 = j1_spin;
				uchar o_a_t = now._a_t;
				now._a_t &= ~(1|16|32);
				if (onstack_now())
				{	II = 3;
#ifdef VERBOSE
					printf("state match on 1st dfs stack\n");
#endif
				}
				now._a_t = o_a_t;
				j1_spin = oj1;
			}
#endif
			if (II == 3 && a_cycles && (now._a_t&1))
			{
#ifndef NOFAIR
			   if (fairness && now._cnt[1] > 1)	/* was != 0 */
			   {
#ifdef VERBOSE
				printf("	fairness count non-zero\n");
#endif
				II = 0;
			   } else
#endif
			   {
#ifndef BITSTATE
				nShadow--;
#endif
same_case:			if (Lstate) depthfound = Lstate->D;
#ifdef NP
				uerror("non-progress cycle");
#else
				uerror("acceptance cycle");
#endif
#if NCORE>1 && defined(FULL_TRAIL)
				if (upto > 0)
				{	Pop_Stack_Tree();
				}
#endif
				goto Up;
			   }
			}
#endif
#ifndef NOREDUCE
	#ifndef SAFETY
		#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)
			if (II != 0 && (!Lstate || Lstate->cpu_id < core_id))
			{	(trpt-1)->tau |= 16;
			}
		#endif
			if ((II && JJ) || (II == 3))
			{	/* marker for liveness proviso */
		#ifndef LOOPSTATE
				(trpt-1)->tau |= 16;
		#endif
				truncs2++;
			}
#else
		#if NCORE>1 && !defined(SEP_STATE) && defined(V_PROVISO)
			if (!(II != 0 && (!Lstate || Lstate->cpu_id < core_id)))
			{	/* treat as stack state */
				(trpt-1)->tau |= 16;
			} else
			{	/* treat as non-stack state */
				(trpt-1)->tau |= 64;
			}
		#endif
			if (!II || !JJ)
			{	/* successor outside stack */
				(trpt-1)->tau |= 64;
			}
	#endif
#endif
#if defined(BCS) && (defined(NOREDUCE) || !defined(SAFETY))
			if (!II || !JJ)
			{	(trpt-1)->tau |= 64;
			}
#endif
			if (II)
			{	truncs++;
#if NCORE>1 && defined(FULL_TRAIL)
				if (upto > 0)
				{	Pop_Stack_Tree();
					if (depth == 0)
					{	return;
				}	}
#endif
				goto Up;
			}
			if (!kk)
			{	static long sdone = (long) 0; long ndone;
				nstates++;
#if defined(ZAPH) && defined(BITSTATE)
				zstates += (double) hfns;
#endif
				ndone = (unsigned long) (nstates/((double) FREQ));
				if (ndone != sdone)
				{	snapshot();
					sdone = ndone;
#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
					if (nstates > ((double)(ONE_L<<(ssize+1))))
					{	void resize_hashtable(void);
						resize_hashtable();
					}
#endif
#if defined(ZAPH) && defined(BITSTATE)
					if (zstates > ((double)(ONE_L<<(ssize-2))))
					{	/* more than half the bits set */
						void zap_hashtable(void);
						zap_hashtable();
						zstates = 0;
					}
#endif
				}
#ifdef SVDUMP
				if (vprefix > 0)
	#ifdef SHO
			/* always use the same hashfunction, for consistency across runs */
				if (HASH_NR != 0)
				{	int oh = HASH_NR;
					HASH_NR = 0;
					d_hash((char *) &now, vsize); /* set K1 */
					HASH_NR = oh;
				}
				if (write(svfd, (uchar *) &K1, sizeof(unsigned long)) != sizeof(unsigned long))
	#else
				if (write(svfd, (uchar *) &now, vprefix) != vprefix)
	#endif
				{	fprintf(efd, "writing %s.svd failed\n", PanSource);
					wrapup();
				}
#endif
#if defined(MA) && defined(W_XPT)
				if ((unsigned long) nstates%W_XPT == 0)
				{	void w_xpoint(void);
					w_xpoint();
				}
#endif
			}
#if defined(FULLSTACK) || defined(CNTRSTACK)
			onstack_put();
#ifdef DEBUG2
#if defined(FULLSTACK) && !defined(MA)
			printf("%d: putting %u (%d)\n", depth,
				trpt->ostate, 
				(trpt->ostate)?trpt->ostate->tagged:0);
#else
			printf("%d: putting\n", depth);
#endif
#endif
#else
	#if NCORE>1
			trpt->ostate = Lstate;
	#endif
#endif
	}	}
	if (depth > mreached)
		mreached = depth;
#ifdef VERI
	if (trpt->tau&4)
#endif
	trpt->tau &= ~(1|2);	/* timeout and -request off */
	_n = 0;
#if SYNC
	(trpt+1)->o_n = 0;
#endif
#ifdef VERI
	if (now._nr_pr == 0)	/* claim terminated */
		uerror("end state in claim reached");
	check_claim(((P0 *)pptr(0))->_p);
Stutter:
	if (trpt->tau&4)	/* must make a claimmove */
	{
#ifndef NOFAIR
		if ((now._a_t&2)	/* A-bit set */
		&&   now._cnt[now._a_t&1] == 1)
		{	now._a_t &= ~2;
			now._cnt[now._a_t&1] = 0;
			trpt->o_pm |= 16;
#ifdef DEBUG
	printf("%3d: fairness Rule 3.: _a_t = %d\n",
		depth, now._a_t);
#endif
		}
#endif
		II = 0;		/* never */
		goto Veri0;
	}
#endif
#ifndef NOREDUCE
	/* Look for a process with only safe transitions */
	/* (special rules apply in the 2nd dfs) */
	if (boq == -1 && From != To

#ifdef SAFETY
 #if NCORE>1
	&& (depth < z_handoff)
 #endif
	)
#else
 #if NCORE>1
	&& ((a_cycles) || (!a_cycles && depth < z_handoff))
 #endif
 #ifdef BCS
	&& (sched_max > 0 || depth > BASE)
 #endif
	&&  (!(now._a_t&1)
	    ||	(a_cycles &&
 #ifndef BITSTATE
#ifdef MA
#ifdef VERI
		 !((trpt-1)->proviso))
#else
		!(trpt->proviso))
#endif
#else
#ifdef VERI
		 (trpt-1)->ostate &&
		!(((char *)&((trpt-1)->ostate->state))[0] & 128))
#else
		!(((char *)&(trpt->ostate->state))[0] & 128))
#endif
#endif
 #else
#ifdef VERI
		(trpt-1)->ostate &&
		(trpt-1)->ostate->proviso == 0)
#else
		trpt->ostate->proviso == 0)
#endif
 #endif
	   ))
#endif
	/* attempt Partial Order Reduction as preselect moves */
#ifdef BCS
	if (trpt->sched_limit < sched_max)
#endif
	for ALL_P
	{
Resume:	/* pick up here if preselect fails */
		this = pptr(II);
		tt = (int) ((P0 *)this)->_p;
		ot = (uchar) ((P0 *)this)->_t;
		if (trans[ot][tt]->atom & 8)
		{	t = trans[ot][tt];
			if (t->qu[0] != 0)
			{	Ccheck++;
				if (!q_cond(II, t))
					continue;
				Cholds++;
			}
			From = To = II; /* the process preselected */
#ifdef NIBIS
			t->om = 0;
#endif
			trpt->tau |= 32; /* preselect marker */
#ifdef DEBUG
#ifdef NIBIS
			printf("%3d: proc %d Pre", depth, II);
			printf("Selected (om=%d, tau=%d)\n", 
				t->om, trpt->tau);
#else
	printf("%3d: proc %d PreSelected (tau=%d)\n", 
		depth, II, trpt->tau);
#endif
#endif
			goto Again;
		}
	}
	trpt->tau &= ~32;
#endif
#if !defined(NOREDUCE) || (defined(ETIM) && !defined(VERI))
Again:
#endif
	trpt->o_pm &= ~(8|16|32|64); /* clear fairness-marks */
#ifndef NOFAIR
	if (fairness && boq == -1
#ifdef VERI
	&& (!(trpt->tau&4) && !((trpt-1)->tau&128))
#endif
	&& !(trpt->tau&8))
	{	/* A_bit = 1; Cnt = N in acc states with A_bit 0 */
		if (!(now._a_t&2))
		{	if (a_cycles && (trpt->o_pm&2))
			{	/* Accepting state */
				now._a_t |= 2;
				now._cnt[now._a_t&1] = now._nr_pr + 1;
				trpt->o_pm |= 8;
#ifdef DEBUG
	printf("%3d: fairness Rule 1: cnt=%d, _a_t=%d\n",
			depth, now._cnt[now._a_t&1], now._a_t);
#endif
			}
		} else
		{	/* A_bit = 0 when Cnt 0 */
			if (now._cnt[now._a_t&1] == 1)
			{	now._a_t &= ~2;
				now._cnt[now._a_t&1] = 0;
				trpt->o_pm |= 16;
#ifdef DEBUG
	printf("%3d: fairness Rule 3: _a_t = %d\n",
		depth, now._a_t);
#endif
	}	}	}
#endif
#ifdef BCS
	trpt->bcs = trpt->b_pno = 0;	/* initial */
	if (From != To		/* not a PO or atomic move */
	&&  depth > BASE)	/* there is a prior move */
	{	trpt->b_pno = now._last + BASE;
		trpt->bcs = B_PHASE1;
	#ifdef VERBOSE
		printf("%3d: BCS phase 1 proc %d limit %d\n",
			depth, trpt->b_pno, trpt->sched_limit);
	#endif
		/* allow only process b_pno to move in this phase */
	}
c_switch:	/* jumps here with bcs == B_PHASE2 with or wo B_FORCED added */
	#ifdef VERBOSE
		printf("%3d: BCS c_switch phase=%d pno=%d [forced %d]\n",
			depth, trpt->bcs, trpt->b_pno, (trpt->bcs&B_FORCED)?1:0);
	#endif
#endif
#ifdef P_RAND
	#ifdef REVERSE
	trpt->p_left = 1 + (To - From);
	#else
	trpt->p_left = 1 + (From - To);
	#endif
	if (trpt->p_left > 1)
	{	trpt->p_skip = rand() % (trpt->p_left);
	} else
	{	trpt->p_skip = -1;
	}
r_switch:
	#ifdef VERBOSE
		printf("%3d: P_RAND r_switch p_skip=%d p_left=%d\n",
			depth, trpt->p_skip, trpt->p_left);
	#endif
#endif
	/* Main Expansion Loop over Processes */
	for ALL_P
	{
#ifdef P_RAND
		if (trpt->p_skip >= 0)
		{	trpt->p_skip--; /* skip random nr of procs */
	#ifdef VERBOSE
		printf("%3d: P_RAND skipping %d [new p_skip=%d p_left=%d]\n",
			depth, II, trpt->p_skip, trpt->p_left);
	#endif
			continue;
		}
		if (trpt->p_left == 0)
		{
	#ifdef VERBOSE
		printf("%3d: P_RAND done at %d\n", depth, II);
	#endif
			break;	/* done */
		}
	#ifdef VERBOSE
		printf("%3d: P_RAND explore %d [p_left=%d]\n",
			depth, II, trpt->p_left);
	#endif
		trpt->p_left--;
#endif
#if SYNC
		/* no rendezvous with same proc */
		if (boq != -1 && trpt->pr == II) continue;
#endif
#ifdef BCS
		if ((trpt->bcs & B_PHASE1)
		&&  trpt->b_pno != II)
		{
	#ifdef VERBOSE
		printf("%3d: BCS NotPre II=%d bcs=%d pno=%d [forced %d]\n",
			depth, II, trpt->bcs, trpt->b_pno, (trpt->bcs&B_FORCED)?1:0);
	#endif
			continue;
		}
	#ifdef VERBOSE
		else if ((trpt->bcs & B_PHASE1) && trpt->b_pno == II)
		printf("%3d: BCS IsPre II=%d bcs=%d pno=%d [forced %d]\n",
			depth, II, trpt->bcs, trpt->b_pno, (trpt->bcs&B_FORCED)?1:0);
	#endif
		if (trpt->bcs & B_PHASE2)	/* 2nd phase */
		{	if (trpt->b_pno == II)	/* was already done in phase 1 */
			{
	#ifdef VERBOSE
		printf("%3d: BCS NoRepeat II=%d bcs=%d pno=%d [forced %d]\n",
			depth, II, trpt->bcs, trpt->b_pno, (trpt->bcs&B_FORCED)?1:0);
	#endif
				continue;
			}
			if (!(trpt->bcs & B_FORCED)	/* unless forced */
			&&  trpt->sched_limit >= sched_max)
			{
	#ifdef VERBOSE
		printf("%3d: BCS Bound II=%d bcs=%d pno=%d [forced %d]\n",
			depth, II, trpt->bcs, trpt->b_pno, (trpt->bcs&B_FORCED)?1:0);
	#endif
				continue;	/* enforce bound */
		}	}
#endif
#ifdef VERI
Veri0:
#endif
		this = pptr(II);
		tt = (int) ((P0 *)this)->_p;
		ot = (uchar) ((P0 *)this)->_t;
#ifdef NIBIS
		/* don't repeat a previous preselected expansion */
		/* could hit this if reduction proviso was false */
		t = trans[ot][tt];
		if (!(trpt->tau&4)
		&& !(trpt->tau&1)
		&& !(trpt->tau&32)
		&& (t->atom & 8)
		&& boq == -1
		&& From != To)
		{	if (t->qu[0] == 0
			||  q_cond(II, t))
			{	_m = t->om;
				if (_m>_n||(_n>3&&_m!=0)) _n=_m;
				continue; /* did it before */
		}	}
#endif
		trpt->o_pm &=  ~1; /* no move in this pid yet */
#ifdef EVENT_TRACE
		(trpt+1)->o_event = now._event;
#endif
		/* Fairness: Cnt++ when Cnt == II */
#ifndef NOFAIR
		trpt->o_pm &= ~64; /* didn't apply rule 2 */
		if (fairness
		&& boq == -1
		&& !(trpt->o_pm&32)
		&& (now._a_t&2)
		&&  now._cnt[now._a_t&1] == II+2)
		{	now._cnt[now._a_t&1] -= 1;
#ifdef VERI
			/* claim need not participate */
			if (II == 1)
				now._cnt[now._a_t&1] = 1;
#endif
#ifdef DEBUG
		printf("%3d: proc %d fairness ", depth, II);
		printf("Rule 2: --cnt to %d (%d)\n",
			now._cnt[now._a_t&1], now._a_t);
#endif
			trpt->o_pm |= (32|64);
		}
#endif
#ifdef HAS_PROVIDED
		if (!provided(II, ot, tt, t)) continue;
#endif
		/* check all trans of proc II - escapes first */
#ifdef HAS_UNLESS
		trpt->e_state = 0;
#endif
		(trpt+1)->pr = (uchar) II;
		(trpt+1)->st = tt;
#ifdef T_RAND
		for (ooi = eoi = 0, t = trans[ot][tt]; t; t = t->nxt, ooi++)
		{	if (strcmp(t->tp, "else") == 0)
			{	eoi++;
		}	}
		if (eoi > 0)
		{	t = trans[ot][tt];
	#ifdef VERBOSE
			printf("randomizer: suppressed, saw else\n");
	#endif
		} else
		{	eoi = rand()%ooi;
	#ifdef VERBOSE
			printf("randomizer: skip %d in %d\n", eoi, ooi);
	#endif
			for (t = trans[ot][tt]; t; t = t->nxt)
				if (eoi-- <= 0) break;
		}
domore:
		for ( ; t && ooi > 0; t = t->nxt, ooi--)
#else
		for (t = trans[ot][tt]; t; t = t->nxt)
#endif
		{
#ifdef HAS_UNLESS
			/* exploring all transitions from
			 * a single escape state suffices
			 */
			if (trpt->e_state > 0
			&&  trpt->e_state != t->e_trans)
			{
#ifdef DEBUG
		printf("skip 2nd escape %d (did %d before)\n",
			t->e_trans, trpt->e_state);
#endif
				break;
			}
#endif
			(trpt+1)->o_t = t;
#ifdef INLINE
#include FORWARD_MOVES
P999:			/* jumps here when move succeeds */
#else
			if (!(_m = do_transit(t, II))) continue;
#endif
#ifdef BCS
			if (depth > BASE
			&& II >= BASE
			&& From != To
			&& boq == -1
			&& (trpt->bcs & B_PHASE2)
			&&  trpt->b_pno != II	/* context switch */
			&& !(trpt->bcs & B_FORCED))	/* unless forced */
			{	(trpt+1)->sched_limit = 1 + trpt->sched_limit;
	#ifdef VERBOSE
		printf("%3d: up sched count to %d\n", depth, (trpt+1)->sched_limit);
	#endif
			} else
			{	(trpt+1)->sched_limit = trpt->sched_limit;
	#ifdef VERBOSE
		printf("%3d: keep sched count at %d\n", depth, (trpt+1)->sched_limit);
	#endif
			}
#endif
			if (boq == -1)
#ifdef CTL
	/* for branching-time, can accept reduction only if */
	/* the persistent set contains just 1 transition */
			{	if ((trpt->tau&32) && (trpt->o_pm&1))
					trpt->tau |= 16;
				trpt->o_pm |= 1; /* we moved */
			}
#else
				trpt->o_pm |= 1; /* we moved */
#endif
#ifdef LOOPSTATE
			if (loopstate[ot][tt])
			{
#ifdef VERBOSE
				printf("exiting from loopstate:\n");
#endif
				trpt->tau |= 16;
				cnt_loops++;
			}
#endif
#ifdef PEG
			peg[t->forw]++;
#endif
#if defined(VERBOSE) || defined(CHECK)
#if defined(SVDUMP)
	cpu_printf("%3d: proc %d exec %d \n", depth, II, t->t_id);
#else
	cpu_printf("%3d: proc %d exec %d, %d to %d, %s %s %s %saccepting [tau=%d]\n", 
				depth, II, t->forw, tt, t->st, t->tp,
				(t->atom&2)?"atomic":"",
				(boq != -1)?"rendez-vous":"",
				(trpt->o_pm&2)?"":"non-", trpt->tau);
#ifdef HAS_UNLESS
			if (t->e_trans)
			cpu_printf("\t(escape to state %d)\n", t->st);
#endif
#endif
#ifdef T_RAND
			cpu_printf("\t(randomizer %d)\n", ooi);
#endif
#endif
#ifdef HAS_LAST
#ifdef VERI
			if (II != 0)
#endif
				now._last = II - BASE;
#endif
#ifdef HAS_UNLESS
			trpt->e_state = t->e_trans;
#endif
			depth++; trpt++;
			trpt->pr = (uchar) II;
			trpt->st = tt;
			trpt->o_pm &= ~(2|4);
			if (t->st > 0)
			{	((P0 *)this)->_p = t->st;
/*	moved down		reached[ot][t->st] = 1; */
			}
#ifndef SAFETY
			if (a_cycles)
			{
#if (ACCEPT_LAB>0 && !defined(NP)) || (PROG_LAB>0 && defined(HAS_NP))
				int ii;
#endif
#define P__Q	((P0 *)pptr(ii))
#if ACCEPT_LAB>0
#ifdef NP
				/* state 1 of np_ claim is accepting */
				if (((P0 *)pptr(0))->_p == 1)
					trpt->o_pm |= 2;
#else
				for (ii = 0; ii < (int) now._nr_pr; ii++)
				{ if (accpstate[P__Q->_t][P__Q->_p])
				  {	trpt->o_pm |= 2;
					break;
			   	} }
#endif
#endif
#if defined(HAS_NP) && PROG_LAB>0
				for (ii = 0; ii < (int) now._nr_pr; ii++)
				{ if (progstate[P__Q->_t][P__Q->_p])
				  {	trpt->o_pm |= 4;
					break;
			   	} }
#endif
#undef P__Q
			}
#endif
			trpt->o_t  =  t; trpt->o_n  = _n;
			trpt->o_ot = ot; trpt->o_tt = tt;
			trpt->o_To = To; trpt->o_m  = _m;
			trpt->tau = 0;
#ifdef T_RAND
			trpt->oo_i = ooi;
#endif
			if (boq != -1 || (t->atom&2))
			{	trpt->tau |= 8;
#ifdef VERI
				/* atomic sequence in claim */
				if((trpt-1)->tau&4)
					trpt->tau |= 4;
				else
					trpt->tau &= ~4;
			} else
			{	if ((trpt-1)->tau&4)
					trpt->tau &= ~4;
				else
					trpt->tau |= 4;
			}
			/* if claim allowed timeout, so */
			/* does the next program-step: */
			if (((trpt-1)->tau&1) && !(trpt->tau&4))
				trpt->tau |= 1;
#else
			} else
				trpt->tau &= ~8;
#endif
			if (boq == -1 && (t->atom&2))
			{	From = To = II; nlinks++;
			} else
			{	From = FROM_P; To = UPTO_P;
			}
#if NCORE>1 && defined(FULL_TRAIL)
			if (upto > 0)
			{	Push_Stack_Tree(II, t->t_id);
			}
#endif
			goto Down;	/* pseudo-recursion */
Up:
#ifdef CHECK
			cpu_printf("%d: Up - %s\n", depth,
				(trpt->tau&4)?"claim":"program");
#endif
#if NCORE>1
			iam_alive();
	#ifdef USE_DISK
			mem_drain();
	#endif
#endif
#if defined(MA) || NCORE>1
			if (depth <= 0) return;
			/* e.g., if first state is old, after a restart */
#endif
#ifdef SC
			if (CNT1 > CNT2
			&& depth < hiwater - (HHH-DDD) - 2)
			{
 				trpt += DDD;
				disk2stack();
				maxdepth -= DDD;
				hiwater -= DDD;
				if(verbose)
				printf("unzap %d: %d\n", CNT2, hiwater);
			}
#endif
#ifndef NOFAIR
			if (trpt->o_pm&128)	/* fairness alg */
			{	now._cnt[now._a_t&1] = trpt->bup.oval;
				_n = 1; trpt->o_pm &= ~128;
				depth--; trpt--;
#if defined(VERBOSE) || defined(CHECK)
	printf("%3d: reversed fairness default move\n", depth);
#endif
				goto Q999;
			}
#endif
#ifdef HAS_LAST
#ifdef VERI
			{ int d; Trail *trl;
			  now._last = 0;
			  for (d = 1; d < depth; d++)
			  {	trl = getframe(depth-d); /* was (trpt-d) */
				if (trl->pr != 0)
				{ now._last = trl->pr - BASE;
				  break;
			} }	}
#else
			now._last = (depth<1)?0:(trpt-1)->pr;
#endif
#endif
#ifdef EVENT_TRACE
			now._event = trpt->o_event;
#endif
#ifndef SAFETY
			if ((now._a_t&1) && depth <= A_depth)
				return;	/* to checkcycles() */
#endif
			t  = trpt->o_t;  _n = trpt->o_n;
			ot = trpt->o_ot; II = trpt->pr;
			tt = trpt->o_tt; this = pptr(II);
			To = trpt->o_To; _m  = trpt->o_m;
#ifdef T_RAND
			ooi = trpt->oo_i;
#endif
#ifdef INLINE_REV
			_m = do_reverse(t, II, _m);
#else
#include REVERSE_MOVES
R999:			/* jumps here when done */
#endif
#ifdef VERBOSE
			cpu_printf("%3d: proc %d reverses %d, %d to %d\n",
				depth, II, t->forw, tt, t->st);
			cpu_printf("\t%s [abit=%d,adepth=%d,tau=%d,%d]\n", 
				t->tp, now._a_t, A_depth, trpt->tau, (trpt-1)->tau);
#endif
#ifndef NOREDUCE
			/* pass the proviso tags */
			if ((trpt->tau&8)	/* rv or atomic */
			&&  (trpt->tau&16))
				(trpt-1)->tau |= 16;
	#ifdef SAFETY
			if ((trpt->tau&8)	/* rv or atomic */
			&&  (trpt->tau&64))
				(trpt-1)->tau |= 64;
	#endif
#endif
#if defined(BCS) && (defined(NOREDUCE) || !defined(SAFETY))
			if ((trpt->tau&8)
			&&  (trpt->tau&64))
				(trpt-1)->tau |= 64;
#endif
			depth--; trpt--;

#ifdef NSUCC
			trpt->n_succ++;
#endif
#ifdef NIBIS
			(trans[ot][tt])->om = _m; /* head of list */
#endif
			/* i.e., not set if rv fails */
			if (_m)
			{
#if defined(VERI) && !defined(NP)
				if (II == 0 && verbose && !reached[ot][t->st])
				{
			printf("depth %d: Claim reached state %d (line %d)\n",
					(int) depth, t->st, src_claim [t->st]);
					fflush(stdout);
				}
#endif
				reached[ot][t->st] = 1;
				reached[ot][tt] = 1;
			}
#ifdef HAS_UNLESS
			else trpt->e_state = 0; /* undo */
#endif
			if (_m>_n||(_n>3&&_m!=0)) _n=_m;
			((P0 *)this)->_p = tt;
		} /* all options */
#ifdef T_RAND
		if (!t && ooi > 0)
		{	t = trans[ot][tt];
	#ifdef VERBOSE
			printf("randomizer: continue for %d more\n", ooi);
	#endif
			goto domore;
		}
	#ifdef VERBOSE
		  else
			printf("randomizer: done\n");
	#endif
#endif
#ifndef NOFAIR
		/* Fairness: undo Rule 2 */
		if ((trpt->o_pm&32)
		&&  (trpt->o_pm&64))
		{	if (trpt->o_pm&1)
			{
#ifdef VERI
				if (now._cnt[now._a_t&1] == 1)
					now._cnt[now._a_t&1] = 2;
#endif
				now._cnt[now._a_t&1] += 1;
#ifdef VERBOSE
		printf("%3d: proc %d fairness ", depth, II);
		printf("undo Rule 2, cnt=%d, _a_t=%d\n",
			now._cnt[now._a_t&1], now._a_t);
#endif
				trpt->o_pm &= ~(32|64);
			} else
			{	if (_n > 0)
				{
					trpt->o_pm &= ~64;
					II = INI_P;
		}	}	}
#endif
#ifdef VERI
		if (II == 0) break;	/* never claim */
#endif
	} /* all processes */
#ifdef NSUCC
	tally_succ(trpt->n_succ);
#endif
#ifdef P_RAND
	if (trpt->p_left > 0)
	{	trpt->p_skip = -1; /* probably rendundant */
	#ifdef VERBOSE
		printf("%3d: P_RAND -- explore remainder\n", depth);
	#endif
		goto r_switch; /* explore the remaining procs */
	} else
	{
	#ifdef VERBOSE
		printf("%3d: P_RAND -- none left\n", depth);
	#endif
	}
#endif
#ifdef BCS
	if (trpt->bcs & B_PHASE1)
	{	trpt->bcs = B_PHASE2;	/* start 2nd phase */
		if (_n == 0 || !(trpt->tau&64))	/* pre-move unexecutable or led to stackstate */
		{	trpt->bcs |= B_FORCED; /* forced switch */
		}
	#ifdef VERBOSE
		printf("%3d: BCS move to phase 2, _n=%d %s\n", depth, _n,
			(trpt->bcs & B_FORCED)?"forced":"free");
	#endif
		From = FROM_P; To = UPTO_P;
		goto c_switch;
	}

	if (_n == 0	/* no process could move */
	&&  II >= BASE	/* not the never claim */
	&&  trpt->sched_limit >= sched_max)
	{	_n = 1;
	#ifdef VERBOSE
		printf("%3d: BCS not a deadlock\n", depth);
	#endif
	}
#endif
#ifndef NOFAIR
	/* Fairness: undo Rule 2 */
	if (trpt->o_pm&32)	/* remains if proc blocked */
	{
#ifdef VERI
		if (now._cnt[now._a_t&1] == 1)
			now._cnt[now._a_t&1] = 2;
#endif
		now._cnt[now._a_t&1] += 1;
#ifdef VERBOSE
		printf("%3d: proc -- fairness ", depth);
		printf("undo Rule 2, cnt=%d, _a_t=%d\n",
			now._cnt[now._a_t&1], now._a_t);
#endif
		trpt->o_pm &= ~32;
	}
#ifndef NP
	if (fairness
	&&  _n == 0		/* nobody moved */
#ifdef VERI
	&& !(trpt->tau&4)	/* in program move */
#endif
	&& !(trpt->tau&8)	/* not an atomic one */
#ifdef OTIM
	&& ((trpt->tau&1) || endstate())
#else
#ifdef ETIM
	&&  (trpt->tau&1)	/* already tried timeout */
#endif
#endif
#ifndef NOREDUCE
	/* see below  */
	&& !((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
#endif
	&& now._cnt[now._a_t&1] > 0)	/* needed more procs */
	{	depth++; trpt++;
		trpt->o_pm |= 128 | ((trpt-1)->o_pm&(2|4));
		trpt->bup.oval = now._cnt[now._a_t&1];
		now._cnt[now._a_t&1] = 1;
#ifdef VERI
		trpt->tau = 4;
#else
		trpt->tau = 0;
#endif
		From = FROM_P; To = UPTO_P;
#if defined(VERBOSE) || defined(CHECK)
		printf("%3d: fairness default move ", depth);
		printf("(all procs block)\n");
#endif
		goto Down;
	}
#endif
Q999:	/* returns here with _n>0 when done */;
	if (trpt->o_pm&8)
	{	now._a_t &= ~2;
		now._cnt[now._a_t&1] = 0;
		trpt->o_pm &= ~8;
#ifdef VERBOSE
		printf("%3d: fairness undo Rule 1, _a_t=%d\n",
			depth, now._a_t);
#endif
	}
	if (trpt->o_pm&16)
	{	now._a_t |= 2;
		now._cnt[now._a_t&1] = 1;
		trpt->o_pm &= ~16;
#ifdef VERBOSE
		printf("%3d: fairness undo Rule 3, _a_t=%d\n",
			depth, now._a_t);
#endif
	}
#endif
#ifndef NOREDUCE
#ifdef SAFETY
#ifdef LOOPSTATE
	/* at least one move that was preselected at this */
	/* level, blocked or was a loop control flow point */
	if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
#else
	/* preselected move - no successors outside stack */
	if ((trpt->tau&32) && !(trpt->tau&64))
#endif
	{	From = FROM_P; To = UPTO_P;
#ifdef DEBUG
		printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", 
			depth, II+1, _n, trpt->tau);
#endif
		_n = 0; trpt->tau &= ~(16|32|64);
		if (MORE_P)	/* II already decremented */
			goto Resume;
		else
			goto Again;
	}
#else
	/* at least one move that was preselected at this */
	/* level, blocked or truncated at the next level  */
	if ((trpt->tau&32) && (_n == 0 || (trpt->tau&16)))
	{
#ifdef DEBUG
		printf("%3d: proc %d UnSelected (_n=%d, tau=%d)\n", 
		depth, II+1, (int) _n, trpt->tau);
#endif
		if (a_cycles && (trpt->tau&16))
		{	if (!(now._a_t&1))
			{
#ifdef DEBUG
			printf("%3d: setting proviso bit\n", depth);
#endif
#ifndef BITSTATE
#ifdef MA
#ifdef VERI
			(trpt-1)->proviso = 1;
#else
			trpt->proviso = 1;
#endif
#else
#ifdef VERI
			if ((trpt-1)->ostate)
			((char *)&((trpt-1)->ostate->state))[0] |= 128;
#else
			((char *)&(trpt->ostate->state))[0] |= 128;
#endif
#endif
#else
#ifdef VERI
			if ((trpt-1)->ostate)
			(trpt-1)->ostate->proviso = 1;
#else
			trpt->ostate->proviso = 1;
#endif
#endif
				From = FROM_P; To = UPTO_P;
				_n = 0; trpt->tau &= ~(16|32|64);
				goto Again; /* do full search */
			} /* else accept reduction */
		} else
		{	From = FROM_P; To = UPTO_P;
			_n = 0; trpt->tau &= ~(16|32|64);
			if (MORE_P)	/* II already decremented */
				goto Resume;
			else
				goto Again;
	}	}
#endif
#endif
	if (_n == 0 || ((trpt->tau&4) && (trpt->tau&2)))
	{
#ifdef DEBUG
		cpu_printf("%3d: no move [II=%d, tau=%d, boq=%d]\n",
			depth, II, trpt->tau, boq);
#endif
#if SYNC
		/* ok if a rendez-vous fails: */
		if (boq != -1) goto Done;
#endif
		/* ok if no procs or we're at maxdepth */
		if ((now._nr_pr == 0 && (!strict || qs_empty()))
#ifdef OTIM
		||  endstate()
#endif
		||  depth >= maxdepth-1) goto Done;	/* undo change from 5.2.3 */
		if ((trpt->tau&8) && !(trpt->tau&4))
		{	trpt->tau &= ~(1|8);
			/* 1=timeout, 8=atomic */
			From = FROM_P; To = UPTO_P;
#ifdef DEBUG
		cpu_printf("%3d: atomic step proc %d unexecutable\n", depth, II+1);
#endif
#ifdef VERI
			trpt->tau |= 4;	/* switch to claim */
#endif
			goto AllOver;
		}
#ifdef ETIM
		if (!(trpt->tau&1)) /* didn't try timeout yet */
		{
#ifdef VERI
			if (trpt->tau&4)
			{
#ifndef NTIM
				if (trpt->tau&2) /* requested */
#endif
				{	trpt->tau |=  1;
					trpt->tau &= ~2;
#ifdef DEBUG
				cpu_printf("%d: timeout\n", depth);
#endif
					goto Stutter;
			}	}
			else
			{	/* only claim can enable timeout */
				if ((trpt->tau&8)
				&&  !((trpt-1)->tau&4))
/* blocks inside an atomic */		goto BreakOut;
#ifdef DEBUG
				cpu_printf("%d: req timeout\n",
					depth);
#endif
				(trpt-1)->tau |= 2; /* request */
#if NCORE>1 && defined(FULL_TRAIL)
				if (upto > 0)
				{	Pop_Stack_Tree();
				}
#endif
				goto Up;
			}
#else
#ifdef DEBUG
			cpu_printf("%d: timeout\n", depth);
#endif
			trpt->tau |=  1;
			goto Again;
#endif
		}
#endif
#ifdef VERI
BreakOut:
#ifndef NOSTUTTER
		if (!(trpt->tau&4))
		{	trpt->tau |= 4;   /* claim stuttering */
			trpt->tau |= 128; /* stutter mark */
#ifdef DEBUG
			cpu_printf("%d: claim stutter\n", depth);
#endif
			goto Stutter;
		}
#else
		;
#endif
#else
		if (!noends && !a_cycles && !endstate())
		{	depth--; trpt--;	/* new 4.2.3 */
			uerror("invalid end state");
			depth++; trpt++;
		}
#ifndef NOSTUTTER
		else if (a_cycles && (trpt->o_pm&2)) /* new 4.2.4 */
		{	depth--; trpt--;
			uerror("accept stutter");
			depth++; trpt++;
		}
#endif
#endif
	}
Done:
	if (!(trpt->tau&8))	/* not in atomic seqs */
	{
#ifndef SAFETY
		if (_n != 0
#ifdef VERI
		/* --after-- a program-step, i.e., */
		/* after backtracking a claim-step */
		&& (trpt->tau&4)
		/* with at least one running process */
		/* unless in a stuttered accept state */
		&& ((now._nr_pr > 1) || (trpt->o_pm&2))
#endif
		&& !(now._a_t&1))
		{
#ifndef NOFAIR
			if (fairness)
			{
#ifdef VERBOSE
			cpu_printf("Consider check %d %d...\n",
				now._a_t, now._cnt[0]);
#endif
				if ((now._a_t&2) /* A-bit */
				&&  (now._cnt[0] == 1))
					checkcycles();
			} else
#endif
			if (a_cycles && (trpt->o_pm&2))
				checkcycles();
		}
#endif
#ifndef MA
#if defined(FULLSTACK) || defined(CNTRSTACK)
#ifdef VERI
		if (boq == -1
		&&  (((trpt->tau&4) && !(trpt->tau&128))
		||  ( (trpt-1)->tau&128)))
#else
		if (boq == -1)
#endif
		{
#ifdef DEBUG2
#if defined(FULLSTACK)
			printf("%d: zapping %u (%d)\n",
				depth, trpt->ostate,
			(trpt->ostate)?trpt->ostate->tagged:0);
#endif
#endif
			onstack_zap();
		}
#endif
#else
#ifdef VERI
		if (boq == -1
		&&  (((trpt->tau&4) && !(trpt->tau&128))
		||  ( (trpt-1)->tau&128)))
#else
		if (boq == -1)
#endif
		{
#ifdef DEBUG
			printf("%d: zapping\n", depth);
#endif
			onstack_zap();
#ifndef NOREDUCE
			if (trpt->proviso)
			gstore((char *) &now, vsize, 1);
#endif
		}
#endif
	}
	if (depth > 0)
	{
#if NCORE>1 && defined(FULL_TRAIL)
		if (upto > 0)
		{	Pop_Stack_Tree();
		}
#endif
		goto Up;
	}
}

#else
void new_state(void) { /* place holder */ }
#endif

void
assert(int a, char *s, int ii, int tt, Trans *t)
{
	if (!a && !noasserts)
	{	char bad[1024];
		strcpy(bad, "assertion violated ");
		if (strlen(s) > 1000)
		{	strncpy(&bad[19], (const char *) s, 1000);
			bad[1019] = '\0';
		} else
			strcpy(&bad[19], s);
		uerror(bad);
	}
}
#ifndef NOBOUNDCHECK
int
Boundcheck(int x, int y, int a1, int a2, Trans *a3)
{
	assert((x >= 0 && x < y), "- invalid array index",
		a1, a2, a3);
	return x;
}
#endif
void
wrap_stats(void)
{
	if (nShadow>0)
	  printf("%9.8g states, stored (%g visited)\n",
			nstates - nShadow, nstates);
	else
	  printf("%9.8g states, stored\n", nstates);
#ifdef BFS
#if SYNC
	printf("	%8g nominal states (- rv and atomic)\n", nstates-midrv-nlinks+revrv);
	printf("	%8g rvs succeeded\n", midrv-failedrv);
#else
	printf("	%8g nominal states (stored-atomic)\n", nstates-nlinks);
#endif
#ifdef DEBUG
	printf("	%8g midrv\n", midrv);
	printf("	%8g failedrv\n", failedrv);
	printf("	%8g revrv\n", revrv);
#endif
#endif
	printf("%9.8g states, matched\n", truncs);
#ifdef CHECK
	printf("%9.8g matches within stack\n",truncs2);
#endif
	if (nShadow>0)
	printf("%9.8g transitions (= visited+matched)\n",
		nstates+truncs);
	else
	printf("%9.8g transitions (= stored+matched)\n",
		nstates+truncs);
	printf("%9.8g atomic steps\n", nlinks);
	if (nlost) printf("%g lost messages\n", (double) nlost);

#ifndef BITSTATE
	#ifndef MA
	printf("hash conflicts: %9.8g (resolved)\n", hcmp);
	#ifndef AUTO_RESIZE
	if (hcmp > (double) (1<<ssize))
	{	printf("hint: increase hashtable-size (-w) to reduce runtime\n");
	}	/* in multi-core: also reduces lock delays on access to hashtable */
	#endif
	#endif
#else
#ifdef CHECK
	printf("%8g states allocated for dfs stack\n", ngrabs);
#endif
	if (udmem)
	printf("\nhash factor: %4g (best if > 100.)\n\n",
		(double)(((double) udmem) * 8.0) / (double) nstates);
	else
	printf("\nhash factor: %4g (best if > 100.)\n\n",
		(double)(1<<(ssize-8)) / (double) nstates * 256.0);
       printf("bits set per state: %u (-k%u)\n", hfns, hfns);
	#if 0
	if (udmem)
	{	printf("total bits available: %8g (-M%ld)\n",
		((double) udmem) * 8.0, udmem/(1024L*1024L));
	} else
	printf("total bits available: %8g (-w%d)\n",
		((double) (ONE_L << (ssize-4)) * 16.0), ssize);
	#endif
#endif
#ifdef BFS_DISK
	printf("bfs disk reads: %ld writes %ld -- diff %ld\n",
		bfs_dsk_reads, bfs_dsk_writes, bfs_dsk_writes-bfs_dsk_reads);
	if (bfs_dsk_read  >= 0) (void) close(bfs_dsk_read);
	if (bfs_dsk_write >= 0) (void) close(bfs_dsk_write);
	(void) unlink("pan_bfs_dsk.tmp");
#endif
}

void
wrapup(void)
{
#if 1
	double nr1, nr2, nr3 = 0.0, nr4, nr5 = 0.0;
	#if !defined(MA) && (defined(MEMCNT) || defined(MEMLIM))
		int mverbose = 1;
	#else
		int mverbose = verbose;
	#endif
#endif
#if NCORE>1
	if (verbose) cpu_printf("wrapup -- %d error(s)\n", errors);
	if (core_id != 0)
	{
#ifdef USE_DISK
		void	dsk_stats(void);
		dsk_stats();
#endif
		if (search_terminated != NULL)
		{	*search_terminated |= 2;	/* wrapup */
		}
		exit(0); /* normal termination, not an error */
	}
#endif
#if !defined(WIN32) && !defined(WIN64)
	signal(SIGINT, SIG_DFL);
#endif
	printf("\n(%s)\n", SpinVersion);
	if (!done) printf("Warning: Search not completed\n");
#ifdef SC
	(void) unlink((const char *)stackfile);
#endif
#if NCORE>1
	if (a_cycles)
	{	printf("	+ Multi-Core (NCORE=%d)\n", NCORE);
	} else
	{	printf("	+ Multi-Core (NCORE=%d -z%d)\n", NCORE, z_handoff);
	}
#endif
#ifdef BFS
	printf("	+ Using Breadth-First Search\n");
#endif
#ifndef NOREDUCE
	printf("	+ Partial Order Reduction\n");
#endif
#ifdef REVERSE
	printf("	+ Reverse Depth-First Search Order\n");
#endif
#ifdef T_REVERSE
	printf("	+ Reverse Transition Ordering\n");
#endif
#ifdef T_RAND
	printf("	+ Randomized Transition Ordering\n");
#endif
#ifdef P_RAND
	printf("	+ Randomized Process Ordering\n");
#endif
#ifdef BCS
	printf("	+ Scheduling Restriction (-L%d)\n", sched_max);
#endif
#ifdef COLLAPSE
	printf("	+ Compression\n");
#endif
#ifdef MA
	printf("	+ Graph Encoding (-DMA=%d)\n", MA);
  #ifdef R_XPT
	printf("	  Restarted from checkpoint %s.xpt\n", PanSource);
  #endif
#endif
#ifdef CHECK
  #ifdef FULLSTACK
	printf("	+ FullStack Matching\n");
  #endif
  #ifdef CNTRSTACK
	printf("	+ CntrStack Matching\n");
  #endif
#endif
#ifdef BITSTATE
	printf("\nBit statespace search for:\n");
#else
#ifdef HC
	printf("\nHash-Compact %d search for:\n", HC);
#else
	printf("\nFull statespace search for:\n");
#endif
#endif
#ifdef EVENT_TRACE
#ifdef NEGATED_TRACE
	printf("	notrace assertion  	+\n");
#else
	printf("	trace assertion    	+\n");
#endif
#endif
#ifdef VERI
	printf("	never claim         	+\n");
	printf("	assertion violations	");
	if (noasserts)
		printf("- (disabled by -A flag)\n");
	else
		printf("+ (if within scope of claim)\n");
#else
#ifdef NOCLAIM
	printf("	never claim         	- (not selected)\n");
#else
	printf("	never claim         	- (none specified)\n");
#endif
	printf("	assertion violations	");
	if (noasserts)
		printf("- (disabled by -A flag)\n");
	else
		printf("+\n");
#endif
#ifndef SAFETY
#ifdef NP
	printf("	non-progress cycles 	");
#else
	printf("	acceptance   cycles 	");
#endif
	if (a_cycles)
		printf("+ (fairness %sabled)\n",
			fairness?"en":"dis");
	else printf("- (not selected)\n");
#else
	printf("	cycle checks       	- (disabled by -DSAFETY)\n");
#endif
#ifdef VERI
	printf("	invalid end states	- ");
	printf("(disabled by ");
	if (noends)
		printf("-E flag)\n\n");
	else
		printf("never claim)\n\n");
#else
	printf("	invalid end states	");
	if (noends)
		printf("- (disabled by -E flag)\n\n");
	else
		printf("+\n\n");
#endif
	printf("State-vector %d byte, depth reached %ld", hmax,
#if NCORE>1
					(nr_handoffs * z_handoff) +
#endif
					mreached);
	printf(", errors: %d\n", errors);
	fflush(stdout);
#ifdef MA
	if (done)
	{	extern void dfa_stats(void);
		if (maxgs+a_cycles+2 < MA)
		printf("MA stats: -DMA=%d is sufficient\n",
			maxgs+a_cycles+2);
		dfa_stats();
	}
#endif
	wrap_stats();
#ifdef CHECK
	printf("stackframes: %d/%d\n\n", smax, svmax);
	printf("stats: fa %d, fh %d, zh %d, zn %d - ",
		Fa, Fh, Zh, Zn);
	printf("check %d holds %d\n", Ccheck, Cholds);
	printf("stack stats: puts %d, probes %d, zaps %d\n",
		PUT, PROBE, ZAPS);
#else
	printf("\n");
#endif

#if !defined(BITSTATE) && defined(NOCOMP)
	if (!verbose) { goto jump_here; }
#endif

#if 1
	nr1 = (nstates-nShadow)*
	      (double)(hmax+sizeof(struct H_el)-sizeof(unsigned));
	#ifdef BFS
	nr2 = 0.0;
	#else
	nr2 = (double) ((maxdepth+3)*sizeof(Trail));
	#endif
	#ifndef BITSTATE
#if !defined(MA) || defined(COLLAPSE)
	nr3 = (double) (ONE_L<<ssize)*sizeof(struct H_el *);
#endif
	#else
	if (udmem)
		nr3 = (double) (udmem);
	else
	nr3 = (double) (ONE_L<<(ssize-3));
#ifdef CNTRSTACK
	nr5 = (double) (ONE_L<<(ssize-3));
#endif
#ifdef FULLSTACK
	nr5 = (double) (maxdepth*sizeof(struct H_el *));
#endif
	#endif
	nr4 = (double) (svmax * (sizeof(Svtack) + hmax))
	    + (double) (smax * (sizeof(Stack) + Maxbody));
#ifndef MA
	if (mverbose || memcnt < nr1+nr2+nr3+nr4+nr5)
#endif
	{ double remainder = memcnt;
	  double tmp_nr = memcnt-nr3-nr4-(nr2-fragment)-nr5;

	#if NCORE>1 && !defined(SEP_STATE)
		tmp_nr -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;
	#endif
		if (tmp_nr < 0.0) tmp_nr = 0.;
		printf("Stats on memory usage (in Megabytes):\n");
		printf("%9.3f	equivalent memory usage for states",
			nr1/1048576.); /* 1024*1024=1048576 */
		printf(" (stored*(State-vector + overhead))\n");
	#if NCORE>1 && !defined(WIN32) && !defined(WIN64)
		printf("%9.3f	shared memory reserved for state storage\n",
			mem_reserved/1048576.);
		#ifdef SEP_HEAP
		printf("		in %d local heaps of %7.3f MB each\n",
			NCORE, mem_reserved/(NCORE*1048576.));
		#endif
		printf("\n");
	#endif
#ifdef BITSTATE
		if (udmem)
		printf("%9.3f	memory used for hash array (-M%ld)\n",
			nr3/1048576., udmem/(1024L*1024L));
		else
		printf("%9.3f	memory used for hash array (-w%d)\n",
			nr3/1048576., ssize);
		if (nr5 > 0.0)
		printf("%9.3f	memory used for bit stack\n",
			nr5/1048576.);
		remainder = remainder - nr3 - nr5;
#else
		printf("%9.3f	actual memory usage for states",
			tmp_nr/1048576.);
		remainder -= tmp_nr;
		printf(" (");
		if (tmp_nr > 0.)
		{	if (tmp_nr > nr1) printf("unsuccessful ");
			printf("compression: %.2f%%)\n",
				(100.0*tmp_nr)/nr1);
		} else
			printf("less than 1k)\n");
#ifndef MA
		if (tmp_nr > 0.)
		{	printf("         	state-vector as stored = %.0f byte",
			(tmp_nr)/(nstates-nShadow) -
			(double) (sizeof(struct H_el) - sizeof(unsigned)));
			printf(" + %ld byte overhead\n",
			(long int) sizeof(struct H_el)-sizeof(unsigned));
		}
#endif
#if !defined(MA) || defined(COLLAPSE)
		printf("%9.3f	memory used for hash table (-w%d)\n",
			nr3/1048576., ssize);
		remainder -= nr3;
#endif
#endif
#ifndef BFS
		printf("%9.3f	memory used for DFS stack (-m%ld)\n",
			nr2/1048576., maxdepth);
		remainder -= nr2;
#endif
#if NCORE>1
		remainder -= ((double) NCORE * LWQ_SIZE) + GWQ_SIZE;
		printf("%9.3f	shared memory used for work-queues\n",
			(GWQ_SIZE + (double) NCORE * LWQ_SIZE) /1048576.);
		printf("		in %d queues of %7.3f MB each",
			NCORE, (double) LWQ_SIZE /1048576.);
		#ifndef NGQ
		printf(" + a global q of %7.3f MB\n",
			(double) GWQ_SIZE / 1048576.);
		#else
		printf("\n");
		#endif
	#endif
		if (remainder - fragment > 1048576.)
		printf("%9.3f	other (proc and chan stacks)\n",
			(remainder-fragment)/1048576.);
		if (fragment > 1048576.)
		printf("%9.3f	memory lost to fragmentation\n",
			fragment/1048576.);
		printf("%9.3f	total actual memory usage\n\n",
			memcnt/1048576.);
	}
	#ifndef MA
	else
	#endif
#endif
#if !defined(BITSTATE) && defined(NOCOMP)
jump_here:
#endif
#ifndef MA
		printf("%9.3f	memory usage (Mbyte)\n\n",
			memcnt/1048576.);
#endif
#ifdef COLLAPSE
	printf("nr of templates: [ globals chans procs ]\n");
	printf("collapse counts: [ ");
	{ int i; for (i = 0; i < 256+2; i++)
		if (ncomps[i] != 0)
			printf("%d ", ncomps[i]);
		printf("]\n");
	}
#endif
	if ((done || verbose) && !no_rck) do_reach();
#ifdef PEG
	{ int i;
	  printf("\nPeg Counts (transitions executed):\n");
	  for (i = 1; i < NTRANS; i++)
	  {	if (peg[i]) putpeg(i, peg[i]);
	} }
#endif
#ifdef VAR_RANGES
	dumpranges();
#endif
#ifdef SVDUMP
	if (vprefix > 0) close(svfd);
#endif
#ifdef LOOPSTATE
	printf("%g loopstates hit\n", cnt_loops);
#endif
#ifdef NSUCC
	dump_succ();
#endif
#if NCORE>1 && defined(T_ALERT)
	crash_report();
#endif
	pan_exit(0);
}

void
stopped(int arg)
{	printf("Interrupted\n");
#if NCORE>1
	was_interrupted = 1;
#endif
	wrapup();
	pan_exit(0);
}

#ifdef SFH
/*
 * super fast hash, based on Paul Hsieh's function
 * http://www.azillionmonkeys.com/qed/hash.html
 */
#include <stdint.h>
	#undef get16bits
	#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
	|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
		#define get16bits(d) (*((const uint16_t *) (d)))
	#endif

	#ifndef get16bits
		#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
                       +(uint32_t)(((const uint8_t *)(d))[0]) )
	#endif

void
d_sfh(const char *s, int len)
{	uint32_t h = len, tmp;
	int rem;

	rem = len & 3;
	len >>= 2;

	for ( ; len > 0; len--)
	{	h  += get16bits(s);
        	tmp = (get16bits(s+2) << 11) ^ h;
        	h   = (h << 16) ^ tmp;
        	s  += 2*sizeof(uint16_t);
		h  += h >> 11;
	}
	switch (rem) {
	case 3: h += get16bits(s);
		h ^= h << 16;
		h ^= s[sizeof(uint16_t)] << 18;
		h += h >> 11;
		break;
	case 2: h += get16bits(s);
		h ^= h << 11;
		h += h >> 17;
		break;
	case 1: h += *s;
		h ^= h << 10;
		h += h >> 1;
		break;
	}
	h ^= h << 3;
	h += h >> 5;
	h ^= h << 4;
	h += h >> 17;
	h ^= h << 25;
	h += h >> 6;

	K1 = h;
}
#endif

#include <stdint.h>
#if defined(HASH64) || defined(WIN64)
/* 64-bit Jenkins hash, 1997
 * http://burtleburtle.net/bob/c/lookup8.c
 */
#define mix(a,b,c) \
{ a -= b; a -= c; a ^= (c>>43); \
  b -= c; b -= a; b ^= (a<<9);  \
  c -= a; c -= b; c ^= (b>>8);  \
  a -= b; a -= c; a ^= (c>>38); \
  b -= c; b -= a; b ^= (a<<23); \
  c -= a; c -= b; c ^= (b>>5);  \
  a -= b; a -= c; a ^= (c>>35); \
  b -= c; b -= a; b ^= (a<<49); \
  c -= a; c -= b; c ^= (b>>11); \
  a -= b; a -= c; a ^= (c>>12); \
  b -= c; b -= a; b ^= (a<<18); \
  c -= a; c -= b; c ^= (b>>22); \
}
#else
/* 32-bit Jenkins hash, 2006
 * http://burtleburtle.net/bob/c/lookup3.c
 */
#define rot(x,k)	(((x)<<(k))|((x)>>(32-(k))))

#define mix(a,b,c) \
{ a -= c;  a ^= rot(c, 4);  c += b; \
  b -= a;  b ^= rot(a, 6);  a += c; \
  c -= b;  c ^= rot(b, 8);  b += a; \
  a -= c;  a ^= rot(c,16);  c += b; \
  b -= a;  b ^= rot(a,19);  a += c; \
  c -= b;  c ^= rot(b, 4);  b += a; \
}

#define final(a,b,c) \
{ c ^= b; c -= rot(b,14); \
  a ^= c; a -= rot(c,11); \
  b ^= a; b -= rot(a,25); \
  c ^= b; c -= rot(b,16); \
  a ^= c; a -= rot(c,4);  \
  b ^= a; b -= rot(a,14); \
  c ^= b; c -= rot(b,24); \
}
#endif

void
d_hash(uchar *kb, int nbytes)
{	uint8_t  *bp;
#if defined(HASH64) || defined(WIN64)
	uint64_t a = 0, b, c, n;
	uint64_t *k = (uint64_t *) kb;
#else
	uint32_t a, b, c, n;
	uint32_t *k = (uint32_t *) kb;
#endif
	/* extend to multiple of words, if needed */
	n = nbytes/WS;	/* nr of words */
	a = nbytes - (n*WS);
	if (a > 0)
	{	n++;
		bp = kb + nbytes;
		switch (a) {
		case 3: *bp++ = 0; /* fall thru */
		case 2: *bp++ = 0; /* fall thru */
		case 1: *bp   = 0;
		case 0: break;
	}	}
#if defined(HASH64) || defined(WIN64)
	b = HASH_CONST[HASH_NR];
	c = 0x9e3779b97f4a7c13LL; /* arbitrary value */
	while (n >= 3)
	{	a += k[0];
		b += k[1];
		c += k[2];
		mix(a,b,c);
		n -= 3;
		k += 3;
	}
	c += (((uint64_t) nbytes)<<3);
	switch (n) {
	case 2: b += k[1];
	case 1: a += k[0];
	case 0: break;
	}
	mix(a,b,c);
#else
	a = c = 0xdeadbeef + (n<<2);
	b = HASH_CONST[HASH_NR];
	while (n > 3)
	{	a += k[0];
		b += k[1];
		c += k[2];
		mix(a,b,c);
		n -= 3;
		k += 3;
	}
	switch (n) { 
	case 3: c += k[2];
	case 2: b += k[1];
	case 1: a += k[0];
	case 0: break;
	}
	final(a,b,c);
#endif
	j1_spin = c&nmask; j3 = a&7;      /* 1st bit */
	j2 = b&nmask; j4 = (a>>3)&7; /* 2nd bit */
	K1 = c; K2 = b;
}

void
s_hash(uchar *cp, int om)
{
#if defined(SFH)
	d_sfh((const char *) cp, om); /* sets K1 */
#else
	d_hash(cp, om);	/* sets K1 etc */
#endif
#ifdef BITSTATE
	if (S_Tab == H_tab)
		j1_spin = K1 % omaxdepth;
	else
#endif
		if (ssize < 8*WS)
			j1_spin = K1&mask;
		else
			j1_spin = K1;
}
#ifndef RANDSTOR
int *prerand;
void
inirand(void)
{	int i;
	srand(123);	/* fixed startpoint */
	prerand = (int *) emalloc((omaxdepth+3)*sizeof(int));
	for (i = 0; i < omaxdepth+3; i++)
		prerand[i] = rand();
}
int
pan_rand(void)
{	if (!prerand) inirand();
	return prerand[depth];
}
#endif

void
set_masks(void)	/* 4.2.5 */
{
	if (WS == 4 && ssize >= 32)
	{	mask = 0xffffffff;
#ifdef BITSTATE
		switch (ssize) {
		case 34: nmask = (mask>>1); break;
		case 33: nmask = (mask>>2); break;
		default: nmask = (mask>>3); break;
		}
#else
		nmask = mask;
#endif
	} else if (WS == 8)
	{	mask = ((ONE_L<<ssize)-1);	/* hash init */
#ifdef BITSTATE
		nmask = mask>>3;
#else
		nmask = mask;
#endif
	} else if (WS != 4)
	{	fprintf(stderr, "pan: wordsize %ld not supported\n", (long int) WS);
		exit(1);
	} else	/* WS == 4 and ssize < 32 */
	{	mask = ((ONE_L<<ssize)-1);	/* hash init */
		nmask = (mask>>3);
	}
}

static long reclaim_size;
static char *reclaim_mem;
#if defined(AUTO_RESIZE) && !defined(BITSTATE) && !defined(MA)
#if NCORE>1
	#error cannot combine AUTO_RESIZE with NCORE>1 yet
#endif
static struct H_el **N_tab;
void
reverse_capture(struct H_el *p)
{	if (!p) return;
	reverse_capture(p->nxt);
	/* last element of list moves first */
	/* to preserve list-order */
	j2 = p->m_K1;
	if (ssize < 8*WS) /* probably always true */
	{	j2 &= mask;
	}
	p->nxt = N_tab[j2];
	N_tab[j2] = p;
}
void
resize_hashtable(void)
{
	if (WS == 4 && ssize >= 27 - 1)
	{	return;	/* canot increase further */
	}

	ssize += 2; /* 4x size */

	printf("pan: resizing hashtable to -w%d.. ", ssize);

	N_tab = (struct H_el **)
		emalloc((ONE_L<<ssize)*sizeof(struct H_el *));

	set_masks();	/* they changed */

	for (j1_spin = 0; j1_spin < (ONE_L << (ssize - 2)); j1_spin++)
	{	reverse_capture(H_tab[j1_spin]);
	}
	reclaim_mem = (char *) H_tab;
	reclaim_size = (ONE_L << (ssize - 2));
	H_tab = N_tab;

	printf(" done\n");
}
#endif
#if defined(ZAPH) && defined(BITSTATE)
void
zap_hashtable(void)
{	cpu_printf("pan: resetting hashtable\n");
	if (udmem)
	{	memset(SS, 0, udmem);
	} else
	{	memset(SS, 0, ONE_L<<(ssize-3));
	}
}
#endif

int
main(int argc, char *argv[])
{	void to_compile(void);

	efd = stderr;	/* default */

#if defined(T_RAND) && (T_RAND>0)
	s_rand = T_RAND;
#elif defined(P_RAND) && (P_RAND>0)
	s_rand = P_RAND;
#endif

#ifdef PUTPID
	{	char *ptr = strrchr(argv[0], '/');
		if (ptr == NULL)
		{	ptr = argv[0];
		} else
		{	ptr++;
		}
		progname = emalloc(strlen(ptr));
		strcpy(progname, ptr);
		/* printf("progname: %s\n", progname); */
	}
#endif

#ifdef BITSTATE
	bstore = bstore_reg; /* default */
#endif
#if NCORE>1
	{	int i, j;
		strcpy(o_cmdline, "");
		for (j = 1; j < argc; j++)
		{	strcat(o_cmdline, argv[j]);
			strcat(o_cmdline, " ");
		}
		/* printf("Command Line: %s\n", o_cmdline); */
		if (strlen(o_cmdline) >= sizeof(o_cmdline))
		{	Uerror("option list too long");
	}	}
#endif
	while (argc > 1 && argv[1][0] == '-')
	{	switch (argv[1][1]) {
#ifndef SAFETY
#ifdef NP
		case 'a': fprintf(efd, "error: -a disabled");
			  usage(efd); break;
#else
		case 'a': a_cycles = 1; break;
#endif
#endif
		case 'A': noasserts = 1; break;
		case 'b': bounded = 1; break;
#ifdef HAS_CODE
		case 'C': coltrace = 1; goto samething;
#endif
		case 'c': upto  = atoi(&argv[1][2]); break;
		case 'd': state_tables++; break;
		case 'e': every_error = 1; upto = 0; Nr_Trails = 1; break;
		case 'E': noends = 1; break;
#ifdef SC
		case 'F': if (strlen(argv[1]) > 2)
				stackfile = &argv[1][2];
			  break;
#endif
#if !defined(SAFETY) && !defined(NOFAIR)
		case 'f': fairness = 1; break;
#endif
#ifdef HAS_CODE
		case 'g': gui = 1; goto samething;
#endif
		case 'h': if (!argv[1][2]) usage(efd); else
			  HASH_NR = atoi(&argv[1][2])%100; break;
		case 'I': iterative = 2; every_error = 1; break;
		case 'i': iterative = 1; every_error = 1; break;
		case 'J': like_java = 1; break; /* Klaus Havelund */
#ifdef BITSTATE
		case 'k': hfns = atoi(&argv[1][2]); break;
#endif
#ifdef BCS
		case 'L':
			sched_max = atoi(&argv[1][2]);
			if (sched_max > 255)	/* stored as one byte */
			{	fprintf(efd, "warning: using max bound (255)\n");
				sched_max = 255;
			}
			break;
#endif
#ifndef SAFETY
#ifdef NP
		case 'l': a_cycles = 1; break;
#else
		case 'l': fprintf(efd, "error: -l disabled");
			  usage(efd); break;
#endif
#endif
#ifdef BITSTATE
		case 'M': udmem = atoi(&argv[1][2]); break;
		case 'G': udmem = atoi(&argv[1][2]); udmem *= 1024; break;
#else
		case 'M': case 'G':
			  fprintf(stderr, "-M and -G affect only -DBITSTATE\n");
			  break;
#endif
		case 'm': maxdepth = atoi(&argv[1][2]); break;
		case 'n': no_rck = 1; break;
		case 'P': readtrail = 1; onlyproc = atoi(&argv[1][2]);
			  if (argv[2][0] != '-') /* check next arg */
			  {	trailfilename = argv[2];
				argc--; argv++; /* skip next arg */
			  }
			  break;
#ifdef SVDUMP
		case 'p': vprefix = atoi(&argv[1][2]); break;
#endif
#if NCORE==1
		case 'Q': quota = (double) 60.0 * (double) atoi(&argv[1][2]); break;
#endif
		case 'q': strict = 1; break;
		case 'R':
#if defined(T_RAND) || defined(P_RAND) || defined(RANDSTOR)
			if (argv[1][2] == 'S') /* e.g., -RS76842 */
			{	s_rand = atoi(&argv[1][3]);
			} else
#endif
			{	Nrun = atoi(&argv[1][2]);
			}
			break;
#ifdef HAS_CODE
		case 'r':
samething:		  readtrail = 1;
			  if (isdigit(argv[1][2]))
				whichtrail = atoi(&argv[1][2]);
			  else if (argc > 2 && argv[2][0] != '-') /* check next arg */
			  {	trailfilename = argv[2];
				argc--; argv++; /* skip next arg */
			  }
			  break;
		case 'S': silent = 1; goto samething;
#endif
#ifdef BITSTATE
		case 's': hfns = 1; break;
#endif
		case 'T': TMODE = 0444; break;
		case 't': if (argv[1][2]) tprefix = &argv[1][2]; break;
		case 'V': start_timer(); printf("Generated by %s\n", SpinVersion);
			  to_compile(); pan_exit(2); break;
		case 'v': verbose++; break;
		case 'w': ssize = atoi(&argv[1][2]); break;
		case 'Y': signoff = 1; break;
		case 'X': efd = stdout; break;
		case 'x': exclusive = 1; break;
#if NCORE>1
		/* -B ip is passthru to proxy of remote ip address: */
		case 'B': argc--; argv++; break;
		case 'Q': worker_pids[0] = atoi(&argv[1][2]); break;
			/* -Un means that the nth worker should be instantiated as a proxy */
		case 'U': proxy_pid = atoi(&argv[1][2]); break;
			/* -W means that this copy is started by a cluster-server as a remote */
			/* this flag is passed to ./pan_proxy, which interprets it */
		case 'W': remote_party++; break;
		case 'Z': core_id = atoi(&argv[1][2]);
			  if (verbose)
			  { printf("cpu%d: pid %d parent %d\n",
				core_id, getpid(), worker_pids[0]);
			  }
			  break;
		case 'z': z_handoff = atoi(&argv[1][2]); break;
#else
		case 'z': break; /* ignored for single-core */
#endif
		default : fprintf(efd, "saw option -%c\n", argv[1][1]); usage(efd); break;
		}
		argc--; argv++;
	}
	if (iterative && TMODE != 0666)
	{	TMODE = 0666;
		fprintf(efd, "warning: -T ignored when -i or -I is used\n");
	}
#if defined(HASH32) && !defined(SFH)
	if (WS > 4)
	{	fprintf(efd, "strong warning: compiling -DHASH32 on a 64-bit machine\n");
		fprintf(efd, " without -DSFH can slow down performance a lot\n");
	}
#endif
#if defined(WIN32) || defined(WIN64)
	if (TMODE == 0666)
		TMODE = _S_IWRITE | _S_IREAD;
	else
		TMODE = _S_IREAD;
#endif
#if NCORE>1
	store_proxy_pid = proxy_pid; /* for checks in mem_file() and someone_crashed() */
	if (core_id != 0) { proxy_pid = 0; }
	#ifndef SEP_STATE
	if (core_id == 0 && a_cycles)
	{	fprintf(efd, "hint: this search may be more efficient ");
		fprintf(efd, "if pan.c is compiled -DSEP_STATE\n");
	}
	#endif
	if (z_handoff < 0)
	{	z_handoff =  20; /* conservative default - for non-liveness checks */
	}
#if defined(NGQ) || defined(LWQ_FIXED)
	LWQ_SIZE = (double) (128.*1048576.);
#else
	LWQ_SIZE = (double) ( z_handoff + 2.) * (double) sizeof(SM_frame);
#endif
	#if NCORE>2
	if (a_cycles)
	{	fprintf(efd, "warning: the intended nr of cores to be used in liveness mode is 2\n");
		#ifndef SEP_STATE
		fprintf(efd, "warning: without -DSEP_STATE there is no guarantee that all liveness violations are found\n");
		#endif
	}
	#endif
	#ifdef HAS_HIDDEN
	#error cannot use hidden variables when compiling multi-core
	#endif
#endif
#ifdef BITSTATE
	if (hfns <= 0)
	{	hfns = 1;
		fprintf(efd, "warning: using -k%d as minimal usable value\n", hfns);
	}
#endif
	omaxdepth = maxdepth;
#ifdef BITSTATE
	if (WS == 4 && ssize > 34)
	{	ssize = 34;
		fprintf(efd, "warning: using -w%d as max usable value\n", ssize);
/*
 *	-w35 would not work: 35-3 = 32 but 1^31 is the largest
 *	power of 2 that can be represented in an unsigned long
 */
	}
#else
	if (WS == 4 && ssize > 27)
	{	ssize = 27;
		fprintf(efd, "warning: using -w%d as max usable value\n", ssize);
/*
 *	for emalloc, the lookup table size multiplies by 4 for the pointers
 *	the largest power of 2 that can be represented in a ulong is 1^31
 *	hence the largest number of lookup table slots is 31-4 = 27
 */
	}
#endif
#ifdef SC
	hiwater = HHH = maxdepth-10;
	DDD = HHH/2;
	if (!stackfile)
	{	stackfile = (char *) emalloc(strlen(PanSource)+4+1);
		sprintf(stackfile, "%s._s_", PanSource);
	}
	if (iterative)
	{	fprintf(efd, "error: cannot use -i or -I with -DSC\n");
		pan_exit(1);
	}
#endif
#if (defined(R_XPT) || defined(W_XPT)) && !defined(MA)
	#warning -DR_XPT and -DW_XPT assume -DMA (ignored)
#endif
	if (iterative && a_cycles)
	fprintf(efd, "warning: -i or -I work for safety properties only\n");
#ifdef BFS
	#ifdef SC
		#error -DBFS not compatible with -DSC
	#endif
	#ifdef HAS_LAST
		#error -DBFS not compatible with _last
	#endif
	#ifdef HAS_STACK
		#error cannot use c_track UnMatched with BFS
	#endif
	#ifdef BCS
		#error -DBFS not compatible with -DBCS
	#endif
	#ifdef REACH
		#warning -DREACH is redundant when -DBFS is used
	#endif
#endif
#ifdef BCS
	#ifdef P_RAND
	#error cannot combine -DBCS and -DP_RAND
	#endif
	#ifdef BFS
	#error cannot combine -DBCS and -DBFS
	#endif
#endif
#if defined(MERGED) && defined(PEG)
	#error to use -DPEG use: spin -o3 -a
#endif
#ifdef HC
	#ifdef SFH
		#error cannot combine -DHC and -DSFH
		/* use of NOCOMP is the real reason */
	#else
		#ifdef NOCOMP
		#error cannot combine -DHC and -DNOCOMP
		#endif
	#endif
	#ifdef BITSTATE
		#error cannot combine -DHC and -DBITSTATE
	#endif
#endif
#if defined(SAFETY) && defined(NP)
	#error cannot combine -DNP and -DBFS or -DSAFETY
#endif
#ifdef MA
	#ifdef BITSTATE
		#error cannot combine -DMA and -DBITSTATE
	#endif
	#if MA <= 0
		#error usage: -DMA=N with N > 0 and N < VECTORSZ
	#endif
#endif
#ifdef COLLAPSE
	#ifdef BITSTATE
		#error cannot combine -DBITSTATE and -DCOLLAPSE
	#endif
	#ifdef SFH
		#error cannot combine -DCOLLAPSE and -DSFH
		/* use of NOCOMP is the real reason */
	#else
		#ifdef NOCOMP
		#error cannot combine -DCOLLAPSE and -DNOCOMP
		#endif
	#endif
#endif
	if (maxdepth <= 0 || ssize <= 1) usage(efd);
#if SYNC>0 && !defined(NOREDUCE)
	if (a_cycles && fairness)
	{ fprintf(efd, "error: p.o. reduction not compatible with ");
	  fprintf(efd, "fairness (-f) in models\n");
	  fprintf(efd, "       with rendezvous operations: ");
	  fprintf(efd, "recompile with -DNOREDUCE\n");
	  pan_exit(1);
	}
#endif
#if defined(REM_VARS) && !defined(NOREDUCE)
	#warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE)
#endif
#if defined(NOCOMP) && !defined(BITSTATE)
	if (a_cycles)
	{ fprintf(efd, "error: use of -DNOCOMP voids -l and -a\n");
	  pan_exit(1);
	}
#endif
#ifdef MEMLIM
	memlim = ((double) MEMLIM) * (double) (1<<20);	/* size in Mbyte */
#endif
#ifndef BITSTATE
	if (Nrun > 1) HASH_NR = Nrun - 1;
#endif
	if (Nrun < 1 || Nrun > 32)
	{	fprintf(efd, "error: invalid arg for -R\n");
		usage(efd);
	}
#ifndef SAFETY
	if (fairness && !a_cycles)
	{	fprintf(efd, "error: -f requires -a or -l\n");
		usage(efd);
	}
	#if ACCEPT_LAB==0
	if (a_cycles)
	{	fprintf(efd, "error: no accept labels defined ");
		fprintf(efd, "in model (for option -a)\n");
		usage(efd);
	}
	#endif
#endif
#ifndef NOREDUCE
	#ifdef HAS_ENABLED
		#error use of enabled() requires -DNOREDUCE
	#endif
	#ifdef HAS_PCVALUE
		#error use of pcvalue() requires -DNOREDUCE
	#endif
	#ifdef HAS_BADELSE
		#error use of 'else' combined with i/o stmnts requires -DNOREDUCE
	#endif
	#if defined(HAS_LAST) && !defined(BCS)
		#error use of _last requires -DNOREDUCE
	#endif
#endif
#if SYNC>0 && !defined(NOREDUCE)
	#ifdef HAS_UNLESS
	fprintf(efd, "warning: use of a rendezvous stmnts in the escape\n");
	fprintf(efd, "	of an unless clause, if present, could make p.o. reduction\n");
	fprintf(efd, "	invalid (use -DNOREDUCE to avoid this)\n");
		#ifdef BFS
		fprintf(efd, "	(this type of rv is also not compatible with -DBFS)\n");
		#endif
	#endif
#endif
#if SYNC>0 && defined(BFS)
	#warning use of rendezvous with BFS does not preserve all invalid endstates
#endif
#if !defined(REACH) && !defined(BITSTATE)
	if (iterative != 0 && a_cycles == 0)
	{	fprintf(efd, "warning: -i and -I need -DREACH to work accurately\n");
	}
#endif
#if defined(BITSTATE) && defined(REACH)
	#warning -DREACH is voided by -DBITSTATE
#endif
#if defined(MA) && defined(REACH)
	#warning -DREACH is voided by -DMA
#endif
#if defined(FULLSTACK) && defined(CNTRSTACK)
	#error cannot combine -DFULLSTACK and -DCNTRSTACK
#endif
#if defined(VERI)
	#if ACCEPT_LAB>0
		#ifndef BFS
			if (!a_cycles
			#ifdef HAS_CODE
			&& !readtrail
			#endif
			#if NCORE>1
			&& core_id == 0
			#endif
			&& !state_tables)
			{ fprintf(efd, "warning: never claim + accept labels ");
			  fprintf(efd, "requires -a flag to fully verify\n");
			}
		#else
			if (!state_tables
			#ifdef HAS_CODE
			&& !readtrail
			#endif
			)
			{ fprintf(efd, "warning: verification in BFS mode ");
			  fprintf(efd, "is restricted to safety properties\n");
			}
		#endif
	#endif
#endif
#ifndef SAFETY
	if (!a_cycles
	#ifdef HAS_CODE
	&& !readtrail
	#endif
	#if NCORE>1
	&& core_id == 0
	#endif
	&& !state_tables)
	{ fprintf(efd, "hint: this search is more efficient ");
	  fprintf(efd, "if pan.c is compiled -DSAFETY\n");
	}
	#ifndef NOCOMP
	if (!a_cycles)
	{	S_A = 0;
	} else
	{	if (!fairness)
		{	S_A = 1; /* _a_t */
		#ifndef NOFAIR
		} else /* _a_t and _cnt[NFAIR] */
		{  S_A = (&(now._cnt[0]) - (uchar *) &now) + NFAIR - 2;
		/* -2 because first two uchars in now are masked */
		#endif
	}	}
	#endif
#endif
	signal(SIGINT, stopped);
	set_masks();
#ifdef BFS
	trail = (Trail *) emalloc(6*sizeof(Trail));
	trail += 3;
#else
	trail = (Trail *) emalloc((maxdepth+3)*sizeof(Trail));
	trail++;	/* protect trpt-1 refs at depth 0 */
#endif
#ifdef SVDUMP
	if (vprefix > 0)
	{	char nm[64];
		sprintf(nm, "%s.svd", PanSource);
		if ((svfd = creat(nm, TMODE)) < 0)
		{	fprintf(efd, "couldn't create %s\n", nm);
			vprefix = 0;
	}	}
#endif
#ifdef RANDSTOR
	srand(s_rand);
#endif
#if SYNC>0 && ASYNC==0
	set_recvs();
#endif
	run();
	done = 1;
	wrapup();
	return 0;
}

void
usage(FILE *fd)
{
	fprintf(fd, "%s\n", SpinVersion);
	fprintf(fd, "Valid Options are:\n");
#ifndef SAFETY
#ifdef NP
	fprintf(fd, "	-a  -> is disabled by -DNP ");
	fprintf(fd, "(-DNP compiles for -l only)\n");
#else
	fprintf(fd, "	-a  find acceptance cycles\n");
#endif
#else
	fprintf(fd, "	-a,-l,-f  -> are disabled by -DSAFETY\n");
#endif
	fprintf(fd, "	-A  ignore assert() violations\n");
	fprintf(fd, "	-b  consider it an error to exceed the depth-limit\n");
	fprintf(fd, "	-cN stop at Nth error ");
	fprintf(fd, "(defaults to -c1)\n");
	fprintf(fd, "	-d  print state tables and stop\n");
	fprintf(fd, "	-e  create trails for all errors\n");
	fprintf(fd, "	-E  ignore invalid end states\n");
#ifdef SC
	fprintf(fd, "	-Ffile  use 'file' to store disk-stack\n");
#endif
#ifndef NOFAIR
	fprintf(fd, "	-f  add weak fairness (to -a or -l)\n");
#endif
	fprintf(fd, "	-hN use different hash-seed N:1..32\n");
	fprintf(fd, "	-i  search for shortest path to error\n");
	fprintf(fd, "	-I  like -i, but approximate and faster\n");
	fprintf(fd, "	-J  reverse eval order of nested unlesses\n");
#ifdef BITSTATE
	fprintf(fd, "	-kN set N bits per state (defaults to 3)\n");
#endif
#ifdef BCS
	fprintf(fd, "	-LN set scheduling restriction to N (default 10)\n");
#endif
#ifndef SAFETY
#ifdef NP
	fprintf(fd, "	-l  find non-progress cycles\n");
#else
	fprintf(fd, "	-l  find non-progress cycles -> ");
	fprintf(fd, "disabled, requires ");
	fprintf(fd, "compilation with -DNP\n");
#endif
#endif
#ifdef BITSTATE
	fprintf(fd, "	-MN use N Megabytes for bitstate hash array\n");
	fprintf(fd, "	-GN use N Gigabytes for bitstate hash array\n");
#endif
	fprintf(fd, "	-mN max depth N steps (default=10k)\n");
	fprintf(fd, "	-n  no listing of unreached states\n");
#ifdef SVDUMP
	fprintf(fd, "	-pN create svfile (save N bytes per state)\n");
#endif
	fprintf(fd, "	-QN set time-limit on execution of N minutes\n");
	fprintf(fd, "	-q  require empty chans in valid end states\n");
#ifdef HAS_CODE
	fprintf(fd, "	-r  read and execute trail - can add -v,-n,-PN,-g,-C\n");
	fprintf(fd, "	-rN read and execute N-th error trail\n");
	fprintf(fd, "	-C  read and execute trail - columnated output (can add -v,-n)\n");
	fprintf(fd, "	-PN read and execute trail - restrict trail output to proc N\n");
	fprintf(fd, "	-g  read and execute trail + msc gui support\n");
	fprintf(fd, "	-S  silent replay: only user defined printfs show\n");
#endif
#if defined(T_RAND) || defined(P_RAND) || defined(RANDSTOR)
	fprintf(fd, "	-RSN use randomization seed N\n");
#endif
#ifdef BITSTATE
	fprintf(fd, "	-RN repeat run Nx with N ");
	fprintf(fd, "[1..32] independent hash functions\n");
	fprintf(fd, "	-s  same as -k1 (single bit per state)\n");
#endif
	fprintf(fd, "	-T  create trail files in read-only mode\n");
	fprintf(fd, "	-tsuf replace .trail with .suf on trailfiles\n");
	fprintf(fd, "	-V  print SPIN version number\n");
	fprintf(fd, "	-v  verbose -- filenames in unreached state listing\n");
	fprintf(fd, "	-wN hashtable of 2^N entries ");
	fprintf(fd, "(defaults to -w%d)\n", ssize);
	fprintf(fd, "	-x  do not overwrite an existing trail file\n");
#if NCORE>1
	fprintf(fd, "	-zN handoff states below depth N to 2nd cpu (multi_core)\n");
#endif
#ifdef HAS_CODE
	fprintf(fd, "\n	options -r, -C, -PN, -g, and -S can optionally be followed by\n");
	fprintf(fd, "	a filename argument, as in '-r filename', naming the trailfile\n");
#endif
#if NCORE>1
	multi_usage(fd);
#endif
	exit(1);
}

char *
Malloc(unsigned long n)
{	char *tmp;
#ifdef MEMLIM
	if (memcnt+ (double) n > memlim) goto err;
#endif
#if defined(WIN32) || defined(WIN64)
	tmp = (char *) malloc(n);
	if (!tmp)
#else
	/* on linux machines, a large amount of memory is set aside
	 * for malloc, whether it is used or not
	 * using sbrk would make this memory arena inaccessible
	 * the reason for using sbrk was originally to provide a
	 * small additional speedup (since this memory is never released)
	 */
	tmp = (char *) sbrk(n);
	if (tmp == (char *) -ONE_L)
#endif
	{
#ifdef MEMLIM
err:
#endif
		printf("pan: out of memory\n");
#ifdef MEMLIM
		printf("	%g bytes used\n", memcnt);
		printf("	%g bytes more needed\n", (double) n);
		printf("	%g bytes limit\n",
			memlim);
#endif
#ifdef COLLAPSE
		printf("hint: to reduce memory, recompile with\n");
#ifndef MA
		printf("  -DMA=%d   # better/slower compression, or\n", hmax);
#endif
		printf("  -DBITSTATE # supertrace, approximation\n");
#else
#ifndef BITSTATE
		printf("hint: to reduce memory, recompile with\n");
#ifndef HC
		printf("  -DCOLLAPSE # good, fast compression, or\n");
#ifndef MA
		printf("  -DMA=%d   # better/slower compression, or\n", hmax);
#endif
		printf("  -DHC # hash-compaction, approximation\n");
#endif
		printf("  -DBITSTATE # supertrace, approximation\n");
#endif
#endif
#if NCORE>1
	#ifdef FULL_TRAIL
		printf("  omit -DFULL_TRAIL or use pan -c0 to reduce memory\n");
	#endif
	#ifdef SEP_STATE
		printf("hint: to reduce memory, recompile without\n");
		printf("  -DSEP_STATE # may be faster, but uses more memory\n");
	#endif
#endif
		wrapup();
	}
	memcnt += (double) n;
	return tmp;
}

#define CHUNK	(100*VECTORSZ)

char *
emalloc(unsigned long n) /* never released or reallocated */
{	char *tmp;
	if (n == 0)
	        return (char *) NULL;
	if (n&(sizeof(void *)-1)) /* for proper alignment */
	        n += sizeof(void *)-(n&(sizeof(void *)-1));
	if ((unsigned long) left < n)
	{       grow = (n < CHUNK) ? CHUNK : n;
	        have = Malloc(grow);
	        fragment += (double) left;
	        left = grow;
	}
	tmp = have;
	have += (long) n;
	left -= (long) n;
	memset(tmp, 0, n);
	return tmp;
}
void
Uerror(char *str)
{	/* always fatal */
	uerror(str);
#if NCORE>1
	sudden_stop("Uerror");
#endif
	wrapup();
}

#if defined(MA) && !defined(SAFETY)
int
Unwind(void)
{	Trans *t; uchar ot, _m; int tt; short II;
#ifdef VERBOSE
	int i;
#endif
	uchar oat = now._a_t;
	now._a_t &= ~(1|16|32);
	memcpy((char *) &comp_now, (char *) &now, vsize);
	now._a_t = oat;
Up:
#ifdef SC
	trpt = getframe(depth);
#endif
#ifdef VERBOSE
	printf("%d	 State: ", depth);
	for (i = 0; i < vsize; i++) printf("%d%s,",
		((char *)&now)[i], Mask[i]?"*":"");
	printf("\n");
#endif
#ifndef NOFAIR
	if (trpt->o_pm&128)	/* fairness alg */
	{	now._cnt[now._a_t&1] = trpt->bup.oval;
		depth--;
#ifdef SC
		trpt = getframe(depth);
#else
		trpt--;
#endif
		goto Q999;
	}
#endif
#ifdef HAS_LAST
#ifdef VERI
	{ int d; Trail *trl;
	  now._last = 0;
	  for (d = 1; d < depth; d++)
	  {	trl = getframe(depth-d); /* was trl = (trpt-d); */
		if (trl->pr != 0)
		{ now._last = trl->pr - BASE;
		  break;
	} }	}
#else
	now._last = (depth<1)?0:(trpt-1)->pr;
#endif
#endif
#ifdef EVENT_TRACE
	now._event = trpt->o_event;
#endif
	if ((now._a_t&1) && depth <= A_depth)
	{	now._a_t &= ~(1|16|32);
		if (fairness) now._a_t |= 2;	/* ? */
		A_depth = 0;
		goto CameFromHere;	/* checkcycles() */
	}
	t  = trpt->o_t;
	ot = trpt->o_ot; II = trpt->pr;
	tt = trpt->o_tt; this = pptr(II);
	_m = do_reverse(t, II, trpt->o_m);
#ifdef VERBOSE
	printf("%3d: proc %d ", depth, II);
	printf("reverses %d, %d to %d,",
		t->forw, tt, t->st);
	printf(" %s [abit=%d,adepth=%d,", 
		t->tp, now._a_t, A_depth);
	printf("tau=%d,%d] <unwind>\n", 
		trpt->tau, (trpt-1)->tau);
#endif
	depth--;
#ifdef SC
	trpt = getframe(depth);
#else
	trpt--;
#endif
	/* reached[ot][t->st] = 1;	3.4.13 */
	((P0 *)this)->_p = tt;
#ifndef NOFAIR
	if ((trpt->o_pm&32))
	{
#ifdef VERI
		if (now._cnt[now._a_t&1] == 0)
			now._cnt[now._a_t&1] = 1;
#endif
		now._cnt[now._a_t&1] += 1;
	}
Q999:
	if (trpt->o_pm&8)
	{	now._a_t &= ~2;
		now._cnt[now._a_t&1] = 0;
	}
	if (trpt->o_pm&16)
		now._a_t |= 2;
#endif
CameFromHere:
	if (memcmp((char *) &now, (char *) &comp_now, vsize) == 0)
		return depth;
	if (depth > 0) goto Up;
	return 0;
}
#endif
static char unwinding;
void
uerror(char *str)
{	static char laststr[256];
	int is_cycle;

	if (unwinding) return; /* 1.4.2 */
	if (strncmp(str, laststr, 254))
#if NCORE>1
	cpu_printf("pan: %s (at depth %ld)\n", str,
#else
	printf("pan: %s (at depth %ld)\n", str,
#endif
#if NCORE>1
		(nr_handoffs * z_handoff) + 
#endif
		((depthfound==-1)?depth:depthfound));
	strncpy(laststr, str, 254);
	errors++;
#ifdef HAS_CODE
	if (readtrail) { wrap_trail(); return; }
#endif
	is_cycle = (strstr(str, " cycle") != (char *) 0);
	if (!is_cycle)
	{	depth++; trpt++;
	}
	if ((every_error != 0)
	||  errors == upto)
	{
#if defined(MA) && !defined(SAFETY)
		if (is_cycle)
		{	int od = depth;
			unwinding = 1;
			depthfound = Unwind();
			unwinding = 0;
			depth = od;
		}
#endif
#if NCORE>1
		writing_trail = 1;
#endif
#ifdef BFS
		if (depth > 1) trpt--;
		nuerror(str);
		if (depth > 1) trpt++;
#else
		putrail();
#endif
#if defined(MA) && !defined(SAFETY)
		if (strstr(str, " cycle"))
		{	if (every_error)
			printf("sorry: MA writes 1 trail max\n");
			wrapup(); /* no recovery from unwind */
		}
#endif
#if NCORE>1
		if (search_terminated != NULL)
		{	*search_terminated |= 4; /* uerror */
		}
		writing_trail = 0;
#endif
	}
	if (!is_cycle)
	{	depth--; trpt--;	/* undo */
	}
#ifndef BFS
	if (iterative != 0 && maxdepth > 0)
	{	if (maxdepth > depth)
		{	maxdepth = (iterative == 1)?(depth+1):(depth/2);	/* was ...?(depth-1):... GH 5.2.3 */
		}
		warned = 1;
		printf("pan: reducing search depth to %ld\n",
			maxdepth);
	} else
#endif
	if (errors >= upto && upto != 0)
	{
#if NCORE>1
		sudden_stop("uerror");
#endif
		wrapup();
	}
	depthfound = -1;
}

int
xrefsrc(int lno, S_F_MAP *mp, int M, int i)
{	Trans *T; int j, retval=1;
	for (T = trans[M][i]; T; T = T->nxt)
	if (T && T->tp)
	{	if (strcmp(T->tp, ".(goto)") == 0
		||  strncmp(T->tp, "goto :", 6) == 0)
			return 1; /* not reported */

		printf("\tline %d", lno);
		if (verbose)
		for (j = 0; j < sizeof(mp); j++)
			if (i >= mp[j].from && i <= mp[j].upto)
			{	printf(", \"%s\"", mp[j].fnm);
				break;
			}
		printf(", state %d", i);
		if (strcmp(T->tp, "") != 0)
		{	char *q;
			q = transmognify(T->tp);
			printf(", \"%s\"", q?q:"");
		} else if (stopstate[M][i])
			printf(", -end state-");
		printf("\n");
		retval = 0; /* reported */
	}
	return retval;
}

void
r_ck(uchar *which, int N, int M, short *src, S_F_MAP *mp)
{	int i, m=0;

#ifdef VERI
	if (M == VERI && !verbose) return;
#endif
	printf("unreached in proctype %s\n", procname[M]);
	for (i = 1; i < N; i++)
	  if (which[i] == 0
	  &&  (mapstate[M][i] == 0
	  ||   which[mapstate[M][i]] == 0))
	  	m += xrefsrc((int) src[i], mp, M, i);
	  else
		m++;
	printf("	(%d of %d states)\n", N-1-m, N-1);
}
#if NCORE>1 && !defined(SEP_STATE)
static long rev_trail_cnt;

#ifdef FULL_TRAIL
void
rev_trail(int fd, volatile Stack_Tree *st_tr)
{	long j; char snap[64];

	if (!st_tr)
	{	return;
	}
	rev_trail(fd, st_tr->prv);
#ifdef VERBOSE
	printf("%d (%d) LRT [%d,%d] -- %9u (root %9u)\n",
		depth, rev_trail_cnt, st_tr->pr, st_tr->t_id, st_tr, stack_last[core_id]);
#endif
	if (st_tr->pr != 255)
	{	sprintf(snap, "%ld:%d:%d\n", 
			rev_trail_cnt++, st_tr->pr, st_tr->t_id);
		j = strlen(snap);
		if (write(fd, snap, j) != j)
		{	printf("pan: error writing trailfile\n");
			close(fd);
			wrapup();
			return;
		}
	} else  /* handoff point */
	{	if (a_cycles)
		{	(void) write(fd, "-1:-1:-1\n", 9);
	}	}
}
#endif
#endif

void
putrail(void)
{	int fd;
#if defined VERI || defined(MERGED)
	char snap[64];
#endif
#if NCORE==1 || defined(SEP_STATE) || !defined(FULL_TRAIL)
	long i, j;
	Trail *trl;
#endif
	fd = make_trail();
	if (fd < 0) return;
#ifdef VERI
	sprintf(snap, "-2:%d:-2\n", VERI);
	if (write(fd, snap, strlen(snap)) < 0) return;
#endif
#ifdef MERGED
	sprintf(snap, "-4:-4:-4\n");
	if (write(fd, snap, strlen(snap)) < 0) return;
#endif
#if NCORE>1 && !defined(SEP_STATE) && defined(FULL_TRAIL)
	rev_trail_cnt = 1;
	enter_critical(GLOBAL_LOCK);
	 rev_trail(fd, stack_last[core_id]);
	leave_critical(GLOBAL_LOCK);
#else
	i = 1; /* trail starts at position 1 */
	#if NCORE>1 && defined(SEP_STATE)
	if (cur_Root.m_vsize > 0) { i++; depth++; }
	#endif
	for ( ; i <= depth; i++)
	{	if (i == depthfound+1)
		{	if (write(fd, "-1:-1:-1\n", 9) != 9)
			{	goto notgood;
		}	}
		trl = getframe(i);
		if (!trl->o_t) continue;
		if (trl->o_pm&128) continue;
		sprintf(snap, "%ld:%d:%d\n", 
			i, trl->pr, trl->o_t->t_id);
		j = strlen(snap);
		if (write(fd, snap, j) != j)
		{
notgood:		printf("pan: error writing trailfile\n");
			close(fd);
			wrapup();
	}	}
#endif
	close(fd);
#if NCORE>1
	cpu_printf("pan: wrote trailfile\n");
#endif
}

void
sv_save(void)	/* push state vector onto save stack */
{	if (!svtack->nxt)
	{  svtack->nxt = (Svtack *) emalloc(sizeof(Svtack));
	   svtack->nxt->body = emalloc(vsize*sizeof(char));
	   svtack->nxt->lst = svtack;
	   svtack->nxt->m_delta = vsize;
	   svmax++;
	} else if (vsize > svtack->nxt->m_delta)
	{  svtack->nxt->body = emalloc(vsize*sizeof(char));
	   svtack->nxt->lst = svtack;
	   svtack->nxt->m_delta = vsize;
	   svmax++;
	}
	svtack = svtack->nxt;
#if SYNC
	svtack->o_boq = boq;
#endif
	svtack->o_delta = vsize; /* don't compress */
	memcpy((char *)(svtack->body), (char *) &now, vsize);
#if defined(C_States) && defined(HAS_STACK) && (HAS_TRACK==1)
	c_stack((uchar *) &(svtack->c_stack[0]));
#endif
#ifdef DEBUG
	cpu_printf("%d:	sv_save\n", depth);
#endif
}

void
sv_restor(void)	/* pop state vector from save stack */
{
	memcpy((char *)&now, svtack->body, svtack->o_delta);
#if SYNC
	boq = svtack->o_boq;
#endif
#if defined(C_States) && (HAS_TRACK==1)
#ifdef HAS_STACK
	c_unstack((uchar *) &(svtack->c_stack[0]));
#endif
	c_revert((uchar *) &(now.c_state[0]));
#endif
	if (vsize != svtack->o_delta)
		Uerror("sv_restor");
	if (!svtack->lst)
		Uerror("error: v_restor");
	svtack  = svtack->lst;
#ifdef DEBUG
	cpu_printf("	sv_restor\n");
#endif
}

void
p_restor(int h)
{	int i; char *z = (char *) &now;

	proc_offset[h] = stack->o_offset;
	proc_skip[h]   = (uchar) stack->o_skip;
#ifndef XUSAFE
	p_name[h] = stack->o_name;
#endif
#ifndef NOCOMP
	for (i = vsize + stack->o_skip; i > vsize; i--)
		Mask[i-1] = 1; /* align */
#endif
	vsize += stack->o_skip;
	memcpy(z+vsize, stack->body, stack->o_delta);
	vsize += stack->o_delta;
#ifndef NOVSZ
	now._vsz = vsize;
#endif
#ifndef NOCOMP
	for (i = 1; i <= Air[((P0 *)pptr(h))->_t]; i++)
		Mask[vsize - i] = 1; /* pad */
	Mask[proc_offset[h]] = 1;	/* _pid */
#endif
	if (BASE > 0 && h > 0)
		((P0 *)pptr(h))->_pid = h-BASE;
	else
		((P0 *)pptr(h))->_pid = h;
	i = stack->o_delqs;
	now._nr_pr += 1;
	if (!stack->lst)	/* debugging */
		Uerror("error: p_restor");
	stack = stack->lst;
	this = pptr(h);
	while (i-- > 0)
		q_restor();
}

void
q_restor(void)
{	char *z = (char *) &now;
#ifndef NOCOMP
	int k, k_end;
#endif
	q_offset[now._nr_qs] = stack->o_offset;
	q_skip[now._nr_qs]   = (uchar) stack->o_skip;
#ifndef XUSAFE
	q_name[now._nr_qs]   = stack->o_name;
#endif
	vsize += stack->o_skip;
	memcpy(z+vsize, stack->body, stack->o_delta);
	vsize += stack->o_delta;
#ifndef NOVSZ
	now._vsz = vsize;
#endif
	now._nr_qs += 1;
#ifndef NOCOMP
	k_end = stack->o_offset;
	k = k_end - stack->o_skip;
#if SYNC
#ifndef BFS
	if (q_zero(now._nr_qs)) k_end += stack->o_delta;
#endif
#endif
	for ( ; k < k_end; k++)
		Mask[k] = 1;
#endif
	if (!stack->lst)	/* debugging */
		Uerror("error: q_restor");
	stack = stack->lst;
}
typedef struct IntChunks {
	int	*ptr;
	struct	IntChunks *nxt;
} IntChunks;
IntChunks *filled_chunks[512];
IntChunks *empty_chunks[512];
int *
grab_ints(int nr)
{	IntChunks *z;
	if (nr >= 512) Uerror("cannot happen grab_int");
	if (filled_chunks[nr])
	{	z = filled_chunks[nr];
		filled_chunks[nr] = filled_chunks[nr]->nxt;
	} else 
	{	z = (IntChunks *) emalloc(sizeof(IntChunks));
		z->ptr = (int *) emalloc(nr * sizeof(int));
	}
	z->nxt = empty_chunks[nr];
	empty_chunks[nr] = z;
	return z->ptr;
}
void
ungrab_ints(int *p, int nr)
{	IntChunks *z;
	if (!empty_chunks[nr]) Uerror("cannot happen ungrab_int");
	z = empty_chunks[nr];
	empty_chunks[nr] = empty_chunks[nr]->nxt;
	z->ptr = p;
	z->nxt = filled_chunks[nr];
	filled_chunks[nr] = z;
}
int
delproc(int sav, int h)
{	int d, i=0;
#ifndef NOCOMP
	int o_vsize = vsize;
#endif
	if (h+1 != (int) now._nr_pr) return 0;

	while (now._nr_qs
	&&     q_offset[now._nr_qs-1] > proc_offset[h])
	{	delq(sav);
		i++;
	}
	d = vsize - proc_offset[h];
	if (sav)
	{	if (!stack->nxt)
		{	stack->nxt = (Stack *)
				emalloc(sizeof(Stack));
			stack->nxt->body = 
				emalloc(Maxbody*sizeof(char));
			stack->nxt->lst = stack;
			smax++;
		}
		stack = stack->nxt;
		stack->o_offset = proc_offset[h];
#if VECTORSZ>32000
		stack->o_skip   = (int) proc_skip[h];
#else
		stack->o_skip   = (short) proc_skip[h];
#endif
#ifndef XUSAFE
		stack->o_name   = p_name[h];
#endif
		stack->o_delta  = d;
		stack->o_delqs  = i;
		memcpy(stack->body, (char *)pptr(h), d);
	}
	vsize = proc_offset[h];
	now._nr_pr = now._nr_pr - 1;
	memset((char *)pptr(h), 0, d);
	vsize -= (int) proc_skip[h];
#ifndef NOVSZ
	now._vsz = vsize;
#endif
#ifndef NOCOMP
	for (i = vsize; i < o_vsize; i++)
		Mask[i] = 0; /* reset */
#endif
	return 1;
}

void
delq(int sav)
{	int h = now._nr_qs - 1;
	int d = vsize - q_offset[now._nr_qs - 1];
#ifndef NOCOMP
	int k, o_vsize = vsize;
#endif
	if (sav)
	{	if (!stack->nxt)
		{	stack->nxt = (Stack *)
				emalloc(sizeof(Stack));
			stack->nxt->body = 
				emalloc(Maxbody*sizeof(char));
			stack->nxt->lst = stack;
			smax++;
		}
		stack = stack->nxt;
		stack->o_offset = q_offset[h];
#if VECTORSZ>32000
		stack->o_skip   = (int) q_skip[h];
#else
		stack->o_skip   = (short) q_skip[h];
#endif
#ifndef XUSAFE
		stack->o_name   = q_name[h];
#endif
		stack->o_delta  = d;
		memcpy(stack->body, (char *)qptr(h), d);
	}
	vsize = q_offset[h];
	now._nr_qs = now._nr_qs - 1;
	memset((char *)qptr(h), 0, d);
	vsize -= (int) q_skip[h];
#ifndef NOVSZ
	now._vsz = vsize;
#endif
#ifndef NOCOMP
	for (k = vsize; k < o_vsize; k++)
		Mask[k] = 0; /* reset */
#endif
}

int
qs_empty(void)
{	int i;
	for (i = 0; i < (int) now._nr_qs; i++)
	{	if (q_sz(i) > 0)
			return 0;
	}
	return 1;
}

int
endstate(void)
{	int i; P0 *ptr;
	for (i = BASE; i < (int) now._nr_pr; i++)
	{	ptr = (P0 *) pptr(i);
		if (!stopstate[ptr->_t][ptr->_p])
			return 0;
	}
	if (strict) return qs_empty();
#if defined(EVENT_TRACE) && !defined(OTIM)
	if (!stopstate[EVENT_TRACE][now._event] && !a_cycles)
	{	printf("pan: event_trace not completed\n");
		return 0;
	}
#endif
	return 1;
}

#ifndef SAFETY
void
checkcycles(void)
{	uchar o_a_t = now._a_t;
#ifndef NOFAIR
	uchar o_cnt = now._cnt[1];
#endif
#ifdef FULLSTACK
#ifndef MA
	struct H_el *sv = trpt->ostate; /* save */
#else
	uchar prov = trpt->proviso; /* save */
#endif
#endif
#ifdef DEBUG
	{ int i; uchar *v = (uchar *) &now;
	  printf("	set Seed state ");
#ifndef NOFAIR
	  if (fairness) printf("(cnt = %d:%d, nrpr=%d) ",
		now._cnt[0], now._cnt[1], now._nr_pr);
#endif
	/* for (i = 0; i < n; i++) printf("%d,", v[i]);	*/
	  printf("\n");
	}
	printf("%d: cycle check starts\n", depth);
#endif
	now._a_t |= (1|16|32);
	/* 1 = 2nd DFS; (16|32) to help hasher */
#ifndef NOFAIR
	now._cnt[1] = now._cnt[0];
#endif
	memcpy((char *)&A_Root, (char *)&now, vsize);
	A_depth = depthfound = depth;
#if NCORE>1
	mem_put_acc();
#else
	new_state();	/* start 2nd DFS */
#endif
	now._a_t = o_a_t;
#ifndef NOFAIR
	now._cnt[1] = o_cnt;
#endif
	A_depth = 0; depthfound = -1;
#ifdef DEBUG
	printf("%d: cycle check returns\n", depth);
#endif
#ifdef FULLSTACK
#ifndef MA
	trpt->ostate = sv;	/* restore */
#else
	trpt->proviso = prov;
#endif
#endif
}
#endif

#if defined(FULLSTACK) && defined(BITSTATE)
struct H_el *Free_list = (struct H_el *) 0;
void
onstack_init(void)	/* to store stack states in a bitstate search */
{	S_Tab = (struct H_el **) emalloc(maxdepth*sizeof(struct H_el *));
}
struct H_el *
grab_state(int n)
{	struct H_el *v, *last = 0;
	if (H_tab == S_Tab)
	{	for (v = Free_list; v && ((int) v->tagged >= n); v=v->nxt)
		{	if ((int) v->tagged == n)
			{	if (last)
					last->nxt = v->nxt;
				else
gotcha:				Free_list = v->nxt;
				v->tagged = 0;
				v->nxt = 0;
#ifdef COLLAPSE
				v->ln = 0;
#endif
				return v;
			}
			Fh++; last=v;
		}
		/* new: second try */
		v = Free_list;
		if (v && ((int) v->tagged >= n))
			goto gotcha;
		ngrabs++;
	}
	return (struct H_el *)
	      emalloc(sizeof(struct H_el)+n-sizeof(unsigned));
}

#else
#if NCORE>1
struct H_el *
grab_state(int n)
{	struct H_el *grab_shared(int);
	return grab_shared(sizeof(struct H_el)+n-sizeof(unsigned));
}
#else
 #ifndef AUTO_RESIZE
  #define grab_state(n) (struct H_el *) \
		emalloc(sizeof(struct H_el)+n-sizeof(unsigned long));
 #else
  struct H_el *
  grab_state(int n)
  {	struct H_el *p;
	int cnt = sizeof(struct H_el)+n-sizeof(unsigned long);

	if (reclaim_size >= cnt+WS)
	{	if ((cnt & (WS-1)) != 0) /* alignment */
		{	cnt += WS - (cnt & (WS-1));
		}
		p = (struct H_el *) reclaim_mem;
		reclaim_mem  += cnt;
		reclaim_size -= cnt;
		memset(p, 0, cnt);
	} else
	{	p = (struct H_el *) emalloc(cnt);
	}
	return p;
  }
 #endif
#endif
#endif
#ifdef COLLAPSE
unsigned long
ordinal(char *v, long n, short tp)
{	struct H_el *tmp, *ntmp; long m;
	struct H_el *olst = (struct H_el *) 0;
	s_hash((uchar *)v, n);
#if NCORE>1 && !defined(SEP_STATE)
	enter_critical(CS_ID);	/* uses spinlock - 1..128 */
#endif
	tmp = H_tab[j1_spin];
	if (!tmp)
	{	tmp = grab_state(n);
		H_tab[j1_spin] = tmp;
	} else
	for ( ;; olst = tmp, tmp = tmp->nxt)
	{	m = memcmp(((char *)&(tmp->state)), v, n);
		if (n == tmp->ln)
		{
			if (m == 0)
				goto done;
			if (m < 0)
			{
Insert:			ntmp = grab_state(n);
				ntmp->nxt = tmp;
				if (!olst)
					H_tab[j1_spin] = ntmp;
				else
					olst->nxt = ntmp;
				tmp = ntmp;
				break;
			} else if (!tmp->nxt)
			{
Append:			tmp->nxt = grab_state(n);
				tmp = tmp->nxt;
				break;
			}
			continue;
		}
		if (n < tmp->ln)
			goto Insert;
		else if (!tmp->nxt)
			goto Append;
	}
	m = ++ncomps[tp];
#ifdef FULLSTACK
	tmp->tagged = m;
#else
	tmp->st_id  = m;
#endif
#if defined(AUTO_RESIZE) && !defined(BITSTATE)
	tmp->m_K1 = K1;
#endif
	memcpy(((char *)&(tmp->state)), v, n);
	tmp->ln = n;
done:
#if NCORE>1 && !defined(SEP_STATE)
	leave_critical(CS_ID);	/* uses spinlock */
#endif
#ifdef FULLSTACK
	return tmp->tagged;
#else
	return tmp->st_id;
#endif
}

int
compress(char *vin, int nin)	/* collapse compression */
{	char	*w, *v = (char *) &comp_now;
	int	i, j;
	unsigned long	n;
	static char	*x;
	static uchar	nbytes[513]; /* 1 + 256 + 256 */
	static unsigned	short nbytelen;
	long col_q(int, char *);
	long col_p(int, char *);
#ifndef SAFETY
	if (a_cycles)
		*v++ = now._a_t;
#ifndef NOFAIR
	if (fairness)
	for (i = 0; i < NFAIR; i++)
		*v++ = now._cnt[i];
#endif
#endif
	nbytelen = 0;
#ifndef JOINPROCS
	for (i = 0; i < (int) now._nr_pr; i++)
	{	n = col_p(i, (char *) 0);
#ifdef NOFIX
		nbytes[nbytelen] = 0;
#else
		nbytes[nbytelen] = 1;
		*v++ = ((P0 *) pptr(i))->_t;
#endif
		*v++ = n&255;
		if (n >= (1<<8))
		{	nbytes[nbytelen]++;
			*v++ = (n>>8)&255;
		}
		if (n >= (1<<16))
		{	nbytes[nbytelen]++;
			*v++ = (n>>16)&255;
		}
		if (n >= (1<<24))
		{	nbytes[nbytelen]++;
			*v++ = (n>>24)&255;
		}
		nbytelen++;
	}
#else
	x = scratch;
	for (i = 0; i < (int) now._nr_pr; i++)
		x += col_p(i, x);
	n = ordinal(scratch, x-scratch, 2); /* procs */
	*v++ = n&255;
	nbytes[nbytelen] = 0;
	if (n >= (1<<8))
	{	nbytes[nbytelen]++;
		*v++ = (n>>8)&255;
	}
	if (n >= (1<<16))
	{	nbytes[nbytelen]++;
		*v++ = (n>>16)&255;
	}
	if (n >= (1<<24))
	{	nbytes[nbytelen]++;
		*v++ = (n>>24)&255;
	}
	nbytelen++;
#endif
#ifdef SEPQS
	for (i = 0; i < (int) now._nr_qs; i++)
	{	n = col_q(i, (char *) 0);
		nbytes[nbytelen] = 0;
		*v++ = n&255;
		if (n >= (1<<8))
		{	nbytes[nbytelen]++;
			*v++ = (n>>8)&255;
		}
		if (n >= (1<<16))
		{	nbytes[nbytelen]++;
			*v++ = (n>>16)&255;
		}
		if (n >= (1<<24))
		{	nbytes[nbytelen]++;
			*v++ = (n>>24)&255;
		}
		nbytelen++;
	}
#endif
#ifdef NOVSZ
	/* 3 = _a_t, _nr_pr, _nr_qs */
	w = (char *) &now + 3 * sizeof(uchar);
#ifndef NOFAIR
	w += NFAIR;
#endif
#else
#if VECTORSZ<65536
	w = (char *) &(now._vsz) + sizeof(unsigned short);
#else
	w = (char *) &(now._vsz) + sizeof(unsigned long);
#endif
#endif
	x = scratch;
	*x++ = now._nr_pr;
	*x++ = now._nr_qs;
	if (now._nr_qs > 0 && qptr(0) < pptr(0))
		n = qptr(0) - (uchar *) w;
	else
		n = pptr(0) - (uchar *) w;
	j = w - (char *) &now;
	for (i = 0; i < (int) n; i++, w++)
		if (!Mask[j++]) *x++ = *w;
#ifndef SEPQS
	for (i = 0; i < (int) now._nr_qs; i++)
		x += col_q(i, x);
#endif
	x--;
	for (i = 0, j = 6; i < nbytelen; i++)
	{	if (j == 6)
		{	j = 0;
			*(++x) = 0;
		} else
			j += 2;
		*x |= (nbytes[i] << j);
	}
	x++;
	for (j = 0; j < WS-1; j++)
		*x++ = 0;
	x -= j; j = 0;
	n = ordinal(scratch, x-scratch, 0); /* globals */
	*v++ = n&255;
	if (n >= (1<< 8)) { *v++ = (n>> 8)&255; j++; }
	if (n >= (1<<16)) { *v++ = (n>>16)&255; j++; }
	if (n >= (1<<24)) { *v++ = (n>>24)&255; j++; }
	*v++ = j;	/* add last count as a byte */
	for (i = 0; i < WS-1; i++)
		*v++ = 0;
	v -= i;
#if 0
	printf("collapse %d -> %d\n",
		vsize, v - (char *)&comp_now);
#endif
	return v - (char *)&comp_now;
}
#else
#if !defined(NOCOMP)
int
compress(char *vin, int n)	/* default compression */
{
#ifdef HC
	int delta = 0;
	s_hash((uchar *)vin, n); /* sets K1 and K2 */
#ifndef SAFETY
	if (S_A)
	{	delta++;	/* _a_t  */
#ifndef NOFAIR
		if (S_A > NFAIR)
			delta += NFAIR;	/* _cnt[] */
#endif
	}
#endif
	memcpy((char *) &comp_now + delta, (char *) &K1, WS);
	delta += WS;
#if HC>0
	memcpy((char *) &comp_now + delta, (char *) &K2, HC);
	delta += HC;
#endif
	return delta;
#else
	char *vv = vin;
	char *v = (char *) &comp_now;
	int i;
  #ifndef NO_FAST_C
	int r = 0, unroll = n/8;
	if (unroll > 0)
	{	i = 0;
		while (r++ < unroll)
		{	/* unroll 8 times, avoid ifs */
	/* 1 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 2 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 3 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 4 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 5 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 6 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 7 */		*v = *vv++;
			v += 1 - Mask[i++];
	/* 8 */		*v = *vv++;
			v += 1 - Mask[i++];
		}
		r = n - i; /* the rest, at most 7 */
		switch (r) {
		case 7: *v = *vv++; v += 1 - Mask[i++];
		case 6: *v = *vv++; v += 1 - Mask[i++];
		case 5: *v = *vv++; v += 1 - Mask[i++];
		case 4: *v = *vv++; v += 1 - Mask[i++];
		case 3: *v = *vv++; v += 1 - Mask[i++];
		case 2: *v = *vv++; v += 1 - Mask[i++];
		case 1: *v = *vv++; v += 1 - Mask[i++];
		case 0: break;
		}
		r = (n+WS-1)/WS; /* words rounded up */
		r *= WS;	 /* bytes */
		i = r - i;	 /* remainder */
		switch (i) {
		case 7: *v++ = 0;    /* fall thru */
		case 6: *v++ = 0;
		case 5: *v++ = 0;
		case 4: *v++ = 0;
		case 3: *v++ = 0;
		case 2: *v++ = 0;
		case 1: *v++ = 0;
		case 0: break;
		default: Uerror("unexpected wordsize");
		}
		v -= i;
	} else
  #endif
	{	for (i = 0; i < n; i++, vv++)
			if (!Mask[i]) *v++ = *vv;
		for (i = 0; i < WS-1; i++)
			*v++ = 0;
		v -= i;
	}
#if 0
	printf("compress %d -> %d\n",
		n, v - (char *)&comp_now);
#endif
	return v - (char *)&comp_now;
#endif
}
#endif
#endif
#if defined(FULLSTACK) && defined(BITSTATE)
#if defined(MA)
#if !defined(onstack_now)
int  onstack_now(void) {}
#endif
#if !defined(onstack_put)
void onstack_put(void) {}
#endif
#if !defined(onstack_zap)
void onstack_zap(void) {}
#endif
#else
void
onstack_zap(void)
{	struct H_el *v, *w, *last = 0;
	struct H_el **tmp = H_tab;
	char *nv; int n, m;
	static char warned = 0;
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	uchar was_last = now._last;
	now._last = 0;
#endif

	H_tab = S_Tab;
#ifndef NOCOMP
	nv = (char *) &comp_now;
	n = compress((char *)&now, vsize);
#else
#if defined(BITSTATE) && defined(LC)
	nv = (char *) &comp_now;
	n = compact_stack((char *)&now, vsize);
#else
	nv = (char *) &now;
	n = vsize;
#endif
#endif
#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
	s_hash((uchar *)nv, n);
#endif
	H_tab = tmp;
	for (v = S_Tab[j1_spin]; v; Zh++, last=v, v=v->nxt)
	{	m = memcmp(&(v->state), nv, n);
		if (m == 0)
			goto Found;
		if (m < 0)
			break;
	}
/* NotFound: */
#ifndef ZAPH
	#if defined(BITSTATE) && NCORE>1
	/* seen this happen, likely harmless, but not yet understood */
	if (warned == 0)
	#endif
	{	/* Uerror("stack out of wack - zap"); */
		cpu_printf("pan: warning, stack incomplete\n");
		warned = 1;
	}
#endif
	goto done;
Found:
	ZAPS++;
	if (last)
		last->nxt = v->nxt;
	else
		S_Tab[j1_spin] = v->nxt;
	v->tagged = (unsigned) n;
#if !defined(NOREDUCE) && !defined(SAFETY)
	v->proviso = 0;
#endif
	v->nxt = last = (struct H_el *) 0;
	for (w = Free_list; w; Fa++, last=w, w = w->nxt)
	{	if ((int) w->tagged <= n)
		{	if (last)
			{	v->nxt = w;
				last->nxt = v;
			} else
			{	v->nxt = Free_list;
				Free_list = v;
			}
			goto done;
		}
		if (!w->nxt)
		{	w->nxt = v;
			goto done;
	}	}
	Free_list = v;
done:
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	now._last = was_last;
#endif
	return;
}
void
onstack_put(void)
{	struct H_el **tmp = H_tab;
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	uchar was_last = now._last;
	now._last = 0;
#endif
	H_tab = S_Tab;
	if (hstore((char *)&now, vsize) != 0)
#if defined(BITSTATE) && defined(LC)
		printf("pan: warning, double stack entry\n");
#else
	#ifndef ZAPH
		Uerror("cannot happen - unstack_put");
	#endif
#endif
	H_tab = tmp;
	trpt->ostate = Lstate;
	PUT++;
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	now._last = was_last;
#endif
}
int
onstack_now(void)
{	struct H_el *tmp;
	struct H_el **tmp2 = H_tab;
	char *v; int n, m = 1;

#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	uchar was_last = now._last;
	now._last = 0;
#endif
	H_tab = S_Tab;
#ifdef NOCOMP
#if defined(BITSTATE) && defined(LC)
	v = (char *) &comp_now;
	n = compact_stack((char *)&now, vsize);
#else
	v = (char *) &now;
	n = vsize;
#endif
#else
	v = (char *) &comp_now;
	n = compress((char *)&now, vsize);
#endif
#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
	s_hash((uchar *)v, n);
#endif
	H_tab = tmp2;
	for (tmp = S_Tab[j1_spin]; tmp; Zn++, tmp = tmp->nxt)
	{	m = memcmp(((char *)&(tmp->state)),v,n);
		if (m <= 0)
		{	Lstate = (struct H_el *) tmp;
			break;
	}	}
	PROBE++;
#if defined(BCS) && defined(NO_LAST) && defined(HAS_LAST)
	now._last = was_last;
#endif
	return (m == 0);
}
#endif
#endif
#ifndef BITSTATE
void
hinit(void)
{
  #ifdef MA
#ifdef R_XPT
	{	void r_xpoint(void);
		r_xpoint();
	}
#else
	dfa_init((unsigned short) (MA+a_cycles));
#if NCORE>1 && !defined(COLLAPSE)
	if (!readtrail)
	{	void init_HT(unsigned long);
		init_HT(0L);
	}
#endif
#endif
  #endif
  #if !defined(MA) || defined(COLLAPSE)
#if NCORE>1
	if (!readtrail)
	{	void init_HT(unsigned long);
		init_HT((unsigned long) (ONE_L<<ssize)*sizeof(struct H_el *));
	} else
#endif
	H_tab = (struct H_el **)
		emalloc((ONE_L<<ssize)*sizeof(struct H_el *));
  #endif
}
#endif

#if !defined(BITSTATE) || defined(FULLSTACK)
#ifdef DEBUG
void
dumpstate(int wasnew, char *v, int n, int tag)
{	int i;
#ifndef SAFETY
	if (S_A)
	{	printf("	state tags %d (%d::%d): ",
			V_A, wasnew, v[0]);
#ifdef FULLSTACK
		printf(" %d ", tag);
#endif
		printf("\n");
	}
#endif
#ifdef SDUMP
#ifndef NOCOMP
	printf("	 State: ");
	for (i = 0; i < vsize; i++) printf("%d%s,",
		((char *)&now)[i], Mask[i]?"*":"");
#endif
	printf("\n	Vector: ");
	for (i = 0; i < n; i++) printf("%d,", v[i]);
	printf("\n");
#endif
}
#endif
#ifdef MA
int
gstore(char *vin, int nin, uchar pbit)
{	int n, i;
	int ret_val = 1;
	uchar *v;
	static uchar Info[MA+1];
#ifndef NOCOMP
	n = compress(vin, nin);
	v = (uchar *) &comp_now;
#else
	n = nin;
	v = (uchar *) vin;
#endif
	if (n >= MA)
	{	printf("pan: error, MA too small, recompile pan.c");
		printf(" with -DMA=N with N>%d\n", n);
		Uerror("aborting");
	}
	if (n > (int) maxgs)
	{	maxgs = (unsigned int) n;
	}
	for (i = 0; i < n; i++)
	{	Info[i] = v[i];
	}
	for ( ; i < MA-1; i++)
	{	Info[i] = 0;
	}
	Info[MA-1] = pbit;
	if (a_cycles)	/* place _a_t at the end */
	{	Info[MA] = Info[0];
		Info[0]  = 0;
	}

#if NCORE>1 && !defined(SEP_STATE)
	enter_critical(GLOBAL_LOCK); /* crude, but necessary */
	/* to make this mode work, also replace emalloc with grab_shared inside store MA routines */
#endif

	if (!dfa_store(Info))
	{	if (pbit == 0
		&& (now._a_t&1)
		&&  depth > A_depth)
		{	Info[MA] &= ~(1|16|32);	/* _a_t */
			if (dfa_member(MA))
			{	Info[MA-1] = 4; /* off-stack bit */
				nShadow++;
				if (!dfa_member(MA-1))
				{	ret_val = 3;
			#ifdef VERBOSE
					printf("intersected 1st dfs stack\n");
			#endif
					goto done;
		}	}	}
		ret_val = 0;
	#ifdef VERBOSE
		printf("new state\n");
	#endif
		goto done;
	}
#ifdef FULLSTACK
	if (pbit == 0)
	{	Info[MA-1] = 1;	/* proviso bit */
#ifndef BFS
		trpt->proviso = dfa_member(MA-1);
#endif
		Info[MA-1] = 4;	/* off-stack bit */
		if (dfa_member(MA-1))
		{	ret_val = 1; /* off-stack */
	#ifdef VERBOSE
			printf("old state\n");
	#endif
		} else
		{	ret_val = 2; /* on-stack */
	#ifdef VERBOSE
			printf("on-stack\n");
	#endif
		}
		goto done;
	}
#endif
	ret_val = 1;
#ifdef VERBOSE
	printf("old state\n");
#endif
done:
#if NCORE>1 && !defined(SEP_STATE)
	leave_critical(GLOBAL_LOCK);
#endif
	return ret_val;	/* old state */
}
#endif
#if defined(BITSTATE) && defined(LC)
int
compact_stack(char *vin, int n)
{	int delta = 0;
	s_hash((uchar *)vin, n); /* sets K1 and K2 */
#ifndef SAFETY
	delta++;	/* room for state[0] |= 128 */
#endif
	memcpy((char *) &comp_now + delta, (char *) &K1, WS);
	delta += WS;
	memcpy((char *) &comp_now + delta, (char *) &K2, WS);
	delta += WS; /* use all available bits */
	return delta;
}
#endif
int
hstore(char *vin, int nin)	/* hash table storage */
{	struct H_el *ntmp;
	struct H_el *tmp, *olst = (struct H_el *) 0;
	char *v; int n, m=0;
#ifdef HC
	uchar rem_a;
#endif
#ifdef NOCOMP
#if defined(BITSTATE) && defined(LC)
	if (S_Tab == H_tab)
	{	v = (char *) &comp_now;
		n = compact_stack(vin, nin);
	} else
	{	v = vin; n = nin;
	}
#else
	v = vin; n = nin;
#endif
#else
	v = (char *) &comp_now;
	#ifdef HC
	rem_a = now._a_t;
	now._a_t = 0;
	#endif
	n = compress(vin, nin);
	#ifdef HC
	now._a_t = rem_a;
	#endif
#ifndef SAFETY
	if (S_A)
	{	v[0] = 0;	/* _a_t  */
#ifndef NOFAIR
		if (S_A > NFAIR)
		for (m = 0; m < NFAIR; m++)
			v[m+1] = 0;	/* _cnt[] */
#endif
		m = 0;
	}
	#endif
#endif
#if !defined(HC) && !(defined(BITSTATE) && defined(LC))
	s_hash((uchar *)v, n);
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
	enter_critical(CS_ID);	/* uses spinlock */
#endif
	tmp = H_tab[j1_spin];
	if (!tmp)
	{  tmp = grab_state(n);
#if NCORE>1
	   if (!tmp)
	   {	/* if we get here -- we've already issued a warning */
		/* but we want to allow the normal distributed termination */
		/* to collect the stats on all cpus in the wrapup */
	#if !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
	#endif
		return 1; /* allow normal termination */
	   }
#endif
	   H_tab[j1_spin] = tmp;
	} else
	{  for (;; hcmp++, olst = tmp, tmp = tmp->nxt)
	   {   /* skip the _a_t and the _cnt bytes */
#ifdef COLLAPSE
		if (tmp->ln != 0)
		{	if (!tmp->nxt) goto Append;
			continue;
		}
#endif
		m = memcmp(((char *)&(tmp->state)) + S_A, 
			v + S_A, n - S_A);
		if (m == 0) {
#ifdef SAFETY
#define wasnew	0
#else
		int wasnew = 0;
#endif
#ifndef SAFETY
#ifndef NOCOMP
		if (S_A)
		{ if ((((char *)&(tmp->state))[0] & V_A) != V_A)
		  {	wasnew = 1; nShadow++;
			((char *)&(tmp->state))[0] |= V_A;
		  }
#ifndef NOFAIR
		  if (S_A > NFAIR)
		  {	/* 0 <= now._cnt[now._a_t&1] < MAXPROC */
			unsigned ci, bp; /* index, bit pos */
			ci = (now._cnt[now._a_t&1] / 8);
			bp = (now._cnt[now._a_t&1] - 8*ci);
			if (now._a_t&1)	/* use tail-bits in _cnt */
			{	ci = (NFAIR - 1) - ci;
				bp = 7 - bp; /* bp = 0..7 */
			}
			ci++;	/* skip over _a_t */
			bp = 1 << bp;	/* the bit mask */
			if ((((char *)&(tmp->state))[ci] & bp)==0)
			{	if (!wasnew)
				{	wasnew = 1;
					nShadow++;
				}
				((char *)&(tmp->state))[ci] |= bp;
			}
		   }
		   /* else: wasnew == 0, i.e., old state */
#endif
		}
#endif
#endif
#if NCORE>1
		Lstate = (struct H_el *) tmp;
#endif
#ifdef FULLSTACK
#ifndef SAFETY
		if (wasnew)
		{	Lstate = (struct H_el *) tmp;
			tmp->tagged |= V_A;
			if ((now._a_t&1)
			&& (tmp->tagged&A_V)
			&& depth > A_depth)
			{
intersect:
#ifdef CHECK
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
	printf("1st dfs-stack intersected on state %d+\n",
		(int) tmp->st_id);
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
				leave_critical(CS_ID);
#endif
				return 3;
			}
#ifdef CHECK
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
	printf("	New state %d+\n", (int) tmp->st_id);
#endif
#ifdef DEBUG
	dumpstate(1, (char *)&(tmp->state),n,tmp->tagged);
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
			leave_critical(CS_ID);
#endif
			return 0;
		} else
#endif
		if ((S_A)?(tmp->tagged&V_A):tmp->tagged)
		{	Lstate = (struct H_el *) tmp;
#ifndef SAFETY
			/* already on current dfs stack */
			/* but may also be on 1st dfs stack */
			if ((now._a_t&1)
			&& (tmp->tagged&A_V)
			&& depth > A_depth
#ifndef NOFAIR
			&& (!fairness || now._cnt[1] <= 1)
#endif
			)
				goto intersect;
#endif
#ifdef CHECK
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
	printf("	Stack state %d\n", (int) tmp->st_id);
#endif
#ifdef DEBUG
	dumpstate(0, (char *)&(tmp->state),n,tmp->tagged);
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
			leave_critical(CS_ID);
#endif
			return 2; /* match on stack */
		}
#else
		if (wasnew)
		{
#ifdef CHECK
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
	printf("	New state %d+\n", (int) tmp->st_id);
#endif
#ifdef DEBUG
	dumpstate(1, (char *)&(tmp->state), n, 0);
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
			leave_critical(CS_ID);
#endif
			return 0;
		}
#endif
#ifdef CHECK
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
	printf("	Old state %d\n", (int) tmp->st_id);
#endif
#ifdef DEBUG
	dumpstate(0, (char *)&(tmp->state), n, 0);
#endif
#if defined(BCS)
  #ifdef CONSERVATIVE
	if (tmp->ctx_low > trpt->sched_limit)
	{	tmp->ctx_low = trpt->sched_limit;
		tmp->ctx_pid[(now._last)/8] = 1 << ((now._last)); /* new */
	#ifdef CHECK
		#if NCORE>1
		printf("cpu%d: ", core_id);
		#endif
		printf("		Revisit with fewer context switches\n");
	#endif
		nstates--;
		#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
		#endif
		return 0;
	} else if ((tmp->ctx_low == trpt->sched_limit
	&&   (tmp->ctx_pid[(now._last)/8] & ( 1 << ((now._last)) )) == 0 ))
	{	tmp->ctx_pid[(now._last)/8] |= 1 << ((now._last)); /* add */
	#ifdef CHECK
		#if NCORE>1
		printf("cpu%d: ", core_id);
		#endif
		printf("		Revisit with same nr of context switches\n");
	#endif
		nstates--;
		#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
		#endif
		return 0;
	}
  #endif
#endif
#ifdef REACH
	if (tmp->D > depth)
	{	tmp->D = depth;
	#ifdef CHECK
		#if NCORE>1
		printf("cpu%d: ", core_id);
		#endif
		printf("		ReVisiting (from smaller depth)\n");
	#endif
		nstates--;
	#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
	#endif
		return 0;
	}
#endif
#if (defined(BFS) && defined(Q_PROVISO)) || NCORE>1
		Lstate = (struct H_el *) tmp;
#endif
#if NCORE>1 && !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
#endif
		return 1; /* match outside stack */
	       } else if (m < 0)
	       {	/* insert state before tmp */
			ntmp = grab_state(n);
#if NCORE>1
			if (!ntmp)
			{
	#if !defined(SEP_STATE) && !defined(BITSTATE)
				leave_critical(CS_ID);
	#endif
				return 1;  /* allow normal termination */
			}
#endif
			ntmp->nxt = tmp;
			if (!olst)
				H_tab[j1_spin] = ntmp;
			else
				olst->nxt = ntmp;
			tmp = ntmp;
			break;
	       } else if (!tmp->nxt)
	       {	/* append after tmp */
#ifdef COLLAPSE
Append:
#endif
			tmp->nxt = grab_state(n);
#if NCORE>1
			if (!tmp->nxt)
			{
	#if !defined(SEP_STATE) && !defined(BITSTATE)
				leave_critical(CS_ID);
	#endif
				return 1;  /* allow normal termination */
			}
#endif
			tmp = tmp->nxt;
			break;
	   }   }
	}
#ifdef CHECK
	tmp->st_id = (unsigned) nstates;
#if NCORE>1
	printf("cpu%d: ", core_id);
#endif
#ifdef BITSTATE
	printf("	Push state %d\n", ((int) nstates) - 1);
#else
	printf("	New state %d\n", (int) nstates);
#endif
#endif
#if defined(BCS)
	tmp->ctx_low = trpt->sched_limit;
	#ifdef CONSERVATIVE
	tmp->ctx_pid[(now._last)/8] = 1 << ((now._last)); /* new limit */
	#endif
#endif
#if !defined(SAFETY) || defined(REACH)
	tmp->D = depth;
#endif
#ifndef SAFETY
#ifndef NOCOMP
	if (S_A)
	{	v[0] = V_A;
#ifndef NOFAIR
		if (S_A > NFAIR)
		{	unsigned ci, bp; /* as above */
			ci = (now._cnt[now._a_t&1] / 8);
			bp = (now._cnt[now._a_t&1] - 8*ci);
			if (now._a_t&1)
			{	ci = (NFAIR - 1) - ci;
				bp = 7 - bp; /* bp = 0..7 */
			}
			v[1+ci] = 1 << bp;
		}
#endif
	}
#endif
#endif
#if defined(AUTO_RESIZE) && !defined(BITSTATE)
	tmp->m_K1 = K1;
#endif
	memcpy(((char *)&(tmp->state)), v, n);
#ifdef FULLSTACK
	tmp->tagged = (S_A)?V_A:(depth+1);
#ifdef DEBUG
		dumpstate(-1, v, n, tmp->tagged);
#endif
	Lstate = (struct H_el *) tmp;
#else
	#ifdef DEBUG
		dumpstate(-1, v, n, 0);
	#endif
	#if NCORE>1
		Lstate = (struct H_el *) tmp;
	#endif
#endif
/* #if NCORE>1 && !defined(SEP_STATE) */
#if NCORE>1
	#ifdef V_PROVISO
		tmp->cpu_id = core_id;
	#endif
	#if !defined(SEP_STATE) && !defined(BITSTATE)
		leave_critical(CS_ID);
	#endif
#endif
	return 0;
}
#endif
#include TRANSITIONS
void
do_reach(void)
{
	r_ck(reached0, nstates0, 0, src_ln0, src_file0);
	r_ck(reached1, nstates1, 1, src_ln1, src_file1);
}

void
iniglobals(void)
{
		now.turn = 0;
	{	int l_in;
		for (l_in = 0; l_in < 2; l_in++)
		{
			now.flag[l_in] = 0;
		}
	}
		now.channel = addqueue(1, 1);
#ifdef VAR_RANGES
		logval("turn", now.turn);
	{	int l_in;
		for (l_in = 0; l_in < 2; l_in++)
		{
			logval("flag[l_in]", now.flag[l_in]);
		}
	}
#endif
	Maxbody = max(Maxbody, ((int) sizeof(Q1)));
	Maxbody = max(Maxbody, sizeof(State)-VECTORSZ);
}

int
addqueue(int n, int is_rv)
{	int j=0, i = now._nr_qs;
#ifndef NOCOMP
	int k;
#endif
	if (i >= MAXQ)
		Uerror("too many queues");
	switch (n) {
	case 1: j = sizeof(Q1); q_flds[1] = 3; q_max[1] = 1; break;
	default: Uerror("bad queue - addqueue");
	}
	if (vsize%WS)
		q_skip[i] = WS-(vsize%WS);
	else
		q_skip[i] = 0;
#ifndef NOCOMP
	k = vsize;
#ifndef BFS
	if (is_rv) k += j;
#endif
	for (k += (int) q_skip[i]; k > vsize; k--)
		Mask[k-1] = 1;
#endif
	vsize += (int) q_skip[i];
	q_offset[i] = vsize;
	now._nr_qs += 1;
	vsize += j;
#ifndef NOVSZ
	now._vsz = vsize;
#endif
	hmax = max(hmax, vsize);
	if (vsize >= VECTORSZ)
		Uerror("VECTORSZ is too small, edit pan.h");
	if (j > 0)
	{	memset((char *)qptr(i), 0, j);
	}
	((Q0 *)qptr(i))->_t = n;
	return i+1;
}

#if NQS>0
void
qsend(int into, int sorted, int fld0, int fld1, int fld2, int args_given)
{	int j; uchar *z;

#ifdef HAS_SORTED
	int k;
#endif
	if (!into--)
	uerror("ref to uninitialized chan name (sending)");
	if (into >= (int) now._nr_qs || into < 0)
		Uerror("qsend bad queue#");
	z = qptr(into);
	j = ((Q0 *)qptr(into))->Qlen;
	switch (((Q0 *)qptr(into))->_t) {
	case 1: /* =rv= */
		(trpt+2)->o_m = 0;
#ifdef HAS_SORTED
		(trpt+1)->ipt = j;
#endif
		((Q1 *)z)->Qlen = ((Q1 *)z)->Qlen + 1;
		((Q1 *)z)->contents[j].fld0 = fld0;
		((Q1 *)z)->contents[j].fld1 = fld1;
		((Q1 *)z)->contents[j].fld2 = fld2;
		if (args_given != 3)
		{	if (args_given > 3)
				uerror("too many parameters in send stmnt");
			else
				uerror("too few parameters in send stmnt");
		}
		break;
	case 0: printf("queue %d was deleted\n", into+1);
	default: Uerror("bad queue - qsend");
	}
#ifdef EVENT_TRACE
	if (in_s_scope(into+1))
		require('s', into);
#endif
}
#endif

#if SYNC
int
q_zero(int from)
{	if (!from--)
	{	uerror("ref to uninitialized chan name (q_zero)");
		return 0;
	}
	switch(((Q0 *)qptr(from))->_t) {
	case 1: return 1;
	case 0: printf("queue %d was deleted\n", from+1);
	}
	Uerror("bad queue q-zero");
	return -1;
}
int
not_RV(int from)
{	if (q_zero(from))
	{	printf("==>> a test of the contents of a rv ");
		printf("channel always returns FALSE\n");
		uerror("error to poll rendezvous channel");
	}
	return 1;
}
#endif
#ifndef XUSAFE
void
setq_claim(int x, int m, char *s, int y, char *p)
{	if (x == 0)
	uerror("x[rs] claim on uninitialized channel");
	if (x < 0 || x > MAXQ)
		Uerror("cannot happen setq_claim");
	q_claim[x] |= m;
	p_name[y] = p;
	q_name[x] = s;
	if (m&2) q_S_check(x, y);
	if (m&1) q_R_check(x, y);
}
short q_sender[MAXQ+1];
int
q_S_check(int x, int who)
{	if (!q_sender[x])
	{	q_sender[x] = who+1;
#if SYNC
		if (q_zero(x))
		{	printf("chan %s (%d), ",
				q_name[x], x-1);
			printf("sndr proc %s (%d)\n",
				p_name[who], who);
			uerror("xs chans cannot be used for rv");
		}
#endif
	} else
	if (q_sender[x] != who+1)
	{	printf("pan: xs assertion violated: ");
		printf("access to chan <%s> (%d)\npan: by ",
			q_name[x], x-1);
		if (q_sender[x] > 0 && p_name[q_sender[x]-1])
			printf("%s (proc %d) and by ",
			p_name[q_sender[x]-1], q_sender[x]-1);
		printf("%s (proc %d)\n",
			p_name[who], who);
		uerror("error, partial order reduction invalid");
	}
	return 1;
}
short q_recver[MAXQ+1];
int
q_R_check(int x, int who)
{	if (!q_recver[x])
	{	q_recver[x] = who+1;
#if SYNC
		if (q_zero(x))
		{	printf("chan %s (%d), ",
				q_name[x], x-1);
			printf("recv proc %s (%d)\n",
				p_name[who], who);
			uerror("xr chans cannot be used for rv");
		}
#endif
	} else
	if (q_recver[x] != who+1)
	{	printf("pan: xr assertion violated: ");
		printf("access to chan %s (%d)\npan: ",
			q_name[x], x-1);
		if (q_recver[x] > 0 && p_name[q_recver[x]-1])
			printf("by %s (proc %d) and ",
			p_name[q_recver[x]-1], q_recver[x]-1);
		printf("by %s (proc %d)\n",
			p_name[who], who);
		uerror("error, partial order reduction invalid");
	}
	return 1;
}
#endif
int
q_len(int x)
{	if (!x--)
	uerror("ref to uninitialized chan name (len)");
	return ((Q0 *)qptr(x))->Qlen;
}

int
q_full(int from)
{	if (!from--)
	uerror("ref to uninitialized chan name (qfull)");
	switch(((Q0 *)qptr(from))->_t) {
	case 1: return (q_sz(from) == 1);
	case 0: printf("queue %d was deleted\n", from+1);
	}
	Uerror("bad queue - q_full");
	return 0;
}

#ifdef HAS_UNLESS
int
q_e_f(int from)
{	/* empty or full */
	return !q_len(from) || q_full(from);
}
#endif
#if NQS>0
int
qrecv(int from, int slot, int fld, int done)
{	uchar *z;
	int j, k, r=0;

	if (!from--)
	uerror("ref to uninitialized chan name (receiving)");
	if (from >= (int) now._nr_qs || from < 0)
		Uerror("qrecv bad queue#");
	z = qptr(from);
#ifdef EVENT_TRACE
	if (done && (in_r_scope(from+1)))
		require('r', from);
#endif
	switch (((Q0 *)qptr(from))->_t) {
	case 1: /* =rv= */
		switch (fld) {
		case 0: r = ((Q1 *)z)->contents[slot].fld0; break;
		case 1: r = ((Q1 *)z)->contents[slot].fld1; break;
		case 2: r = ((Q1 *)z)->contents[slot].fld2; break;
		default: Uerror("too many fields in recv");
		}
		if (done)
		{	j = ((Q1 *)z)->Qlen - 1;
			((Q1 *)z)->Qlen = 0;
			((Q1 *)z)->contents[j].fld0 = 0;
			((Q1 *)z)->contents[j].fld1 = 0;
			((Q1 *)z)->contents[j].fld2 = 0;
			if (fld+1 != 3)
				uerror("missing pars in receive");
		}
		break;
	case 0: printf("queue %d was deleted\n", from+1);
	default: Uerror("bad queue - qrecv");
	}
	return r;
}
#endif

#ifndef BITSTATE
#ifdef COLLAPSE
long
col_q(int i, char *z)
{	int j=0, k;
	char *x, *y;
	Q0 *ptr = (Q0 *) qptr(i);
	switch (ptr->_t) {
	case 1: j = sizeof(Q1); break;
	default: Uerror("bad qtype - collapse");
	}
	if (z) x = z; else x = scratch;
	y = (char *) ptr; k = q_offset[i];
	/* no need to store the empty slots at the end */
	j -= (q_max[ptr->_t] - ptr->Qlen) * ((j - 2)/q_max[ptr->_t]);
	for ( ; j > 0; j--, y++)
		if (!Mask[k++]) *x++ = *y;
	for (j = 0; j < WS-1; j++)
		*x++ = 0;
	x -= j;
	if (z) return (long) (x - z);
	return ordinal(scratch, x-scratch, 1); /* chan */
}
#endif
#endif
int 
unsend(int into)
{	int _m=0, j; uchar *z;

#ifdef HAS_SORTED
	int k;
#endif
	if (!into--)
		uerror("ref to uninitialized chan (unsend)");
	z = qptr(into);
	j = ((Q0 *)z)->Qlen;
	((Q0 *)z)->Qlen = --j;
	switch (((Q0 *)qptr(into))->_t) {
	case 1:
		((Q1 *)z)->contents[j].fld0 = 0;
		((Q1 *)z)->contents[j].fld1 = 0;
		((Q1 *)z)->contents[j].fld2 = 0;
		_m = (trpt+1)->o_m;
		if (_m) (trpt-1)->o_pm |= 1;
		UnBlock;
		break;
	default: Uerror("bad queue - unsend");
	}
	return _m;
}

void
unrecv(int from, int slot, int fld, int fldvar, int strt)
{	int j; uchar *z;

	if (!from--)
		uerror("ref to uninitialized chan (unrecv)");
	z = qptr(from);
	j = ((Q0 *)z)->Qlen;
	if (strt) ((Q0 *)z)->Qlen = j+1;
	switch (((Q0 *)qptr(from))->_t) {
	case 1:
		if (strt) boq = from+1;
		if (strt) {
			((Q1 *)z)->contents[slot].fld0 = 0;
			((Q1 *)z)->contents[slot].fld1 = 0;
			((Q1 *)z)->contents[slot].fld2 = 0;
		}
		switch (fld) {
		case 0:	((Q1 *)z)->contents[slot].fld0 = fldvar; break;
		case 1:	((Q1 *)z)->contents[slot].fld1 = fldvar; break;
		case 2:	((Q1 *)z)->contents[slot].fld2 = fldvar; break;
		}
		break;
	default: Uerror("bad queue - qrecv");
	}
}
int
q_cond(short II, Trans *t)
{	int i = 0;
	for (i = 0; i < 6; i++)
	{	if (t->ty[i] == TIMEOUT_F) return 1;
		if (t->ty[i] == ALPHA_F)
#ifdef GLOB_ALPHA
			return 0;
#else
			return (II+1 == (short) now._nr_pr && II+1 < MAXPROC);
#endif
		switch (t->qu[i]) {
		case 0: break;
		case 1: if (	(t->ty[i] == Q_FULL_F && ( q_full((int) ( now.channel) )))
			 || 	(t->ty[i] == Q_FULL_T && (!q_full((int) ( now.channel) )))
			 || 	(t->ty[i] == Q_EMPT_F && ( !q_len((int) ( now.channel) )))
			 || 	(t->ty[i] == Q_EMPT_T && (  q_len((int) ( now.channel) )))
			    ) return 0; break;
		default: Uerror("unknown qid - q_cond");
				return 0;
		}
	}
	return 1;
}
void
to_compile(void)
{	char ctd[1024], carg[64];
#ifdef BITSTATE
	strcpy(ctd, "-DBITSTATE ");
#else
	strcpy(ctd, "");
#endif
#ifdef NOVSZ
	strcat(ctd, "-DNOVSZ ");
#endif
#ifdef REVERSE
	strcat(ctd, "-DREVERSE ");
#endif
#ifdef T_REVERSE
	strcat(ctd, "-DT_REVERSE ");
#endif
#ifdef T_RAND
	#if T_RAND>0
	sprintf(carg, "-DT_RAND=%d ", T_RAND);
	strcat(ctd, carg);
	#else
	strcat(ctd, "-DT_RAND ");
	#endif
#endif
#ifdef P_RAND
	#if P_RAND>0
	sprintf(carg, "-DP_RAND=%d ", P_RAND);
	strcat(ctd, carg);
	#else
	strcat(ctd, "-DP_RAND ");
	#endif
#endif
#ifdef BCS
	sprintf(carg, "-DBCS=%d ", BCS);
	strcat(ctd, carg);
#endif
#ifdef BFS
	strcat(ctd, "-DBFS ");
#endif
#ifdef MEMLIM
	sprintf(carg, "-DMEMLIM=%d ", MEMLIM);
	strcat(ctd, carg);
#else
#ifdef MEMCNT
	sprintf(carg, "-DMEMCNT=%d ", MEMCNT);
	strcat(ctd, carg);
#endif
#endif
#ifdef NOCLAIM
	strcat(ctd, "-DNOCLAIM ");
#endif
#ifdef SAFETY
	strcat(ctd, "-DSAFETY ");
#else
#ifdef NOFAIR
	strcat(ctd, "-DNOFAIR ");
#else
#ifdef NFAIR
	if (NFAIR != 2)
	{	sprintf(carg, "-DNFAIR=%d ", NFAIR);
		strcat(ctd, carg);
	}
#endif
#endif
#endif
#ifdef NOREDUCE
	strcat(ctd, "-DNOREDUCE ");
#else
#ifdef XUSAFE
	strcat(ctd, "-DXUSAFE ");
#endif
#endif
#ifdef NP
	strcat(ctd, "-DNP ");
#endif
#ifdef PEG
	strcat(ctd, "-DPEG ");
#endif
#ifdef VAR_RANGES
	strcat(ctd, "-DVAR_RANGES ");
#endif
#ifdef HC0
	strcat(ctd, "-DHC0 ");
#endif
#ifdef HC1
	strcat(ctd, "-DHC1 ");
#endif
#ifdef HC2
	strcat(ctd, "-DHC2 ");
#endif
#ifdef HC3
	strcat(ctd, "-DHC3 ");
#endif
#ifdef HC4
	strcat(ctd, "-DHC4 ");
#endif
#ifdef CHECK
	strcat(ctd, "-DCHECK ");
#endif
#ifdef CTL
	strcat(ctd, "-DCTL ");
#endif
#ifdef NIBIS
	strcat(ctd, "-DNIBIS ");
#endif
#ifdef NOBOUNDCHECK
	strcat(ctd, "-DNOBOUNDCHECK ");
#endif
#ifdef NOSTUTTER
	strcat(ctd, "-DNOSTUTTER ");
#endif
#ifdef REACH
	strcat(ctd, "-DREACH ");
#endif
#ifdef PRINTF
	strcat(ctd, "-DPRINTF ");
#endif
#ifdef OTIM
	strcat(ctd, "-DOTIM ");
#endif
#ifdef COLLAPSE
	strcat(ctd, "-DCOLLAPSE ");
#endif
#ifdef MA
	sprintf(carg, "-DMA=%d ", MA);
	strcat(ctd, carg);
#endif
#ifdef SVDUMP
	strcat(ctd, "-DSVDUMP ");
#endif
#ifdef VECTORSZ
	if (VECTORSZ != 1024)
	{	sprintf(carg, "-DVECTORSZ=%d ", VECTORSZ);
		strcat(ctd, carg);
	}
#endif
#ifdef VERBOSE
	strcat(ctd, "-DVERBOSE ");
#endif
#ifdef CHECK
	strcat(ctd, "-DCHECK ");
#endif
#ifdef SDUMP
	strcat(ctd, "-DSDUMP ");
#endif
#if NCORE>1
	sprintf(carg, "-DNCORE=%d ", NCORE);
	strcat(ctd, carg);
#endif
#ifdef SFH
	sprintf(carg, "-DSFH ");
	strcat(ctd, carg);
#endif
#ifdef VMAX
	if (VMAX != 256)
	{	sprintf(carg, "-DVMAX=%d ", VMAX);
		strcat(ctd, carg);
	}
#endif
#ifdef PMAX
	if (PMAX != 16)
	{	sprintf(carg, "-DPMAX=%d ", PMAX);
		strcat(ctd, carg);
	}
#endif
#ifdef QMAX
	if (QMAX != 16)
	{	sprintf(carg, "-DQMAX=%d ", QMAX);
		strcat(ctd, carg);
	}
#endif
#ifdef SET_WQ_SIZE
	sprintf(carg, "-DSET_WQ_SIZE=%d ", SET_WQ_SIZE);
	strcat(ctd, carg);
#endif
	printf("Compiled as: cc -o pan %span.c\n", ctd);
}
void
active_procs(void)
{
	if (!permuted) {
	Addproc(0);
	Addproc(1);
	Addproc(1);
	} else {
	Addproc(1);
	Addproc(1);
	Addproc(0);
	}
}
#ifdef MA
/*
#include <stdio.h>
#define uchar	unsigned char
*/
#define ulong	unsigned long
#define ushort	unsigned short

#define TWIDTH		256
#define HASH(y,n)	(n)*(((long)y))
#define INRANGE(e,h)	((h>=e->From && h<=e->To)||(e->s==1 && e->S==h))

extern char	*emalloc(unsigned long);	/* imported routine  */
extern void	dfa_init(ushort);	/* 4 exported routines */
extern int	dfa_member(ulong);
extern int	dfa_store(uchar *);
extern void	dfa_stats(void);

typedef struct Edge {
	uchar From, To;		/* max range 0..255 */
	uchar s, S;		/* if s=1, S is singleton */
	struct Vertex	*Dst;
	struct Edge	*Nxt;
} Edge;

typedef struct Vertex {
	ulong	key, num;	/* key for splay tree, nr incoming edges */
	uchar	from[2], to[2];	/* in-node predefined edge info    */
	struct	Vertex	*dst[2];/* most nodes have 2 or more edges */
	struct	Edge	*Succ;	/* in case there are more edges */
	struct	Vertex	*lnk, *left, *right; /* splay tree plumbing */
} Vertex;

static Edge	*free_edges;
static Vertex	*free_vertices;
static Vertex	**layers;	/* one splay tree of nodes per layer */
static Vertex	**path;		/* run of word in the DFA */
static Vertex	*R, *F, *NF;	/* Root, Final, Not-Final */
static uchar	*word, *lastword;/* string, and last string inserted */
static int	dfa_depth, iv=0, nv=0, pfrst=0, Tally;

static void	insert_it(Vertex *, int);	/* splay-tree code */
static void	delete_it(Vertex *, int);
static Vertex	*find_it(Vertex *, Vertex *, uchar, int);

static void
recyc_edges(Edge *e)
{
	if (!e) return;
	recyc_edges(e->Nxt);
	e->Nxt = free_edges;
	free_edges = e;
}

static Edge *
new_edge(Vertex *dst)
{	Edge *e;

	if (free_edges)
	{	e = free_edges;
		free_edges = e->Nxt;
		e->From = e->To = e->s = e->S = 0;
		e->Nxt = (Edge *) 0;
	} else
		e = (Edge *) emalloc(sizeof(Edge));
	e->Dst = dst;

	return e;
}

static void
recyc_vertex(Vertex *v)
{
	recyc_edges(v->Succ);
	v->Succ = (Edge *) free_vertices;
	free_vertices = v;
	nr_states--;
}

static Vertex *
new_vertex(void)
{	Vertex *v;

	if (free_vertices)
	{	v = free_vertices;
		free_vertices = (Vertex *) v->Succ;
		v->Succ = (Edge *) 0;
		v->num  = 0;
	} else
		v = (Vertex *) emalloc(sizeof(Vertex));

	nr_states++;
	return v; 
}

static Vertex *
allDelta(Vertex *v, int n)
{	Vertex *dst = new_vertex();

	v->from[0] = 0;
	v->to[0] = 255;
	v->dst[0] = dst;
	dst->num = 256;
	insert_it(v, n);
	return dst;
}

static void
insert_edge(Vertex *v, Edge *e)
{	/* put new edge first */
	if (!v->dst[0])
	{	v->dst[0] = e->Dst;
		v->from[0] = e->From;
		v->to[0] = e->To;
		recyc_edges(e);
		return;
	}
	if (!v->dst[1])
	{	v->from[1] = v->from[0]; v->from[0] = e->From;
		v->to[1]   = v->to[0];   v->to[0]   = e->To;
		v->dst[1]  = v->dst[0];  v->dst[0]  = e->Dst;
		recyc_edges(e);
		return;
	} /* shift */
	{	int f      = v->from[1];
		int t      = v->to[1];
		Vertex *d  = v->dst[1];
		v->from[1] = v->from[0]; v->from[0] = e->From;
		v->to[1]   = v->to[0];   v->to[0]   = e->To;
		v->dst[1]  = v->dst[0];  v->dst[0]  = e->Dst;
		e->From = f;
		e->To   = t;
		e->Dst  = d;
	}
	e->Nxt = v->Succ;
	v->Succ = e;
}

static void
copyRecursive(Vertex *v, Edge *e)
{	Edge *f;
	if (e->Nxt) copyRecursive(v, e->Nxt);
	f = new_edge(e->Dst);
	f->From = e->From;
	f->To   = e->To;
	f->s    = e->s;
	f->S    = e->S;
	f->Nxt  = v->Succ;
	v->Succ = f;
}

static void
copyEdges(Vertex *to, Vertex *from)
{	int i;
	for (i = 0; i < 2; i++)
	{	to->from[i] = from->from[i];
		to->to[i]   = from->to[i];
		to->dst[i]  = from->dst[i];
	}
	if (from->Succ) copyRecursive(to, from->Succ);
}

static Edge *
cacheDelta(Vertex *v, int h, int first)
{	static Edge *ov, tmp;  int i;

	if (!first && INRANGE(ov,h))
		return ov; /* intercepts about 10% */
	for (i = 0; i < 2; i++)
		if (v->dst[i] && h >= v->from[i] && h <= v->to[i])
		{	tmp.From = v->from[i];
			tmp.To   = v->to[i];
			tmp.Dst  = v->dst[i];
			tmp.s    =  tmp.S = 0;
			ov = &tmp;
			return ov;
		}
	for (ov = v->Succ; ov; ov = ov->Nxt)
		if (INRANGE(ov,h)) return ov;

	Uerror("cannot get here, cacheDelta");
	return (Edge *) 0;
}

static Vertex *
Delta(Vertex *v, int h)	/* v->delta[h] */
{	Edge *e;

	if (v->dst[0] && h >= v->from[0] && h <= v->to[0])
		return v->dst[0];	/* oldest edge */
	if (v->dst[1] && h >= v->from[1] && h <= v->to[1])
		return v->dst[1];
	for (e = v->Succ; e; e = e->Nxt)
		if (INRANGE(e,h))
			return e->Dst;
	Uerror("cannot happen Delta");
	return (Vertex *) 0;
}

static void
numDelta(Vertex *v, int d)
{	Edge *e;
	ulong cnt;
	int i;

	for (i = 0; i < 2; i++)
	if (v->dst[i])
	{	cnt = v->dst[i]->num + d*(1 + v->to[i] - v->from[i]);
		if (d == 1 && cnt < v->dst[i]->num) goto bad;
		v->dst[i]->num = cnt;
	}
	for (e = v->Succ; e; e = e->Nxt)
	{	cnt = e->Dst->num + d*(1 + e->To - e->From + e->s);
		if (d == 1 && cnt < e->Dst->num)
bad:			Uerror("too many incoming edges");
		e->Dst->num = cnt;
	}
}

static void
setDelta(Vertex *v, int h, Vertex *newdst)	/* v->delta[h] = newdst; */
{	Edge *e, *f = (Edge *) 0, *g;
	int i;

	/* remove the old entry, if there */
	for (i = 0; i < 2; i++)
		if (v->dst[i] && h >= v->from[i] && h <= v->to[i])
		{	if (h == v->from[i])
			{	if (h == v->to[i])
				{	v->dst[i] = (Vertex *) 0;
					v->from[i] = v->to[i] = 0;
				} else
					v->from[i]++;
			} else if (h == v->to[i])
			{	v->to[i]--;
			} else
			{	g = new_edge(v->dst[i]);/* same dst */
				g->From    = v->from[i];
				g->To      = h-1;	/* left half */
				v->from[i] = h+1;	/* right half */
				insert_edge(v, g);
			}
			goto part2;
		}
	for (e = v->Succ; e; f = e, e = e->Nxt)
	{	if (e->s == 1 && e->S == h)
		{	e->s = e->S = 0;
			goto rem_tst;
		}
		if (h >= e->From && h <= e->To)
		{	if (h == e->From)
			{	if (h == e->To)
				{	if (e->s)
					{	e->From = e->To = e->S;
						e->s = 0;
						break;
					} else
						goto rem_do;
				} else
					e->From++;
			} else if (h == e->To)
			{	e->To--;
			} else				/* split */
			{	g = new_edge(e->Dst);	/* same dst */
				g->From = e->From;
				g->To   = h-1;		/* g=left half */
				e->From = h+1;		/* e=right half */
				g->Nxt  = e->Nxt;	/* insert g */
				e->Nxt  = g;		/* behind e */
				break;			/* done */
			}

rem_tst:		if (e->From > e->To)
			{	if (e->s == 0) {
rem_do:				if (f)
						f->Nxt = e->Nxt;
					else
						v->Succ = e->Nxt;
					e->Nxt = (Edge *) 0;
					recyc_edges(e);
				} else
				{	e->From = e->To = e->S;
					e->s = 0;
			}	}
			break;
	}	}
part2:
	/* check if newdst is already there */
	for (i = 0; i < 2; i++)
		if (v->dst[i] == newdst)
		{	if (h+1 == (int) v->from[i])
			{	v->from[i] = h;
				return;
			}
			if (h == (int) v->to[i]+1)
			{	v->to[i] = h;
				return;
		}	}
	for (e = v->Succ; e; e = e->Nxt)
	{	if (e->Dst == newdst)
		{	if (h+1 == (int) e->From)
			{	e->From = h;
				if (e->s == 1 && e->S+1 == e->From)
				{	e->From = e->S;
					e->s = e->S = 0;
				}
				return;
			}
			if (h == (int) e->To+1)
			{	e->To = h;
				if (e->s == 1 && e->S == e->To+1)
				{	e->To = e->S;
					e->s = e->S = 0;
				}
				return;
			}
			if (e->s == 0)
			{	e->s = 1;
				e->S = h;
				return;
	}	}	}
	/* add as a new edge */
	e = new_edge(newdst);
	e->From = e->To = h;
	insert_edge(v, e);
}

static ulong
cheap_key(Vertex *v)
{	ulong vk2 = 0;

	if (v->dst[0])
	{	vk2 = (ulong) v->dst[0];
		if ((ulong) v->dst[1] > vk2)
			vk2 = (ulong) v->dst[1];
	} else if (v->dst[1])
		vk2 = (ulong) v->dst[1]; 
	if (v->Succ)
	{	Edge *e;
		for (e = v->Succ; e; e = e->Nxt)
			if ((ulong) e->Dst > vk2)
				vk2 = (ulong) e->Dst;
	}
	Tally = (vk2>>2)&(TWIDTH-1);
	return v->key;
}

static ulong
mk_key(Vertex *v)	/* not sensitive to order */
{	ulong m = 0, vk2 = 0;
	Edge *e;

	if (v->dst[0])
	{	m += HASH(v->dst[0], v->to[0] - v->from[0] + 1);
		vk2 = (ulong) v->dst[0]; 
	}
	if (v->dst[1])
	{	m += HASH(v->dst[1], v->to[1] - v->from[1] + 1);
		if ((ulong) v->dst[1] > vk2) vk2 = (ulong) v->dst[1]; 
	}
	for (e = v->Succ; e; e = e->Nxt)
	{	m += HASH(e->Dst, e->To - e->From + 1 + e->s);
		if ((ulong) e->Dst > vk2) vk2 = (ulong) e->Dst; 
	}
	Tally = (vk2>>2)&(TWIDTH-1);
	return m;
}

static ulong
mk_special(int sigma, Vertex *n, Vertex *v)
{	ulong m = 0, vk2 = 0;
	Edge *f;
	int i;

	for (i = 0; i < 2; i++)
		if (v->dst[i])
		{	if (sigma >= v->from[i] && sigma <= v->to[i])
			{	m += HASH(v->dst[i], v->to[i]-v->from[i]);
				if ((ulong) v->dst[i] > vk2
				&&   v->to[i] > v->from[i])
					vk2 = (ulong) v->dst[i]; 
			} else
			{	m += HASH(v->dst[i], v->to[i]-v->from[i]+1);
				if ((ulong) v->dst[i] > vk2)
					vk2 = (ulong) v->dst[i]; 
		}	}
	for (f = v->Succ; f; f = f->Nxt)
	{	if (sigma >= f->From && sigma <= f->To)
		{	m += HASH(f->Dst, f->To - f->From + f->s);
			if ((ulong) f->Dst > vk2
			&&   f->To - f->From + f->s > 0)
				vk2 = (ulong) f->Dst; 
		} else if (f->s == 1 && sigma == f->S)
		{	m += HASH(f->Dst, f->To - f->From + 1);
			if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; 
		} else
		{	m += HASH(f->Dst, f->To - f->From + 1 + f->s);
			if ((ulong) f->Dst > vk2) vk2 = (ulong) f->Dst; 
	}	}

	if ((ulong) n > vk2) vk2 = (ulong) n; 
	Tally = (vk2>>2)&(TWIDTH-1);
	m += HASH(n, 1);
	return m;
}

void 
dfa_init(ushort nr_layers)
{	int i; Vertex *r, *t;

	dfa_depth = nr_layers;	/* one byte per layer */
	path   = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));
	layers = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));
	lastword = (uchar *) emalloc((dfa_depth+1)*sizeof(uchar));
	lastword[dfa_depth] = lastword[0] = 255;
	path[0] = R = new_vertex(); F = new_vertex();

	for (i = 1, r = R; i < dfa_depth; i++, r = t)
		t = allDelta(r, i-1);
	NF = allDelta(r, i-1);
}

#if 0
static void complement_dfa(void) { Vertex *tmp = F; F = NF; NF = tmp; }
#endif

double
tree_stats(Vertex *t)
{	Edge *e; double cnt=0.0;
	if (!t) return 0;
	if (!t->key) return 0;
	t->key = 0; /* precaution */
	if (t->dst[0]) cnt++;
	if (t->dst[1]) cnt++;
	for (e = t->Succ; e; e = e->Nxt)
		cnt++;
	cnt += tree_stats(t->lnk);
	cnt += tree_stats(t->left);
	cnt += tree_stats(t->right);
	return cnt;
}

void
dfa_stats(void)
{	int i, j; double cnt = 0.0;
	for (j = 0; j < TWIDTH; j++)
	for (i = 0; i < dfa_depth+1; i++)
		cnt += tree_stats(layers[i*TWIDTH+j]);
	printf("Minimized Automaton:	%6d nodes and %6g edges\n",
		nr_states, cnt);
}

int
dfa_member(ulong n)
{	Vertex **p, **q;
	uchar *w = &word[n];
	int i;

	p = &path[n]; q = (p+1);
	for (i = n; i < dfa_depth; i++)
		*q++ = Delta(*p++, *w++);
	return (*p == F);
}

int
dfa_store(uchar *sv)
{	Vertex **p, **q, *s, *y, *old, *new = F;
	uchar *w, *u = lastword;
	int i, j, k;

	w = word = sv;
	while (*w++ == *u++)	/* find first byte that differs */
		;
	pfrst = (int) (u - lastword) - 1;
	memcpy(&lastword[pfrst], &sv[pfrst], dfa_depth-pfrst);
	if (pfrst > iv) pfrst = iv;
	if (pfrst > nv) pfrst = nv;
/* phase1: */
	p = &path[pfrst]; q = (p+1); w = &word[pfrst];
	for (i = pfrst; i < dfa_depth; i++)
		*q++ = Delta(*p++, *w++);	/* (*p)->delta[*w++]; */

	if (*p == F) return 1;	/* it's already there */
/* phase2: */
	iv = dfa_depth;
	do {	iv--;
		old = new;
		new = find_it(path[iv], old, word[iv], iv);
	} while (new && iv > 0);

/* phase3: */
	nv = k = 0; s = path[0];
	for (j = 1; j <= iv; ++j) 
		if (path[j]->num > 1)
		{	y = new_vertex();
			copyEdges(y, path[j]);
			insert_it(y, j);
			numDelta(y, 1);
			delete_it(s, j-1);
			setDelta(s, word[j-1], y);
			insert_it(s, j-1);
			y->num = 1;	/* initial value 1 */
			s = y;
			path[j]->num--;	/* only 1 moved from j to y */
			k = 1;
		} else
		{	s = path[j];
			if (!k) nv = j;
		}
	y = Delta(s, word[iv]);
	y->num--;
	delete_it(s, iv); 
	setDelta(s, word[iv], old);
	insert_it(s, iv); 
	old->num++;

	for (j = iv+1; j < dfa_depth; j++)
		if (path[j]->num == 0)
		{	numDelta(path[j], -1);
			delete_it(path[j], j);
			recyc_vertex(path[j]);
		} else
			break;
	return 0;
}

static Vertex *
splay(ulong i, Vertex *t)
{	Vertex N, *l, *r, *y;

	if (!t) return t;
	N.left = N.right = (Vertex *) 0;
	l = r = &N;
	for (;;)
	{	if (i < t->key)
		{	if (!t->left) break;
			if (i < t->left->key)
			{	y = t->left;
				t->left = y->right;
				y->right = t;
				t = y;
				if (!t->left) break;
			}
			r->left = t;
			r = t;
			t = t->left;
		} else if (i > t->key)
		{	if (!t->right) break;
			if (i > t->right->key)
			{	y = t->right;
				t->right = y->left;
				y->left = t;
				t = y;
				if (!t->right) break;
			}
			l->right = t;
			l = t;
			t = t->right;
		} else
			break;
	}
	l->right = t->left;
	r->left = t->right;
	t->left = N.right;
	t->right = N.left;
	return t;
}

static void
insert_it(Vertex *v, int L)
{	Vertex *new, *t;
	ulong i; int nr;

	i = mk_key(v);
	nr = ((L*TWIDTH)+Tally);
	t = layers[nr];

	v->key = i; 
	if (!t)
	{	layers[nr] = v;
		return;
	}
	t = splay(i, t);
	if (i < t->key)
	{	new = v;
		new->left = t->left;
		new->right = t;
		t->left = (Vertex *) 0;
	} else if (i > t->key)
	{	new = v;
		new->right = t->right;
		new->left = t;
		t->right = (Vertex *) 0;
	} else	 /* it's already there */
	{	v->lnk = t->lnk; /* put in linked list off v */
		t->lnk = v;
		new = t;
	}
	layers[nr] = new;
}

static int
checkit(Vertex *h, Vertex *v, Vertex *n, uchar sigma)
{	Edge *g, *f;
	int i, k, j = 1;

	for (k = 0; k < 2; k++)
		if (h->dst[k])
		{	if (sigma >= h->from[k] && sigma <= h->to[k])
			{	if (h->dst[k] != n) goto no_match;
			}
			for (i = h->from[k]; i <= h->to[k]; i++)
			{	if (i == sigma) continue;
				g = cacheDelta(v, i, j); j = 0;
				if (h->dst[k] != g->Dst)
					goto no_match;
				if (g->s == 0 || g->S != i)
					i = g->To;
		}	}
	for (f = h->Succ; f; f = f->Nxt)
	{	if (INRANGE(f,sigma))
		{	if (f->Dst != n) goto no_match;
		}
		for (i = f->From; i <= f->To; i++)
		{	if (i == sigma) continue;
			g = cacheDelta(v, i, j); j = 0;
			if (f->Dst != g->Dst)
				goto no_match;
			if (g->s == 1 && i == g->S)
				continue;
			i = g->To;
		}
		if (f->s && f->S != sigma)
		{	g = cacheDelta(v, f->S, 1);
			if (f->Dst != g->Dst)
				goto no_match;
		}
	}
	if (h->Succ || h->dst[0] || h->dst[1]) return 1;
no_match:
	return 0;
}

static Vertex *
find_it(Vertex *v, Vertex *n, uchar sigma, int L)
{	Vertex *z, *t;
	ulong i; int nr;

	i = mk_special(sigma,n,v);
	nr = ((L*TWIDTH)+Tally);
	t = layers[nr];

	if (!t) return (Vertex *) 0;
	layers[nr] = t = splay(i, t);
	if (i == t->key)
	for (z = t; z; z = z->lnk)
		if (checkit(z, v, n, sigma))
			return z;

	return (Vertex *) 0;
}

static void
delete_it(Vertex *v, int L)
{	Vertex *x, *t;
	ulong i; int nr;

	i = cheap_key(v);
	nr = ((L*TWIDTH)+Tally);
	t = layers[nr];
	if (!t) return;

	t = splay(i, t);
	if (i == t->key)
	{	Vertex *z, *y = (Vertex *) 0;
		for (z = t; z && z != v; y = z, z = z->lnk)
			;
		if (z != v) goto bad;
		if (y)
		{	y->lnk = z->lnk;
			z->lnk = (Vertex *) 0;
			layers[nr] = t;
			return;
		} else if (z->lnk)	/* z == t == v */
		{	y = z->lnk;
			y->left = t->left;
			y->right = t->right;
			t->left = t->right = t->lnk = (Vertex *) 0;
			layers[nr] = y;
			return;
		}
		/* delete the node itself */
		if (!t->left)
		{	x = t->right;
		} else
		{	x = splay(i, t->left);
			x->right = t->right;
		}
		t->left = t->right = t->lnk = (Vertex *) 0;
		layers[nr] = x;
		return;
	}
bad:	Uerror("cannot happen delete");
}
#endif
#if defined(MA) && (defined(W_XPT) || defined(R_XPT))
static Vertex	**temptree;
static char	wbuf[4096];
static int	WCNT = 4096, wcnt=0;
static uchar	stacker[MA+1];
static ulong	stackcnt = 0;
extern double	nstates, nlinks, truncs, truncs2;

static void
xwrite(int fd, char *b, int n)
{
	if (wcnt+n >= 4096)
	{	write(fd, wbuf, wcnt);
		wcnt = 0;
	}
	memcpy(&wbuf[wcnt], b, n);
	wcnt += n;
}

static void
wclose(fd)
{
	if (wcnt > 0)
		write(fd, wbuf, wcnt);
	wcnt = 0;
	close(fd);
}

static void
w_vertex(int fd, Vertex *v)
{	char t[3]; int i; Edge *e;

	xwrite(fd, (char *) &v,  sizeof(Vertex *));
	t[0] = 0;
	for (i = 0; i < 2; i++)
		if (v->dst[i])
		{	t[1] = v->from[i], t[2] = v->to[i];
			xwrite(fd, t, 3);
			xwrite(fd, (char *) &(v->dst[i]), sizeof(Vertex *));
		}
	for (e = v->Succ; e; e = e->Nxt)
	{	t[1] = e->From, t[2] = e->To;
		xwrite(fd, t, 3);
		xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));

		if (e->s)
		{	t[1] = t[2] = e->S;
			xwrite(fd, t, 3);
			xwrite(fd, (char *) &(e->Dst), sizeof(Vertex *));
	}	}
}

static void
w_layer(int fd, Vertex *v)
{	uchar c=1;

	if (!v) return;
	xwrite(fd, (char *) &c, 1);
	w_vertex(fd, v);
	w_layer(fd, v->lnk);
	w_layer(fd, v->left);
	w_layer(fd, v->right);
}

void
w_xpoint(void)
{	int fd; char nm[64];
	int i, j;  uchar c;
	static uchar xwarned = 0;

	sprintf(nm, "%s.xpt", PanSource);
	if ((fd = creat(nm, 0666)) <= 0)
	if (!xwarned)
	{	xwarned = 1;
		printf("cannot creat checkpoint file\n");
		return;
	}
	xwrite(fd, (char *) &nstates, sizeof(double));
	xwrite(fd, (char *) &truncs, sizeof(double));
	xwrite(fd, (char *) &truncs2, sizeof(double));
	xwrite(fd, (char *) &nlinks, sizeof(double));
	xwrite(fd, (char *) &dfa_depth, sizeof(int));
	xwrite(fd, (char *) &R,  sizeof(Vertex *));
	xwrite(fd, (char *) &F,  sizeof(Vertex *));
	xwrite(fd, (char *) &NF, sizeof(Vertex *));

	for (j = 0; j < TWIDTH; j++)
	for (i = 0; i < dfa_depth+1; i++)
	{	w_layer(fd, layers[i*TWIDTH+j]);
		c = 2; xwrite(fd, (char *) &c, 1);
	}
	wclose(fd);
}

static void
xread(int fd, char *b, int n)
{	int m = wcnt; int delta = 0;
	if (m < n)
	{	if (m > 0) memcpy(b, &wbuf[WCNT-m], m);
		delta = m;
		WCNT = wcnt = read(fd, wbuf, 4096);
		if (wcnt < n-m)
			Uerror("xread failed -- insufficient data");
		n -= m;
	}
	memcpy(&b[delta], &wbuf[WCNT-wcnt], n);
	wcnt -= n;
}

static void
x_cleanup(Vertex *c)
{	Edge *e;	/* remove the tree and edges from c */
	if (!c) return;
	for (e = c->Succ; e; e = e->Nxt)
		x_cleanup(e->Dst);
	recyc_vertex(c);
}

static void
x_remove(void)
{	Vertex *tmp; int i, s;
	int r, j;
	/* double-check: */
	stacker[dfa_depth-1] = 0; r = dfa_store(stacker);
	stacker[dfa_depth-1] = 4; j = dfa_member(dfa_depth-1);
	if (r != 1 || j != 0)
	{	printf("%d: ", stackcnt);
		for (i = 0; i < dfa_depth; i++)
			printf("%d,", stacker[i]);
		printf(" -- not a stackstate <o:%d,4:%d>\n", r, j);
		return;
	}
	stacker[dfa_depth-1] = 1;
	s = dfa_member(dfa_depth-1);

	{ tmp = F; F = NF; NF = tmp; }	/* complement */
		if (s) dfa_store(stacker);
		stacker[dfa_depth-1] = 0;
		dfa_store(stacker);
		stackcnt++;
	{ tmp = F; F = NF; NF = tmp; }
}

static void
x_rm_stack(Vertex *t, int k)
{	int j; Edge *e;

	if (k == 0)
	{	x_remove();
		return;
	}
	if (t)
	for (e = t->Succ; e; e = e->Nxt)
	{	for (j = e->From; j <= (int) e->To; j++)
		{	stacker[k] = (uchar) j;
			x_rm_stack(e->Dst, k-1);
		}
		if (e->s)
		{	stacker[k] = e->S;
			x_rm_stack(e->Dst, k-1);
	}	}
}

static Vertex *
insert_withkey(Vertex *v, int L)
{	Vertex *new, *t = temptree[L];

	if (!t) { temptree[L] = v; return v; }
	t = splay(v->key, t);
	if (v->key < t->key)
	{	new = v;
		new->left = t->left;
		new->right = t;
		t->left = (Vertex *) 0;
	} else if (v->key > t->key)
	{	new = v;
		new->right = t->right;
		new->left = t;
		t->right = (Vertex *) 0;
	} else
	{	if (t != R && t != F && t != NF)
			Uerror("double insert, bad checkpoint data");
		else
		{	recyc_vertex(v);
			new = t;
	}	}
	temptree[L] = new;

	return new;
}

static Vertex *
find_withkey(Vertex *v, int L)
{	Vertex *t = temptree[L];
	if (t)
	{	temptree[L] = t = splay((ulong) v, t);
		if (t->key == (ulong) v)
			return t;
	}
	Uerror("not found error, bad checkpoint data");
	return (Vertex *) 0;
}

void
r_layer(int fd, int n)
{	Vertex *v;
	Edge *e;
	char c, t[2];

	for (;;)
	{	xread(fd, &c, 1);
		if (c == 2) break;
		if (c == 1)
		{	v = new_vertex();
			xread(fd, (char *) &(v->key), sizeof(Vertex *));
			v = insert_withkey(v, n);
		} else	/* c == 0 */
		{	e = new_edge((Vertex *) 0);
			xread(fd, t, 2);
			e->From = t[0];
			e->To = t[1];
			xread(fd, (char *) &(e->Dst), sizeof(Vertex *));
			insert_edge(v, e);
	}	}
}

static void
v_fix(Vertex *t, int nr)
{	int i; Edge *e;

	if (!t) return;

	for (i = 0; i < 2; i++)
	if (t->dst[i])
		t->dst[i] = find_withkey(t->dst[i], nr);

	for (e = t->Succ; e; e = e->Nxt)
		e->Dst = find_withkey(e->Dst, nr);
		
	v_fix(t->left, nr);
	v_fix(t->right, nr);
}

static void
v_insert(Vertex *t, int nr)
{	Edge *e; int i;

	if (!t) return;
	v_insert(t->left, nr);
	v_insert(t->right, nr);

	/* remove only leafs from temptree */
	t->left = t->right = t->lnk = (Vertex *) 0;
	insert_it(t, nr);	/* into layers */
	for (i = 0; i < 2; i++)
		if (t->dst[i])
			t->dst[i]->num += (t->to[i] - t->from[i] + 1);
	for (e = t->Succ; e; e = e->Nxt)
		e->Dst->num += (e->To - e->From + 1 + e->s);
}

static void
x_fixup(void)
{	int i;

	for (i = 0; i < dfa_depth; i++)
		v_fix(temptree[i], (i+1));

	for (i = dfa_depth; i >= 0; i--)
		v_insert(temptree[i], i);
}

static Vertex *
x_tail(Vertex *t, ulong want)
{	int i, yes, no; Edge *e; Vertex *v = (Vertex *) 0;

	if (!t) return v;

	yes = no = 0;
	for (i = 0; i < 2; i++)
		if ((ulong) t->dst[i] == want)
		{	/* was t->from[i] <= 0 && t->to[i] >= 0 */
			/* but from and to are uchar */
			if (t->from[i] == 0)
				yes = 1;
			else
			if (t->from[i] <= 4 && t->to[i] >= 4)
				no = 1;
		}

	for (e = t->Succ; e; e = e->Nxt)
		if ((ulong) e->Dst == want)
		{	/* was INRANGE(e,0) but From and To are uchar */
			if ((e->From == 0) || (e->s==1 && e->S==0))
				yes = 1;
			else if (INRANGE(e, 4))
				no = 1;
		}
	if (yes && !no) return t;
	v = x_tail(t->left, want);  if (v) return v;
	v = x_tail(t->right, want); if (v) return v;
	return (Vertex *) 0;
}

static void
x_anytail(Vertex *t, Vertex *c, int nr)
{	int i; Edge *e, *f; Vertex *v;

	if (!t) return;

	for (i = 0; i < 2; i++)
		if ((ulong) t->dst[i] == c->key)
		{	v = new_vertex(); v->key = t->key;
			f = new_edge(v);
			f->From = t->from[i];
			f->To = t->to[i];
			f->Nxt = c->Succ;
			c->Succ = f;
			if (nr > 0)
			x_anytail(temptree[nr-1], v, nr-1);
		}

	for (e = t->Succ; e; e = e->Nxt)
		if ((ulong) e->Dst == c->key)
		{	v = new_vertex(); v->key = t->key;
			f = new_edge(v);
			f->From = e->From;
			f->To = e->To;
			f->s = e->s;
			f->S = e->S;
			f->Nxt = c->Succ;
			c->Succ = f;
			x_anytail(temptree[nr-1], v, nr-1);
		}

	x_anytail(t->left, c, nr);
	x_anytail(t->right, c, nr);
}

static Vertex *
x_cpy_rev(void)
{	Vertex *c, *v;	/* find 0 and !4 predecessor of F */

	v = x_tail(temptree[dfa_depth-1], F->key);
	if (!v) return (Vertex *) 0;

	c = new_vertex(); c->key = v->key;

	/* every node on dfa_depth-2 that has v->key as succ */
	/* make copy and let c point to these (reversing ptrs) */

	x_anytail(temptree[dfa_depth-2], c, dfa_depth-2);
 
	return c;
}

void
r_xpoint(void)
{	int fd; char nm[64]; Vertex *d;
	int i, j;

	wcnt = 0;
	sprintf(nm, "%s.xpt", PanSource);
	if ((fd = open(nm, 0)) < 0)	/* O_RDONLY */
		Uerror("cannot open checkpoint file");

	xread(fd, (char *) &nstates,   sizeof(double));
	xread(fd, (char *) &truncs,    sizeof(double));
	xread(fd, (char *) &truncs2,   sizeof(double));
	xread(fd, (char *) &nlinks,    sizeof(double));
	xread(fd, (char *) &dfa_depth, sizeof(int));

	if (dfa_depth != MA+a_cycles)
		Uerror("bad dfa_depth in checkpoint file");

	path	  = (Vertex **) emalloc((dfa_depth+1)*sizeof(Vertex *));
	layers	  = (Vertex **) emalloc(TWIDTH*(dfa_depth+1)*sizeof(Vertex *));
	temptree  = (Vertex **) emalloc((dfa_depth+2)*sizeof(Vertex *));
	lastword  = (uchar *)   emalloc((dfa_depth+1)*sizeof(uchar));
	lastword[dfa_depth] = lastword[0] = 255; 

	path[0] = R = new_vertex();
	xread(fd, (char *) &R->key, sizeof(Vertex *));
	R = insert_withkey(R, 0);

	F = new_vertex();
	xread(fd, (char *) &F->key, sizeof(Vertex *));
	F = insert_withkey(F, dfa_depth);

	NF = new_vertex();
	xread(fd, (char *) &NF->key, sizeof(Vertex *));
	NF = insert_withkey(NF, dfa_depth);

	for (j = 0; j < TWIDTH; j++)
	for (i = 0; i < dfa_depth+1; i++)
		r_layer(fd, i);

	if (wcnt != 0) Uerror("bad count in checkpoint file");

	d = x_cpy_rev();
	x_fixup();
	stacker[dfa_depth-1] = 0;
	x_rm_stack(d, dfa_depth-2);
	x_cleanup(d);
	close(fd);

	printf("pan: removed %d stackstates\n", stackcnt);
	nstates -= (double) stackcnt;
}
#endif
void
set_recvs(void)
{
	Is_Recv[23] = 1;
	Is_Recv[14] = 1;
	Is_Recv[6] = 1;
	Is_Recv[4] = 1;
	Is_Recv[2] = 1;
	Is_Recv[0] = 1;
}
#ifdef VERI
void
check_claim(int st)
{
	if (st == endclaim)
		uerror("claim violated!");
	if (stopstate[VERI][st])
		uerror("end state in claim reached");
}
#endif
void
c_globals(void)
{	/* int i; */
	printf("global vars:\n");
	printf("	bit    turn:	%d\n", now.turn);
	{	int l_in;
		for (l_in = 0; l_in < 2; l_in++)
		{
			printf("	bit    flag[%d]:	%d\n", l_in, now.flag[l_in]);
		}
	}
	printf("	chan channel (=%d):	len %d:\t", now.channel, q_len(now.channel));
	c_chandump(now.channel);
}
void
c_locals(int pid, int tp)
{	/* int i; */
	switch(tp) {
	case 1:
		printf("local vars proc %d (pcbit_receive):\n", pid);
		break;
	case 0:
		printf("local vars proc %d (memory_manager):\n", pid);
	printf("	int    who:	%d\n", ((P0 *)pptr(pid))->who);
		break;
	}
}
void
printm(int x)
{
	switch (x) {
	case 1: Printf("FREE"); break;
	case 2: Printf("GIMME"); break;
	case 3: Printf("EPIC_FAIL"); break;
	case 4: Printf("DONE"); break;
	case 5: Printf("FROM_P"); break;
	case 6: Printf("FROM_M"); break;
	default: Printf("%d", x);
	}
}
void
c_chandump(int from)
{	uchar *z; int slot;
	from--;
	if (from >= (int) now._nr_qs || from < 0)
	{	printf("pan: bad qid %d\n", from+1);
		return;
	}
	z = qptr(from);
	switch (((Q0 *)z)->_t) {
	case 1:
		for (slot = 0; slot < ((Q1 *)z)->Qlen; slot++)
		{	printf(" [");
			printf("%d,", ((Q1 *)z)->contents[slot].fld0);
			printm(((Q1 *)z)->contents[slot].fld1);
			printm(((Q1 *)z)->contents[slot].fld2);
			printf("],");
		}
		break;
	}
	printf("\n");
}
