#include<vector>
#include<list>

namespace MAPITL 
{
  ////////////////////////////////////////////////////////////////////////////
  // CtoM and MtoC: Conversions between MuPAD and C++ types
  ////////////////////////////////////////////////////////////////////////////
  
  // Description:
  //
  // CtoM / MtoC allows for easy conversions between C++ values and
  // MuPAD values and reciprocally. Most builtin types, as well as
  // containers (including recursive containers) are supported, and
  // overloading allows for easy user extensions.
  //
  // Conversions to/from void* pointers are also supported. This can
  // be usefull to store a handle to a C++ variables as a MuPAD
  // variable of type DOM_INT. It would be possible to have this
  // conversion for any other pointer types. However, it seems to us
  // that this is a dangerous enough operation to force the user to
  // make an explicit cast to/from void*, or to define explicitely the
  // the conversion (see defining user extensions)
  //
  //
  // Examples of use:
  //
  //	MTcell c; int i; float f; string s;
  //	vector<int> v; vector<list<float> > vv;
  //	vector<int> * p;
  //	c=CtoM(i);
  //	c=CtoM(f);
  //	c=CtoM(s);
  //	c=CtoM(c);
  //	c=CtoM(vv);
  //	i=MtoC<int>(c)
  //	vv=MtoC<vector<list<float> > >(c);
  //	c=CtoM((void *) p);
  //	p=MtoC<vector<int> *>(c);		// To be implemented
  //
  //
  // Defining User extensions:
  //
  // For conversions C++ -> MuPAD, CtoM can simply be overloaded with
  // a function (or template function) which takes the C++ type, and
  // returns a MTcell.
  //
  //	MTcell CtoM(mytype v) {
  //      ...
  //    }
  //
  //    template<class T>
  //    MTcell CtoM(mytype<T> v) {
  //      ...
  //    }
  //
  // For conversions MuPAD -> C++, there is a little trick, because
  // partial specialization of functions is not yet standardized in
  // C++. So, the overloaded conversion function MtoC has to take a
  // second dummy argument, which is a place holder for the type.
  //
  //    mytype MtoC(MTcell c, MAPITL::Type<mytype>) {
  //      ...
  //    }
  //
  //	template<class T>
  //    mytype<T> MtoC(MTcell c, MAPITL::Type<mytype<T>>) {
  //      ...
  //    }
  //
  // The compiler expands a call of MtoC<mytype>(c) into a call of
  // MtoC(c,MAPITL::Type<mytype>()), and then selects the proper
  // implementation according to the usual overloading resolution
  // rules. The use of the template class MAPITL::Type avoids the
  // construction of a mytype element when passing the dummy argument.
  // As a complete example, here is how to define a generic conversion
  // C++ pointer <-> MuPAD DOM_INT.
  //
  //    template<class T>
  //    T * MtoC(MTcell c, MAPITL::Type<T *>) {
  //      return (T *)(MtoC<void *>(c));
  //    }
  //
  //    template<class T>
  //    MTcell CtoM(T *p) {
  //      return CtoM((void *)p);
  //    }
  //
  //
  // Bugs:
  //
  //  - MuPAD bools can have 3 values, instead of just 2 for C++ bools
  //  - For some reason, floats and doubles loose precision
  //  - In CtoM(x), if the type of x does not match *exactly* one of
  //	the supported type, it will be considered by default as a
  //	container. No implicit conversions will be applied (see
  //	Stroustrup's book, 13.3.2). Moreover, the error message is
  //	pretty obscure ("request for member `begin' in `x', which is
  //	of non-aggregate type `unsigned short int'"). To avoid this,
  //	one can either cast x explicitely in into one of the supported
  //	types in each call, or define once for all a new conversion,
  //	as in:
  //	MTcell MtoC (unsigned short int x) { return MtoC(int(x)); }
  //    Hence the proliferation of variants of CtoM below.
  //  - MtoC is not yet implemented for output iterators
  //  - MtoC is not yet implemented for containers that can not be
  //    constructed with a given size (like in vector<int>(5)).

  ////////////////////////////////////////////////////////////////////////////
  // CtoM: conversions from C++ to MuPAD

  MTcell CtoM();			// DOM_NULL
  MTcell CtoM(bool	   );		// DOM_BOOL
  MTcell CtoM(char	   );		// DOM_INT
  MTcell CtoM(unsigned char);		// DOM_INT
  MTcell CtoM(int	   );		// DOM_INT
  MTcell CtoM(short int	   );		// DOM_INT
  MTcell CtoM(unsigned int );		// DOM_INT
  MTcell CtoM(long unsigned int);	// DOM_INT
  MTcell CtoM(long	   );		// DOM_INT
  MTcell CtoM(long long int);		// DOM_INT
  MTcell CtoM(float	   );		// DOM_FLOAT
  MTcell CtoM(double	   );		// DOM_FLOAT
  MTcell CtoM(string	   );		// DOM_STRING
  MTcell CtoM(void *	   );		// DOM_INT
  template<class S, class T>
  MTcell CtoM(pair<S,T> p);		// DOM_LIST with two elements
  MTcell CtoM(MTcell	   );		// To allow for Container<MTcell>
  template<class Iterator>
  MTcell CtoM(Iterator first, Iterator last);			// DOM_LIST
  template<class Iterator>
  MTcell CtoM(Iterator first, Iterator last, int distance);	// DOM_LIST

  // Default conversion: assume t is a container:
  template<class Container>
  MTcell CtoM(Container t) { return CtoM(t.begin(), t.end()); }

  /*
  // DOM_LIST (works recursively)
  template<class T>
  MTcell CtoM(std::vector<T> t) { return CtoM(t.begin(), t.end()); }
  template<class T>
  MTcell CtoM(std::list<T> t)   { return CtoM(t.begin(), t.end()); }
  */
  ////////////////////////////////////////////////////////////////////////////
  // MtoC: conversions from MuPAD to C++
  
  template<class T>
  class Type {};

  template<class T>
  T MtoC(MTcell c) {
    return MtoC(c, Type<T>());
  }

  bool		MtoC(MTcell, Type<bool>);
  char		MtoC(MTcell, Type<char>);
  unsigned char	MtoC(MTcell, Type<unsigned char>);
  int		MtoC(MTcell, Type<int>);
  short int	MtoC(MTcell, Type<short int>);
  unsigned int	MtoC(MTcell, Type<unsigned int>);
  long unsigned int	MtoC(MTcell, Type<long unsigned int>);
  long		MtoC(MTcell, Type<long>);
  string	MtoC(MTcell, Type<string>);
  float		MtoC(MTcell, Type<float>);
  double	MtoC(MTcell, Type<double>);
  void *	MtoC(MTcell, Type<void *>);
  template<class S, class T>
  pair<S,T> MAPITL::MtoC(MTcell c, Type<pair<S,T> >);
  MTcell	MtoC(MTcell, Type<MTcell>);
  template<class Container>
  Container	MtoC(MTcell, Type<Container>);
}
