//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//

#ifndef LIDIA_BASE_FACTOR__H
#define LIDIA_BASE_FACTOR__H

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:lidia.h>
#include <LiDIA:interface_lib.h>
#else
#include <LiDIA/lidia.h>
#include <LiDIA/interface_lib.h>
#endif



/****************************************************************
				class decomposable_object

handles information about primality
base class for 
	- base_factor< T >
	- factorization< T >
****************************************************************/

class decomposable_object
{
 public :
    enum decomp_state { not_prime = 0, unknown = 1, prime = 2 };
	//if state = prime or state=not_prime, we know that this object is prime 
	//or composite, respectively.
	//if state=unknown, we haven't tested it for primality, yet.

 private:
    decomp_state S;

 protected:
    void share_state(const decomposable_object &a)
    {
	//this function is called whenever two decomposable_object are discovered to be equal
	//if this->S is 'unknown', the value of a.S is copied
	//an error is raised if this->S = 'prime' and a.S = 'not_prime' or vice versa
	if (S == unknown)
	    S = a.S;
	else if (S != a.S  && a.S != unknown)
	    lidia_error_handler( "decomposable_object", "share_state( decomposable_object&)::conflicting states" );
    }

    void concat_state(const decomposable_object &a, const decomposable_object &b)
    {
	//this->S = state of product of a and b
	if (b.S == not_prime)
	    S = not_prime;
	else if (b.S == unknown && S == prime)
	    S = unknown;
	else S = a.S;
    }
 
 
 public:
 
#ifndef HEADBANGER 
    void set_prime_flag(decomp_state new_S)
    { S = new_S; }
    
    decomp_state prime_flag() const
    { return S; }
#endif		//HEADBANGER

};






/****************************************************************
				class base_factor<T>
		only a base class for single_factor<T>
****************************************************************/

template< class T >
class base_factor: public decomposable_object
{

 protected:
    T rep;
   
 protected: 
	/**
	** constructors, destructor
	**/
	
    base_factor();
    base_factor(const base_factor< T > &);
    ~base_factor() {};

 public:
    base_factor & operator= ( const base_factor< T > & )
    {
	lidia_error_handler( "base_factor< T >", "operator = ( base_factor< T >& )::not implemented" );
	return *this;
    }
	
	
	/**
	** access functions
	**/
	
    const T& base() const
    { return rep; }
    
    T& base ()
    { return rep; }


	/**
	** arithmetic operations
	**/

#ifndef HEADBANGER
    friend void multiply(base_factor< T > & c, const base_factor< T > & a,
                                               const base_factor< T > & b)
    //c = a*b
    { multiply(c.rep, a.rep, b.rep); c.set_prime_flag(unknown); }
   
   
    friend void divide(base_factor< T > & c, const base_factor< T > & a,
                                             const base_factor< T > & b)
    //c = a/b
    // ********************************************************************
    // *WARNING : the software cannot check if 'b' is a divisor of 'a' !!!*
    // ********************************************************************
    { divide(c.rep, a.rep, b.rep); c.set_prime_flag(unknown); }
#endif
    
    friend base_factor< T > 
    operator *(const base_factor< T > & a, const base_factor< T > & b)
    { base_factor< T > tmp; multiply(tmp, a, b); return tmp; }

    friend base_factor< T >
    operator /(const base_factor< T > & a, const base_factor< T > & b)
    { base_factor< T > tmp; divide(tmp, a, b); return tmp; }
   
    friend void gcd(base_factor< T > & c, const base_factor< T > & a,
                                          const base_factor< T > & b)
    //c = gcd(a,b)
    { c.rep=gcd(a.rep, b.rep); c.set_prime_flag(unknown); }


	/**
	** comparisons
	**/
    friend bool operator < (const base_factor< T > & a, const base_factor< T > & b)
    { return (a.rep < b.rep); }
    friend bool operator > (const base_factor< T > & a, const base_factor< T > & b)
    { return !(a.rep <= b.rep); }
    friend bool operator <= (const base_factor< T > & a, const base_factor< T > & b)
    { return (a.rep <= b.rep); }
    friend bool operator >= (const base_factor< T > & a, const base_factor< T > & b)
    { return !(a.rep < b.rep); }
    friend bool operator == (const base_factor< T > & a, const base_factor< T > & b)
    { return (a.rep == b.rep); }
    friend bool operator != (const base_factor< T > & a, const base_factor< T > & b)
    { return (a.rep != b.rep); }

 public:
    friend istream & operator >> (istream &in, base_factor< T > &f)
    { in >> f.rep; f.set_prime_flag(unknown); return in; }
   
    friend ostream & operator << (ostream &out, const base_factor< T > &f)
    { out << f.rep; return out; }
};


#ifdef LIDIA_INCLUDE_C
#include <LiDIA/base_factor.c>
#endif

#endif

