// Copyright (C) 2003  Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_VECTOR_ABSTRACT_
#ifdef DLIB_VECTOR_ABSTRACT_

#include "../serialize.h"
#include <functional>
#include <iostream>
#include "../matrix/matrix_abstract.h"

namespace dlib
{
    class point;

    template <
        typename T
        >
    class vector
    {
        /*!
            REQUIREMENTS ON T
                T should be some object that provides an interface that is 
                compatible with double, float and the like.

            INITIAL VALUE
                x() == 0 
                y() == 0 
                z() == 0

            WHAT THIS OBJECT REPRESENTS
                This object represents a three dimensional vector.

            THREAD SAFETY
                Note that the vector object is not allowed to be reference counted.
                This is to ensure a minimum amount of thread safety.
        !*/

    public:

        typedef T type;
        
        vector (
        );
        /*!
            ensures
               - #*this has been properly initialized
        !*/

        vector (
            const T _x,
            const T _y,
            const T _z
        );
        /*!
            ensures
                - #*this properly initialized 
                - #x() == _x 
                - #y() == _y 
                - #z() == _z 
        !*/

        vector (
            const point& p
        );
        /*!
            ensures
                - #*this properly initialized 
                - #x() == p.x() 
                - #y() == p.y()
                - #z() == 0 
        !*/

        vector (
            const vector& v
        );
        /*!
            ensures
                - #*this properly initialized 
                - #x() == v.x() 
                - #y() == v.y() 
                - #z() == v.z() 
        !*/

        template <typename EXP>
        vector ( 
            const matrix_exp<EXP>& m
        );
        /*!
            requires
                - m.size() == 3
                - m.nr() == 1 || m.nc() == 1 (i.e. m must be a row or column matrix)
            ensures
                - #x() == m(0)
                - #y() == m(1)
                - #z() == m(2)
        !*/

        template <long NR, long NC, typename MM>
        operator matrix<T,NR, NC, MM> (
        ) const;
        /*!
            ensures
                - provides automatic conversions from a vector object to a column 
                  matrix
                - returns a matrix object m such that:
                    - m.nr() == 3
                    - m.nc() == 1
                    - m(0) == x()
                    - m(1) == y()
                    - m(2) == z()
        !*/

        ~vector (
        );
        /*!
            ensures
                - all resources associated with *this have been released
        !*/


        T length(
        ) const;
        /*!
            ensures
                - returns the length of the vector
        !*/

        T& x (
        );
        /*!
            ensures
                - returns a reference to the x component of the vector
        !*/

        T& y (
        );
        /*!
            ensures
                - returns a reference to the y component of the vector
        !*/

        T& z (
        );
        /*!
            ensures
                - returns a reference to the z component of the vector
        !*/

        const T& x (
        ) const;
        /*!
            ensures
                - returns a const reference to the x component of the vector
        !*/

        const T& y (
        ) const;
        /*!
            ensures
                - returns a const reference to the y component of the vector
        !*/

        const T& z (
        ) const;
        /*!
            ensures
                - returns a const reference to the z component of the vector
        !*/

        T dot (
            const vector& rhs
        ) const;
        /*!
            ensures
                - returns the result of the dot product between *this and rhs
        !*/

        vector cross (
            const vector& rhs
        ) const;
        /*!
            ensures
                - returns the result of the cross product between *this and rhs
        !*/

        vector normalize (
        ) const;
        /*!
            ensures
                - returns a vector with length() == 1 and in the same direction as *this
        !*/

        vector operator+ (
            const vector& rhs
        ) const;
        /*!
            ensures
                - returns the result of adding *this to rhs
        !*/

        vector operator- (
            const vector& rhs
        ) const;
        /*!
            ensures
                - returns the result of subtracting rhs from *this
        !*/

        vector operator/ (
            const T rhs
        ) const;
        /*!
            ensures
                - returns the result of dividing *this by rhs 
        !*/

        vector& operator= (
            const vector& rhs
        );
        /*!
            ensures
                - #x() == rhs.x() 
                - #y() == rhs.y() 
                - #z() == rhs.z()
                - returns #*this
        !*/

        vector& operator += (
            const vector& rhs
        );
        /*!
            ensures
                - #*this == *this + rhs
                - returns #*this
        !*/

        vector& operator -= (
            const vector& rhs
        );
        /*!
            ensures
                - #*this == *this - rhs
                - returns #*this
        !*/

        vector& operator *= (
            const T rhs
        );
        /*!
            ensures
                - #*this == *this * rhs
                - returns #*this
        !*/

        vector& operator /= (
            const T rhs
        );
        /*!
            ensures
                - #*this == *this / rhs
                - returns #*this
        !*/

        bool operator== (
            const vector& rhs
        ) const;
        /*!
            ensures
                - if (x() == rhs.x() && y() == rhs.y() && z() == rhs.z()) then
                    - returns true
                - else
                    - returns false
        !*/

        bool operator!= (
            const vector& rhs
        ) const;
        /*!
            ensures
                - returns !((*this) == rhs)
        !*/

        void swap (
            vector& item
        );
        /*!
            ensures
                - swaps *this and item
        !*/

    };

    template<typename T, typename U>
    vector<T>  operator* (
        const vector<T> & lhs,
        const U rhs
    );
    /*!
        ensures
            - returns the result of multiplying the scalar rhs by lhs
    !*/
    
