// =================================================================== //
// Copyright(c)'1994-2009 by The Givaro group
// This file is part of Givaro.
// Givaro is governed by the CeCILL-B license under French law
// and abiding by the rules of distribution of free software.
// see the COPYRIGHT file for more details.
// Needs Container structures : stl ones for instance
// Time-stamp: <16 Jun 15 16:05:40 Jean-Guillaume.Dumas@imag.fr>
// =================================================================== //

/*! @file givintfactor.h
 * @ingroup integers
 * @brief factorisation
 *- Prime numbers
 * - Factor sets :
 * - Pollard's rho method for factorization
 * - Elliptic curves factorization by Lenstra
 * .
 */

#ifndef __GIVARO_factorisation_H
#define __GIVARO_factorisation_H

#include <iostream>
#include "givaro/givinteger.h"
#include "givaro/givintprime.h"
#include "givaro/givrandom.h"

// #define BOUNDARY_factor TABMAX2

#define factor_first_primes(tmp,n) (tmp = isZero(mod(tmp,n,23))?23:( isZero(mod(tmp,n,19))?19:( isZero(mod(tmp,n,17))?17:  (isZero(mod(tmp,n,2))?2:( isZero(mod(tmp,n,3))?3:( isZero(mod(tmp,n,5))?5:( isZero(mod(tmp,n,7))?7: ( isZero(mod(tmp,n,11))?11:13 ))))))))

#define factor_second_primes(tmp,n) (tmp = isZero(mod(tmp,n,31))?31:( isZero(mod(tmp,n,29))?29: ( isZero(mod(tmp,n,37))?37: ( isZero(mod(tmp,n,41))?41:( isZero(mod(tmp,n,43))?43:  ( isZero(mod(tmp,n,71))?71:( isZero(mod(tmp,n,67))?67:( isZero(mod(tmp,n,61))?61:( isZero(mod(tmp,n,59))?59: ( isZero(mod(tmp,n,53))?53:( isZero(mod(tmp,n,47))?47: ( isZero(mod(tmp,n,97))?97: ( isZero(mod(tmp,n,89))?89:( isZero(mod(tmp,n,83))?83:( isZero(mod(tmp,n,79))?79:73)))))))))))))))

namespace Givaro {

	// =================================================================== //
	// Set or Container of divisors, factors.
	// =================================================================== //

	//! Integer Factor Domain.
	template<class MyRandIter = GivRandom>
	class IntFactorDom : public IntPrimeDom {
	private:
		// 2*3*5*7*11*13*17*19*23
		const int PROD_first_primes;
		// 29*31*37*41*43*47*53*59*61*67*71*73*79*83*89*97
		const Rep PROD_second_primes;
	protected:
		MyRandIter _g;

	public:
		typedef MyRandIter random_generator;

		IntFactorDom(MyRandIter g = MyRandIter()) :
			IntPrimeDom(),
			PROD_first_primes(223092870),
			PROD_second_primes("10334565887047481278774629361"),
			_g(g)
		{
		}

		//  loops defaulted to 0 forces Pollard's factorization to
		//  be complete
		Rep& factor(Rep& r, const Rep& n, unsigned long loops = 0) const
		{
			if (isOne(gcd(r,n,PROD_first_primes)))
				if (isOne(gcd(r,n,PROD_second_primes))) {
#ifdef GIVARO_LENSTRA
					return Lenstra((const MyRandIter&)_g, r, n);
#else
					return Pollard((const MyRandIter&)_g, r, n, loops);
#endif
				} else
					return factor_second_primes(r,n);
				else
					return factor_first_primes(r,n);
		}

		//  Factors are checked for primality
		Rep& iffactorprime (Rep& r, const Rep& n, unsigned long loops = 0) const
		{
			if (factor(r, n, loops) != 1) {
				if (! isprime(r,_GIVARO_ISPRIMETESTS_) ) {
					Rep nn = r; factor(r,nn, loops);
				}
				while (! isprime(r,_GIVARO_ISPRIMETESTS_) ) {
					Rep nn = r;
					if (isOne(gcd(r,nn,PROD_first_primes))) {
						if (isOne(gcd(r,nn,PROD_second_primes))) {
							Pollard((const MyRandIter&)_g, r, nn, loops);
						} else {
							factor_second_primes(r,nn);
						}
					} else {
						factor_first_primes(r,nn);
					}
					if (r == nn) {
						Lenstra((const MyRandIter&)_g, r, nn) ;
						break; // In case Lenstra fails also
					}
				}
			}
			return r;
		}

		Rep& primefactor(Rep& r, const Rep& n) const {
			while ((iffactorprime(r,n,0) == 1) && (! isprime(n, _GIVARO_ISPRIMETESTS_)) ) {}
			return r;
		}


		/// Factors with primes
		//  loops defaulted to 0 forces factorization to be complete
		//  otherwise returns if factorization is complete or not
		//  Factors are checked for primality
		template<class Container1, class Container2> bool set
		( Container1& setint, Container2& setpwd,  const Rep& a, unsigned long loops = 0) const ;
		///
		template<class Container> void set( Container&,  const Rep&) const ;
		///
		template<class Container> void Erathostene(Container&, const Rep&) const ;
		///
		template<class Container, class Cont2, class Cont3> Container& divisors(Container& L, const Cont2& Lf, const Cont3& Le)  const;
		template<class Container> Container& divisors(Container&, const Rep& ) const ;

		/// returns a small factor
		Rep& Erathostene(Rep&,  const Rep& p ) const ;

		// Pollard with a bound on the number of loops
		// Bound 0 is without bound
		Rep& Pollard(const MyRandIter&, Rep&, const Rep& n, unsigned long threshold = 0) const ;
		// returns a factor by Lenstra's elliptic curves method
		Rep& Lenstra(const MyRandIter&, Rep&, const Rep& n, const Rep& B1 = 10000000, const unsigned long curves = 30) const ;

		std::ostream& write(std::ostream& o, const Rep& n) const;
		template<class Array> std::ostream& write(std::ostream& o, Array&, const Rep& n) const;


	private:
		// Those are parameters for Pollard's algorithms
		// Pollard_fctin : must be somewhat a "random" function in Z/nZ
		// Pollard_cst can be a slight alternative for the Pfct x^2+1
#ifndef Pollard_cst
#define Pollard_cst 1
#endif

		Rep& Pollard_fctin(Rep & x, const Rep& n) const
		{
			mulin(x,x);
			addin(x,Pollard_cst);
			return modin(x,n);
		}

	};

} // Givaro

#include "givaro/givintfactor.inl"

#endif // __GIVARO_factorisation_H

/*  -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
