


//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : multi_bigmod.h 
//		 (Most of the code was taken from the old 
//		  LiDIA - version of bigmod written by Thomas Papanikolaou.)
// Author      : Markus Maurer (MM)
// Last change : MM, Oct 18 1995, initial version
//		 MM, Dec 18 1995, added void set_modulus(const multi_bigmod&)
//		 MM, Apr 25 1996, appended const to function const bigint & modulus ()
//


#ifndef LIDIA_BIGMOD_H
#define LIDIA_BIGMOD_H  


class bigmod;

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:base_bigmod.h>
#include <LiDIA:bigmod.h>
#else

#include <LiDIA/base_bigmod.h>
#include <LiDIA/bigmod.h>
#endif



 class multi_bigmod : public base_bigmod
  {
    private :

#ifndef HEADBANGER
        static residue_class<bigint> *zero;
        residue_class<bigint> *Mp ;
        void assign_modulus ( residue_class<bigint> *a )
	 {if (Mp != a){(base_bigmod::L)->clear(Mp); Mp = (base_bigmod::L)->set_to(a);}}

        void initialize();
	void read  ( istream & in );
        void print ( ostream & out ) const;
#endif

    public :

        //
	// ***** modulus handling *****
	//

        void set_modulus ( const bigint & m );
        void set_modulus ( const multi_bigmod & a )
	 {this->assign_modulus(a.Mp);}

	const bigint & modulus () const
	 {return Mp->get_mod();}


        //
	// ***** constructors / destructor
	//
	// (remark: There exists no constructor multi_bigmod(bigmod)
	// to avoid implicit casts (int,long,bigint,...) -> bigmod -> multi_bigmod.
	// These implicit casts are traps for the programmer, who
	// used the wrong constructor, i.e. multi_bigmod(5), which sets the
        // modulus to bigmod::modulus(), instead of multi_bigmod(5,m),
	// which sets the modulus to m.)
	//

	multi_bigmod ();
	multi_bigmod ( int i, const bigint & m );
	multi_bigmod ( long l, const bigint & m );
	multi_bigmod ( unsigned long ul, const bigint & m );
	multi_bigmod ( double d, const bigint & m );
	multi_bigmod ( const bigint & i, const bigint & m );
	multi_bigmod ( const multi_bigmod & a );
        ~multi_bigmod ();

#ifndef HEADBANGER

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

        bigint invert ( int i = 0 )
         {return base_bigmod::invert(Mp->get_mod(),i);}

        void negate ()
         {base_bigmod::negate(Mp->get_mod());}

        void normalize ()
	 {base_bigmod::normalize(Mp->get_mod());}

        //
        // ***** assignments *****
        //

        void assign_zero ()
         {I.assign_zero();}

        void assign_one ()
         {I.assign_one();}

        void assign_zero (const bigint & m)
         {I.assign_zero(); this->set_modulus(m);}

        void assign_one (const bigint & m)
         {I.assign_one(); this->set_modulus(m);}

        void assign (int i, const bigint & m)
         {I.assign (i); this->set_modulus(m); this->normalize();}

        void assign (long i, const bigint & m)
         {I.assign (i); this->set_modulus(m); this->normalize();}

        void assign (unsigned long i, const bigint & m)
         {I.assign (i); this->set_modulus(m); this->normalize();}

        void assign (const bigint & a, const bigint & m)
         {I.assign (a); this->set_modulus(m); this->normalize();}

        void assign (const multi_bigmod & a)
         {I.assign(a.I); this->assign_modulus(a.Mp);}



        void set_mantissa (int i)
         {I.assign (i); this->normalize();}

        void set_mantissa (long i)
         {I.assign (i); this->normalize();}

        void set_mantissa (unsigned long i)
         {I.assign (i); this->normalize();}

        void set_mantissa (const bigint & a)
         {I.assign (a); this->normalize();}


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

	void multiply_by_2 ()
	 {base_bigmod::multiply_by_2(Mp->get_mod());}

	void divide_by_2 ()
	 {base_bigmod::divide_by_2(Mp->get_mod());}

        void randomize (const bigint & m);

#endif

	//
	// ***** assignment operator *****
	//

	int operator = ( int i )
	 {I.assign(i); this->normalize(); return i;}

	long operator = ( long l )
         {I = l; this->normalize(); return l;}

	unsigned long operator = ( unsigned long ul )
         {I = ul; this->normalize(); return ul;}

	double operator = ( double d )
	 {I = d; this->normalize(); return d;}

	bigint operator = ( const bigint & a )
	 {I.assign(a); this->normalize(); return a;}

	const multi_bigmod & operator = ( const multi_bigmod  & a );
	const multi_bigmod & operator = ( const bigmod & a );




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

	friend bool operator == ( const multi_bigmod & a, const multi_bigmod & b )
	 {if ((a.I.compare(b.I) == 0) && (a.Mp == b.Mp)) return true; else return false; }

	friend bool operator != ( const multi_bigmod & a, const multi_bigmod & b )
	 {if ((a.I.compare(b.I) != 0) || (a.Mp != b.Mp)) return true; else return false; }

	friend bool operator == ( const multi_bigmod & a, const bigmod & b );
	friend bool operator == ( const bigmod & a, const multi_bigmod & b );
	friend bool operator != ( const multi_bigmod & a, const bigmod & b );
	friend bool operator != ( const bigmod & a, const multi_bigmod & b );
   


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

	friend multi_bigmod operator - ( const multi_bigmod & a ) ;

        friend multi_bigmod operator + ( const multi_bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( const multi_bigmod & a, const bigint & b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( const bigint & a, const multi_bigmod & b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( const multi_bigmod & a, long b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( long a, const multi_bigmod & b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( const multi_bigmod & a, const bigmod & b )
	 {multi_bigmod c; add(c, a, b); return c;}

        friend multi_bigmod operator + ( const bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; add(c, a, b); return c;}


        friend multi_bigmod operator - ( const multi_bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( const multi_bigmod & a, const bigint & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( const bigint & a, const multi_bigmod & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( const multi_bigmod & a, long b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( long a, const multi_bigmod & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( const multi_bigmod & a, const bigmod & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}

        friend multi_bigmod operator - ( const bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; subtract(c, a, b); return c;}


	friend multi_bigmod operator * ( const multi_bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( const multi_bigmod & a, const bigint & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( const bigint & a, const multi_bigmod & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( const multi_bigmod & a, long b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( long a, const multi_bigmod & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( const multi_bigmod & a, const bigmod & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}

        friend multi_bigmod operator * ( const bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; multiply(c, a, b); return c;}


	friend multi_bigmod operator / ( const multi_bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( const multi_bigmod & a, const bigint & b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( const bigint & a, const multi_bigmod & b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( const multi_bigmod & a, long b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( long a, const multi_bigmod & b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( const multi_bigmod & a, const bigmod & b )
	 {multi_bigmod c; divide(c, a, b); return c;}

        friend multi_bigmod operator / ( const bigmod & a, const multi_bigmod & b )
	 {multi_bigmod c; divide(c, a, b); return c;}


	const multi_bigmod & operator += ( const multi_bigmod & a )
	 {add(*this, *this, a); return *this;}

	const multi_bigmod & operator += ( const bigint & a )
	 {add(*this, *this, a); return *this;}

	const multi_bigmod & operator += ( long a )
	 {add(*this, *this, a); return *this;}

	const multi_bigmod & operator += ( const bigmod & a )
	 {add(*this, *this, a); return *this;}


	const multi_bigmod & operator -= ( const multi_bigmod & a )
	 {subtract(*this, *this, a); return *this;}

	const multi_bigmod & operator -= ( const bigint & a )
	 {subtract(*this, *this, a); return *this;}

	const multi_bigmod & operator -= ( long a )
	 {subtract(*this, *this, a); return *this;}

	const multi_bigmod & operator -= ( const bigmod & a )
	 {subtract(*this, *this, a); return *this;}


	const multi_bigmod & operator *= ( const multi_bigmod & a )
	 {multiply(*this, *this, a); return *this;}

	const multi_bigmod & operator *= ( const bigint & a )
	 {multiply(*this, *this, a); return *this;}

	const multi_bigmod & operator *= ( long a )
	 {multiply(*this, *this, a); return *this;}

	const multi_bigmod & operator *= ( const bigmod & a )
	 {multiply(*this, *this, a); return *this;}


	const multi_bigmod & operator /= ( const multi_bigmod & a )
	 {divide(*this, *this, a); return *this;}

	const multi_bigmod & operator /= ( const bigint & a )
	 {divide(*this, *this, a); return *this;}

	const multi_bigmod & operator /= ( long a )
	 {divide(*this, *this, a); return *this;}

	const multi_bigmod & operator /= ( const bigmod & a )
	 {divide(*this, *this, a); return *this;}


	const multi_bigmod & operator ++ ()
	 {inc(*this); return *this;}

	const multi_bigmod & operator -- ()
	 {dec(*this); return *this;}

	int operator ! ()
	 {return I.is_zero();}


#ifndef HEADBANGER

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

	friend void invert ( multi_bigmod & a, const multi_bigmod & b )
	 {a.assign(b); a.invert();}

        friend void negate ( multi_bigmod & a, const multi_bigmod & b )
	 {a.assign(b);a.negate();}


	friend void add ( multi_bigmod & c, const multi_bigmod & a, const multi_bigmod & b );
	friend void add ( multi_bigmod & c, const bigint & a, const multi_bigmod & b );
	friend void add ( multi_bigmod & c, const multi_bigmod & a, const bigint & b );
	friend void add ( multi_bigmod & c, long a, const multi_bigmod & b );
	friend void add ( multi_bigmod & c, const multi_bigmod & a, long b );
	friend void add ( multi_bigmod & c, const bigmod & a, const multi_bigmod & b );
	friend void add ( multi_bigmod & c, const multi_bigmod & a, const bigmod & b );

	friend void subtract ( multi_bigmod & c, const multi_bigmod & a, const multi_bigmod & b );
	friend void subtract ( multi_bigmod & c, const bigint & a, const multi_bigmod & b );
	friend void subtract ( multi_bigmod & c, const multi_bigmod & a, const bigint & b );
	friend void subtract ( multi_bigmod & c, long a, const multi_bigmod & b );
	friend void subtract ( multi_bigmod & c, const multi_bigmod & a, long b );
	friend void subtract ( multi_bigmod & c, const bigmod & a, const multi_bigmod & b );
	friend void subtract ( multi_bigmod & c, const multi_bigmod & a, const bigmod & b );

	friend void multiply ( multi_bigmod & c, const multi_bigmod & a, const multi_bigmod & b );
	friend void multiply ( multi_bigmod & c, const bigint & a, const multi_bigmod & b );
	friend void multiply ( multi_bigmod & c, const multi_bigmod & a, const bigint & b );
	friend void multiply ( multi_bigmod & c, long a, const multi_bigmod & b );
	friend void multiply ( multi_bigmod & c, const multi_bigmod & a, long b );
	friend void multiply ( multi_bigmod & c, const bigmod & a, const multi_bigmod & b );
	friend void multiply ( multi_bigmod & c, const multi_bigmod & a, const bigmod & b );

	friend void divide ( multi_bigmod & c, const multi_bigmod & a, const multi_bigmod & b );
	friend void divide ( multi_bigmod & c, const bigint & a, const multi_bigmod & b );
	friend void divide ( multi_bigmod & c, const multi_bigmod & a, const bigint & b );
	friend void divide ( multi_bigmod & c, long a, const multi_bigmod & b );
	friend void divide ( multi_bigmod & c, const multi_bigmod & a, long b );
	friend void divide ( multi_bigmod & c, const bigmod & a, const multi_bigmod & b );
	friend void divide ( multi_bigmod & c, const multi_bigmod & a, const bigmod & b );


	friend void power ( multi_bigmod & c, const multi_bigmod & a, const bigint & b );
	friend void power ( multi_bigmod & c, const multi_bigmod & a, long b );

	friend void inc ( multi_bigmod & c )
	 {inc(c,(c.Mp)->get_mod());}

	friend void dec ( multi_bigmod & c )
	 {dec(c,(c.Mp)->get_mod());}

#endif

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

	friend multi_bigmod inverse ( const multi_bigmod & a )
         {multi_bigmod c; c.assign(a); c.invert(); return c;}

	friend multi_bigmod randomize ( const multi_bigmod & a );
	friend void normalize ( multi_bigmod & a, const multi_bigmod & b );
	friend void square ( multi_bigmod & a, const multi_bigmod & b );
	friend void swap ( multi_bigmod & a, multi_bigmod & b );


	//
	// ***** input / output *****
	//

        friend istream & operator >> ( istream & in, multi_bigmod & a ) ;
        friend ostream & operator << ( ostream & out, const multi_bigmod & a ) ;

	friend int string_to_multi_bigmod ( char * s, multi_bigmod & a ) ;
	friend int multi_bigmod_to_string ( const multi_bigmod & a, char * &s ) ;

	void read_from_file ( FILE * fp ) ;
	void write_to_file  ( FILE * fp ) ;
	void scan_from_file ( FILE * fp ) ;
	void print_to_file  ( FILE * fp ) ;
  };


#endif

