#ifndef VECTOR3_H
#define VECTOR3_H

#include <math.h>

typedef float Real;

class Vector3
{
  public:
  
    Vector3( );
    Vector3(const Vector3&);
    Vector3(const Real, const Real = Real(0), const Real = Real(0));
    ~Vector3();    
  
    Real & operator () (unsigned int);
    Real operator () (unsigned int) const;
    bool operator < (const Vector3&) const;
    bool operator > (const Vector3&) const;
    bool operator == (const Vector3&) const;
    bool operator != (const Vector3&) const;
    Vector3 & operator =  (const Vector3&);
    Vector3 & operator += (const Vector3&);
    Vector3 & operator -= (const Vector3&);
    Vector3 & operator *= (const Real&);
    Vector3 & operator /= (const Real&);
    Vector3 operator % (const Vector3&) const;
    
    friend Real operator * (const Vector3&, const Vector3&);
    friend Vector3 operator * (const Vector3&, const Real&);
    friend Vector3 operator * (const Real&, const Vector3&); 
    friend Vector3 operator / (const Vector3&, const Real&);
    friend Vector3 operator + (const Vector3&, const Vector3&);
    friend Vector3 operator - (const Vector3&, const Vector3&);
    
    Real norm() const;
    Real normSq() const;
    void normalize();
    void set(const Real, const Real = Real(0), const Real = Real(0));
    void set(const Real *);
    void zero();
    Real *get();  
      
  private:
  
    Real data[3];
};
 
inline void Vector3::zero( )
{
  data[0] = Real(0);
  data[1] = Real(0);
  data[2] = Real(0);
}

inline void Vector3::set(const Real x, const Real y, const Real z)
{
  data[0] = x;
  data[1] = y;
  data[2] = z;
}

inline void Vector3::set(const Real *r)
{
  data[0] = r[0];
  data[1] = r[1];
  data[2] = r[2];
}

inline Real * Vector3::get()
{
  return data;
}

inline Real& Vector3::operator () (const unsigned int index)
{ 
  return data[index];
} 

inline Real Vector3::operator () (const unsigned int index) const
{ 
  return data[index];
} 

inline bool Vector3::operator > (const Vector3 &v) const
{
  return (normSq() > v.normSq());
}
 
inline bool Vector3::operator < (const Vector3 &v) const
{
  return (normSq() < v.normSq());
}

inline bool Vector3::operator == (const Vector3 &v) const
{
  return ( (data[0] == v(0)) && (data[1] == v(1)) && (data[2] == v(2)) );
}

inline bool Vector3::operator != (const Vector3 &v) const
{
  return !(*this == v);
}

inline Vector3 Vector3::operator % (const Vector3 &v) const
{
  return Vector3(data[1] * v(2) - data[2] * v(1), 
                 data[2] * v(0) - data[0] * v(2),
		 data[0] * v(1) - data[1] * v(0));
}

inline Vector3& Vector3::operator = (const Vector3 &v)
{
  data[0] = v(0);
  data[1] = v(1);
  data[2] = v(2);

  return *this;
}
 
inline Vector3& Vector3::operator += (const Vector3& v)
{
  data[0] += v(0);
  data[1] += v(1);
  data[2] += v(2);
  
  return *this; 
}
 
inline Vector3& Vector3::operator -= (const Vector3& v)
{
  data[0] -= v(0);
  data[1] -= v(1);
  data[2] -= v(2);
  
  return *this; 
}
 
inline Vector3& Vector3::operator *= (const Real& r)
{
  data[0] *= r;
  data[1] *= r;
  data[2] *= r;
  
  return *this; 
}
 
inline Vector3& Vector3::operator /= (const Real& r)
{
  Real d = Real(1) / r;

  data[0] *= d;
  data[1] *= d;
  data[2] *= d;
  
  return *this; 
}

inline Real operator * (const Vector3 &va, const Vector3 &vb)
{
  return ( va(0) * vb(0) + va(1) * vb(1) + va(2) * vb(2) );
}

inline Vector3 operator * (const Vector3 &va, const Real &r)
{
  return Vector3(va) *= r;
}

inline Vector3 operator * (const Real &r, const Vector3 &va)
{
  return Vector3(va) *= r;
}
 
inline Vector3 operator / (const Vector3 &va, const Real& r)
{
  Real d = Real(1) / r;

  return Vector3(va) *= d;
}

inline Vector3 operator + (const Vector3 &va, const Vector3 &vb)
{
  return Vector3(va) += vb;
}

inline Vector3 operator - (const Vector3 &va, const Vector3 &vb)
{
  return Vector3(va) -= vb;
}

inline Real Vector3::norm() const
{
  return sqrtf(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
}

inline Real Vector3::normSq() const
{
  return (data[0] * data[0] + data[1] * data[1] + data[2] * data[2]);
}

inline void Vector3::normalize()
{
  Real mron = norm();

  if(mron == Real(0))
    return;

  mron = Real(1) / mron;

  data[0] *= mron;
  data[1] *= mron;
  data[2] *= mron;
}

inline Vector3::Vector3( )
{
}

inline Vector3::Vector3(const Vector3 &v)
{
  *this = v;
}

inline Vector3::Vector3(const Real x, const Real y, const Real z)
{
  data[0] = x;
  data[1] = y;
  data[2] = z;
}

inline Vector3::~Vector3()
{
}

#endif
