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



#ifndef LIDIA_FACTORIZATION__H
#define LIDIA_FACTORIZATION__H

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)

#include <LiDIA:sort_vector.h>
#include <LiDIA:ppair.h>
#include <LiDIA:single_factor.h>

#else

#include <LiDIA/sort_vector.h>
#include <LiDIA/ppair.h>
#include <LiDIA/single_factor.h>

#endif



//template< class T > class single_factor;
//class decomposable_object;

class factorization_flags
{
protected:
    enum factorization_state { sorted = 1, normalized = 2, refined = 4 };
};

template< class T >
class factorization: public factorization_flags, private decomposable_object
{

 private:

    /* data elements */

    single_factor< T > *epsilon;		/*   unit   */
    sort_vector< ppair< single_factor< T >, lidia_size_t > > 
	    prime_component, composite_component;


    udigit attributes;		/*   information not concerning primality   */


    /* access functions for 'attributes' */
    void clear_attributes()
    { attributes &= ~ (sorted | normalized | refined); }

    void check_for_trivial_case();
    /*   sets information for simple factorizations   */
   

    static void
    refine2(sort_vector<ppair<single_factor< T >, lidia_size_t> > & v,
	    ppair<single_factor< T >, lidia_size_t> & SF,
	    const ppair<single_factor< T >, lidia_size_t> & a,
	    const ppair<single_factor< T >, lidia_size_t> & b);

    static void 
    normalize(sort_vector<ppair<single_factor< T >, lidia_size_t> > &v);

public:
	/**
	** constructors, destructor
	**/
	
    factorization();
    factorization(const factorization< T > & F); 
    factorization(const single_factor< T > & f); 
    ~factorization();
  
    void kill();
   
	/**
	** assignment
	**/
	
    factorization< T > & operator = (const factorization< T > & x);
    const single_factor< T > & operator = (const single_factor< T > & x);

#ifndef HEADBANGER
    void assign (const factorization< T > & F);
    void assign (const single_factor< T > & f);
#endif 		//HEADBANGER

	/**
	** comparisons
	**/

    bool operator == (const factorization< T > & b) const;
    inline bool operator != (const factorization< T > & b)
    { return !(*this == b); }


	/**
	** access functions
	**/

    const single_factor< T > & prime_base(lidia_size_t) const;
    lidia_size_t prime_exponent(lidia_size_t) const;
    const single_factor< T > & composite_base(lidia_size_t) const;
    lidia_size_t composite_exponent(lidia_size_t) const;

    T unit() const;

    T value() const;

    inline lidia_size_t no_of_prime_components() const
    { return prime_component.size(); }
    
    inline lidia_size_t no_of_composite_components() const
    { return composite_component.size(); }
    
    inline lidia_size_t no_of_components() const
    { return prime_component.size() + composite_component.size(); }
	
 
   /**
   ** queries
   **/

    bool is_prime_factorization()
    { return this->is_prime_factorization(0); }
	
    bool is_prime_factorization(int test);
    /*  test==0 -> no explicit primality test (only flags are checked)
	           returns true iff (*this) is known to be a prime factorization
	test!=0 -> explicit primality test for unknown elements
	           returns false iff (*this) has an element which is not_prime
	(see manual)   */

    bool is_sorted() const;	/*   'sorted' means: both lists are sorted   */
   
    bool is_normalized() const;

    bool is_refined() const;
   
   /**
   ** input / output
   **/

    //
    // I/O - format :
    // [ [unit] ]
    // or
    // [ [unit], [base_1,exp_1], [base_2,exp_2], ..., [base_i,exp_i] ]
    // or
    // [ [base_1,exp_1], [base_2,exp_2], ..., [base_i,exp_i] ]
    //

    void read(istream &is);
    void write(ostream &os) const;
   
    inline friend istream & operator >> (istream &is, factorization< T > &F)
    { F.read(is); return is; }
   
    inline friend ostream & operator << (ostream &os, const factorization< T > &F)
    { F.write(os); return os; }


//#####
   // NUR ZU TESTZWECKEN !!!
   void output() const;
   // **********************


   /**
   **  functions for computing with factorizations
   **/

#ifndef HEADBANGER
    void concat(const factorization< T > &a, const factorization< T > &b);
    // *this = a concat b
#endif		//HEADBANGER

    void invert();	//== power(-1)
    inline friend void invert(factorization< T > & f, 
			const factorization< T > & a)
    { f.assign(a); f.invert(); }
    inline friend factorization< T > inverse(const factorization< T > &f)
    { factorization< T > tmp(f); tmp.invert(); return tmp; }
   
    void square();	//== power(2)
    inline friend void square(factorization< T > & f,
			const factorization< T > & a)
    { f.assign(a); f.square(); }
   
    void power(lidia_size_t p);
    inline friend void power(factorization< T > & f,
			const factorization< T > & a,
			lidia_size_t p)
    { f.assign(a); f.power(p); }

#ifndef HEADBANGER
    inline friend void multiply (factorization< T > &c, 
			const factorization< T > &a,    
			const factorization< T > &b)
    { c.concat(a,b); }
#endif 		//HEADBANGER

    inline friend factorization< T >
    operator *(const factorization< T > & a,
		const factorization< T > & b)
    { factorization< T > c; multiply(c, a, b); return c; }

#ifndef HEADBANGER
    inline friend void divide (factorization< T > &c, 
			const factorization< T > &a,
			const factorization< T > &b)
    { factorization< T > tmp(b); tmp.invert(); multiply(c, a, tmp); }
#endif		//HEADBANGER    

    inline friend factorization< T > 
    operator / (const factorization< T > & a,
		const factorization< T > & b)
    { factorization< T > c; divide(c, a, b); return c; }


   /**
   **  high-level functions 
   **/

    void sort();

    void normalize();    //was: compose();

    void refine();	//factor refinement algorithm
    bool refine(const single_factor< T > & x);
	    //returns true iff *this can be refined by computing gcd's 
	    //of all (composite) elements with 'x'

    void replace(lidia_size_t ix, const factorization< T > & F);
    /*   deletes composite_component[i] and appends the components of F   */
	     
    void append(const single_factor< T > & a)
    { append(a, 1); }
    void append(const single_factor< T > & a, lidia_size_t exp);
    /*   appends 'a^exp' to the factorization   */
    
    void append(const T& a, lidia_size_t exp);

   /**
   ** factoring
   **/

    void factor_all_components();

    friend factorization< T > factor_all_components(const factorization < T >&);
    /*   calls 'single_factor<T>::factor()' for all composite components
         and replaces the components by the resulting factorizations   */
};

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

#endif

