//
// linteger.cxx 
//
// Copyright (C) 1996-7 by Leonard Janke (janke@unixg.ubc.ca)

#include <linteger/linteger.hxx>
#include <strstream.h>
#include <iomanip.h>
#include <cassert>
#include <cstring>

const LInteger LInteger::Zero=LInteger(0u);
const LInteger LInteger::One=LInteger(1u);

const u8 integerTag(0x02u);
const u8 longLengthFlag(0x80u);

//
// Private methods
//

int LInteger::compress(unsigned int*& x, const int digits) 
{
  int leadZeros=0;
  int scanPos=0;

  while ( x[scanPos]==0u && scanPos<(digits-1) )
    {
      leadZeros++;
      scanPos++;
    }
    
  if ( leadZeros )
    {
      unsigned int* compressedMag=new unsigned int[digits-leadZeros];
      
      LMisc::MemCopy(compressedMag,x+leadZeros,digits-leadZeros);
      
      delete[] x;
      x=compressedMag;
    }

  return digits-leadZeros;
}

int LInteger::CompareMagnitudes(const LInteger& x, const LInteger& y)
{
  // this should be rewritten in assembly and put in BMath 

  assert( !x.IsCompressable() && !y.IsCompressable() );

  if ( x._digits > y._digits )
    return 1;
  
  if ( y._digits > x._digits )
    return -1;

  // can assume x._digits == y._digits now

  int scanPos(0);

  while ( scanPos<x._digits )
    if ( x._magnitude[scanPos] > y._magnitude[scanPos] )
      return 1;
    else if ( x._magnitude[scanPos] < y._magnitude[scanPos] )
      return -1;
    else
      scanPos++;

  return 0;
}

//
// Public Methods
//

LInteger::LInteger() : _magnitude(NULL), _sign(0), _digits(0)
{
}

LInteger::LInteger(unsigned int* magnitude, const int digits, 
		   const unsigned char sign=0, const int copyMag=1) 
  : _digits(digits), _sign(sign) 
{
  if ( copyMag )
    {
      _magnitude=new unsigned int[_digits];
      LMisc::MemCopy(_magnitude,magnitude,_digits);
    }
  else
    _magnitude=magnitude;
}

LInteger::LInteger(const int maxBits, PRNG& prng, const int fewerBits=0, 
		   const int requireOdd=0)
  : _sign(0)
{
  if ( maxBits==0 )
    {
      _magnitude=new unsigned int[1];
      _magnitude[0]=0u;
      _digits=0;
    }
  else 
    {
      _digits=LMisc::Ceiling(maxBits,LMisc::bitsPerUInt );

      _magnitude=new unsigned int[_digits];
      
      prng.NextQuantum(_magnitude,_digits*sizeof(unsigned int));
      LMisc::MemSwap(_magnitude,_digits); 
  
      if ( !fewerBits )
	BMath::SetBit(maxBits-1,_magnitude,_digits);
      
      if ( maxBits % LMisc::bitsPerUInt )
	_magnitude[0]&=LMisc::LowBitFilter(maxBits % LMisc::bitsPerUInt);
      
      _digits=compress(_magnitude,_digits);
      
      if ( requireOdd )
	_magnitude[_digits-1]|=1u;
    }
}

LInteger::LInteger(PRNG& prng, const LInteger& x, const LInteger& y)
{
  // create a random LInteger, z,  on [x,y]

  assert (  (y-x).IsNonNegative()  );

  LInteger range(y-x);
  int maxBits=range.NumberOfBits();

  LInteger z;

  LInteger delta(maxBits,prng,1,0);
  while ( delta > range )
    delta=LInteger(maxBits,prng,1,0);

  z=x+delta;

  _sign=z._sign;
  _digits=z._digits;
  _magnitude=new unsigned int[_digits];
  LMisc::MemCopy(_magnitude,z._magnitude,_digits);
}

LInteger::LInteger(const char* magString, int base=10)
{
  LInteger x;

  int parseError=StringToLInteger(magString,x,base);

  assert ( !parseError );

  _digits=x._digits;
  _magnitude=new unsigned int[_digits];
  LMisc::MemCopy(_magnitude,x._magnitude,_digits);
  _sign=x._sign;
}

