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

#include <linteger/linteger.hxx>

template<class T>
T LC_SmallExp(const T& g, const LInteger& x)
{
  // This algorithm is from Henri Cohen's book 
  // _A_Course_in_Computational_Algebraic_Number_Theory_ 

  T y=T::MultiplicativeIdentity();

  const int f(x.NumberOfBits());

  for (int e(f-1); e>=0; e--)
    {
      y.Square();
      if (BMath::TestBit(e,x.magnitude(),x.digits()))
	y*=g;
    }

  return y;
}

template<class T>
T LC_MediumExp(const T& g, const LInteger& x)
{
  // This algorithm is from Henri Cohen's book 
  // _A_Course_in_Computational_Algebraic_Number_Theory_ 

  if ( x.IsZero() )
    return T::MultiplicativeIdentity();

  T* z=new T[16];

  z[1]=g;

  T zSquared(z[1]);
  zSquared.Square();

  // make z[3] ... z[15]

  for (int i=3; i<16; i+=2)
    {
      z[i]=z[i-2];
      z[i]*=zSquared;
    }

  int start=LC_FirstNonZeroNibble(x.magnitude()[0]);

  // deal with first non zero nibble of x 
  
  int a=LC_LUNibble(x.magnitude(),start);
  int b=LC_AToB[a];
  T y(z[b]);
  int t=LC_AToT[a];
  for (int i=0; i<t; i++)
    y.Square();

  // deal with remainder of x 

  for (int nibble=start+1; nibble<x.digits()*LMisc::nibblesPerUInt ; nibble++)
    {
      a=LC_LUNibble(x.magnitude(),nibble);
      b=LC_AToB[a];
      t=LC_AToT[a];
      for (int i=0; i<4-t; i++)
	y.Square();
      if ( a )
	y*=z[b];
      for (int i=0; i<t; i++)
	y.Square();
    }

  delete[] z;

  return y;
}

template<class T>
T LC_BigExp(const T& g, const LInteger& x)
{
  // This algorithm is from Henri Cohen's book 
  // _A_Course_in_Computational_Algebraic_Number_Theory_ 

  if ( x.IsZero() )
    return T::MultiplicativeIdentity();

  T* z=new T[256];

  z[1]=g;

  T zSquared(z[1]);
  zSquared.Square();

  // make z[3] ... z[255]

  for (int i=3; i<256; i+=2)
    {
      z[i]=z[i-2];
      z[i]*=zSquared;
    }

  int start=LC_FirstNonZeroByte(x.magnitude()[0]);

  // deal with first non zero byte of x 
  
  int a=LC_LUByte(x.magnitude(),start);
  int b=LC_AToB[a];
  T y(z[b]);
  int t=LC_AToT[a];
  for (int i=0; i<t; i++)
    y.Square();

  // deal with remainder of x 

  for (int byte=start+1; byte<x.digits()*LMisc::bytesPerUInt ; byte++)
    {
      a=LC_LUByte(x.magnitude(),byte);
      b=LC_AToB[a];
      t=LC_AToT[a];
      for (int i=0; i<8-t; i++)
	y.Square();
      if ( a )
	y*=z[b];
      for (int i=0; i<t; i++)
	y.Square();
    }

  delete[] z;

  return y;
}

template<class T>
T LC_Exp(const T& g, const LInteger& x)
{
  const int numBits=x.NumberOfBits();

  if ( numBits <= 32 )
    return LC_SmallExp(g,x);
  else if ( numBits <= 960 )
    return LC_MediumExp(g,x);
  else  // numBits > 960
    return LC_BigExp(g,x);
}

