/*
 * Copyright (c) 1994 David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * Data structure declarations for extended precision integer arithmetic.
 * The assumption made is that a long is 32 bits and shorts are 16 bits,
 * and longs must be addressible on word boundaries.
 */

#if !defined(ZMATH_H)
#define	ZMATH_H

#include <stdio.h>
#include "alloc.h"
#include "endian_calc.h"
#include "longbits.h"
#include "longlong.h"

#include "have_stdlib.h"
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif


#ifndef ALLOCTEST
# if defined(CALC_MALLOC)
#  define freeh(p) (((void *)p == (void *)_zeroval_) ||			\
		    ((void *)p == (void *)_oneval_) || free((void *)p))
# else
#  define freeh(p) { if (((void *)p != (void *)_zeroval_) &&		\
			 ((void *)p != (void *)_oneval_)) free((void *)p); }
# endif
#endif


typedef	int FLAG;			/* small value (e.g. comparison) */
typedef int BOOL;			/* TRUE or FALSE value */

extern long _quomod_;
extern long _quo_;
extern long _mod_;

#if !defined(TRUE)
#define	TRUE	((BOOL) 1)			/* booleans */
#endif
#if !defined(FALSE)
#define	FALSE	((BOOL) 0)
#endif


/*
 * NOTE: FULL must be twice the storage size of a HALF
 *	 HALF must be BASEB bits long
 */

#if LONG_BITS == 64

#define BASEB	32			/* use base 2^32 */
typedef unsigned int HASH;		/* 32 bit hash value */
typedef unsigned int HALF;		/* unit of number storage */
typedef int SHALF;			/* signed HALF */
typedef unsigned long FULL;		/* double unit of number storage */
typedef long SFULL;			/* signed FULL */
typedef FULL PRINT;			/* cast HALF to FULL for printf */

#elif LONGLONG_BITS == 64	/* XXX - only 64 bit longlong supported now */

#define BASEB	32			/* use base 2^32 */
typedef unsigned int HASH;		/* 32 bit hash value */
typedef unsigned int HALF;		/* unit of number storage */
typedef int SHALF;			/* signed HALF */
typedef unsigned long long FULL;	/* double unit of number storage */
typedef long long SFULL;		/* signed FULL */
typedef HALF PRINT;			/* leave HALF as HALF for printf */

#else

#define BASEB	16			/* use base 2^16 */
typedef unsigned long HASH;		/* 32 bit hash value */
typedef unsigned short HALF;		/* unit of number storage */
typedef short SHALF;			/* signed HALF */
typedef unsigned long FULL;		/* double unit of number storage */
typedef long SFULL;			/* signed FULL */
typedef FULL PRINT;			/* cast HALF to FULL for printf */

#endif

#define BASE	((FULL) U(1)<<BASEB)	/* base for calculations */
#define BASE1	((FULL) (BASE - U(1)))	/* one less than base */
#define	BASEDIG	((BASEB/16)*5)		/* number of digits in base */
#define FULL_BITS (2*BASEB)		/* bits in a FULL */

#define	TOPHALF	((FULL) (U(1) << (BASEB-1))) 	  /* highest bit in a HALF */
#define MAXHALF ((FULL) (TOPHALF - U(1)))	  /* largest SHALF value */

#define	TOPFULL	((FULL) (U(1) << (FULL_BITS-1)))  /* highest bit in a FULL */
#define MAXFULL ((FULL) (TOPFULL - U(1)))	  /* largest SFULL value */
#define MAXUFULL (MAXFULL | TOPHALF)		  /* largest FULL value */

#define	TOPLONG	((unsigned long) (1UL << (LONG_BITS-1)))  /* top long bit */
#define MAXLONG ((long) (TOPLONG - 1UL))	          /* largest long val */
#define MAXULONG (MAXLONG | TOPLONG)		 /* largest unsigned long val */


/*
 * The largest power of 10 we will compute for our decimal conversion
 * internal constants is: 10^(2^TEN_MAX).
 */
#define TEN_MAX 33	/* 10^2^33 requires about 1.66 * 2^31 bytes */


/*
 * LEN storage size must be <= FULL storage size
 */
