



#include <LiDIA/base_bigmod.h>


 residue_class_list<bigint>* base_bigmod::L = nil;


 //
 // ***** normalize *****
 //

 void base_bigmod::normalize ( const bigint & m )
  {
     bigint q, r;
     div_rem(q, r, I, m);
     I.assign(r);
     if (I.is_negative())
	add(I, I, m);
  }


 //
 // ***** constructor / destructor *****
 //


 base_bigmod::base_bigmod ()
  {
     debug_handler ( "base_bigmod", "base_bigmod()" ) ;
  }


 base_bigmod::~base_bigmod ()
  {
     debug_handler ( "base_bigmod", "~base_bigmod()" ) ;
  }



 //
 // ***** member functions *****
 //

 bigint base_bigmod::invert(const bigint & m, int i)
  {
    bigint d, u, v;
    d = xgcd(u, v, I, m);
    if (d.is_one())
    {
        I.assign(u);
	this->normalize(m);
    }
    else
    {
	if (i)
	 {
	    warning_handler("base_bigmod", "invert::inverse does not exist.");
	 }
	else
	 {
	    lidia_error_handler("base_bigmod", "invert::inverse does not exist.");
         }
    }
    return d;
  }

 void base_bigmod::negate ( const bigint & m )
  {
    if (!I.is_zero())
    {
        I.negate ();
	add (I, I, m);
    }
  }
 
 void base_bigmod::multiply_by_2 (const bigint & m)
  {
    I.multiply_by_2();
    if (I.compare(m) >= 0)
	subtract(I, I, m);
  }

 void base_bigmod::divide_by_2 (const bigint & m)
  {
    if (m.is_even() && !I.is_even())
	lidia_error_handler("base_bigmod", "divide_by_2(bigint&)::even modulus");
    if (!I.is_even())
	add(I, I, m);
    I.divide_by_2();
  }



 //
 // ***** comparisons *****
 //

	 
 //
 // ***** operator overloading *****
 //


 //
 // ***** arithmetical procedures *****
 //


 void invert(base_bigmod & a, const base_bigmod & b, const bigint & m)
  {
    a.assign(b);
    a.invert(m);
  }

 void negate(base_bigmod & a, const base_bigmod & b, const bigint & m)
  {
    a.assign(b);
    a.negate(m);
  }

 void add ( base_bigmod & c, const base_bigmod & a, const base_bigmod & b, const bigint & m )
  {
    add (c.I, a.I, b.I);
    if (c.I.compare(m) >= 0)
      subtract (c.I, c.I, m);   
  }

 void add ( base_bigmod & c, const base_bigmod & a, long i, const bigint & m )
  {
    add (c.I, a.I, i);
    c.normalize (m) ;
  }

 void add ( base_bigmod & c, long i, const base_bigmod & a, const bigint & m )
  {
    add (c.I, i, a.I );
    c.normalize (m) ;
  }

 void add ( base_bigmod & c, const base_bigmod & a, const bigint & i, const bigint & m )
  {
    add (c.I, a.I, i);
    c.normalize (m) ;
  }

 void add ( base_bigmod & c, const bigint & i, const base_bigmod & a, const bigint & m )
  {
    add (c.I, i, a.I );
    c.normalize (m) ;
  }

 void subtract ( base_bigmod & c, const base_bigmod & a, const base_bigmod & b, const bigint & m )
  {
    subtract (c.I, a.I, b.I);
    if (c.I.is_negative())
      add (c.I, c.I, m);
  }

 void subtract ( base_bigmod & c, const base_bigmod & a, long i, const bigint & m )
  {
    subtract (c.I, a.I, i);
    c.normalize (m);
  }

 void subtract ( base_bigmod & c, long i, const base_bigmod & a, const bigint & m )
  {
    subtract (c.I, a.I, i);
    c.negate (m);
    c.normalize (m);
  }

 void subtract ( base_bigmod & c, const base_bigmod & a, const bigint & i, const bigint & m )
  {
    subtract (c.I, a.I, i);
    c.normalize (m);
  }

 void subtract ( base_bigmod & c, const bigint & i, const base_bigmod & a, const bigint & m )
  {
    subtract (c.I, a.I, i);
    c.negate (m);
    c.normalize (m);
  }

 void multiply ( base_bigmod & c, const base_bigmod & a, const base_bigmod & b, const bigint & m )
  {
    multiply (c.I, a.I, b.I);
    remainder (c.I, c.I, m);
  }

 void multiply ( base_bigmod & c, const base_bigmod & a, long i, const bigint & m )
  {
    multiply (c.I, a.I, i);
    c.normalize (m);
  }

 void multiply ( base_bigmod & c, long i, const base_bigmod & a, const bigint & m )
  {
    multiply (c.I, a.I, i);
    c.normalize (m);
  }

 void multiply ( base_bigmod & c, const base_bigmod & a, const bigint & i, const bigint & m )
  {
    multiply (c.I, a.I, i);
    c.normalize (m);
  }

 void multiply ( base_bigmod & c, const bigint & i, const base_bigmod & a, const bigint & m )
  {
    multiply (c.I, a.I, i);
    c.normalize (m);
  }

 void divide ( base_bigmod & c, const base_bigmod & a, const base_bigmod & b, const bigint & m )
  {
    bigint d, u, v;
    d = xgcd(u, v, b.I, m);
    if (!d.is_one())
	lidia_error_handler("base_bigmod", "divide::inverse undefined");
    if (u.is_negative())
	add(u, u, m);
    multiply(c.I, a.I, u);
    remainder(c.I, c.I, m);
  }

 void divide ( base_bigmod & c, const base_bigmod & a, long i, const bigint & m )
  {
    bigint d, u, v, b = i;
    d = xgcd(u, v, b, m);
    if (!d.is_one())
	lidia_error_handler("base_bigmod", "divide::inverse undefined");
    if (u.is_negative())
	add(u, u, m);
    multiply(c.I, a.I, u);
    remainder(c.I, c.I, m);
  }

 void divide ( base_bigmod & c, long i, const base_bigmod & a, const bigint & m )
  {
    invert(c, a, m);
    multiply(c, c, i, m);
  }

 void divide ( base_bigmod & c, const base_bigmod & a, const bigint & i, const bigint & m )
  {
    bigint d, u, v, b = i;
    d = xgcd(u, v, b, m);
    if (!d.is_one())
	lidia_error_handler("base_bigmod", "operator/::inverse undefined");
    if (u.is_negative())
	add(u, u, m);
    multiply(c.I, a.I, u);
    remainder(c.I, c.I, m);
  }

 void divide ( base_bigmod & c, const bigint & i, const base_bigmod & a, const bigint & m )
  {
    invert(c, a, m);
    multiply(c, c, i, m);
  }

 void power ( base_bigmod & c, const base_bigmod & a, const bigint & b, const bigint & m )
  {
    base_bigmod multiplier = a;
    bigint exponent;
    if (b.is_zero() || a.is_one())
        c.assign_one();
    else
    {
        exponent.assign(b);
        if (exponent.is_negative())
        {
            bigint d, u, v;
	    d = xgcd(u, v, a.I, m);
            if (!d.is_one())
                lidia_error_handler("base_bigmod", "power::inverse undefined");
            if (u.is_negative())
		add(u, u, m);
            multiplier.I.assign(u);
            exponent.negate();
        }
        else
            multiplier.I.assign(a.I);
        c.assign_one();
        while (exponent.is_gt_zero())
        {
            if (!exponent.is_even())
		multiply(c, c, multiplier, m);
            square(multiplier, multiplier, m);
            exponent.divide_by_2();
        }
    }
  }

 void power ( base_bigmod & c, const base_bigmod & a, long i, const bigint & m )
  {
    base_bigmod multiplier;
    long exponent;
    if ((i == 0) || a.is_one())
        c.assign_one();
    else
    {
        exponent = i;
        if (exponent < 0)
        {
            bigint d, u, v;
	    d = xgcd(u, v, a.I, m);
            if (!d.is_one())
		lidia_error_handler("base_bigmod", "operator^::inverse undefined");
            if (u.is_negative())
		add(u, u, m);
            multiplier.I.assign(u);
            exponent = -exponent;
        }
        else
            multiplier.I = a.I;
        c.assign_one();
        while (exponent > 0)
        {
            if (exponent & 1)
		multiply(c, c, multiplier, m);
            square(multiplier, multiplier, m);
            exponent >>= 1;
        }
    }
  }

 void square(base_bigmod & a, const base_bigmod & b, const bigint & m)
  {
    square(a.I, b.I);
    remainder(a.I, a.I, m);
  }


 void inc ( base_bigmod & c, const bigint & m )
  {
    inc(c.I);
    if (c.I.abs_compare(m) == 0)
	c.I.assign_zero();
  }

 void dec ( base_bigmod & c, const bigint & m )
  {
    if (c.I.is_zero())
	c.I.assign(m);
    dec(c.I);
  }


 //
 // ***** functions *****
 //

 base_bigmod inverse(const base_bigmod & a, const bigint & m)
  {
    base_bigmod c;
    c.assign(a);
    c.invert(m);
    return c;
  }

