//
// linteger.hxx
//
// A C++ class of objects representing multiple precision integers
//
// Copyright (C) 1996-7 by Leonard Janke (janke@unixg.ubc.ca)

#ifndef _LINTEGER_HXX
#define _LINTEGER_HXX

#include <linteger/bmath.hxx>
#include <prng/prng.hxx>
#include <fstream.h>

class LInteger 
{
private:
  // DER Stuff
  unsigned int* _magnitude;
  unsigned char _sign;
  int _digits;

  static const u8 integerTag;
  static const u8 longLengthFlag;

  static inline int DigitValue(char digit);
  static inline char DigitToCharacter(const int digit, const int upcase=1);

  static int compress(unsigned int*& x, const int digits);

  static inline void ConvertToTwosComplement(u8* x, int bytes);
public:
  LInteger();
  LInteger(unsigned int* magnitude, const int digits, 
	   const unsigned char sign=0, const int copyMag=1);
  LInteger(const int maxBits, PRNG& prng, const int fewerBits=0, 
	   const int requireOdd=0);
  LInteger(PRNG& prng, const LInteger& x, const LInteger& y);
  LInteger(const char* magString, int base=10);
  LInteger(const u8* magnitude, const int magBytes, 
	   const unsigned char sign=0);
  LInteger(const LInteger& x);
  LInteger(const unsigned int x, const unsigned char sign=0);
  LInteger(const int x);

  static int StringToLInteger(const char* magString, LInteger& x, int base=10);

  LInteger& operator=(const char* magString); 
  LInteger& operator=(const LInteger& x); 
  LInteger& operator=(const unsigned int x);
  LInteger& operator=(const int x);

  ~LInteger();

  static LInteger TwoToThe(const int x); // 2**x

  inline unsigned char sign() const;
  inline int digits() const;
  inline unsigned int* magnitude() const;

  inline void SetDigits(const int newDigits);
  inline void SetSign(const unsigned char newSign);
  inline void SetMagnitude(unsigned int* newMagnitude);

  friend inline LInteger operator-(const LInteger& x);
  friend inline LInteger operator+(const LInteger& x);

  friend LInteger operator+(const LInteger& x, const LInteger& y);
  inline LInteger& operator+=(const LInteger& x);

  inline LInteger operator++(int); // postfix
  inline LInteger& operator++();    // prefix

  friend inline LInteger operator-(const LInteger& x, const LInteger& y);
  inline LInteger& operator-=(const LInteger& x);

  inline LInteger operator--(int); // postfix 
  inline LInteger& operator--();    // prefix

  friend LInteger operator*(const LInteger& x, const LInteger& y);
  friend LInteger operator*(const unsigned int x, const LInteger& y);
  friend inline LInteger operator*(const LInteger& x, const unsigned int y);
  friend inline LInteger operator*(const int x, const LInteger& y);
  friend inline LInteger operator*(const LInteger& x, const int y);
  inline LInteger& operator*=(const LInteger& x);
  inline LInteger& operator*=(const int x);
  inline LInteger& operator*=(const unsigned int x);

  friend LInteger operator/(const LInteger& dividend, const LInteger& divisor);
  inline LInteger& operator/=(const LInteger& divisor);

  friend LInteger operator%(const LInteger& dividend, const LInteger& divisor);
  inline LInteger& operator%=(const LInteger& divisor);

  friend LInteger operator^(const LInteger& x, const LInteger& y);
  inline LInteger& operator^=(const LInteger& x);

  friend LInteger operator|(const LInteger& x, const LInteger& y);
  inline LInteger& operator|=(const LInteger& x);

  friend LInteger operator&(const LInteger& x, const LInteger& y);
  inline LInteger& operator&=(const LInteger& x);

  friend LInteger operator~(const LInteger& x);

  friend LInteger operator>>(const LInteger& x, const int distance);
  LInteger& operator>>=(const int distance);

  friend LInteger operator<<(const LInteger& x, const int distance);
  inline LInteger& operator<<=(const int distance);

  friend inline int operator<(const LInteger& x, const LInteger& y);
  friend inline int operator>(const LInteger& x, const LInteger& y);
  friend inline int operator==(const LInteger& x, const LInteger& y);
  friend inline int operator!=(const LInteger& x, const LInteger& y);
  friend inline int operator<=(const LInteger& x, const LInteger& y);
  friend inline int operator>=(const LInteger& x, const LInteger& y);

  // RIP inline operator int() const: no end of compiler warnings...

  void compress();

  //  properties
  inline int NumberOfBits() const;
  inline int NumberOfBytes() const;

  // static instances

  static const LInteger One;
  static const LInteger Zero;
  static const LInteger& MultiplicativeIdentity() { return One; }
  static const LInteger& AdditiveIdentity() { return Zero; }

  // simple math
  LInteger& Square();
  inline LInteger& DivByTwo();

  static inline LInteger Negative(const LInteger& x);
  static inline LInteger AbsoluteValue(const LInteger& x);
  static int CompareMagnitudes(const LInteger& x, const LInteger& y);

  // some predicates

  unsigned int HasSmallPrimeFactor() const;
  inline int IsOdd() const;
  inline int IsEven() const;
  inline int IsZero() const;
  inline int IsOne() const;
  inline int IsPositive() const;
  inline int IsNegative() const;
  inline int IsNonNegative() const;
  inline int IsNonZero() const;
  inline int IsCompressable() const;

  // IO Stuff
  ostream& Print(ostream& os, const int base=10) const;

  friend ostream& operator<<(ostream& os, const LInteger& x);
  friend istream& operator>>(istream& is, LInteger& x);

  friend ofstream& operator<<(ofstream& ofs, const LInteger& x);
  friend ifstream& operator>>(ifstream& ifs, LInteger& x);

  friend class LMath;
  friend class MontyRing;
  friend class MontyRep;
};     

#include <linteger/linteger.inl>

#endif
