#ifndef BASE_BIGNUM_H
#define BASE_BIGNUM_H

#include "misc/compat.h"

#include "base/dblock.h"


//wordsize: BN8 = 8 bit word, BN16 = 16 bit words, BN32 = 32bit words
#define BN32

//#define USE_PTR //define this for bignums to use direct pointers. don't expect real speed gain.


#ifdef BN8
typedef uint8 BND;		//the standard word size for a BigNum
typedef uint16 BND2;	//double the standard word size
typedef int16 BND2S;	//double the standard word size but is signed

#define BND_MAX   0xff
#define BND_MAX1 0x100

#define BND_MAX_BIT 0x80

#define BND_BITS_MASK 0x07

#define BND_BITS 8
#define BND_BITS1 7
#define BND_BITS_BITS 3

#define BND_LOWBITS {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}
#endif

#ifdef BN16
typedef uint16 BND;
typedef uint32 BND2;
typedef int32 BND2S;

#define BND_MAX   0xffff
#define BND_MAX1 0x10000

#define BND_MAX_BIT 0x8000

#define BND_BITS_MASK 0x0f

#define BND_BITS 16
#define BND_BITS1 15
#define BND_BITS_BITS 4

#define BND_LOWBITS {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}
#endif

#ifdef BN32
typedef uint32 BND;
typedef uint64 BND2;
typedef int64 BND2S;

#define BND_MAX   0xffffffff
#define BND_MAX1 0x100000000

#define BND_MAX_BIT 0x80000000

#define BND_BITS_MASK 0x1f

#define BND_BITS 32
#define BND_BITS1 31
#define BND_BITS_BITS 5

#define BND_LOWBITS {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x01FFFF, 0x03FFFF, 0x07FFFF, 0x0FFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF}

#define BN_FORCE_MUL_2 //force casting to BND2 when multiplying
//#define BN_FORCE_SUB_2 //force casting to BND2 when subtracting
//#define BN_FORCE_ADD_2 //force casting to BND2 when adding
#endif

typedef struct BigNum {
	int maxSize;
	int size;
	DataBlock *dblock; //this is a pointer back to the data block this bignum is stored in.
	BND		data[1];		
} BigNum;

void bignumFree(BigNum *num);
BigNum *bignumDropZeros(BigNum *num);
BigNum *bignumFromInt(uint64 n);
uint64 bignumToInt(BigNum *num);
BigNum *bignumFromBuffer(uint8 *buf, int len);
int bignumToBuf(uint8 *buf, BigNum *big, int maxlength);
BigNum *bignumFromHex(char *hex);
char *bignumToHex(BigNum *num);
BigNum *bignumCopy(BigNum *src);
BigNum *bignumAssign(BigNum *dst, BigNum *src);
BigNum *bignumExpand(BigNum *num, int length);
BigNum *bignumMake(int maxlen, int initlen);
BigNum *bignumAdd(BigNum *n1, BigNum *n2);
BigNum *bignumAddKeep(BigNum *n1, BigNum *n2);
BigNum *bignumSubtract(BigNum *n1, BigNum *n2);
BigNum *bignumSubtractKeep(BigNum *n1, BigNum *n2);
BigNum *bignumCarry(BigNum *num, uint8 carry, int dignum);
BigNum *bignumDec(BigNum *num);
int bignumIsZero(BigNum *num);
int bignumIsEqual(BigNum *n1, BigNum *n2);
int bignumCompare(BigNum *n1, BigNum *n2);
BigNum *bignumShiftLeftWords(BigNum *num, int nbytes);
BigNum *bignumShiftRightWords(BigNum *num, int nbytes);
BigNum *bignumShiftLeft(BigNum *num);
BigNum *bignumShiftRight(BigNum *num);
BigNum *bignumShiftRightX(BigNum *num, int shift);
int bignumBottomBit(BigNum *num);
BigNum *bignumMod(BigNum *num, BigNum *modulus, BigNum **scratch);
BigNum *bignumModKeep(BigNum *num, BigNum *modulus, BigNum **scratch);
BigNum *bignumDiv(BigNum *num, BigNum *dividend);
BigNum *bignumDivKeep(BigNum *num, BigNum *dividend);
BigNum *bignumMulKeep(BigNum *result, BigNum *num1, BigNum *num2);
BigNum *bignumMul(BigNum *result, BigNum *num1, BigNum *num2);
BigNum *bignumSqrKeep(BigNum *result, BigNum *num);
BigNum *bignumSqr(BigNum *result, BigNum *num);
BigNum *bignumExpKeep(BigNum *n, BigNum *y);
BigNum *bignumExpModKeep(BigNum *n, BigNum *y, BigNum *modulus);
BigNum *bignumAvgKeep(BigNum *n1, BigNum *n2);
int bignumNumBits(BigNum *n);
BigNum *bignumSqrt(BigNum *n);
BigNum *bignumModInverseKeep(BigNum *num, BigNum *modulus);

BigNum *bignumExpandOnly(BigNum *num, int length);
BigNum *bignumSubtractOffset(BigNum *n1, BigNum *n2, int offset1);
int bignumCompareOffset(BigNum *n1, BigNum *n2, int offset1);


#endif //BASE_BIGNUM_H