typedef long LEN;			/* unit of length storage */
#define MAXLEN	((LEN) 0x7fffffffL)	/* longest value allowed */


#define	MAXREDC	5			/* number of entries in REDC cache */
#define	SQ_ALG2	20			/* size for alternative squaring */
#define	MUL_ALG2 20			/* size for alternative multiply */
#define	POW_ALG2 40			/* size for using REDC for powers */
#define	REDC_ALG2 50			/* size for using alternative REDC */


typedef union {
	FULL	ivalue;
	struct {
		HALF Svalue1;
		HALF Svalue2;
	} sis;
} SIUNION;


#if !defined(BYTE_ORDER)
#include <machine/endian.h>
#endif

#if !defined(LITTLE_ENDIAN)
#define LITTLE_ENDIAN	1234	/* Least Significant Byte first */
#endif
#if !defined(BIG_ENDIAN)
#define BIG_ENDIAN	4321	/* Most Significant Byte first */
#endif
/* PDP_ENDIAN - LSB in word, MSW in long is not supported */

#if BYTE_ORDER == LITTLE_ENDIAN
# define silow	sis.Svalue1	/* low order half of full value */
# define sihigh	sis.Svalue2	/* high order half of full value */
#else
# if BYTE_ORDER == BIG_ENDIAN
#  define silow	sis.Svalue2	/* low order half of full value */
#  define sihigh sis.Svalue1	/* high order half of full value */
# else
   /\oo/\    BYTE_ORDER must be BIG_ENDIAN or LITTLE_ENDIAN    /\oo/\  !!!
# endif
#endif


typedef struct {
	HALF	*v;		/* pointer to array of values */
	LEN	len;		/* number of values in array */
	BOOL	sign;		/* sign, nonzero is negative */
} ZVALUE;



/*
 * Function prototypes for integer math routines.
 */
#if defined(__STDC__) && __STDC__ != 0
#define MATH_PROTO(a) a
#else
#define MATH_PROTO(a) ()
#endif

extern HALF * alloc MATH_PROTO((LEN len));
#ifdef	ALLOCTEST
extern void freeh MATH_PROTO((HALF *));
#endif


/*
 * Input, output, and conversion routines.
 */
extern void zcopy MATH_PROTO((ZVALUE z, ZVALUE *res));
extern void itoz MATH_PROTO((long i, ZVALUE *res));
extern void utoz MATH_PROTO((FULL i, ZVALUE *res));
extern void atoz MATH_PROTO((char *s, ZVALUE *res));
extern long ztoi MATH_PROTO((ZVALUE z));
extern FULL ztou MATH_PROTO((ZVALUE z));
extern void zprintval MATH_PROTO((ZVALUE z, long decimals, long width));
extern void zprintx MATH_PROTO((ZVALUE z, long width));
extern void zprintb MATH_PROTO((ZVALUE z, long width));
extern void zprinto MATH_PROTO((ZVALUE z, long width));


/*
 * Basic numeric routines.
 */
extern void zmuli MATH_PROTO((ZVALUE z, long n, ZVALUE *res));
extern long zdivi MATH_PROTO((ZVALUE z, long n, ZVALUE *res));
extern long zmodi MATH_PROTO((ZVALUE z, long n));
extern void zadd MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zsub MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zmul MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zdiv MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res, ZVALUE *rem));
extern void zquo MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *rem));
extern BOOL zdivides MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern void zor MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zand MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zxor MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zshift MATH_PROTO((ZVALUE z, long n, ZVALUE *res));
extern void zsquare MATH_PROTO((ZVALUE z, ZVALUE *res));
extern long zlowbit MATH_PROTO((ZVALUE z));
extern long zhighbit MATH_PROTO((ZVALUE z));
extern void zbitvalue MATH_PROTO((long n, ZVALUE *res));
extern BOOL zisset MATH_PROTO((ZVALUE z, long n));
extern BOOL zisonebit MATH_PROTO((ZVALUE z));
extern BOOL zisallbits MATH_PROTO((ZVALUE z));
extern FLAG ztest MATH_PROTO((ZVALUE z));
extern FLAG zrel MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern BOOL zcmp MATH_PROTO((ZVALUE z1, ZVALUE z2));


