#ifndef _3DMATH__
#define _3DMATH__
#include <math.h>

typedef struct _VECTOR {
    union {
    double x;
    double dvX;
    };
    union {
    double y;
    double dvY;
    };
    union {
    double z;
    double dvZ;
    };

public:
    // constructors
    _VECTOR() { }
    _VECTOR(double f);
    _VECTOR(double _x, double _y, double _z);
    _VECTOR(const double f[3]);

    // accessors
    const double&operator[](int i) const;
    double&operator[](int i);

    // assignment operators
    _VECTOR& operator += (const _VECTOR& v);
    _VECTOR& operator -= (const _VECTOR& v);
    _VECTOR& operator *= (const _VECTOR& v);
    _VECTOR& operator /= (const _VECTOR& v);
    _VECTOR& operator *= (double s);
    _VECTOR& operator /= (double s);

    // unary operators
    friend _VECTOR operator + (const _VECTOR& v);
    friend _VECTOR operator - (const _VECTOR& v);

    // binary operators
    friend _VECTOR operator + (const _VECTOR& v1, const _VECTOR& v2);
    friend _VECTOR operator - (const _VECTOR& v1, const _VECTOR& v2);
    friend _VECTOR operator * (const _VECTOR& v, double s);
    friend _VECTOR operator * (double s, const _VECTOR& v);
    friend _VECTOR operator / (const _VECTOR& v, double s);
    friend _VECTOR operator * (const _VECTOR& v1, const _VECTOR& v2);
    friend _VECTOR operator / (const _VECTOR& v1, const _VECTOR& v2);
    friend int operator < (const _VECTOR& v1, const _VECTOR& v2);
    friend int operator <= (const _VECTOR& v1, const _VECTOR& v2);
    friend int operator == (const _VECTOR& v1, const _VECTOR& v2);
    friend double SquareMagnitude (const _VECTOR& v);
    friend double Magnitude (const _VECTOR& v);
    friend _VECTOR Normalize (const _VECTOR& v);
    friend double Min (const _VECTOR& v);
    friend double Max (const _VECTOR& v);
    friend _VECTOR Minimize (const _VECTOR& v1, const _VECTOR& v2);
    friend _VECTOR Maximize (const _VECTOR& v1, const _VECTOR& v2);
    friend double DotProduct (const _VECTOR& v1, const _VECTOR& v2);
    friend _VECTOR CrossProduct (const _VECTOR& v1, const _VECTOR& v2);

} VECTOR, *LPVECTOR, COLOR;

typedef struct _POLYGON{
	VECTOR point[3];
	VECTOR normal;

	_POLYGON();
	_POLYGON(VECTOR a, VECTOR b, VECTOR c);
	_POLYGON(const VECTOR[3]);

    const double&operator[](int i) const;
    double&operator[](int i);

    friend int operator == (const _POLYGON& v1, const _POLYGON& v2);

} POLYGON;

inline _POLYGON::_POLYGON() {
    point[0] = VECTOR(0.0,0.0,0.0);
	point[1] = VECTOR(0.0,0.0,0.0);
	point[2] = VECTOR(0.0,0.0,0.0);
}

inline _POLYGON::_POLYGON(_VECTOR _a, _VECTOR _b, _VECTOR _c) {
    point[0] = _a; point[1] = _b; point[2] = _c;
}

inline _POLYGON::_POLYGON(const VECTOR f[3]) {
    point[0] = f[0]; point[1] = f[1]; point[2] = f[2];
}


// constructors
inline _VECTOR::_VECTOR(double f) {
    x = y = z = f;
}

inline _VECTOR::_VECTOR(double _x, double _y, double _z) {
    x = _x; y = _y; z = _z;
}

inline _VECTOR::_VECTOR(const double f[3]) {
    x = f[0]; y = f[1]; z = f[2];
}

// accessors
inline const double& _VECTOR::operator[](int i) const {
    return (&x)[i];
}

inline double& _VECTOR::operator[](int i) {
    return (&x)[i];
}

// assignment operators
inline _VECTOR& _VECTOR::operator += (const _VECTOR& v) {
   x += v.x;   y += v.y;   z += v.z;
   return *this;
}

