#ifndef __SL_VECTOR_2D_H
#define __SL_VECTOR_2D_H

#include "slMathTraits.h"

template<class T>
class vector2D {
 private :
  T data[2];
 public :
  static vector2D<T> ZERO;
  static vector2D<T> X, NEG_X;
  static vector2D<T> Y, NEG_Y;
 public :
  T& operator [](int index);
  const T& operator [](int index)const;
 public :
  vector2D<T>& operator  =(const vector2D<T>& v);
  vector2D<T>& operator +=(const vector2D<T>& v);
  vector2D<T>& operator -=(const vector2D<T>& v);
  vector2D<T>& operator *=(T scalar);
  vector2D<T>& operator /=(T scalar);
 public :
  vector2D<T> operator +(const vector2D<T>& v)const;
  vector2D<T> operator -(const vector2D<T>& v)const;
  vector2D<T> operator *(const vector2D<T>& v)const;
  vector2D<T> operator /(const vector2D<T>& v)const;
  vector2D<T> operator *(T scalar)const;
  vector2D<T> operator /(T scalar)const;
 public :
  vector2D<T> operator +(void)const;
  vector2D<T> operator -(void)const;
 public :
  bool operator ==(const vector2D<T>& v)const;
  bool operator !=(const vector2D<T>& v)const;
  bool operator !(void)const;
 public :
  const T* c_ptr(void)const;
  T length(void)const;
  T length_squared(void)const;
  void normalize(void);
 public :
  T dot(const vector2D<T>& v)const;
  T cross(const vector2D<T>& v)const;
 public :
  friend T abs(const vector2D<T>& v);
  friend T dot_product(const vector2D<T>& v1, const vector2D<T>& v2);
  friend T cross_product(const vector2D<T>& v1, const vector2D<T>& v2);
  friend vector2D<T> min(const vector2D<T>& v1, const vector2D<T>& v2);
  friend vector2D<T> max(const vector2D<T>& v1, const vector2D<T>& v2);
 public :
  friend vector2D<T> operator *(T scalar, const vector2D<T>& v);
  friend std::ostream& operator <<(std::ostream& os, const vector2D<T>& v);
 public :
  vector2D();
  vector2D(T x, T y);
  explicit vector2D(T fill);
  explicit vector2D(const T* v);
  vector2D(const vector2D& v);
 ~vector2D();
};

template<class T> vector2D<T> vector2D<T>::ZERO = vector2D<T>(math_traits<T>::zero(), math_traits<T>::zero());
template<class T> vector2D<T> vector2D<T>::X = vector2D<T>(math_traits<T>::one(), math_traits<T>::zero());
template<class T> vector2D<T> vector2D<T>::Y = vector2D<T>(math_traits<T>::zero(), math_traits<T>::one());
template<class T> vector2D<T> vector2D<T>::NEG_X = vector2D<T>(math_traits<T>::neg_one(), math_traits<T>::zero());
template<class T> vector2D<T> vector2D<T>::NEG_Y = vector2D<T>(math_traits<T>::zero(), math_traits<T>::neg_one());

template<class T>
inline vector2D<T>::vector2D()
{
}

template<class T>
inline vector2D<T>::vector2D(T x, T y)
{
 data[0] = x;
 data[1] = y;
}

template<class T>
inline vector2D<T>::vector2D(T fill)
{
 data[0] = fill;
 data[1] = fill;
}

template<class T>
inline vector2D<T>::vector2D(const T* v)
{
 data[0] = v[0];
 data[1] = v[1];
}

template<class T>
inline vector2D<T>::vector2D(const vector2D<T>& v)
{
 data[0] = v.data[0];
 data[1] = v.data[1];
}

template<class T>
inline vector2D<T>::~vector2D()
{
}

template<class T>
inline vector2D<T>& vector2D<T>::operator =(const vector2D<T>& v)
{
 if(this == &v) return *this;
 data[0] = v.data[0];
 data[1] = v.data[1];
 return *this;
}

template<class T>
inline vector2D<T>& vector2D<T>::operator +=(const vector2D<T>& v)
{
 data[0] += v.data[0];
 data[1] += v.data[1];
 return *this;
}

template<class T>
inline vector2D<T>& vector2D<T>::operator -=(const vector2D<T>& v)
{
 data[0] -= v.data[0];
 data[1] -= v.data[1];
 return *this;
}

template<class T>
inline vector2D<T>& vector2D<T>::operator *=(T scalar)
{
 data[0] *= scalar;
 data[1] *= scalar;
 return *this;
}