LInteger::LInteger(const u8* magnitude, const int magBytes, 
		   const unsigned char sign=0) 
  : _sign(sign) 
{
  const int overflow=magBytes%LMisc::bytesPerUInt;
  const int pad= overflow ? LMisc::bytesPerUInt-overflow : 0;

  _digits=LMisc::Ceiling(magBytes,LMisc::bytesPerUInt);
  _magnitude=new unsigned int[_digits];

  u8* firstByte=(u8*) _magnitude;
  LMisc::MemZero(firstByte,pad);
  memcpy(firstByte+pad,magnitude,magBytes);

  LMisc::MemSwap(_magnitude,_digits);
}

LInteger::LInteger(const LInteger& x) : _digits(x._digits), _sign(x._sign)
{
  if ( x._magnitude )
    {
      _magnitude=new unsigned int[_digits];
      LMisc::MemCopy(_magnitude,x._magnitude,_digits);
    }
  else
    _magnitude=NULL;
}

LInteger::LInteger(const unsigned int x, const unsigned char sign=0) :
  _digits(1), _sign(sign)
{
  _magnitude=new unsigned int[1];

  _magnitude[0]=x;
}

LInteger::LInteger(const int x) : _digits(1)
{
  _magnitude=new unsigned int[1];

  if ( x >= 0 )
    {
      _magnitude[0]=x; // implicit conversion here
      _sign=0;
    }
  else
    {
      _magnitude[0]=-1*x; // implicit conversion here
      _sign=1;
    }
}

int LInteger::StringToLInteger(const char* magString, LInteger& x, 
			       int base=10)
{
  const magStringLength=strlen(magString);

  unsigned char sign=0;
  LInteger mag(0);

  if ( magStringLength )
    {
      int parsePos=0; 
      if ( magString[parsePos] == '+' ) 
	parsePos++;
      else if ( magString[parsePos] == '-' ) 
	{
	  sign=1;
	  parsePos++;
	}

      if ( parsePos <= magStringLength-1 )
	{
	  if ( magString[parsePos]=='0' )
	    {
	      base=8;
	      parsePos++;
	      if ( parsePos <= (magStringLength -1) && 
		   magString[parsePos]=='x' )
		{
		  base=16;
		  parsePos++;
		}
	    }
	}

      LInteger positionValue=1;
      int dv;

      for (int i=(magStringLength-1); i>=parsePos; i--)
	{
	  dv=DigitValue(magString[i]);
	  if ( dv == -1 || dv >= base ) 
	    return -1;
	  mag+=positionValue*dv;
	  positionValue*=base;
	}
    }

  if ( mag.IsZero() )
    sign=0; // need this for lib consistency

  x=LInteger(mag._magnitude,mag._digits,sign);

  return 0;
}

LInteger& LInteger::operator=(const char* magString) 
{
  *this=LInteger(magString);
  return *this;
}

LInteger& LInteger::operator=(const LInteger& x) 
{
  if ( this != &x )
    {
      delete[] _magnitude;

      if ( x._magnitude )
	{
	  _digits=x._digits;
	  _sign=x._sign;

	  _magnitude=new unsigned int[_digits];
	  LMisc::MemCopy(_magnitude,x._magnitude,_digits);
	}
      else
	_magnitude=NULL;
    }
}

LInteger& LInteger::operator=(const int x) 
{
  delete[] _magnitude;

  _magnitude=new unsigned int[1];

  _digits=1;

  if ( x >= 0 )
    {
      _magnitude[0]=x; // implicit conversion here
      _sign=0;
    }
  else
    {
      _magnitude[0]=-1*x; // implicit conversion here
      _sign=1;
    }
}

LInteger& LInteger::operator=(const unsigned int x) 
{
  delete[] _magnitude;

  _magnitude=new unsigned int[1];
  _magnitude[0]=x; // implicit conversion here

  _digits=1;
  _sign=0;
}


LInteger::~LInteger()
{
  delete[] _magnitude;
}