inline _VECTOR& _VECTOR::operator -= (const _VECTOR& v) {
   x -= v.x;   y -= v.y;   z -= v.z;
   return *this;
}

inline _VECTOR& _VECTOR::operator *= (const _VECTOR& v) {
   x *= v.x;   y *= v.y;   z *= v.z;
   return *this;
}

inline _VECTOR& _VECTOR::operator /= (const _VECTOR& v) {
   x /= v.x;   y /= v.y;   z /= v.z;
   return *this;
}

inline _VECTOR& _VECTOR::operator *= (double s) {
   x *= s;   y *= s;   z *= s;
   return *this;
}

inline _VECTOR& _VECTOR::operator /= (double s) {
   x /= s;   y /= s;   z /= s;
   return *this;
}

inline _VECTOR operator + (const _VECTOR& v) {
   return v;
}

inline _VECTOR operator - (const _VECTOR& v) {
   return _VECTOR(-v.x, -v.y, -v.z);
}

inline _VECTOR operator + (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);
}

inline _VECTOR operator - (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR(v1.x-v2.x, v1.y-v2.y, v1.z-v2.z);
}

inline _VECTOR operator * (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
}

inline _VECTOR operator / (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z);
}

inline int operator < (const _VECTOR& v1, const _VECTOR& v2) {
   return v1[0] < v2[0] && v1[1] < v2[1] && v1[2] < v2[2];
}

inline int operator <= (const _VECTOR& v1, const _VECTOR& v2) {
   return v1[0] <= v2[0] && v1[1] <= v2[1] && v1[2] <= v2[2];
}

inline _VECTOR operator * (const _VECTOR& v, double s) {
   return _VECTOR(s*v.x, s*v.y, s*v.z);
}

inline _VECTOR operator * (double s, const _VECTOR& v) {
   return _VECTOR(s*v.x, s*v.y, s*v.z);
}

inline _VECTOR operator / (const _VECTOR& v, double s) {
   return _VECTOR(v.x/s, v.y/s, v.z/s);
}

inline int operator == (const _VECTOR& v1, const _VECTOR& v2) {
   return v1.x==v2.x && v1.y==v2.y && v1.z == v2.z;
}

inline double Magnitude (const _VECTOR& v) {
   return (double) sqrt(SquareMagnitude(v));
}

inline double SquareMagnitude (const _VECTOR& v) {
   return v.x*v.x + v.y*v.y + v.z*v.z;
}

inline _VECTOR Normalize (const _VECTOR& v) {
   return v / Magnitude(v);
}

inline double Min (const _VECTOR& v) {
   double ret = v.x;
   if (v.y < ret) ret = v.y;
   if (v.z < ret) ret = v.z;
   return ret;
}

inline double Max (const _VECTOR& v) {
   double ret = v.x;
   if (ret < v.y) ret = v.y;
   if (ret < v.z) ret = v.z;
   return ret;
}

inline _VECTOR Minimize (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR( v1[0] < v2[0] ? v1[0] : v2[0],
                   v1[1] < v2[1] ? v1[1] : v2[1],
                   v1[2] < v2[2] ? v1[2] : v2[2]);
}

inline _VECTOR Maximize (const _VECTOR& v1, const _VECTOR& v2) {
   return _VECTOR( v1[0] > v2[0] ? v1[0] : v2[0],
                   v1[1] > v2[1] ? v1[1] : v2[1],
                   v1[2] > v2[2] ? v1[2] : v2[2]);
}

inline double DotProduct (const _VECTOR& v1, const _VECTOR& v2) {
   return v1.x*v2.x + v1.y * v2.y + v1.z*v2.z;
}

inline _VECTOR CrossProduct (const _VECTOR& v1, const _VECTOR& v2) {
	_VECTOR result;

	result[0] = v1[1] * v2[2] - v1[2] * v2[1];
	result[1] = v1[2] * v2[0] - v1[0] * v2[2];
	result[2] = v1[0] * v2[1] - v1[1] * v2[0];

	return result;
}

#endif _3DMATH__