//	Zinc Application Framework - Z_BNUM.HPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#if !defined(Z_BNUM_HPP)
#define Z_BNUM_HPP
#include <z_fdata.hpp>
#include <z_stdarg.hpp>

// --------------------------------------------------------------------------
// ----- ZafBignumData ------------------------------------------------------
// --------------------------------------------------------------------------

#if !defined(ZAF_BIGNUM_SIZE)
#	define ZAF_BIGNUM_SIZE	ZAF_WORD_SIZE
#endif
#define ZAF_NUMBER_WHOLE	30
#define ZAF_NUMBER_DECIMAL	8
// Warning, if ZAF_DIGITS is not 4 or 8, you must rewrite "*" and "/" and "%".
#if ZAF_BIGNUM_SIZE > 16
#	define ZAF_DIGITS		8
#else
#	define ZAF_DIGITS		4
#endif

typedef ZafInt32 ZafIBignum;
typedef double ZafRBignum;
#if ZAF_DIGITS == 4
	typedef ZafUInt16 ZafNumber;
#elif ZAF_DIGITS == 8
	typedef ZafUInt32 ZafNumber;
#endif

class ZAF_EXPORT ZafBignumData : public ZafFormatData
{
public:
	// --- General members ---
	ZafBignumData(void);
	ZafBignumData(long value);
	ZafBignumData(double value);
	ZafBignumData(const ZafIChar *string, const ZafIChar *format = ZAF_NULLP(ZafIChar));
	ZafBignumData(const ZafBignumData &copy);
	virtual ~ZafBignumData(void);

	// --- Persistent members --
	virtual ZafData *Duplicate(void);
#if defined(USE_ZAF_PERSISTENCE)
	ZafBignumData(const ZafIChar *name, ZafDataPersistence &persist);
	static ZafElement *Read(const ZafIChar *name, ZafDataPersistence &persist);
	virtual void Write(ZafDataPersistence &persist);
#endif

	// --- Attributes & data access ---
	// get
	long IValue(void) const;
	double RValue(void) const;
	virtual int FormattedText(ZafIChar *buffer, int maxLength, const ZafIChar *format = 0) const;
	// set
	virtual void Clear(void);
	virtual ZafError SetBignum(long value);
	virtual ZafError SetBignum(double value);
	virtual ZafError SetBignum(const ZafIChar *buffer, const ZafIChar *format);
	virtual ZafError SetBignum(const ZafBignumData &number);

	// --- Operators ---
	operator long() { return IValue(); }
	operator double() { return RValue(); }

	ZafBignumData operator++(void);
	ZafBignumData operator++(int);
	ZafBignumData operator--(void);
	ZafBignumData operator--(int);

	ZafBignumData &operator=(const ZafBignumData &number);
	ZafBignumData &operator=(long value);
	ZafBignumData &operator=(double value);
	ZafBignumData &operator+=(const ZafBignumData &number);
	ZafBignumData &operator+=(long value);
	ZafBignumData &operator+=(double value);
	ZafBignumData &operator-=(const ZafBignumData &number);
	ZafBignumData &operator-=(long value);
	ZafBignumData &operator-=(double value);
	ZafBignumData &operator*=(const ZafBignumData &number);
	ZafBignumData &operator*=(long value);
	ZafBignumData &operator*=(double value);
	ZafBignumData &operator/=(const ZafBignumData &number);
	ZafBignumData &operator/=(long value);
	ZafBignumData &operator/=(double value);
	ZafBignumData &operator%=(const ZafBignumData &number);
	ZafBignumData &operator%=(long value);
	ZafBignumData &operator%=(double value);

