#pragma once
/************************************
 * common.h
 * Вектора и прочие прикольные штуки
 *
************************************/

#define PELM_VECTOR

#include <cmath>
#define RTOD 180.0/3.1415
#define DTOR 3.1415/180.0

#ifndef _STL_ALGOBASE_H

template<typename T>
constexpr const T& max(const T &a, const T &b)
{
    return ((a>b)?a:b);
}

template<typename T>
constexpr const T& min(const T &a, const T &b)
{
    return ((a<b)?a:b);
}

template<typename T>
constexpr T& max(T &a, T &b)
{
    return ((a>b)?a:b);
}

template<typename T>
constexpr T& min(T &a, T &b)
{
    return ((a<b)?a:b);
}

#else

using std::max;
using std::min;
using std::swap;

#endif // _STL_ALGOBASE_H

template<typename T>
constexpr const T &clamp(const T &value, const T &mn, const T &mx)
{
    if(value > mx)
        return mx;
    if(value < mn)
        return mn;
    return value;
}

template<typename T>
constexpr T mix(const T &_1, const T &_2, float a) noexcept
{
    return _1*a+_2*(1.0f-a);
}

template<typename T>
constexpr T quadMix(const T &_1, const T &_2, float a) noexcept
{
    a *= a;
    return _1*a+_2*(1.0f-a);
}

typedef unsigned char  uchar;
typedef unsigned short ushort;
typedef unsigned int   uint;

template<typename T>
struct vec2
{
    union
    {
        struct{T x, y;};
        struct{T s, t;};
        struct{T u, v;};
        struct{T left, top;};
        T data[2];
    };

    constexpr vec2():x(0),y(0){}

    #ifdef B2_MATH_H
    constexpr vec2(const b2Vec2 &a):x(a.x),y(a.y){}
    constexpr operator b2Vec2() const{return b2Vec2(x,y);}
    operator b2Vec2() {return b2Vec2(x,y);}
    #endif // B2_MATH_H

    template<typename S>
    constexpr vec2(const S &a):x(a),y(a){}

    template<typename S, typename Q>
    constexpr vec2(const S &a, const Q &b):x(a),y(b){}

    template<typename S>
    constexpr vec2(const vec2<S> &a):x(a.x),y(a.y){}