LInteger operator+(const LInteger& x, const LInteger& y) 
{
  unsigned int digitsZ;
  unsigned char signZ;
  unsigned int* magZ;

  char carry;

  if ( x._sign != y._sign ) 
    {
      switch ( LInteger::CompareMagnitudes(x,y) )
	{
	case 1:   // abs(x) > abs (y)
	  signZ=x._sign;
	  digitsZ=x._digits;
	  magZ=new unsigned int[digitsZ];
	  BMath::Subtract(x._magnitude,x._digits,y._magnitude, y._digits, 
			  magZ);
	  digitsZ=LInteger::compress(magZ,digitsZ);
	  break;
	case -1: // abs(y) > abs(x)
	  signZ=y._sign;
	  digitsZ=y._digits;
	  magZ=new unsigned int[digitsZ];
	  BMath::Subtract(y._magnitude, y._digits, x._magnitude, x._digits, 
			  magZ);
	  digitsZ=LInteger::compress(magZ,digitsZ);
	  break;
	case 0:   // abs(x)==abs(y)
	  signZ=0;
	  digitsZ=1;
	  magZ=new unsigned int[1];
	  magZ[0]=0u;
	  break;
	}
    }
  else // x._sign == y._sign
    {
      signZ=x._sign;

      if ( x._digits > y._digits )
	{
	  digitsZ=x._digits;
	  magZ=new unsigned int[digitsZ];
	  carry=BMath::Add(x._magnitude,x._digits,
			   y._magnitude,y._digits, 
			   magZ);
	}
      else
	{
	  digitsZ=y._digits;
	  magZ=new unsigned int[digitsZ];
	  carry=BMath::Add(y._magnitude,y._digits,
			   x._magnitude,x._digits, 
			   magZ);
	}

      if ( carry )
	{
	  unsigned int* scratch;

	  scratch=new unsigned int[digitsZ+1];

	  scratch[0]=1u;
	  LMisc::MemCopy(scratch+1,magZ,digitsZ);

  	  delete[] magZ;
	  magZ=scratch;
	  digitsZ++;
	}
  }

  return LInteger(magZ,digitsZ,signZ,0);
}

LInteger operator*(const LInteger& x, const LInteger& y) 
{
  unsigned int digitsZ;
  unsigned char signZ;
  unsigned int* magZ;

  if ( x.IsZero() || y.IsZero() )
    {
      signZ=0;
      digitsZ=1;
      magZ=new unsigned int[1];
      magZ[0]=0u;
    }
  else
    {
      signZ=x._sign^y._sign;
      digitsZ=x._digits+y._digits;
      magZ=new unsigned int[digitsZ];
      LMisc::MemZero(magZ,digitsZ);
      BMath::Multiply(x._magnitude,x._digits,y._magnitude,y._digits,magZ);
      digitsZ=LInteger::compress(magZ,digitsZ);
    }

  return LInteger(magZ,digitsZ,signZ,0);
}

LInteger operator*(const unsigned int x, const LInteger& y) 
{
  unsigned int digitsZ;
  unsigned char signZ;
  unsigned int* magZ;

  if ( x==0 || y.IsZero() )
    {
      signZ=0;
      digitsZ=1;
      magZ=new unsigned int[1];
      magZ[0]=0u;
    }
  else
    {
      signZ=y._sign;
      digitsZ=y._digits+1;
      magZ=new unsigned int[digitsZ];
      LMisc::MemZero(magZ,digitsZ);
      BMath::BasicMultiply(y._magnitude,x,magZ,y._digits);
      digitsZ=LInteger::compress(magZ,digitsZ);
    }

  return LInteger(magZ,digitsZ,signZ,0);
}

LInteger operator/(const LInteger& dividend, const LInteger& divisor) 
{
  assert ( divisor.IsNonZero() );

  if ( divisor._digits > dividend._digits )
    if ( dividend.IsNonNegative() )
      return 0;
    else
      if ( divisor.IsPositive() )
	return -LInteger::One;
      else
	return 1;

  unsigned int* q;
  unsigned int* r;

  BMath::Divide(dividend._magnitude, dividend._digits, 
		divisor._magnitude, divisor._digits, q, r);

  if (  dividend.IsNonNegative() && divisor.IsNonNegative() )
    {
      delete[] r;
      int qDigits=LInteger::compress(q,dividend._digits-divisor._digits+1);
      return LInteger(q,qDigits,0,0);
    }
  else if ( dividend.IsNonNegative() && divisor.IsNegative() )
    {
      delete[] r;
      int qDigits=LInteger::compress(q,dividend._digits-divisor._digits+1);
      return -LInteger(q,qDigits,0,0);
    }
  else if ( dividend.IsNegative() && divisor.IsNonNegative() )
    {
      int qDigits=LInteger::compress(q,dividend._digits-divisor._digits+1);
      int rDigits=LInteger::compress(r,dividend._digits+1);
      LInteger R(r,rDigits,0,0);

      if ( R.IsZero() )
	return -LInteger(q,qDigits,0,0);

      return -(LInteger(q,qDigits,0,0)+LInteger::One);
    }
  else // dividend.IsNegative() && divisor.IsNegative()
    {
      int qDigits=LInteger::compress(q,dividend._digits-divisor._digits+1);
      int rDigits=LInteger::compress(r,dividend._digits+1);
      LInteger R(r,rDigits,0,0);

      if ( R.IsZero() )
	return LInteger(q,qDigits,0,0);

      return LInteger(q,qDigits,0,0)+1;
    }
}