	// --- Class identification ---
	static ZafClassID classID;
	static ZafClassNameChar ZAF_FARDATA className[];
	virtual ZafClassID ClassID(void) const { return (classID); }
	virtual ZafClassName ClassName(void) const { return (className); }
	virtual bool IsA(ZafClassID compareID) const;
	virtual bool IsA(ZafClassName compareName) const;

protected:
#if defined(USE_ZAF_PERSISTENCE)
	// --- Persistent members --
	void ReadData(ZafDataPersistence &persist);
	virtual ZafDataConstructor ReadFunction(void) { return (ZafBignumData::Read); }
	void WriteData(ZafDataPersistence &persist);
#endif

private:
	friend ZafBignumData abs(const ZafBignumData &number);
	friend ZafBignumData ceil(const ZafBignumData &number);
	friend ZafBignumData floor(const ZafBignumData &number);
	friend ZafBignumData round(const ZafBignumData &number, int places = 0);
	friend ZafBignumData truncate(const ZafBignumData &number, int places = 0);

	friend inline bool operator<(const ZafBignumData &number1, const ZafBignumData &number2);
	friend inline bool operator<=(const ZafBignumData &number1, const ZafBignumData &number2);
	friend inline bool operator>(const ZafBignumData &number1, const ZafBignumData &number2);
	friend inline bool operator>=(const ZafBignumData &number1, const ZafBignumData &number2);
	friend inline bool operator==(const ZafBignumData &number1, const ZafBignumData &number2);
	friend inline bool operator!=(const ZafBignumData &number1, const ZafBignumData &number2);

	// --- General members ---
	int sign;
	ZafNumber num[(ZAF_NUMBER_WHOLE+ZAF_NUMBER_DECIMAL+ZAF_DIGITS-1)/ZAF_DIGITS];

	int DoAddLoop(const ZafNumber *aw, int carry);
	void NinesComplement(void);
	ZafError AddSameSign(const ZafBignumData &b);
	ZafError AddDiffSign(const ZafBignumData &b);
	bool Compare(const ZafBignumData &b, bool gt, bool ls, bool eq) const;
	void CheckZero(void);
	void Itob(long value);
	void Rtob(double value);

	void Abs(void);
	ZafBignumData DivMod(const ZafBignumData &div);
	void FloorCeil(int sign);
	void Round(int places);
	void Truncate(int places);

	// printf support
	static void Bind(void);
	static void Format(va_list *argList, ZafIChar **ptrBuffer, ZafIChar fmtch, int flags, int width, int prec);
	static int Parse(va_list *argList, ZafIChar **ptrBuffer, ZafIChar fmtch, int flags, int width, const ZafIChar **fmt);
};

