#ifndef _functor_type_
#define _functor_type_

#include<functional>

#include"select.h"

struct empty_type{};

template<typename R> struct generator {
  typedef R result_type;
};


template<typename T> struct functor_info {

  typedef char one;
  typedef struct {char a[2];} two;

  template<typename C> static one test_res(typename C::result_type *);
  template<typename C> static two test_res(...);
  template<typename C> static one test_arg(typename C::argument_type *);
  template<typename C> static two test_arg(...);
  template<typename C> static one test_arg1(typename C::first_argument_type *);
  template<typename C> static two test_arg1(...);
  template<typename C> static one test_arg2(typename C::second_argument_type *);  template<typename C> static two test_arg2(...);
  

  enum {has_result  = (sizeof(test_res<T>(0))==sizeof(one))};
  enum {has_argument  = (sizeof(test_arg<T>(0))==sizeof(one))};
  enum {has_first_argument  = (sizeof(test_arg1<T>(0))==sizeof(one))};
  enum {has_second_argument  = (sizeof(test_arg2<T>(0))==sizeof(one))};  
  enum {has_one_argument  = has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {has_two_arguments = has_first_argument && has_second_argument 
	&& !has_argument};
  enum {has_no_arguments = !has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {is_generator = has_result && has_no_arguments};
  enum {is_unary_function = has_result && has_one_argument};
  enum {is_binary_function = has_result && has_two_arguments};
  enum {is_functor = is_generator || is_unary_function || is_binary_function};
  enum {is_function= false};


  enum {n_args = is_generator?
	0:(is_unary_function?
	   1:(is_binary_function?2:-1))};
  

};

template<typename A1,typename A2 , typename R > 
struct functor_info<R (*)(A1,A2) >{

  enum {has_result  = true};
  enum {has_argument  = false};
  enum {has_first_argument  = true};
  enum {has_second_argument  = true};
  
  enum {has_one_argument  = has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {has_two_arguments = has_first_argument && has_second_argument 
	&& !has_argument};
  enum {has_no_arguments = !has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {is_generator = has_result && has_no_arguments};
  enum {is_unary_function = has_result && has_one_argument};
  enum {is_binary_function = has_result && has_two_arguments};
  enum {is_functor = is_generator || is_unary_function || is_binary_function};
  enum {is_function= true};
  enum {n_args = is_generator?
	0:(is_unary_function?
	   1:(is_binary_function?2:-1))};
};

template<typename A1, typename R > struct functor_info<R (*)(A1)> {


  enum {has_result  = true};
  enum {has_argument  = true};
  enum {has_first_argument  = false};
  enum {has_second_argument  = false};
  
  enum {has_one_argument  = has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {has_two_arguments = has_first_argument && has_second_argument 
	&& !has_argument};
  enum {has_no_arguments = !has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {is_generator = has_result && has_no_arguments};
  enum {is_unary_function = has_result && has_one_argument};
  enum {is_binary_function = has_result && has_two_arguments};
  enum {is_functor = is_generator || is_unary_function || is_binary_function};
  enum {is_function= true};
  enum {n_args = is_generator?
	0:(is_unary_function?
	   1:(is_binary_function?2:-1))};
  

};

template<typename R > struct functor_info<R (*)()> {


  enum {has_result  = true};
  enum {has_argument  = false};
  enum {has_first_argument  = false};
  enum {has_second_argument  = false};
  
  enum {has_one_argument  = has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {has_two_arguments = has_first_argument && has_second_argument 
	&& !has_argument};
  enum {has_no_arguments = !has_argument && !has_first_argument 
	&& !has_second_argument};
  enum {is_generator = has_result && has_no_arguments};
  enum {is_unary_function = has_result && has_one_argument};
  enum {is_binary_function = has_result && has_two_arguments};
  enum {is_functor = is_generator || is_unary_function || is_binary_function};
  enum {is_function= true};
  enum {n_args = is_generator?
	0:(is_unary_function?
	   1:(is_binary_function?2:-1))};
};


template<typename F,int n_args = functor_info<F>::n_args> 
struct functor_traits ;

template<typename F> struct functor_traits<F,2> {
  typedef typename F::result_type result_type; 
  typedef typename F::first_argument_type  arg1_type; 
  typedef typename F::second_argument_type arg2_type; 
  typedef std::binary_function<arg1_type,arg2_type,result_type> f_type;
  enum {n_args=2};
};

template<typename F> struct functor_traits<F,1> {
  typedef typename F::result_type result_type; 
  typedef typename F::argument_type  arg1_type; 
  typedef empty_type  arg2_type; 
  typedef std::unary_function<arg1_type,result_type> f_type;
  enum {n_args=1};
};

template<typename F> struct functor_traits<F,0> {
  typedef typename F::result_type result_type; 
  typedef empty_type   arg1_type; 
  typedef empty_type  arg2_type; 
  typedef generator<result_type> f_type;
  enum {n_args=0};
};

template<typename R,typename A1,typename A2> 
struct functor_traits<R (*)(A1,A2),2> {
  typedef R   result_type; 
  typedef A1  arg1_type; 
  typedef A2  arg2_type; 
  typedef std::binary_function<arg1_type,arg2_type,result_type> f_type;
  enum {n_args=2};
};

template<typename R,typename A1> 
struct functor_traits<R (*)(A1),1> {
  typedef R   result_type; 
  typedef A1  arg1_type; 
  typedef empty_type  arg2_type; 
  typedef std::unary_function<arg1_type,result_type> f_type;
  enum {n_args=1};
};


template<typename R> 
struct functor_traits<R (*)(),0> {
  typedef R   result_type;
  typedef empty_type arg1_type; 
  typedef empty_type arg2_type; 
  typedef generator<result_type> f_type;
  enum {n_args=0};
};


#endif