LInteger operator%(const LInteger& dividend, const LInteger& divisor) 
{
  assert( divisor.IsNonZero() );

  if ( divisor._digits > dividend._digits )
    if ( dividend.IsNonNegative() )
      return dividend;
    else
      return LInteger::AbsoluteValue(divisor)+dividend;

  unsigned int* q;
  unsigned int* r;

  BMath::Divide(dividend._magnitude, dividend._digits, 
		divisor._magnitude, divisor._digits,q,r);

  delete[] q;
  int rDigits=LInteger::compress(r,dividend._digits+1);

  if (  dividend.IsNonNegative() ) 
    return LInteger(r,rDigits,0,0);
  else // dividend<0
    {
      LInteger R(r,rDigits,0,0);

      if ( R.IsZero() )
	return R;
      else
	return LInteger::AbsoluteValue(divisor)-R;
    }
}

LInteger operator^(const LInteger& x, const LInteger& y)
{
  assert( !x._sign && !y._sign && ( x._digits == y._digits ) );

  int newMagDigits=x._digits;
  unsigned int* newMag=new unsigned int[newMagDigits];

  for (int i=0; i<newMagDigits; i++)
    newMag[i]=x._magnitude[i]^y._magnitude[i];

  newMagDigits=LInteger::compress(newMag,newMagDigits);

  return  LInteger(newMag,newMagDigits,0,0);
}

LInteger operator|(const LInteger& x, const LInteger& y)
{
  assert( !x._sign && !y._sign && ( x._digits == y._digits ));

  unsigned int* newMag;
  int newMagDigits=x._digits;

  newMag=new unsigned int[newMagDigits];

  for (int i=0; i<newMagDigits; i++)
    newMag[i]=x._magnitude[i]|y._magnitude[i];

  return  LInteger(newMag,newMagDigits,0,0);
}

LInteger operator&(const LInteger& x, const LInteger& y)
{
  assert(!x._sign && !y._sign && (x._digits == y._digits ));

  unsigned int* newMag;
  int newMagDigits=x._digits;

  newMag=new unsigned int[newMagDigits];

  for (int i=0; i<newMagDigits; i++)
    newMag[i]=x._magnitude[i]&y._magnitude[i];

  newMagDigits=LInteger::compress(newMag,newMagDigits);

  return  LInteger(newMag,newMagDigits,0,0);
}

LInteger operator~(const LInteger& x)
{
  assert( x.IsNonNegative() );

  unsigned int* newMag;
  int newMagDigits=x._digits;

  newMag=new unsigned int[newMagDigits];

  for (int i=0; i<newMagDigits; i++)
    newMag[i]=~x._magnitude[i];

  newMagDigits=LInteger::compress(newMag,newMagDigits);

  return  LInteger(newMag,newMagDigits,0,0);
}

LInteger operator>>(const LInteger& x, const int distance) 
{
  assert ( distance >= 0 );

  int newMagDigits=x._digits; 
  unsigned int* newMag=new unsigned int[newMagDigits];
  unsigned char newMagSign;
  
  LMisc::MemCopy(newMag,x._magnitude,newMagDigits);
  BMath::ShiftRight(newMag,newMagDigits,distance);

  if ( newMagDigits==1 && newMag[0]==0u )
    newMagSign=0u;
  else
    newMagSign=x._sign;

  return LInteger(newMag,newMagDigits,newMagSign,0);
}