// --- ZafBignumData operators ---
inline ZafBignumData &ZafBignumData::operator+=(long value) { return (*this += ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator+=(double value) { return (*this += ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator-=(long value) { return (*this -= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator-=(double value) { return (*this -= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator*=(long value) { return (*this *= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator*=(double value) { return (*this *= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator/=(long value) { return (*this /= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator/=(double value) { return (*this /= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator%=(long value) { return (*this %= ZafBignumData(value)); }
inline ZafBignumData &ZafBignumData::operator%=(double value) { return (*this %= ZafBignumData(value)); }

ZAF_EXPORT ZafBignumData operator+(const ZafBignumData &number1, const ZafBignumData &number2);
ZAF_EXPORT ZafBignumData operator+(const ZafBignumData &number, long value);
ZAF_EXPORT ZafBignumData operator+(long value, const ZafBignumData &number);
ZAF_EXPORT ZafBignumData operator+(const ZafBignumData &number, double value);
ZAF_EXPORT ZafBignumData operator+(double value, const ZafBignumData &number);

ZAF_EXPORT ZafBignumData operator-(const ZafBignumData &number1, const ZafBignumData &number2);
ZAF_EXPORT ZafBignumData operator-(const ZafBignumData &number, long value);
ZAF_EXPORT ZafBignumData operator-(long value, const ZafBignumData &number);
ZAF_EXPORT ZafBignumData operator-(const ZafBignumData &number, double value);
ZAF_EXPORT ZafBignumData operator-(double value, const ZafBignumData &number);

ZAF_EXPORT ZafBignumData operator*(const ZafBignumData &number1, const ZafBignumData &number2);
ZAF_EXPORT ZafBignumData operator*(const ZafBignumData &number, long value);
ZAF_EXPORT ZafBignumData operator*(long value, const ZafBignumData &number);
ZAF_EXPORT ZafBignumData operator*(const ZafBignumData &number, double value);
ZAF_EXPORT ZafBignumData operator*(double value, const ZafBignumData &number);

ZAF_EXPORT ZafBignumData operator/(const ZafBignumData &number1, const ZafBignumData &number2);
ZAF_EXPORT ZafBignumData operator/(const ZafBignumData &number, long value);
ZAF_EXPORT ZafBignumData operator/(long value, const ZafBignumData &number);
ZAF_EXPORT ZafBignumData operator/(const ZafBignumData &number, double value);
ZAF_EXPORT ZafBignumData operator/(double value, const ZafBignumData &number);

ZAF_EXPORT ZafBignumData operator%(const ZafBignumData &number1, const ZafBignumData &number2);
ZAF_EXPORT ZafBignumData operator%(const ZafBignumData &number, long value);
ZAF_EXPORT ZafBignumData operator%(long value, const ZafBignumData &number);
ZAF_EXPORT ZafBignumData operator%(const ZafBignumData &number, double value);
ZAF_EXPORT ZafBignumData operator%(double value, const ZafBignumData &number);

inline bool operator<(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, false, true, false)); }
inline bool operator<(const ZafBignumData &number, long value) { return (number.IValue() < value); }
inline bool operator<(long value, const ZafBignumData &number) { return (value < number.IValue()); }
inline bool operator<(const ZafBignumData &number, double value) { return (number.RValue() < value); }
inline bool operator<(double value, const ZafBignumData &number) { return (value < number.RValue()); }

inline bool operator<=(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, false, true, true)); }
inline bool operator<=(const ZafBignumData &number, long value) { return (number.IValue() <= value); }
inline bool operator<=(long value, const ZafBignumData &number) { return (value <= number.IValue()); }
inline bool operator<=(const ZafBignumData &number, double value) { return (number.RValue() <= value); }
inline bool operator<=(double value, const ZafBignumData &number) { return (value <= number.RValue()); }

inline bool operator>(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, true, false, false)); }
inline bool operator>(const ZafBignumData &number, long value) { return (number.IValue() > value); }
inline bool operator>(long value, const ZafBignumData &number) { return (value > number.IValue()); }
inline bool operator>(const ZafBignumData &number, double value) { return (number.RValue() > value); }
inline bool operator>(double value, const ZafBignumData &number) { return (value > number.RValue()); }

inline bool operator>=(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, true, false, true)); }
inline bool operator>=(const ZafBignumData &number, long value) { return (number.IValue() >= value); }
inline bool operator>=(long value, const ZafBignumData &number) { return (value >= number.IValue()); }
inline bool operator>=(const ZafBignumData &number, double value) { return (number.RValue() >= value); }
inline bool operator>=(double value, const ZafBignumData &number) { return (value >= number.RValue()); }

inline bool operator==(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, false, false, true)); }
inline bool operator==(const ZafBignumData &number, long value) { return (number.IValue() == value); }
inline bool operator==(long value, const ZafBignumData &number) { return (value == number.IValue()); }
inline bool operator==(const ZafBignumData &number, double value) { return (number.RValue() == value); }
inline bool operator==(double value, const ZafBignumData &number) { return (value == number.RValue()); }

inline bool operator!=(const ZafBignumData &number1, const ZafBignumData &number2) { return (number1.Compare(number2, true, true, false)); }
inline bool operator!=(const ZafBignumData &number, long value) { return (number.IValue() != value); }
inline bool operator!=(long value, const ZafBignumData &number) { return (value != number.IValue()); }
inline bool operator!=(const ZafBignumData &number, double value) { return (number.RValue() != value); }
inline bool operator!=(double value, const ZafBignumData &number) { return (value != number.RValue()); }

#endif // Z_BNUM_HPP
