/*
 * Arbitrary precision integer math package
 * 
 * (c) Copyright 1991 by David A. Barrett (barrett@asgard.UUCP)
 *
 * Not to be used for profit or distributed in systems sold for profit
 */
#ifndef BASE
typedef unsigned short	prefc;		/* reference counter type */
typedef prefc		*precision;	/* this a a private data structure */
extern	int		pfree();	/* free (private) */
#endif

typedef precision 	*pvector;	/* a vector of precision */
typedef pvector		*parray;	/* 2d array */

/*
 * Error values passed to errorp
 */
#define PNOERROR	0
#define PNOMEM		1
#define PREFCOUNT	2
#define PUNDEFINED	3
#define PDOMAIN		4
#define POVERFLOW	5

#define pUndef		((precision) 0)		/* An undefined value */
#define pNull		((precision *) 0)

#define peq(u, v)	(pcmp((u), (v)) == 0)
#define pne(u, v)	(pcmp((u), (v)) != 0)
#define pgt(u, v)	(pcmp((u), (v)) >  0)
#define plt(u, v)	(pcmp((u), (v)) <  0)
#define pge(u, v)	(pcmp((u), (v)) >= 0)
#define ple(u, v)	(pcmp((u), (v)) <= 0)

#define peqz(u)		(pcmpz(u) == 0)
#define pnez(u)		(pcmpz(u) != 0)
#define pltz(u)		(pcmpz(u) <  0)
#define pgtz(u)		(pcmpz(u) >  0)
#define plez(u)		(pcmpz(u) <= 0)
#define pgez(u)		(pcmpz(u) >= 0)

#define peven(u)	(!podd(u))
#define pdiv(u,v)	(pdivmod(u,v, (precision *) -1, pNull))
#define pmod(u,v)	(pdivmod(u,v, pNull, (precision *) -1))
#define pdivr(u,v,r)	(pdivmod(u,v, (precision *) -1, r))
#define pmodq(u,v,q)	(pdivmod(u,v, q, (precision *) -1))

/*
 * Application programs should only use the following definitions;
 *
 *    pnew, pdestroy, pparm, presult and pset
 *
 * Other variants are internal only!  
 * All are side-effect safe except for pparm and presult.
 * -DDEBUG will enable argument checking for pset and pparm
 */

/* LLVM - Disable all of the inlining */
#if 0
#ifdef __GNUC__		/* inline is NOT ansii!  Sigh. */
#ifndef BWGC
extern inline precision pnew(precision u) { (* (prefc *) u)++; return u; }
extern inline void      pdestroy(precision u) {
   if (u != pUndef && --(*(prefc *) u) == 0) pfree(u);
}
extern inline precision pparmq(precision u) { 
   if (u != pUndef) (* (prefc *) u)++; return u;
}
extern inline precision presult(precision u) {
   if (u != pUndef) --(*(prefc *) u); return u;
}
extern inline precision psetq(precision *up, precision v) {
   precision u = *up;
   *up = v;
   if (v != pUndef) (* (prefc *) v)++;
   if (u != pUndef && --(* (prefc *) u) == 0) pfree(u);
   return v;
}
#define pvoid(u)	pdestroy(u)
#else
extern inline precision pnew(precision u) { return u; }
extern inline void      pdestroy(precision u) {}
extern inline precision pparmq(precision u) { return u; }
extern inline precision presult(precision u) { return u; }
extern inline precision psetq(precision *up, precision v) {
   precision u = *up;
   *up = v;
   return v;
}
#define pvoid(u)	pdestroy(u)
#endif
#else
#ifndef BWGC
#define pdestroy(u)     (void) ((u)!=pUndef&&--(*(prefc *)(u))==0&&pfree(u))
#define pparmq(u)	((u) != pUndef && (* (prefc *) (u))++, (u))
#define pvoid(u)	pdestroyf(u)
#else
#define pdestroy(u)     (void) (0)
#define pparmq(u)	(u)
#define pvoid(u)	pdestroyf(u)
#endif
#endif
#else
#define pdestroy(u)     (void) ((u)!=pUndef&&--(*(prefc *)(u))==0&&pfree(u))
#define pparmq(u)	((u) != pUndef && (* (prefc *) (u))++, (u))
#define pvoid(u)	pdestroyf(u)
#endif