LInteger& LInteger::operator>>=(const int distance) 
{
  assert ( distance >= 0 );
  
  BMath::ShiftRight(_magnitude,_digits,distance);

  if ( _digits==1 && _magnitude[0]==0u ) 
    _sign=0;

  return *this;
}

LInteger operator<<(const LInteger& x, const int distance) 
{
  assert ( distance >= 0 );

  int newMagDigits(x._digits); 
  unsigned int* newMag=new unsigned int[newMagDigits];
  
  LMisc::MemCopy(newMag,x._magnitude,newMagDigits);
  BMath::ShiftLeft(newMag,newMagDigits,distance);

  return LInteger(newMag,newMagDigits,x._sign,0);
}

LInteger LInteger::TwoToThe(const int x)
{
  assert( x >= 0 );

  int magDigits=LMisc::Ceiling(x+1,LMisc::bitsPerUInt);

  unsigned int* mag=new unsigned int[magDigits];

  LMisc::MemZero(mag,magDigits);

  mag[0]=1u<<(x%LMisc::bitsPerUInt);

  return LInteger(mag,magDigits,0,0);
}

void LInteger::compress() 
{
  int leadZeros=0;
  int scanPos=0;

  while ( _magnitude[scanPos]==0u && scanPos <(_digits-1))
    {
      leadZeros++;
      scanPos++;
    }
    
  if ( leadZeros )
    {
      unsigned int* compressedMag=new unsigned int[_digits-leadZeros];
      
      LMisc::MemCopy(compressedMag,_magnitude+leadZeros,_digits-leadZeros);
      
      delete[] _magnitude;

      _magnitude=compressedMag;
      _digits-=leadZeros;
    }
}

LInteger& LInteger::Square()
{
  unsigned int digitsOfY=2*(this->_digits);
  unsigned int* magOfY=new unsigned int[digitsOfY];
  LMisc::MemZero(magOfY,digitsOfY);
  
  BMath::Square(_magnitude,_digits, magOfY);
  delete[] _magnitude;
  _magnitude=magOfY;
  _digits=compress(_magnitude,digitsOfY);
  _sign=0u;

  return *this;
}

unsigned int LInteger::HasSmallPrimeFactor() const
{
  for (int i=0; i<BMath::SmallPrimesAvailable ; i++)
    if ( BMath::ModSmall(_magnitude,_digits,BMath::SmallPrime[i]) == 0u)
      return BMath::SmallPrime[i];

  return 0u;
}

ostream& LInteger::Print(ostream& os, const int base=10) const
{
  assert ( base >=2 && base <=36 );

  if ( _sign )
    os <<'-';

  if ( base==16 )
    os <<"0x";  // maybe should add high speed printing for this special case?
  else if ( base==8 )
    os <<"0";

  if ( this->IsZero() )
    return os <<"0";
  
  LInteger remainder=AbsoluteValue(*this);
  const LInteger baseAsLInteger(base);
  int charsAdded=0;
  strstream buf;

  while ( ! remainder.IsZero() )
    {
      LInteger digitL;
      int  digit;

      digitL=(remainder%baseAsLInteger);
      digit=digitL._magnitude[digitL._digits-1];
      remainder/=baseAsLInteger;
      buf <<DigitToCharacter(digit); charsAdded++;
    }

  char* oBuf=new char[charsAdded];
  char* oBufRev=new char[charsAdded];
  buf.read(oBufRev,charsAdded);

  for (int i=0; i<charsAdded; i++)
    oBuf[i]=oBufRev[charsAdded-1-i];
  delete[] oBufRev;
  os.write(oBuf,charsAdded);
  delete[] oBuf;

  return os;
}

ostream& operator<<(ostream& os, const LInteger& x)
{
  return x.Print(os);
}

istream& operator>>(istream& is, LInteger& x)
{
  const int readQuantum=512;
  static char inBuf[readQuantum+2];

  int parseError;

  is.getline(inBuf,readQuantum+2);

  if ( ! is.fail() )
    parseError=LInteger::StringToLInteger(inBuf,x);
  else // a really long string...
    {
      char* iBuf=new char[readQuantum];
      memcpy(iBuf,inBuf,readQuantum);
      int iBufSize=readQuantum;

      while ( is.fail() && ! is.bad() )
	{
	  char* newBuf=new char[2*iBufSize+2];
	  memcpy(newBuf,iBuf,iBufSize);
	  delete[] iBuf;

	  is.clear();
	  is.getline(newBuf+iBufSize,iBufSize+2);
	  iBufSize*=2;
	  
	  iBuf=new char[iBufSize];
	  memcpy(iBuf,newBuf,iBufSize);
	  delete[] newBuf;
	}

      parseError=LInteger::StringToLInteger(iBuf,x);
      delete[] iBuf;
    }

  if ( parseError )
    is.setstate(ios::failbit);

  return is;
}