    template<typename T, typename U>
    vector<T>  operator* (
        const U lhs,
        const vector<T> & rhs   
    );
    /*! 
        ensures
            - returns the result of multiplying the scalar lhs by rhs
    !*/

    template<typename T>
    inline void swap (
        vector<T> & a, 
        vector<T> & b 
    ) { a.swap(b); }   
    /*!
        provides a global swap function
    !*/

    template<typename T>
    void serialize (
        const vector<T> & item, 
        std::ostream& out
    );   
    /*!
        provides serialization support 
    !*/

    template<typename T>
    void deserialize (
        vector<T> & item, 
        std::istream& in
    );   
    /*!
        provides deserialization support 
    !*/

    template<typename T>
    std::ostream& operator<< (
        std::ostream& out, 
        const vector<T>& item 
    );   
    /*!
        ensures
            - writes item to out in the form "(x, y, z)"
    !*/

    template<typename T>
    std::istream& operator>>(
        std::istream& in, 
        vector<T>& item 
    );   
    /*!
        ensures
            - reads a vector from the input stream in and stores it in #item.
              The data in the input stream should be of the form (x, y, z)
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class point
    {
        /*!
            INITIAL VALUE
                The initial value of this object is defined by its constructor.                

            WHAT THIS OBJECT REPRESENTS
                This object represents a point inside a Cartesian coordinate system.
        !*/

    public:

        point (
        );
        /*!
            ensures
                - #x() == 0
                - #y() == 0
        !*/

        point (
            long x_
            long y_
        );
        /*!
            ensures
                - #x() == x_
                - #y() == y_
        !*/

        point (
            const point& p
        );
        /*!
            ensures
                - #x() == p.x()
                - #y() == p.y()
        !*/

        template <typename T>
        point (
            const vector<T>& v
        );
        /*!
            ensures
                - #x() == floor(v.x()+0.5)
                - #y() == floor(v.y()+0.5)
        !*/

        long x (
        ) const;
        /*!
            ensures
                - returns the x coordinate of this point
        !*/

        long y (
        ) const;
        /*!
            ensures
                - returns the y coordinate of this point
        !*/

        long& x (
        );
        /*!
            ensures
                - returns a non-const reference to the x coordinate of 
                  this point
        !*/

        long& y (
        );
        /*!
            ensures
                - returns a non-const reference to the y coordinate of 
                  this point
        !*/

        const point operator+ (
            const point& rhs
        ) const;
        /*!
            ensures
                - returns point(x()+rhs.x(), y()+rhs.y())
        !*/

        const point operator- (
            const point& rhs
        ) const;
        /*!
            ensures
                - returns point(x()-rhs.x(), y()-rhs.y())
        !*/

        point& operator= (
            const point& p
        );
        /*!
            ensures
                - #x() == p.x()
                - #y() == p.y()
                - returns #*this
        !*/

        point& operator+= (
            const point& rhs
        );
        /*!
            ensures
                - #*this = *this + rhs
                - returns #*this
        !*/

        point& operator-= (
            const point& rhs
        );
        /*!
            ensures
                - #*this = *this - rhs
                - returns #*this
        !*/

        bool operator== (
            const point& p
        ) const;
        /*!
            ensures
                - if (x() == p.x() && y() == p.y()) then 
                    - returns true
                - else
                    - returns false
        !*/

        bool operator!= (
            const point& p
        ) const;
        /*!
            ensures
                - returns !(*this == p)
        !*/
    };

// ----------------------------------------------------------------------------------------

    void serialize (
        const point& item, 
        std::ostream& out
    );   
    /*!
        provides serialization support 
    !*/

    void deserialize (
        point& item, 
        std::istream& in
    );   
    /*!
        provides deserialization support 
    !*/

    std::ostream& operator<< (
        std::ostream& out, 
        const point& item 
    );   
    /*!
        ensures
            - writes item to out in the form "(x, y)"
    !*/

    std::istream& operator>>(
        std::istream& in, 
        point& item 
    );   
    /*!
        ensures
            - reads a point from the input stream in and stores it in #item.
              The data in the input stream should be of the form (x, y)
    !*/

// ----------------------------------------------------------------------------------------

}

namespace std
{
    /*!
        Define std::less<vector<T> > so that you can use vectors in the associative containers.
    !*/
    template<typename T>
    struct less<dlib::vector<T> > : public binary_function<dlib::vector<T> ,dlib::vector<T> ,bool>
    {
        inline bool operator() (const dlib::vector<T> & a, const dlib::vector<T> & b) const
        { 
            if      (a.x() < b.x()) return true;
            else if (a.x() > b.x()) return false;
            else if (a.y() < b.y()) return true;
            else if (a.y() > b.y()) return false;
            else if (a.z() < b.z()) return true;
            else if (a.z() > b.z()) return false;
            else                    return false;
        }
    };

    /*!
        Define std::less<point> so that you can use points in the associative containers.
    !*/
    template<>
    struct less<dlib::point> : public binary_function<dlib::point,dlib::point,bool>
    {
        inline bool operator() (const dlib::point& a, const dlib::point& b) const
        { 
            if      (a.x() < b.x()) return true;
            else if (a.x() > b.x()) return false;
            else if (a.y() < b.y()) return true;
            else if (a.y() > b.y()) return false;
            else                    return false;
        }
    };
}

#endif // DLIB_VECTOR_ABSTRACT_