#ifdef PDEBUG
#define pset(u, v)	psetv(u, v)
#define pparm(u)	pparmv(u)
#else
#define pset(u, v)	psetq(u, v)
#define pparm(u)	pparmq(u)
#endif

#ifdef __STDC__		/* if ANSI compiler */
#ifndef __GNUC__
extern	precision 	pnew(precision);		/* initialization */
extern	precision 	presult(precision);		/* function result */
extern	precision	psetq(precision *, precision);	/* quick assignment */
#endif
extern	precision	psetv(precision *, precision); /* checked assignment */
extern	precision	pparmv(precision);	/* checked parameter */
extern	precision	pparmf(precision);	/* unchecked parameter (fn) */

extern	int		pcmpz(precision);		/* compare to zero */
extern	int		pcmp(precision, precision);	/* compare */
extern	int		picmp(precision, int);	        /* single digit cmp */

extern	precision	padd(precision, precision);	/* add */
extern	precision	psub(precision, precision);	/* subtract */
extern	precision	pmul(precision, precision);	/* multiply */

extern	precision	pdivmod(precision, precision, 
			        precision *q, precision *r);

extern 	precision	pidiv(precision, int);		/* single digit pdiv */
extern 	int		pimod(precision, int);		/* single digit pmod */
extern 	void		pidivmod(precision, int, 	/* single pdivmod */
				precision *q, int *r);

extern	precision	pneg(precision);		/* negate */
extern	precision	pabs(precision);		/* absolute value */
extern	int		podd(precision);		/* true if odd */
extern	precision	phalf(precision);		/* divide by two */

extern	precision	pmin(precision, precision);	/* minimum value */
extern	precision	pmax(precision, precision);	/* maximum value */

extern	precision	prand(precision);	/* random number generator */

extern	precision	itop(int);		/* int to precision */
extern	precision	utop(unsigned);		/* unsigned to precision */
extern	precision	ltop(long);		/* long to precision */
extern	precision	ultop(unsigned long);	/* unsigned long to precision */

extern	int		ptoi(precision);	/* precision to int */
extern	unsigned int	ptou(precision);	/* precision to unsigned */
extern	long		ptol(precision);	/* precision to long */
extern	unsigned long	ptoul(precision);	/* precision to unsigned long */

extern	precision	atop(char *);		/* ascii to precision */
extern	char		*ptoa(precision);	/* precision to ascii */

extern	int 		btop(precision *result, /* base to precision */
   char *src, unsigned size, int *digitmap, unsigned radix);

extern	int				/* precision to base */
   ptob(precision, char *result, unsigned size, char *alphabet, unsigned radix);

/*
 * Can't do prototyping for these unless stdio.h has been included 
 */
#ifdef BUFSIZ
extern	precision	fgetp(FILE *stream);	        /* input precision */
extern	int		fputp(FILE *stream, precision); /* output precision */
extern	int		
   fprintp(FILE *stream, precision, int minWidth); /* output within a field */
#else
extern	precision	fgetp();	        /* input precision */
extern	int		fputp(); 		/* output precision */
extern	int		fprintp(); 		/* output within a field */
#endif

extern	int		putp(precision);  	  /* stdout  with '\n' */

extern	void		pshow(precision);	  /* display debug info */
extern	precision	prandnum();		  /* debug and profil only */
extern	precision	pshift(precision, int);	  /* shift left */

extern	precision	errorp(int errnum, char *routine, char *message);

extern	precision	pzero, pone, ptwo;	  /* constants 0, 1, and 2 */
extern	precision	p_one;			  /* constant -1 */