    template<typename S>
    constexpr vec2<T>& operator+=(const vec2<S> &a) noexcept
    {
        x+=a.x;
        y+=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator-=(const vec2<S> &a) noexcept
    {
        x-=a.x;
        y-=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator*=(const vec2<S> &a) noexcept
    {
        x*=a.x;
        y*=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator*=(const S &a) noexcept
    {
        x*=a;
        y*=a;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator/=(const vec2<S> &a) noexcept
    {
        x/=a.x;
        y/=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator/=(const S &a) noexcept
    {
        x/=a;
        y/=a;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator=(const vec2<S> &a) noexcept
    {
        x=a.x;
        y=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> operator+(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x+a.x, y+a.y);
    }

    template <typename S>
    constexpr vec2<T> operator-(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x-a.x, y-a.y);
    }

    constexpr vec2<T> operator-() const noexcept
    {
        return vec2<T>(-x, -y);
    }

    template <typename S>
    constexpr vec2<T> operator*(const S &a) const noexcept
    {
        return vec2<T>(x*a, y*a);
    }

    template <typename S>
    constexpr vec2<T> operator*(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x*a.x, y*a.y);
    }

    template <typename S>
    constexpr vec2<T> operator/(const S &a) const noexcept
    {
        return vec2<T>(x/a, y/a);
    }

    template <typename S>
    constexpr vec2<T> operator/(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x/a.x, y/a.y);
    }

    template <typename S>
    constexpr bool operator!=(const vec2<S> &a) const noexcept
    {
        return x != a.x || y != a.y;
    }

    template <typename S>
    constexpr bool operator==(const vec2<S> &a) const noexcept
    {
        return x == a.x && y == a.y;
    }

    template <typename S>
    constexpr bool operator>(const vec2<S> &a) const noexcept
    {
        return x*x+y*y > a.x*a.x+a.y*a.y;
    }

    template <typename S>
    constexpr bool operator>=(const vec2<S> &a) const noexcept
    {
        return x*x+y*y >= a.x*a.x+a.y*a.y;
    }

    template <typename S>
    constexpr bool operator<=(const vec2<S> &a) const noexcept
    {
        return !(operator>(a));
    }

    template <typename S>
    constexpr bool operator<(const vec2<S> &a) const noexcept
    {
        return !(operator>=(a));
    }

    inline T length() const noexcept{return sqrt(x*x+y*y);}
    inline vec2<T> normalize() const noexcept{return (*this/length());}

    constexpr T &max(){return max(x, y);}
    constexpr T &min(){return min(x, y);}

    //inline T *data(){return &x;}
    constexpr T &operator[](uint a){return *((&x)+a);}

    template <typename S>
    constexpr T dot(const vec2<S> &a) const noexcept
    {
        return T(x*a.x+y*a.y);
    }

    template <typename S>
    constexpr T cross(const vec2<S> &a) const noexcept
    {
        return T(x*a.y-y*a.x);
    }

    constexpr vec2<T> yx() const noexcept
    {
        return vec2<T>(y, x);
    }
};

template<typename T>
struct vec3
{
    union
    {
        struct{T x, y, z;};
        struct{T r, g, b;};
        struct{T h, s, v;};
        T data[3];
    };

    constexpr vec3():x(0),y(0),z(0){}

    template<typename A, typename B, typename C>
    constexpr vec3(const A _1, const B _2, const C _3):x(_1),y(_2),z(_3){}

    template<typename A>
    constexpr vec3(const vec2<A> &_1):x(_1.x),y(_1.y),z(0){}

    template<typename A, typename B>
    constexpr vec3(const vec2<A> &_1, const B _2 = 0):x(_1.x),y(_1.y),z(_2){}

    template<typename A, typename B>
    constexpr vec3(const A _1, const vec2<B> &_2):x(_1),y(_2.x),z(_2.y){}

    template<typename S>
    constexpr vec3(const vec3<S> &a):x(a.x),y(a.y),z(a.z){}

    template<typename S>
    constexpr vec3(const S &a):x(a),y(a),z(a){}

    template <typename S>
    constexpr vec3<T> &operator+=(const vec3<S> &a) noexcept
    {
        x += static_cast<T>(a.x);
        y += static_cast<T>(a.y);
        z += static_cast<T>(a.z);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> &operator-=(const vec3<S> &a) noexcept
    {
        x -= static_cast<T>(a.x);
        y -= static_cast<T>(a.y);
        z -= static_cast<T>(a.z);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> &operator*=(const vec3<S> &a) noexcept
    {
        x *= static_cast<T>(a.x);
        y *= static_cast<T>(a.y);
        z *= static_cast<T>(a.z);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> &operator*=(const S &a) noexcept
    {
        x *= static_cast<T>(a);
        y *= static_cast<T>(a);
        z *= static_cast<T>(a);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> &operator/=(const vec3<S> &a) noexcept
    {
        x /= static_cast<T>(a.x);
        y /= static_cast<T>(a.y);
        z /= static_cast<T>(a.z);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> &operator/=(const S &a) noexcept
    {
        x /= static_cast<T>(a);
        y /= static_cast<T>(a);
        z /= static_cast<T>(a);
        return *this;
    }

    template <typename S>
    constexpr vec3<T> operator+(const vec3<S> &a) const noexcept
    {
        return vec3<T>(x+a.x, y+a.y, z+a.z);
    }

    template <typename S>
    constexpr vec3<T> operator-(const vec3<S> &a) const noexcept
    {
        return vec3<T>(x-static_cast<T>(a.x),
                       y-static_cast<T>(a.y),
                       z-static_cast<T>(a.z));
    }

    template <typename S>
    constexpr vec3<T> operator*(const S &a) const noexcept
    {
        return vec3<T>(x*static_cast<T>(a),
                       y*static_cast<T>(a),
                       z*static_cast<T>(a));
    }

    template <typename S>
    constexpr vec3<T> operator*(const vec3<S> &a) const noexcept
    {
        return vec3<T>(x*static_cast<T>(a.x),
                       y*static_cast<T>(a.y),
                       z*static_cast<T>(a.z));
    }

    template <typename S>
    constexpr vec3<T> operator/(const S &a) const noexcept
    {
        return vec3<T>(x/static_cast<T>(a),
                       y/static_cast<T>(a),
                       z/static_cast<T>(a));
    }

    template <typename S>
    constexpr vec3<T> operator/(const vec3<S> &a) const noexcept
    {
        return vec3<T>(x/static_cast<T>(a.x),
                       y/static_cast<T>(a.y),
                       z/static_cast<T>(a.z));
    }

    template <typename S>
    constexpr bool operator==(const vec3<S> &a) const noexcept
    {
        return x == static_cast<T>(a.x)
            && y == static_cast<T>(a.y)
            && z == static_cast<T>(a.z);
    }

    template <typename S>
    constexpr bool operator!=(const vec3<S> &a) const noexcept
    {
        return x != static_cast<T>(a.x)
            || y != static_cast<T>(a.y)
            || z != static_cast<T>(a.z);
    }

    template <typename S>
    constexpr bool operator>(const vec3<S> &a) const noexcept
    {
        return x*x+y*y+z*z > static_cast<T>(a.x*a.x+a.y*a.y+a.z*a.z);
    }

    template <typename S>
    constexpr bool operator>=(const vec3<S> &a) const noexcept
    {
        return x*x+y*y+z*z >= static_cast<T>(a.x*a.x+a.y*a.y+a.z*a.z);
    }

    template <typename S>
    constexpr bool operator<=(const vec3<S> &a) const noexcept
    {
        return !(operator>(a));
    }

    template <typename S>
    constexpr bool operator<(const vec3<S> &a) const noexcept
    {
        return !(operator>=(a));
    }

    constexpr const T &max()const noexcept{return ::max(x, ::max(y, z));}
    constexpr const T &min()const noexcept{return ::min(x, ::min(y, z));}
    constexpr T &max()noexcept{return ::max(x, ::max(y, z));}
    constexpr T &min()noexcept{return ::min(x, ::min(y, z));}

    constexpr T &operator[](uint a){return *((&x)+a);}

    template <typename S>
    constexpr T dot(const vec3<S> &a) const noexcept
    {
        return T(x*a.x+y*a.y+z*a.z);
    }

    template <typename S>
    constexpr vec3<T> cross(const vec3<S> &a) const noexcept
    {
        return vec3<T>(y*a.z-a.y*z, z*a.x-a.z*x, x*a.y-y*a.x);
    }

    constexpr vec2<T> xy() const noexcept
    {
        return vec2<T>(x, y);
    }

    constexpr vec2<T> yx() const noexcept
    {
        return vec2<T>(y, x);
    }

    constexpr vec2<T> xz() const noexcept
    {
        return vec2<T>(x, z);
    }

    constexpr vec2<T> yz() const noexcept
    {
        return vec2<T>(y, z);
    }

    constexpr vec2<T> zy() const noexcept
    {
        return vec2<T>(z, y);
    }

    constexpr vec2<T> zx() const noexcept
    {
        return vec2<T>(z, x);
    }

    constexpr vec3<T> yzx() const noexcept
    {
        return vec3<T>(y, z, x);
    }

    constexpr vec3<T> zyx() const noexcept
    {
        return vec3<T>(z, y, x);
    }
};

template<typename T>
struct vec4
{
    union
    {
        struct{T x, y, z, w;};
        struct{
            union{
            struct{T r, g, b;};
            struct{T h, s, v;};};
            T a;
        };
        struct{T left, top, right, bottom;};

        T data[4];
    };

    constexpr vec4():x(0),y(0),z(0),w(0){}

    template<typename S>
    constexpr vec4(const S &a):x(a),y(a),z(a),w(a){}

    template<typename A, typename B, typename C, typename D>
    constexpr vec4(const A &_1, const B &_2, const C &_3 = 0, const D &_4 = 0):x(_1),y(_2),z(_3),w(_4){}

    template<typename S>
    constexpr vec4(const vec2<S> &_1, const S &_2 = 0, const S &_3 = 0):x(_1.x),y(_1.y),z(_2),w(_3){}

    template<typename S>
    constexpr vec4(S _1, const vec2<S> &_2, const S &_3 = 0):x(_1),y(_2.x),z(_2.y),w(_3){}

    template<typename S>
    constexpr vec4(S _1, S _2, const vec2<S> &_3 = 0):x(_1),y(_2),z(_3.x),w(_3.y){}

    template<typename S>
    constexpr vec4(const vec2<S> &_1, const vec2<S> &_2):x(_1.x),y(_1.y),z(_2.x),w(_2.y){}

    template<typename S, typename Q = S>
    constexpr vec4(const vec3<S> &_1, Q _2 = 1):x(_1.x),y(_1.y),z(_1.z),w(_2){}

    template<typename S>
    constexpr vec4(S _1, const vec3<S> &_2):x(_1),y(_1.x),z(_1.y),w(_1.z){}

    template <typename S>
    constexpr vec4<T> &operator=(const vec4<S> &a) noexcept
    {
        x=a.x;
        y=a.y;
        z=a.z;
        w=a.w;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> &operator+=(const vec4<S> &a) noexcept
    {
        x += a.x;
        y += a.y;
        z += a.z;
        w += a.w;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> &operator-=(const vec4<S> &a) noexcept
    {
        x -= a.x;
        y -= a.y;
        z -= a.z;
        w -= a.w;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> &operator*=(const vec4<S> &a) noexcept
    {
        x *= a.x;
        y *= a.y;
        z *= a.z;
        w *= a.w;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> &operator/=(const vec4<S> &a) noexcept
    {
        x /= a.x;
        y /= a.y;
        z /= a.z;
        w /= a.w;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> &operator/=(const S &a) noexcept
    {
        x /= a;
        y /= a;
        z /= a;
        w /= a;
        return *this;
    }

    template <typename S>
    constexpr vec4<T> operator+(const vec4<S> &a) const noexcept
    {
        return vec4<T>(x+a.x, y+a.y, z+a.z, w+a.w);
    }

    template <typename S>
    constexpr vec4<T> operator-(const vec4<S> &a) const noexcept
    {
        return vec4<T>(x-a.x, y-a.y, z-a.z, w-a.w);
    }

    template <typename S>
    constexpr vec4<T> operator*(const S &a) const noexcept
    {
        return vec4<T>(x*a, y*a, z*a, w*a);
    }

    template <typename S>
    constexpr vec4<T> operator*(const vec4<S> &a) const noexcept
    {
        return vec4<T>(x*a.x, y*a.y, z*a.z, w*a.w);
    }

    template <typename S>
    constexpr vec4<T> operator/(const S &a) const noexcept
    {
        return vec4<T>(x/a, y/a, z/a, w/a);
    }

    template <typename S>
    constexpr vec4<T> operator/(const vec4<S> &a) const noexcept
    {
        return vec4<T>(x/a.x, y/a.y, z/a.z, w/a.w);
    }

    template <typename S>
    constexpr bool operator==(const vec4<S> &a) const noexcept
    {
        return (x==a.x && y==a.y && z==a.z && w==a.w);
    }

    template <typename S>
    constexpr bool operator!=(const vec4<S> &a) const noexcept
    {
        return !(operator==(a));
    }

    constexpr vec3<T> xyz() const noexcept
    {
        return vec3<T>(x, y, z);
    }
};

#define IM_VEC4_CLASS_EXTRA ImVec4(const vec4<float> &a){x=a.x;y=a.y;z=a.z;w=a.w;}


typedef vec2<char> vec2c;
typedef vec2<short> vec2s;
typedef vec2<int> vec2i;
typedef vec2<uchar> vec2b;
typedef vec2<ushort> vec2w;
typedef vec2<uint> vec2u;
typedef vec2<float> vec2f;
typedef vec2<double> vec2d;

typedef vec3<char> vec3c;
typedef vec3<int> vec3i;
typedef vec3<float> vec3f;

typedef vec3<uchar> vec3b;
typedef vec3<uint> vec3u;
typedef vec3<double> vec3d;

typedef vec4<char> vec4c;
typedef vec4<int> vec4i;
typedef vec4<float> vec4f;

typedef vec4<uchar> vec4b;
typedef vec4<uint> vec4u;
typedef vec4<double> vec4d;
