//
// LiDIA - a library for computational number theory
//   Copyright (c) 1996 by the LiDIA Group
//
// File        : gf_element.c
// Author      : Detlef Anton (DA), Thomas Pfahler (TPf)
// Last change : DA, Jan 17 1996, initial version
//               TPf, Jul 1 1996, some min. changes
//


#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:gf_p_base.h>
#include <LiDIA:gf_p_element.h>
#include <LiDIA:gf_element.h>
#else
#include <LiDIA/gf_p_base.h>
#include <LiDIA/gf_p_element.h>
#include <LiDIA/gf_element.h>
#endif


/************* Implementation *************/

  /**
  ** base functions
  **/


  /**
  ** high level functions
  **/

template< class T >
const bigint 
gf_element< T >::element_order(const T & elem) const
{ 
    bigint p_i, m = elem.base().field().number_of_elements();
    m--;
    lidia_size_t e_i;
    register lidia_size_t i, end;
    if (elem.is_zero()) return 0;
    if (m==1) return 1;

    T pow;
//    power(pow, elem, m);
//    if ( !(pow.is_one()) ) 
//	lidia_error_handler("gf_element", "element_order: elem^m !=1");

    const rational_factorization & f
             = elem.base().field().factorization_of_mult_order();
    end = f.no_of_comp();
    for (i = 0; i < end; i++)
    { 
	p_i = f.base(i);
	e_i = f.exponent(i);
	power(pow, elem, (m/p_i));
	while ( (e_i > 0) && (pow.is_one()) ) 
	{ 
	    m=m/p_i;
	    e_i--;
	    power(pow, elem, (m/p_i));
	}
    }
    return m;
}

template< class T >
const T 
gf_element< T >::element_norm(const T & elem) const
{ 
    T prod(elem);
    lidia_size_t n = elem.base().field().degree();
    bigint p = elem.base().field().characteristic();
    bigint h;
    power(h, p, n);
    dec(h);
    dec(p);
    power(prod, elem, ( h/p ));		//prod = elem^((p^n-1)/p-1)
    return prod;
}

template< class T >
bool 
gf_element< T >::element_is_primitive_element(const T & elem) const
{ 
    return(  elem.compute_order() == 
             ( elem.base().field().number_of_elements() - 1 )  );
}

template< class T >
bool 
gf_element< T >::element_is_free_element(const T & elem) const
{ 
    T h_i_elem(elem), pp(elem);
    lidia_size_t i, n = elem.base().field().degree();
    const bigint &p = elem.base().field().characteristic();

    Fp_polynomial f, h;
    f.set_modulus(p);
    f.set_coefficient(n);	//f[n] = 1
    f.set_coefficient(-1, 0);	//f[0] = -1
    
#if 0
original code:
    math_vector<multi_bigmod> fv (n+1, n+1), hv (n, n);
    hv = elem.vector();
    for (i = 0; i < n; i++)
    { 
	pth_power(pp, elem, i);
	multiply(h_i_elem, elem, pp);
	hv[i] = h_i_elem.compute_trace();
    }
    Fp_polynomial fp = vec_to_pol(fv), hp = vec_to_pol(hv);
#endif
    
    h.set_modulus(p);
    h.set_max_degree(n-1);
    for (i = 0; i < n; i++)
    {
	pth_power(pp, elem, i);
	multiply(h_i_elem, elem, pp);
	h.set_coefficient( h_i_elem.compute_trace().mantissa(), i);
    }
    
    Fp_polynomial x;
    gcd(x, f, h);
    return (x.is_one());
}

template< class T >
void element_power(T & c, const T & a, const bigint & e)
{ 
    T multiplier;
    bigint exponent;
    if (a.is_zero())
	c.assign_zero();
    else
    { 
	T result((a.base()));
	result.assign_one();
	if ( (!(e.is_zero())) && (!(a.is_one())) )
	{ 
	    if (e.is_negative())
	    { 
		exponent.assign(-e);
		multiplier.assign(inverse(a));
	    }
	    else
	    { 
		exponent.assign(e);
		multiplier.assign(a);
	    }
	    while (exponent.is_gt_zero())
	    { 
		if (!exponent.is_even())
		    multiply(result, result, multiplier);
		square(multiplier, multiplier);
		exponent.divide_by_2();
	    }
	}
	c.assign(result);
    }
}

  /**
  ** input / output - functions
  **/