extern	precision	psqrt(precision);	     /* square root */
extern	precision	pfactorial(precision);	     /* factorial */
extern	precision	pipow(precision, unsigned);  /* unsigned int power */
extern	precision	ppow(precision, precision);  /* precision power */
extern	precision
   ppowmod(precision, precision, precision);	     /* precision power mod m */
extern	int		plogb(precision, precision); /* log base b of n */

extern	precision	dtop(double);		/* double to precision */
extern	double		ptod(precision);	/* precision to double */

/*
 * vector operations
 */
pvector pvundef(pvector, unsigned size);	/* local variable entry */
void    pvdestroy(pvector, unsigned size);	/* local variable exit */

pvector pvalloc(unsigned size);			/* pvec allocate */
void    pvfree(pvector, unsigned size);		/* pvec free */

pvector pvset(pvector, unsigned size, precision value);

#else

/*
 * Function versions of above if you still want side effects
 */

#ifndef __GNUC__
extern	precision 	pnew();		/* initialization */
extern	precision 	presult();	/* function result */
extern	precision	psetq();	/* quick assignment */
#endif
extern	precision	psetv(); 	/* checked assignment */
extern	precision	pparmv();	/* checked parameter */
extern	precision	pparmf();	/* unchecked parameter (fn) */

extern	int		pcmpz();	/* compare to zero */
extern	int		pcmp();		/* compare */
extern	int		picmp();	/* single digit compare */

extern	precision	padd();		/* add */
extern	precision	psub();		/* subtract */
extern	precision	pmul();		/* multiply */

extern	precision	pdivmod();	/* divide/remainder */
extern 	void		pidivmod();	/* single digit divide/remainder */
extern 	precision	pidiv();	/* single digit divide */
extern 	int		pimod();	/* single digit remainder */
extern	precision	pneg();		/* negate */
extern	precision	pabs();		/* absolute value */
extern	int		podd();		/* true if odd */
extern	precision	phalf();	/* divide by two */

extern	precision	pmin();		/* minimum value */
extern	precision	pmax();		/* maximum value */

extern	precision	prand();	/* random number generator */

extern	precision	itop();		/* int to precision */
extern	precision	utop();		/* unsigned to precision */
extern	precision	ltop();		/* long to precision */
extern	precision	ultop();	/* unsigned long to precision */

extern	int		ptoi();		/* precision to int */
extern	unsigned int	ptou();		/* precision to unsigned */
extern	long		ptol();		/* precision to long */
extern	unsigned long	ptoul();	/* precision to unsigned long */

extern	precision	atop();		/* ascii to precision */
extern	char		*ptoa();	/* precision to ascii */

extern	int		btop();		/* base to precision */
extern	int		ptob();		/* precision to base */

extern	precision	fgetp();	/* input a precision */
extern	int		fputp();	/* output a precision */
extern	int		putp();		/* output precision '\n' to stdout */
extern	int		fprintp();	/* output a precision within a field */

extern	void		pshow();	/* display debug info */
extern	precision	prandnum();	/* for debug and profil only */
extern	precision	pshift();	/* shift left */

extern	precision	errorp();	/* user-substitutable error handler */

extern	precision	pzero, pone, ptwo;	/* constants 0, 1, and 2 */
extern	precision	p_one;			/* constant -1 */

extern	precision	psqrt();	/* square root */
extern	precision	pfactorial();	/* factorial */
extern	precision	pipow();	/* unsigned int power */
extern	precision	ppow();		/* precision power */
extern	precision	ppowmod();	/* precision power mod m */
extern	int		plogb();	/* log base b of n */

extern	precision	dtop();		/* double to precision */
extern	double		ptod();		/* precision to double */

/*
 * vector operations
 */
pvector pvundef();			/* local variable entry */
void    pvdestroy();			/* local variable exit */
pvector pvalloc();			/* pvec allocate */
void    pvfree();			/* pvec free */
pvector pvset();			/* set each element to scaler */

#endif