/*
 * More complicated numeric functions.
 */
extern FULL uugcd MATH_PROTO((FULL i1, FULL i2));
extern long iigcd MATH_PROTO((long i1, long i2));
extern void zgcd MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zlcm MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zreduce MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *z1res, ZVALUE *z2res));
extern void zfact MATH_PROTO((ZVALUE z, ZVALUE *dest));
extern void zperm MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zcomb MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern FLAG zjacobi MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern void zfib MATH_PROTO((ZVALUE z, ZVALUE *res));
extern void zpowi MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void ztenpow MATH_PROTO((long power, ZVALUE *res));
extern void zpowermod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res));
extern BOOL zmodinv MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern BOOL zrelprime MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern long zlog MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern long zlog10 MATH_PROTO((ZVALUE z));
extern long zdivcount MATH_PROTO((ZVALUE z1, ZVALUE z2));
extern long zfacrem MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *rem));
extern void zgcdrem MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern long zdigits MATH_PROTO((ZVALUE z1));
extern FLAG zdigit MATH_PROTO((ZVALUE z1, long n));
extern BOOL zsqrt MATH_PROTO((ZVALUE z1, ZVALUE *dest));
extern void zroot MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *dest));
extern BOOL zissquare MATH_PROTO((ZVALUE z));


/*
 * Prime related functions.
 */
extern FLAG zisprime MATH_PROTO((ZVALUE z));
extern FULL znprime MATH_PROTO((ZVALUE z));
extern FULL next_prime MATH_PROTO((FULL v));
extern FULL zpprime MATH_PROTO((ZVALUE z));
extern void zpfact MATH_PROTO((ZVALUE z, ZVALUE *dest));
extern BOOL zprimetest MATH_PROTO((ZVALUE z, long count, long skip));
extern int znextcand MATH_PROTO((ZVALUE z1, long count, long skip, long modval, long modulus, ZVALUE *cand));
extern int zprevcand MATH_PROTO((ZVALUE z1, long count, long skip, long modval, long modulus, ZVALUE *cand));
extern FULL zlowfactor MATH_PROTO((ZVALUE z, long count));
extern FLAG zfactor MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern long zpix MATH_PROTO((ZVALUE z1));
extern void zlcmfact MATH_PROTO((ZVALUE z, ZVALUE *dest));


/*
 * Misc misc functions. :-)
 */
#if 0
extern void zapprox MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res1, ZVALUE *res2));
extern void zmulmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res));
extern void zsquaremod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zsubmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE z3, ZVALUE *res));
#endif
extern void zminmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern BOOL zcmpmod MATH_PROTO((ZVALUE z1, ZVALUE z2, ZVALUE z3));


/*
 * These functions are for internal use only.
 */
extern void ztrim MATH_PROTO((ZVALUE *z));
extern void zshiftr MATH_PROTO((ZVALUE z, long n));
extern void zshiftl MATH_PROTO((ZVALUE z, long n));
extern HALF *zalloctemp MATH_PROTO((LEN len));
extern void initmasks MATH_PROTO((void));


/*
 * Modulo arithmetic definitions.
 * Structure holding state of REDC initialization.
 * Multiple instances of this structure can be used allowing
 * calculations with more than one modulus at the same time.
 * Len of zero means the structure is not initialized.
 */
typedef	struct {
	LEN len;		/* number of words in binary modulus */
	ZVALUE mod;		/* modulus REDC is computing with */
	ZVALUE inv;		/* inverse of modulus in binary modulus */
	ZVALUE one;		/* REDC format for the number 1 */
} REDC;

extern REDC *zredcalloc MATH_PROTO((ZVALUE z1));
extern void zredcfree MATH_PROTO((REDC *rp));
extern void zredcencode MATH_PROTO((REDC *rp, ZVALUE z1, ZVALUE *res));
extern void zredcdecode MATH_PROTO((REDC *rp, ZVALUE z1, ZVALUE *res));
extern void zredcmul MATH_PROTO((REDC *rp, ZVALUE z1, ZVALUE z2, ZVALUE *res));
extern void zredcsquare MATH_PROTO((REDC *rp, ZVALUE z1, ZVALUE *res));
extern void zredcpower MATH_PROTO((REDC *rp, ZVALUE z1, ZVALUE z2, ZVALUE *res));