template<class T>
inline vector2D<T>& vector2D<T>::operator /=(T scalar)
{
 scalar = math_traits<T>::one()/scalar;
 data[0] *= scalar;
 data[1] *= scalar;
 return *this;
}

template<class T>
inline vector2D<T> vector2D<T>::operator +(const vector2D<T>& v)const
{
 return vector2D<T>(data[0] + v.data[0], data[1] + v.data[1]);
}

template<class T>
inline vector2D<T> vector2D<T>::operator -(const vector2D<T>& v)const
{
 return vector2D<T>(data[0] - v.data[0], data[1] - v.data[1]);
}

template<class T>
inline vector2D<T> vector2D<T>::operator *(const vector2D<T>& v)const
{
 return vector2D<T>(data[0] * v.data[0], data[1] * v.data[1]);
}

template<class T>
inline vector2D<T> vector2D<T>::operator /(const vector2D<T>& v)const
{
 T scalar[2] = {
   math_traits<T>::one()/v.data[0],
   math_traits<T>::one()/v.data[1],
 }; 
 return vector2D<T>(data[0] * scalar[0], data[1] * scalar[1]);
}

template<class T>
inline vector2D<T> vector2D<T>::operator *(T scalar)const
{
 return vector2D<T>(data[0]*scalar, data[1]*scalar);
}

template<class T>
inline vector2D<T> vector2D<T>::operator /(T scalar)const
{
 scalar = math_traits<T>::one()/scalar;
 return vector2D<T>(data[0]*scalar, data[1]*scalar);
}

template<class T>
inline vector2D<T> vector2D<T>::operator +(void)const
{
 return *this;
}

template<class T>
inline vector2D<T> vector2D<T>::operator -(void)const
{
 return vector2D<T>(-data[0], -data[1]);
}

template<class T>
inline bool vector2D<T>::operator ==(const vector2D<T>& v)const
{
 return ((data[0] == v.data[0]) && (data[1] == v.data[1]));
}

template<class T>
inline bool vector2D<T>::operator !=(const vector2D<T>& v)const
{
 return !(*this == v);
}

template<class T>
inline bool vector2D<T>::operator !(void)const
{
 return ((data[0] == math_traits<T>::zero()) && (data[1] == math_traits<T>::zero()));
}

template<class T>
inline T& vector2D<T>::operator [](int index)
{
 return data[index];
}

template<class T>
inline const T& vector2D<T>::operator [](int index)const
{
 return data[index];
}

template<class T>
inline const T* vector2D<T>::c_ptr(void)const
{
 return &data[0];
}

template<class T>
inline T vector2D<T>::length(void)const
{
 return std::sqrt(data[0]*data[0] + data[1]*data[1]);
}

template<class T>
inline T vector2D<T>::length_squared(void)const
{
 return data[0]*data[0] + data[1]*data[1];
}

template<class T>
inline void vector2D<T>::normalize(void)
{
 T denom = math_traits<T>::one()/length();
 data[0] *= denom;
 data[1] *= denom;
}

template<class T>
inline T vector2D<T>::dot(const vector2D<T>& v)const
{
 return data[0]*v.data[0] + data[1]*v.data[1];
}

template<class T>
inline T vector2D<T>::cross(const vector2D<T>& v)const
{
 return data[0]*v.data[1] - data[1]*v.data[0];
}

template<class T>
inline T abs(const vector2D<T>& v)
{
 return v.length();
}

template<class T>
inline T dot_product(const vector2D<T>& v1, const vector2D<T>& v2)
{
 return v1.dot(v2);
}

template<class T>
inline T cross_product(const vector2D<T>& v1, const vector2D<T>& v2)
{
 return v1.data[0]*v2.data[1] - v1.data[1]*v2.data[0];
}

template<class T>
inline vector2D<T> min(const vector2D<T>& v1, const vector2D<T>& v2)
{
 return vector2D<T>(std::min(v1.data[0], v2.data[0]), std::min(v1.data[1], v2.data[1]));
}

template<class T>
inline vector2D<T> max(const vector2D<T>& v1, const vector2D<T>& v2)
{
 return vector2D<T>(std::max(v1.data[0], v2.data[0]), std::max(v1.data[1], v2.data[1]));
}

template<class T>
inline vector2D<T> operator *(T scalar, const vector2D<T>& v)
{
 return vector2D<T>(v.data[0]*scalar, v.data[1]*scalar);
}

template<class T>
inline std::ostream& operator <<(std::ostream& os, const vector2D<T>& v)
{
 os << "<" << v.data[0] << "," << v.data[1] << "," << v.data[2] << ">";
 return os;
}

#endif
