// ==========================================================================
// $Id: point_n_dim_special.cpp,v 1.6 2010/11/08 16:29:26 jlang Exp $
// CSI2372 example Code for lecture 9
// ==========================================================================
// (C)opyright:
//
//   Jochen Lang
//   SITE, University of Ottawa
//   800 King Edward Ave.
//   Ottawa, On., K1N 6N5
//   Canada. 
//   http://www.site.uottawa.ca
// 
// Creator: jlang (Jochen Lang)
// Email:   jlang@site.uottawa.ca
// ==========================================================================
// $Log: point_n_dim_special.cpp,v $
// Revision 1.6  2010/11/08 16:29:26  jlang
// Added extra print outs, a specialized class function and a preprocessor option
//
// Revision 1.5  2010/08/09 16:07:47  jlang
// Checkin for 2010
//
// Revision 1.4  2008/11/03 22:32:16  jlang
// Added comments
//
// Revision 1.3  2008/11/03 22:24:00  jlang
// Added example of partial specialization of first but not the second
// template parameter
//
// Revision 1.2  2007/09/18 00:24:50  jlang
// Added comments
//
// Revision 1.1  2006/10/29 00:32:05  jlang
// Check in for lecture 9
//
//
// ==========================================================================
#include <iostream>

using std::cout;
using std::endl;

/**
 * The generic point template with two template paramater: type T and
 * non-type NUM
 */
template <class T, const int NUM>
class Point{
  T d_components[NUM];
public:
  Point();
  Point( T* _components );
  Point<T,NUM> add( const Point<T,NUM>& _oPoint ) const;
  void print() const;
};

template <class T, const int NUM>
Point<T,NUM>::Point() {
  cout << "General Template: dimension=" << NUM << endl;
}

template <class T, const int NUM>
Point<T,NUM>::Point( T* _components ) 
{
  for ( int i=0; i<NUM; i++ ) {
    d_components[i] = _components[i];
  }
}


template <class T, const int NUM>
Point<T,NUM> Point<T,NUM>::add( const Point<T,NUM>& _oPoint ) const 
{
  Point<T,NUM> res;
  for ( int i=0; i<NUM; i++ ) {
    res.d_components[i] = d_components[i] + _oPoint.d_components[i];
  }  
  return res;
}

template <class T, const int NUM>
void Point<T,NUM>::print() const
{
  cout << "( ";
  for ( int i=0; i<NUM; i++ ) {
    if ( i!=0 ) cout << ", ";
    cout << d_components[i];
  }  
  cout << " )" << endl;
}

/**
 * The specialized function; all template parameters must be
 * specialized (just as in global functions).
 */
template <>
void Point<double,3>::print() const
{
  cout << "{3D Point specialized print} ";
  cout << "( ";
  for ( int i=0; i<3; i++ ) {
    if ( i!=0 ) cout << ", ";
    cout << d_components[i];
  }  
  cout << " )" << endl;
}



/**
 * The partially specialized point template for the non-type template
 * parameter NUM
 */

template <class T>
class Point<T,2>{
  T d_components[2];
public:
  Point();
  Point( T* _components );
  Point( T& _x, T& _y );
  Point( T _x, T _y );
  Point<T,2> add( const Point<T,2>& _oPoint ) const;
  void print() const;
};

template <class T>
Point<T,2>::Point() {
  cout << "2D Template" << endl;
}

template <class T>
Point<T,2>::Point( T* _components ) 
{
  for ( int i=0; i<2; i++ ) {
    d_components[i] = _components[i];
  }
}


template <class T>
Point<T,2> Point<T,2>::add( const Point<T,2>& _oPoint ) const 
{
  Point<T,2> res;
  for ( int i=0; i<2; i++ ) {
    res.d_components[i] = d_components[i] + _oPoint.d_components[i];
  }  
  return res;
}

template <class T>
void Point<T,2>::print() const
{
  cout << "( ";
  for ( int i=0; i<2; i++ ) {
    if ( i!=0 ) cout << ", ";
    cout << d_components[i];
  }  
  cout << " )" << endl;
}

template <class T>
Point<T,2>::Point( T& _x, T& _y ) 
  : d_components( _x, _y )
{}

template <class T>
Point<T,2>::Point( T _x, T _y ) 
{
  d_components[0] =_x;
  d_components[1] =_y;
}


/**
 * The partially specialized point template for the type parameter
 * T equals to unsigned char
 */


template <const int NUM>
class Point<unsigned char,NUM>{
  unsigned char d_components[NUM];
public:
  Point();
  Point( unsigned char* _components );
  Point<unsigned char,NUM> add( const Point<unsigned char,NUM>& _oPoint ) const;
  void print() const;
};

template <const int NUM>
Point<unsigned char,NUM>::Point() {
  cout << "unsigned char template with dimension=" << NUM << endl;
}

template <const int NUM>
Point<unsigned char,NUM>::Point( unsigned char* _components ) 
{
  for ( int i=0; i<NUM; i++ ) {
    d_components[i] = _components[i];
  }
}


template <const int NUM>
Point<unsigned char,NUM> 
Point<unsigned char,NUM>::add( const Point<unsigned char,NUM>& _oPoint ) const 
{
  Point<unsigned char,NUM> res;
  for ( int i=0; i<NUM; i++ ) {
    res.d_components[i] = 
      (static_cast<int>(d_components[i] + _oPoint.d_components[i])%256);
  }  
  return res;
}

template <const int NUM>
void Point<unsigned char,NUM>::print() const
{
  cout << "( ";
  for ( int i=0; i<NUM; i++ ) {
    if ( i!=0 ) cout << ", ";
    cout << static_cast<short>(d_components[i]);
  }  
  cout << " )" << endl;
}


/**
 * Main routine instantiating all three templates
 */

int main() {
  int initA[] = {3,8};
  // int initB[] = {-24,12};
  Point<int,2> intPt1(initA), intPt2(-24,12), intPt3;
  intPt3 = intPt1.add( intPt2 );
  intPt1.print();
  cout << " + ";
  intPt2.print();
  cout << " = ";
  intPt3.print();
  cout << endl; 

  double initC[] = {3.2,8.09,7.1};
  double initD[] = {-24.9,12.1,2.3};
  Point<double,3> dPt1(initC), dPt2(initD), dPt3;
  dPt3 = dPt1.add( dPt2 );
  dPt1.print();
  cout << " + ";
  dPt2.print();
  cout << " = ";
  dPt3.print();
  cout << endl;


  unsigned char initE[] = {15,127,3,225};
  unsigned char initF[] = {8,127,11,100};
  Point<unsigned char,4> ucPt1(initE), ucPt2(initF), ucPt3;
  ucPt3 = ucPt1.add( ucPt2 );
  ucPt1.print();
  cout << " + ";
  ucPt2.print();
  cout << " = ";
  ucPt3.print();
  cout << endl;

#if 0
  // If this compiled an error will result. The compiler cannot
  // decide which template to use for unsigned char,2 since both
  // specialized classes (for unsigned char and for dimension 2) match
  unsigned char initG[] = {15,127};
  unsigned char initH[] = {8,127};
  Point<unsigned char,2> ucPt4(initG), ucPt5(initH), ucPt6;
  ucPt6 = ucPt4.add( ucPt5 );
  ucPt6.print();
  cout << endl;
#endif
}