/*
 * macro expansions to speed this thing up
 */
#define ziseven(z)	(!(*(z).v & 01))
#define zisodd(z)	(*(z).v & 01)
#define ziszero(z)	((*(z).v == 0) && ((z).len == 1))
#define zisneg(z)	((z).sign)
#define zispos(z)	(((z).sign == 0) && (*(z).v || ((z).len > 1)))
#define zisunit(z)	((*(z).v == 1) && ((z).len == 1))
#define zisone(z)	((*(z).v == 1) && ((z).len == 1) && !(z).sign)
#define zisnegone(z)	((*(z).v == 1) && ((z).len == 1) && (z).sign)
#define zistwo(z)	((*(z).v == 2) && ((z).len == 1) && !(z).sign)
#define zisabstwo(z)	((*(z).v == 2) && ((z).len == 1))
#define zisabsleone(z)	((*(z).v <= 1) && ((z).len == 1))
#define zislezero(z)	(zisneg(z) || ziszero(z))
#define zisleone(z)	(zisneg(z) || zisabsleone(z))
#define zistiny(z)	((z).len == 1)

/*
 * zgtmaxfull(z)	TRUE if abs(z) > MAXFULL
 */
#define zgtmaxfull(z)	(((z).len > 2) || (((z).len == 2) && (((SHALF)(z).v[1]) < 0)))

/*
 * zgtmaxufull(z)	TRUE if abs(z) will not fit into a FULL (> MAXUFULL)
 */
#define zgtmaxufull(z)	((z).len > 2)

/*
 * zgtmaxulong(z)	TRUE if abs(z) > MAXULONG
 */
#if BASEB >= LONG_BITS
#define zgtmaxulong(z)	((z).len > 1)
#else
#define zgtmaxulong(z)	zgtmaxufull(z)
#endif

/*
 * zgtmaxlong(z)	TRUE if abs(z) > MAXLONG
 */
#if BASEB >= LONG_BITS
#define zgtmaxlong(z)	(((z).len > 1) || (((z).len == 1) && (((SHALF)(z).v[0]) < 0)))
#else
#define zgtmaxlong(z)	zgtmaxfull(z)
#endif

/*
 * Some algorithms testing for values of a certain length.  Macros such as
 * zistiny() do this well.  In other cases algorthms require tests for values
 * in comparison to a given power of 2.  In the later case, zistiny() compares
 * against a different power of 2 on a 64 bit machine.  The macros below
 * provide a tests against powers of 2 that are independent of the work size.
 *
 * 	zge16b(z)	TRUE if abs(z) >= 2^16
 * 	zge24b(z)	TRUE if abs(z) >= 2^24
 * 	zge31b(z)	TRUE if abs(z) >= 2^31
 * 	zge32b(z)	TRUE if abs(z) >= 2^32
 * 	zge64b(z)	TRUE if abs(z) >= 2^64
 */
#if BASEB == 32

#define zge16b(z)	(!zistiny(z) && ((z).v[0] >= (HALF)0x10000))
#define zge24b(z)	(!zistiny(z) && ((z).v[0] >= (HALF)0x1000000))
#define zge31b(z)	(!zistiny(z) || (((SHALF)(z).v[0]) < 0))
#define zge32b(z)	(!zistiny(z))
#define zge64b(z)	((z).len > 2)

#else

#define zge16b(z)	(!zistiny(z))
#define zge24b(z)	(((z).len > 2) || (((z).len == 2) && ((z).v[1] >= (HALF)0x100)))
#define zge31b(z)	(((z).len > 2) || (((z).len == 2) && (((SHALF)(z).v[1]) < 0)))
#define zge32b(z)	((z).len > 2)
#define zge64b(z)	((z).len > 4)

#endif