ofstream& operator<<(ofstream& ofs, const LInteger& x)
{
  // write to files using DER encoding for portability and
  // fairly good compactness

  // first set up encoding of integer

  const int xBytes=x.NumberOfBytes();
  const int xBits=x.NumberOfBits();

  unsigned int* magCopy=new unsigned int[x._digits];
  LMisc::MemCopy(magCopy,x._magnitude,x._digits);
  LMisc::MemSwap(magCopy,x._digits);

  int overflow=0;

  if ( xBits % 8 == 0 ) 
    if ( x.IsPositive() )
      overflow=1;
    else if ( LInteger::AbsoluteValue(x)!=LInteger::TwoToThe(xBits-1) ) 
      overflow=1;
  
  unsigned int encodingBytes= xBytes+overflow;
  u8* encoding=new u8[encodingBytes];

  int leadZeros=x._digits*LMisc::bytesPerUInt-xBytes;
  memcpy(encoding+overflow,((u8*) magCopy)+leadZeros,xBytes);

  if ( overflow )
    encoding[0]=0u;

  if ( x.IsNegative() )
    LInteger::ConvertToTwosComplement(encoding,encodingBytes);

  // ready to write

  // tag

  ofs <<integerTag;

  // length encoding

  if ( encodingBytes < 128 )
    {
      u8 lengthEncoding=encodingBytes;
      ofs <<lengthEncoding;
    }
  else // encodingBytes >= 128
    {
      u8 lengthEncoding[LMisc::bytesPerUInt];

      u8 bytesInLengthEncoding=0;

      unsigned int temp=encodingBytes;
      while ( temp ) 
	{
	  bytesInLengthEncoding++;
	  temp>>=8;
	}

      const u8 lengthPrefix=longLengthFlag|bytesInLengthEncoding;

      ofs << lengthPrefix;

      unsigned int HELength=LMisc::BSwap(encodingBytes);
      int leadZeros=LMisc::bytesPerUInt-int(bytesInLengthEncoding);

      memcpy(lengthEncoding,&HELength,LMisc::bytesPerUInt);
      ofs.write(lengthEncoding+leadZeros,int(bytesInLengthEncoding));
    }

  // the actual encoding

  ofs.write(encoding,encodingBytes);
  
  // clean up

  delete[] magCopy;
  delete[] encoding;

  return ofs;
}

ifstream& operator>>(ifstream& ifs, LInteger& x)
{

  // read a DER encoded integer from a file stream

  u8 tag;
  ifs >>tag;

  if ( tag != integerTag )
    {
      ifs.setstate(ios::failbit);
      return ifs;
    }

  u8 lengthType;
  unsigned int length;

  ifs >>lengthType;

  if ( !ifs )
    return ifs;

  if ( lengthType & longLengthFlag ) // long length
    {
      int lengthEncodedLength= lengthType & ~longLengthFlag;
      if ( lengthEncodedLength > LMisc::bytesPerUInt )
	{
	  ifs.setstate(ios::failbit);
	  return ifs;
	}
      
      unsigned int preLength; 
      int leadZeros=LMisc::bytesPerUInt-lengthEncodedLength;

      LMisc::MemZero((u8*) &preLength,leadZeros);
      ifs.read(((u8*) &preLength)+leadZeros,lengthEncodedLength);
      
      length=LMisc::BSwap(preLength);
    }
  else // short length
    length=lengthType;

  if ( !ifs )
    return ifs;

  u8* preMag=new u8[length];
  u8 sign=0u;
  ifs.read(preMag,length);

  if ( preMag[0]&0x80u )  // got a negative number
    {
      sign=1u;
      LInteger::ConvertToTwosComplement(preMag,length);
    }

  x=LInteger(preMag,length,sign);
  x.compress();
    
  delete[] preMag;
  return ifs;
}

