%module template_partial_specialization

%inline %{
namespace One {
  template <typename T> struct OneParm                  { void a() {} };
  template <typename T> struct OneParm<T *>             { void b() {} };
  template <typename T> struct OneParm<T &>             { void c() {} };
  template <typename T> struct OneParm<T const &>       { void d() {} };
  template <typename T> struct OneParm<T * const &>     { void e() {} };

  template <>           struct OneParm<int>             { void f() {} };
  template <>           struct OneParm<int * const &>   { void g() {} };
  template <>           struct OneParm<int **>          { void h() {} };

  template <>           struct OneParm<float>           { void j() {} };
  template <>           struct OneParm<float *>         { void k() {} };
  template <>           struct OneParm<float **>        { void m() {} };
  template <>           struct OneParm<float ***>       { void n() {} };
}
%}

// partial specializations
%template(A) One::OneParm<double>;
%template(B) One::OneParm<double *>;
%template(C) One::OneParm<double &>;
%template(D) One::OneParm<const double &>;
%template(E) One::OneParm<double * const &>;

// explicit specializations
%template(F) One::OneParm<int>;
%template(G) One::OneParm<int * const &>;
%template(H) One::OneParm<int **>;

// %template scope explicit specializations
namespace One {
  %template(J) One::OneParm<float>;
  %template(K) ::One::OneParm<float *>;
}
%template(M) ::One::OneParm<float **>;
namespace One {
  %template(N) OneParm<float ***>;
}

// %template scope partial specializations
namespace One {
  %template(BB) One::OneParm<bool *>;
  %template(BBB) ::One::OneParm<char *>;
}
%template(BBBB) ::One::OneParm<short *>;
namespace One {
  %template(BBBBB) OneParm<long *>;
}

// non-exact match
%template(B1) One::OneParm<unsigned int **>;
%template(B2) One::OneParm<unsigned int ***>;
%template(B3) One::OneParm<const unsigned int *>;
%template(B4) One::OneParm<const unsigned int **>;


// Two parameter specialization tests
%inline %{
struct Concrete {};
namespace Two {
  template <typename T1, typename T2> struct TwoParm                          { void a() {} };
  template <typename T1, typename T2> struct TwoParm<T1 *, T2 *>              { void b() {} };
  template <typename T1, typename T2> struct TwoParm<T1 *, const T2 *>        { void c() {} };
  template <typename T1, typename T2> struct TwoParm<const T1 *, const T2 *>  { void d() {} };
  template <typename T1>              struct TwoParm<T1 *, int *>             { void e() {} };
  template <typename T1>              struct TwoParm<T1, int>                 { void f() {} };
  template <>                         struct TwoParm<int *, const int *>      { void g() {} };
  template <>                         struct TwoParm<Concrete, Concrete *>    { void h() {} };
}
%}

namespace Two {
  %template(A_) TwoParm<double, double>;
  %template(B_) TwoParm<double *, double *>;
  %template(C_) TwoParm<double *, const double *>;
  %template(D_) TwoParm<const int *, const int *>;
  %template(E_) TwoParm<int *, int *>;
  %template(F_) TwoParm<int *, int>;
  %template(G_) TwoParm<int *, const int *>;

  %template(C1_) TwoParm<Concrete *, const Concrete *>;
  %template(C2_) TwoParm<int *, const ::Concrete *>;
}

%template(C3_) Two::TwoParm<double *, const ::Concrete *>;
%template(C4_) ::Two::TwoParm<void *, const ::Concrete *>;
%template(B1_) ::Two::TwoParm<char *, ::Concrete *>;
%template(E1_) Two::TwoParm<const int *, int *>;
%template(E2_) Two::TwoParm<int **, int *>;
%template(H_) Two::TwoParm< ::Concrete, ::Concrete * >;


// Many template parameters
%inline %{
template <typename T1, typename T2, typename T3, typename T4, typename T5> struct FiveParm                               { void a() {} };
template <typename T1>                                                     struct FiveParm<T1, int, int, double, short>  { void b() {} };
%}

%template(FiveParm1) FiveParm<bool, int, int, double, short>;

%inline %{
template <typename T, int N = 0, int M = 0> struct ThreeParm;
template <typename T, int N, int M>         struct ThreeParm          { void a1() {} };
template <typename T>                       struct ThreeParm<T, 0, 0> { void a2() {} };
template <typename T, int N>                struct ThreeParm<T, N, N> { void a3() {} };
%}

%template(ThreeParmInt) ThreeParm<int, 0, 0>;

%inline %{
namespace S {
  template<typename T> struct X      { void a() {} };
  template<typename T> struct X<T *> { void b() {} };
  template<>           struct X<int *> { void c() {} };
}
%}

namespace S {
  %template(X2) X<int *>;
};