/*
 * ztofull - convert an absolute value of a ZVALUE to a FULL if possible
 *
 * If the value is too large, only the low order bits that are able to
 * be converted into a FULL will be used.
 */
#define ztofull(z)	(zistiny(z) ? ((FULL)((z).v[0])) :		\
				      ((FULL)((z).v[0]) +		\
				       ((FULL)((z).v[1]) << BASEB)))

#define z1tol(z)	((long)((z).v[0]))
#define z2tol(z)	((long)(((z).v[0]) + \
				(((z).v[1] & MAXHALF) << BASEB)))

/*
 * ztoulong - convert an absolute value of a ZVALUE to an unsigned long
 *
 * If the value is too large, only the low order bits that are able to
 * be converted into a long will be used.
 */
#if BASEB >= LONG_BITS
# define ztoulong(z)	((unsigned long)z1tol(z))
#else
# define ztoulong(z)	((unsigned long)ztofull(z))
#endif

/*
 * ztolong - convert an absolute value of a ZVALUE to a long
 *
 * If the value is too large, only the low order bits that are able to
 * be converted into a long will be used.
 */
#define ztolong(z)	((long)(ztoulong(z) & MAXLONG))

#define	zclearval(z)	memset((z).v, 0, (z).len * sizeof(HALF))
#define	zcopyval(z1,z2)	memcpy((z2).v, (z1).v, (z1).len * sizeof(HALF))
#define zquicktrim(z)	{if (((z).len > 1) && ((z).v[(z).len-1] == 0)) \
				(z).len--;}
#define	zfree(z)	freeh((z).v)


/*
 * Output modes for numeric displays.
 */
#define MODE_DEFAULT	0
#define MODE_FRAC	1
#define MODE_INT	2
#define MODE_REAL	3
#define MODE_EXP	4
#define MODE_HEX	5
#define MODE_OCTAL	6
#define MODE_BINARY	7
#define MODE_MAX	7

#define MODE_INITIAL	MODE_REAL


/*
 * Output routines for either FILE handles or strings.
 */
extern void math_chr MATH_PROTO((int ch));
extern void math_str MATH_PROTO((char *str));
extern void math_fill MATH_PROTO((char *str, long width));
extern void math_flush MATH_PROTO((void));
extern void math_divertio MATH_PROTO((void));
extern void math_cleardiversions MATH_PROTO((void));
extern void math_setfp MATH_PROTO((FILE *fp));
extern char *math_getdivertedio MATH_PROTO((void));
extern int math_setmode MATH_PROTO((int mode));
extern long math_setdigits MATH_PROTO((long digits));


#ifdef VARARGS
extern void math_fmt();
#else
extern void math_fmt MATH_PROTO((char *, ...));
#endif


/*
 * The error routine.
 */
#ifdef VARARGS
extern void math_error();
#else
extern void math_error MATH_PROTO((char *, ...));
#endif


/*
 * constants used often by the arithmetic routines
 */
extern HALF _zeroval_[], _oneval_[], _twoval_[], _threeval_[], _fourval_[];
extern HALF _fiveval_[], _sixval_[], _sevenval_[], _eightval_[], _nineval_[];
extern HALF _tenval_[], _elevenval_[], _twelveval_[], _thirteenval_[];
extern HALF _fourteenval_[], _fifteenval_[];

extern ZVALUE zconst[];		/* ZVALUE integers from 0 thru 15 */

extern ZVALUE _zero_, _one_, _two_, _ten_;

extern BOOL _math_abort_;	/* nonzero to abort calculations */
extern ZVALUE _tenpowers_[];	/* table of 10^2^n */
extern int _outmode_;		/* current output mode */
extern LEN _mul2_;		/* size of number to use multiply algorithm 2 */
extern LEN _sq2_;		/* size of number to use square algorithm 2 */
extern LEN _pow2_;		/* size of modulus to use REDC for powers */
extern LEN _redc2_;		/* size of modulus to use REDC algorithm 2 */
extern HALF *bitmask;		/* bit rotation, norm 0 */
extern HALF lowhalf[];		/* bit masks from low end of HALF */
extern HALF highhalf[];		/* bit masks from high end of HALF */

#endif

/* END CODE */
