#ifndef INC_USR_TABUSEARCH
#define INC_USR_TABUSEARCH

#include <iostream.h>
#include <mallba/iopacket.hh>
#include <LEDA/list.h>


namespace TabuSearch {

  class Movement;
  class TabuStorage;
  class State;
//  class Tabu2List;


  class SetUpParams {
  
	public:

		int    independent_runs;
		int    nb_iterations;		// 1000*N
		bool   use_delta_function;
		double known_suboptimum;
		int    max_neighbors;
		int    tabu_size;			// n/2...3n/2
		int    min_tabu_status;
		int    max_tabu_status;
		int    max_repetitions;		// 100*N
		int    nb_diversifications; // 10*N;
		int    nb_intensifications; // 10*N;

		SetUpParams ();

		friend ostream& operator<< (ostream& os, const SetUpParams& setup);
		friend istream& operator>> (istream& is, SetUpParams& setup);
		friend opacket& operator<< (opacket& op, const SetUpParams& setup);
		friend ipacket& operator>> (ipacket& ip, SetUpParams& setup);
  };

 

  enum Direction { minimize = -1, maximize = 1 };


  class Problem {

	public:

		Problem ();
		Direction direction () const;

		Problem& operator=  (const Problem& pbm);
		bool     operator== (const Problem& pbm) const;
		bool     operator!= (const Problem& pbm) const;

		friend ostream& operator<< (ostream& os, const Problem& pbm);
		friend istream& operator>> (istream& is, Problem& pbm);
		friend opacket& operator<< (opacket& op, const Problem& pbm);
		friend ipacket& operator>> (ipacket& ip, Problem& pbm);
  };



  class Solution {

	public:

		Solution (const Problem& pbm);

		void   initial_solution();
		void   escape();
		
		double fitness () const;
		double delta (const Movement& mov) const;
		bool   aspiration (const Movement& mov, const TabuStorage& ts, const State& state) const;

		void   apply ( const Movement& mov );
		void   unapply ( const Movement& mov );

		void   penalize();
		void   unpenalize();
		
		void   reward();
		void   unreward();

		Solution& operator=  (const Solution& sol);
		bool      operator== (const Solution& sol) const;
		bool      operator!= (const Solution& sol) const;

		friend ostream& operator<< (ostream& os, const Solution& sol);
		friend istream& operator>> (istream& is, Solution& sol);
		friend opacket& operator<< (opacket& op, const Solution& sol);
		friend ipacket& operator>> (ipacket& ip, Solution& sol);
  };



  class Movement {

	public:

		int tabulife;

		Movement ();

		void generate ();
		void invert ();

		Movement& operator= (const Movement& mov);
		friend bool operator== (const Movement&, const Movement&);
		friend bool operator!= (const Movement&, const Movement&);
		
		friend ostream& operator<< (ostream& os, const Movement& mov);
		friend istream& operator>> (istream& is, Movement& mov);
		friend opacket& operator<< (opacket& op, const Movement& mov);
		friend ipacket& operator>> (ipacket& ip, Movement& mov);
  };
  


  class TabuStorage {

	private:
	
		const SetUpParams& config;
		const Problem&     problem;
		list<Movement>     tlist;

	public:

		TabuStorage (const SetUpParams& setup, const Problem& pbm);
					
		bool is_in_tabu_storage (const Movement& mov, const Solution& sol, const State& state) const;
		bool is_tabu (const Movement& mov, const Solution& sol, const State& state) const;
		void make_tabu (Movement& mov, const Solution& sol, const State& state);
		void make_tabu_inv (Movement& mov, const Solution& sol, const State& state);
		void update ();		
		int  size () const;
  };



  class UserStatistics {

  public:

		int total_nb_tabu_moves;
		int total_nb_explored_moves;

		UserStatistics ();

		friend ostream& operator<< (ostream& os, const UserStatistics& ustat);
		friend opacket& operator<< (opacket& op, const UserStatistics& ustat);
  };



  bool choose_best_move (const Problem& pbm, const Solution& sol, 
  						 const SetUpParams& setup, const TabuStorage& ts,
						 const State& state, Movement& mov);

  bool choose_best_all (const Problem& pbm, const Solution& sol, 
  						const SetUpParams& setup, const TabuStorage& ts,
						const State& state, Movement& mov);

  bool terminateQ (const Problem& pbm, const Solution& sol, const SetUpParams& setup,
				   const int current_iter);


}

#endif
