//
//  Lol Engine
//
//  Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
//
//  This library is free software. It comes without any warranty, to
//  the extent permitted by applicable law. You can redistribute it
//  and/or modify it under the terms of the Do What the Fuck You Want
//  to Public License, Version 2, as published by the WTFPL Task Force.
//  See http://www.wtfpl.net/ for more details.
//

#pragma once

//
// The vector classes
// ------------------
//

#include <lol/base/assert.h>
#include <lol/math/functions.h>
#include <lol/math/half.h>
#include <lol/math/real.h>
#include <lol/math/ops.h>

#include <ostream>
#include <type_traits>

namespace lol
{

#if !LOL_FEATURE_CXX11_CONSTEXPR
#   define constexpr /* */
#endif

#if !LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS
#   define _____ const
#else
#   define _____ /* */
#endif

/*
 * Magic vector swizzling
 *
 * These vectors are empty, but thanks to static_cast we can take their
 * address and access the vector of T's that they are union'ed with. We
 * use static_cast instead of reinterpret_cast because there is a stronger
 * guarantee (by the standard) that the address will stay the same across
 * casts.
 *
 * Using swizzled vectors as lvalues:
 * We need to implement an assignment operator _and_ override the default
 * assignment operator. We try to pass arguments by value so that we don't
 * have to care about overwriting ourselves (e.g. c.rgb = c.bgr).
 * However, Visual Studio 2013 doesn't support unrestricted unions, so
 * fuck it for now.
 */

template<typename T, int N, int SWIZZLE>
struct vec_t
    /* MUST have a different base than e.g. vec_t<T,2> otherwise the unions
     * in vec_t<T,2> with the same base will cause empty base optimisation
     * failures. */
  : public swizzle_ops::base<T, SWIZZLE>
{
    static int const count = N;
    typedef T element;
    typedef vec_t<T,N> type;

    /* Disable all default constructors and destructors; this object
     * is only intended to exist as part of a union. */
#if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS
    vec_t() = delete;
    vec_t(vec_t<T, N, SWIZZLE> const &) = delete;
    ~vec_t() = delete;

    /* Allow the assignment operator if unrestricted unions are supported. */
    inline vec_t<T, N, SWIZZLE>& operator =(vec_t<T, N> that)
    {
        for (int i = 0; i < N; ++i)
            (*this)[i] = that[i];
        return *this;
    }

    inline vec_t<T, N, SWIZZLE>& operator =(vec_t<T, N, SWIZZLE> const &that)
    {
        /* Pass by value in case this == &that */
        return *this = (vec_t<T,N>)that;
    }
#endif

    inline T& operator[](size_t n)
    {
        int const lut[] = { 1000, 100, 10, 1 };
        int const i = SWIZZLE / lut[n + 4 - N] % 10;
        return static_cast<T*>(static_cast<void*>(this))[i];
    }

    inline T const& operator[](size_t n) const
    {
        int const lut[] = { 1000, 100, 10, 1 };
        int const i = SWIZZLE / lut[n + 4 - N] % 10;
        return static_cast<T const*>(static_cast<void const *>(this))[i];
    }
};

/* The generic “vec_t” type, which is a fixed-size vector with no
 * swizzling. There's an override for N=2, N=3, N=4 that has swizzling. */
template<typename T, int N>
struct vec_t<T, N, FULL_SWIZZLE>
  : public componentwise_ops::base<T>
{
    static int const count = N;
    typedef T element;
    typedef vec_t<T,N> type;

    /* Default constructor, copy constructor, and destructor */
    inline constexpr vec_t() {}
    inline vec_t(vec_t<T,N> const &v)
    {
        for (int i = 0; i < N; ++i)
            m_data[i] = v[i];
    }
    inline ~vec_t() {}

    /* Explicit constructor that takes exactly N arguments thanks to SFINAE. */
    template<typename... ARGS>
#if LOL_FEATURE_CXX11_SFINAE_FOR_CTORS
    explicit inline vec_t(T const &X,
        typename std::enable_if<sizeof...(ARGS) + 2 == N, T>::type const &Y,
        ARGS... args)
#else
    explicit inline vec_t(T const &X, T const &Y, ARGS... args)
#endif
    {
        static_assert(sizeof...(ARGS) + 2 == N,
                      "wrong argument count in vec_t constructor");
        internal_init(m_data, X, Y, args...);
    }

    /* Various explicit constructors */
    explicit inline vec_t(T const &X)
    {
        for (auto &value : m_data)
            value = X;
    }

    /* Explicit constructor for type conversion */
    template<typename U>
    explicit inline vec_t(vec_t<U, N> const &v)
    {
        for (int i = 0; i < N; ++i)
            m_data[i] = T(v[i]);
    }

    /* Factory for base axis vectors, e.g. [1,0,0,…,0] */
    static inline vec_t<T, N> axis(int i)
    {
        ASSERT(i >= 0);
        ASSERT(i < N);
        vec_t<T, N> ret(T(0));
        ret[i] = T(1);
        return ret;
    }

    /* Constructor for initializer_list. We need these ugly
     * loops until C++ lets us initialize m_data directly. */
    inline vec_t(std::initializer_list<element> const &list)
    {
        auto l = list.begin();
        for (int i = 0; i < count && l != list.end(); ++i, ++l)
            m_data[i] = *l;
        for (int i = (int)list.size(); i < count; ++i)
            m_data[i] = T(0);
    }

    inline T& operator[](size_t n) { return m_data[n]; }
    inline T const& operator[](size_t n) const { return m_data[n]; }

    static const vec_t<T,N> zero;

private:
    template<typename... ARGS>
    static inline void internal_init(T *data, T const &x, ARGS... args)
    {
        *data++ = x;
        internal_init(data, args...);
    }

    static inline void internal_init(T *data)
    {
        UNUSED(data);
    }

    T m_data[count];
};

/*
 * Helper macros for vector type member functions
 */

#define LOL_COMMON_MEMBER_OPS(first) \
    inline T& operator[](size_t n) { return (&this->first)[n]; } \
    inline T const& operator[](size_t n) const { return (&this->first)[n]; } \
    \
    /* Visual Studio insists on having an assignment operator. */ \
    inline type & operator =(type const &that) \
    { \
        for (int i = 0; i < type::count; ++i) \
            (*this)[i] = that[i]; \
        return *this; \
    } \
    \
    void printf() const; \
    class String tostring() const;

/*
 * 2-element vectors
 */

template <typename T>
struct vec_t<T,2>
  : public swizzle_ops::base<T>
{
    static int const count = 2;
    typedef T element;
    typedef vec_t<T,2> type;

    /* Default constructor, copy constructor, and destructor */
    inline constexpr vec_t() : x(), y() {}
    inline constexpr vec_t(vec_t<T,2> const &v) : x(v.x), y(v.y) {}
    inline ~vec_t() { x.~T(); y.~T(); }

    /* Implicit constructor for swizzling */
    template<int SWIZZLE>
    inline constexpr vec_t(vec_t<T, 2, SWIZZLE> const &v)
      : x(v[0]), y(v[1]) {}

    /* Explicit constructor for type conversion */
    template<typename U, int SWIZZLE>
    explicit inline constexpr vec_t(vec_t<U, 2, SWIZZLE> const &v)
      : x(T(v[0])), y(T(v[1])) {}

    /* Constructor for initializer_list. We need these ugly
     * loops until C++ lets us initialize m_data directly. */
    inline vec_t(std::initializer_list<element> const &list)
    {
        auto l = list.begin();
        for (int i = 0; i < count && l != list.end(); ++i, ++l)
            m_data[i] = *l;
        for (int i = (int)list.size(); i < count; ++i)
            m_data[i] = T(0);
    }

    /* Various explicit constructors */
    explicit inline constexpr vec_t(T X, T Y)
      : x(X), y(Y) {}
    explicit inline constexpr vec_t(T X)
      : x(X), y(X) {}

    /* Factory for base axis vectors, e.g. [1,0,0,…,0] */
    static inline vec_t<T,2> axis(int i)
    {
        ASSERT(i >= 0);
        ASSERT(i < 2);
        return vec_t<T,2>(T(i == 0), T(i == 1));
    }

    LOL_COMMON_MEMBER_OPS(x)

    static const vec_t<T,2> zero;
    static const vec_t<T,2> axis_x;
    static const vec_t<T,2> axis_y;

    template<typename U>
    friend std::ostream &operator<<(std::ostream &stream, vec_t<U,2> const &v);

    union
    {
        struct { T x, y; };
        struct { T r, g; };
        struct { T s, t; };

#if !_DOXYGEN_SKIP_ME
        vec_t<T,2,9000> const xx, rr, ss;
        vec_t<T,2,9001> _____ xy, rg, st;
        vec_t<T,2,9010> _____ yx, gr, ts;
        vec_t<T,2,9011> const yy, gg, tt;

        vec_t<T,3,90000> const xxx, rrr, sss;
        vec_t<T,3,90001> const xxy, rrg, sst;
        vec_t<T,3,90010> const xyx, rgr, sts;
        vec_t<T,3,90011> const xyy, rgg, stt;
        vec_t<T,3,90100> const yxx, grr, tss;
        vec_t<T,3,90101> const yxy, grg, tst;
        vec_t<T,3,90110> const yyx, ggr, tts;
        vec_t<T,3,90111> const yyy, ggg, ttt;

        vec_t<T,4,900000> const xxxx, rrrr, ssss;
        vec_t<T,4,900001> const xxxy, rrrg, ssst;
        vec_t<T,4,900010> const xxyx, rrgr, ssts;
        vec_t<T,4,900011> const xxyy, rrgg, sstt;
        vec_t<T,4,900100> const xyxx, rgrr, stss;
        vec_t<T,4,900101> const xyxy, rgrg, stst;
        vec_t<T,4,900110> const xyyx, rggr, stts;
        vec_t<T,4,900111> const xyyy, rggg, sttt;
        vec_t<T,4,901000> const yxxx, grrr, tsss;
        vec_t<T,4,901001> const yxxy, grrg, tsst;
        vec_t<T,4,901010> const yxyx, grgr, tsts;
        vec_t<T,4,901011> const yxyy, grgg, tstt;
        vec_t<T,4,901100> const yyxx, ggrr, ttss;
        vec_t<T,4,901101> const yyxy, ggrg, ttst;
        vec_t<T,4,901110> const yyyx, gggr, ttts;
        vec_t<T,4,901111> const yyyy, gggg, tttt;
#endif

        T m_data[count];
    };
};

static_assert(sizeof(i8vec2) == 2, "sizeof(i8vec2) == 2");
static_assert(sizeof(i16vec2) == 4, "sizeof(i16vec2) == 4");
static_assert(sizeof(ivec2) == 8, "sizeof(ivec2) == 8");
static_assert(sizeof(i64vec2) == 16, "sizeof(i64vec2) == 16");

#if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS
static_assert(sizeof(f16vec2) == 4, "sizeof(f16vec2) == 4");
#endif
static_assert(sizeof(vec2) == 8, "sizeof(vec2) == 8");
static_assert(sizeof(dvec2) == 16, "sizeof(dvec2) == 16");

/*
 * 3-element vectors
 */

template <typename T>
struct vec_t<T,3>
  : public swizzle_ops::base<T>
{
    static int const count = 3;
    typedef T element;
    typedef vec_t<T,3> type;

    /* Default constructor, copy constructor, and destructor */
    inline constexpr vec_t() : x(), y(), z() {}
    inline constexpr vec_t(vec_t<T,3> const &v) : x(v.x), y(v.y), z(v.z) {}
    inline ~vec_t() { x.~T(); y.~T(); z.~T(); }

    /* Implicit constructor for swizzling */
    template<int SWIZZLE>
    inline constexpr vec_t(vec_t<T, 3, SWIZZLE> const &v)
      : x(v[0]), y(v[1]), z(v[2]) {}

    /* Explicit constructor for type conversion */
    template<typename U, int SWIZZLE>
    explicit inline constexpr vec_t(vec_t<U, 3, SWIZZLE> const &v)
      : x(T(v[0])), y(T(v[1])), z(T(v[2])) {}

    /* Constructor for initializer_list. We need these ugly
     * loops until C++ lets us initialize m_data directly. */
    inline vec_t(std::initializer_list<element> const &list)
    {
        auto l = list.begin();
        for (int i = 0; i < count && l != list.end(); ++i, ++l)
            m_data[i] = *l;
        for (int i = (int)list.size(); i < count; ++i)
            m_data[i] = T(0);
    }

    /* Various explicit constructors */
    explicit inline constexpr vec_t(T X)
      : x(X), y(X), z(X) {}
    explicit inline constexpr vec_t(T X, T Y, T Z)
      : x(X), y(Y), z(Z) {}
    explicit inline constexpr vec_t(vec_t<T,2> XY, T Z)
      : x(XY.x), y(XY.y), z(Z) {}
    explicit inline constexpr vec_t(T X, vec_t<T,2> YZ)
      : x(X), y(YZ.x), z(YZ.y) {}

    /* Factory for base axis vectors, e.g. [1,0,0,…,0] */
    static inline vec_t<T,3> axis(int i)
    {
        ASSERT(i >= 0);
        ASSERT(i < 3);
        return vec_t<T,3>(T(i == 0), T(i == 1), T(i == 2));
    }

    LOL_COMMON_MEMBER_OPS(x)

    static vec_t<T,3> toeuler_xyx(quat_t<T> const &q);
    static vec_t<T,3> toeuler_xzx(quat_t<T> const &q);
    static vec_t<T,3> toeuler_yxy(quat_t<T> const &q);
    static vec_t<T,3> toeuler_yzy(quat_t<T> const &q);
    static vec_t<T,3> toeuler_zxz(quat_t<T> const &q);
    static vec_t<T,3> toeuler_zyz(quat_t<T> const &q);

    static vec_t<T,3> toeuler_xyz(quat_t<T> const &q);
    static vec_t<T,3> toeuler_xzy(quat_t<T> const &q);
    static vec_t<T,3> toeuler_yxz(quat_t<T> const &q);
    static vec_t<T,3> toeuler_yzx(quat_t<T> const &q);
    static vec_t<T,3> toeuler_zxy(quat_t<T> const &q);
    static vec_t<T,3> toeuler_zyx(quat_t<T> const &q);

    /* Return the cross product (vector product) of “a” and “b” */ \
    friend inline type cross(type const &a, type const &b)
    {
        return type(a.y * b.z - a.z * b.y,
                    a.z * b.x - a.x * b.z,
                    a.x * b.y - a.y * b.x);
    }

    /* Return a vector that is orthogonal to “a” */
    friend inline type orthogonal(type const &a)
    {
        return lol::abs(a.x) > lol::abs(a.z)
             ? type(-a.y, a.x, T(0))
             : type(T(0), -a.z, a.y);
    }

    /* Return a vector that is orthonormal to “a” */
    friend inline type orthonormal(type const &a)
    {
        return normalize(orthogonal(a));
    }

    static const vec_t<T,3> zero;
    static const vec_t<T,3> axis_x;
    static const vec_t<T,3> axis_y;
    static const vec_t<T,3> axis_z;

    template<typename U>
    friend std::ostream &operator<<(std::ostream &stream, vec_t<U,3> const &v);

    union
    {
        struct { T x, y, z; };
        struct { T r, g, b; };
        struct { T s, t, p; };

#if !_DOXYGEN_SKIP_ME
        vec_t<T,2,9000> const xx, rr, ss;
        vec_t<T,2,9001> _____ xy, rg, st;
        vec_t<T,2,9002> _____ xz, rb, sp;
        vec_t<T,2,9010> _____ yx, gr, ts;
        vec_t<T,2,9011> const yy, gg, tt;
        vec_t<T,2,9012> _____ yz, gb, tp;
        vec_t<T,2,9020> _____ zx, br, ps;
        vec_t<T,2,9021> _____ zy, bg, pt;
        vec_t<T,2,9022> const zz, bb, pp;

        vec_t<T,3,90000> const xxx, rrr, sss;
        vec_t<T,3,90001> const xxy, rrg, sst;
        vec_t<T,3,90002> const xxz, rrb, ssp;
        vec_t<T,3,90010> const xyx, rgr, sts;
        vec_t<T,3,90011> const xyy, rgg, stt;
        vec_t<T,3,90012> _____ xyz, rgb, stp;
        vec_t<T,3,90020> const xzx, rbr, sps;
        vec_t<T,3,90021> _____ xzy, rbg, spt;
        vec_t<T,3,90022> const xzz, rbb, spp;
        vec_t<T,3,90100> const yxx, grr, tss;
        vec_t<T,3,90101> const yxy, grg, tst;
        vec_t<T,3,90102> _____ yxz, grb, tsp;
        vec_t<T,3,90110> const yyx, ggr, tts;
        vec_t<T,3,90111> const yyy, ggg, ttt;
        vec_t<T,3,90112> const yyz, ggb, ttp;
        vec_t<T,3,90120> _____ yzx, gbr, tps;
        vec_t<T,3,90121> const yzy, gbg, tpt;
        vec_t<T,3,90122> const yzz, gbb, tpp;
        vec_t<T,3,90200> const zxx, brr, pss;
        vec_t<T,3,90201> _____ zxy, brg, pst;
        vec_t<T,3,90202> const zxz, brb, psp;
        vec_t<T,3,90210> _____ zyx, bgr, pts;
        vec_t<T,3,90211> const zyy, bgg, ptt;
        vec_t<T,3,90212> const zyz, bgb, ptp;
        vec_t<T,3,90220> const zzx, bbr, pps;
        vec_t<T,3,90221> const zzy, bbg, ppt;
        vec_t<T,3,90222> const zzz, bbb, ppp;

        vec_t<T,4,900000> const xxxx, rrrr, ssss;
        vec_t<T,4,900001> const xxxy, rrrg, ssst;
        vec_t<T,4,900002> const xxxz, rrrb, sssp;
        vec_t<T,4,900010> const xxyx, rrgr, ssts;
        vec_t<T,4,900011> const xxyy, rrgg, sstt;
        vec_t<T,4,900012> const xxyz, rrgb, sstp;
        vec_t<T,4,900020> const xxzx, rrbr, ssps;
        vec_t<T,4,900021> const xxzy, rrbg, sspt;
        vec_t<T,4,900022> const xxzz, rrbb, sspp;
        vec_t<T,4,900100> const xyxx, rgrr, stss;
        vec_t<T,4,900101> const xyxy, rgrg, stst;
        vec_t<T,4,900102> const xyxz, rgrb, stsp;
        vec_t<T,4,900110> const xyyx, rggr, stts;
        vec_t<T,4,900111> const xyyy, rggg, sttt;
        vec_t<T,4,900112> const xyyz, rggb, sttp;
        vec_t<T,4,900120> const xyzx, rgbr, stps;
        vec_t<T,4,900121> const xyzy, rgbg, stpt;
        vec_t<T,4,900122> const xyzz, rgbb, stpp;
        vec_t<T,4,900200> const xzxx, rbrr, spss;
        vec_t<T,4,900201> const xzxy, rbrg, spst;
        vec_t<T,4,900202> const xzxz, rbrb, spsp;
        vec_t<T,4,900210> const xzyx, rbgr, spts;
        vec_t<T,4,900211> const xzyy, rbgg, sptt;
        vec_t<T,4,900212> const xzyz, rbgb, sptp;
        vec_t<T,4,900220> const xzzx, rbbr, spps;
        vec_t<T,4,900221> const xzzy, rbbg, sppt;
        vec_t<T,4,900222> const xzzz, rbbb, sppp;
        vec_t<T,4,901000> const yxxx, grrr, tsss;
        vec_t<T,4,901001> const yxxy, grrg, tsst;
        vec_t<T,4,901002> const yxxz, grrb, tssp;
        vec_t<T,4,901010> const yxyx, grgr, tsts;
        vec_t<T,4,901011> const yxyy, grgg, tstt;
        vec_t<T,4,901012> const yxyz, grgb, tstp;
        vec_t<T,4,901020> const yxzx, grbr, tsps;
        vec_t<T,4,901021> const yxzy, grbg, tspt;
        vec_t<T,4,901022> const yxzz, grbb, tspp;
        vec_t<T,4,901100> const yyxx, ggrr, ttss;
        vec_t<T,4,901101> const yyxy, ggrg, ttst;
        vec_t<T,4,901102> const yyxz, ggrb, ttsp;
        vec_t<T,4,901110> const yyyx, gggr, ttts;
        vec_t<T,4,901111> const yyyy, gggg, tttt;
        vec_t<T,4,901112> const yyyz, gggb, tttp;
        vec_t<T,4,901120> const yyzx, ggbr, ttps;
        vec_t<T,4,901121> const yyzy, ggbg, ttpt;
        vec_t<T,4,901122> const yyzz, ggbb, ttpp;
        vec_t<T,4,901200> const yzxx, gbrr, tpss;
        vec_t<T,4,901201> const yzxy, gbrg, tpst;
        vec_t<T,4,901202> const yzxz, gbrb, tpsp;
        vec_t<T,4,901210> const yzyx, gbgr, tpts;
        vec_t<T,4,901211> const yzyy, gbgg, tptt;
        vec_t<T,4,901212> const yzyz, gbgb, tptp;
        vec_t<T,4,901220> const yzzx, gbbr, tpps;
        vec_t<T,4,901221> const yzzy, gbbg, tppt;
        vec_t<T,4,901222> const yzzz, gbbb, tppp;
        vec_t<T,4,902000> const zxxx, brrr, psss;
        vec_t<T,4,902001> const zxxy, brrg, psst;
        vec_t<T,4,902002> const zxxz, brrb, pssp;
        vec_t<T,4,902010> const zxyx, brgr, psts;
        vec_t<T,4,902011> const zxyy, brgg, pstt;
        vec_t<T,4,902012> const zxyz, brgb, pstp;
        vec_t<T,4,902020> const zxzx, brbr, psps;
        vec_t<T,4,902021> const zxzy, brbg, pspt;
        vec_t<T,4,902022> const zxzz, brbb, pspp;
        vec_t<T,4,902100> const zyxx, bgrr, ptss;
        vec_t<T,4,902101> const zyxy, bgrg, ptst;
        vec_t<T,4,902102> const zyxz, bgrb, ptsp;
        vec_t<T,4,902110> const zyyx, bggr, ptts;
        vec_t<T,4,902111> const zyyy, bggg, pttt;
        vec_t<T,4,902112> const zyyz, bggb, pttp;
        vec_t<T,4,902120> const zyzx, bgbr, ptps;
        vec_t<T,4,902121> const zyzy, bgbg, ptpt;
        vec_t<T,4,902122> const zyzz, bgbb, ptpp;
        vec_t<T,4,902200> const zzxx, bbrr, ppss;
        vec_t<T,4,902201> const zzxy, bbrg, ppst;
        vec_t<T,4,902202> const zzxz, bbrb, ppsp;
        vec_t<T,4,902210> const zzyx, bbgr, ppts;
        vec_t<T,4,902211> const zzyy, bbgg, pptt;
        vec_t<T,4,902212> const zzyz, bbgb, pptp;
        vec_t<T,4,902220> const zzzx, bbbr, ppps;
        vec_t<T,4,902221> const zzzy, bbbg, pppt;
        vec_t<T,4,902222> const zzzz, bbbb, pppp;
#endif

        T m_data[count];
    };
};

static_assert(sizeof(i8vec3) == 3, "sizeof(i8vec3) == 3");
static_assert(sizeof(i16vec3) == 6, "sizeof(i16vec3) == 6");
static_assert(sizeof(ivec3) == 12, "sizeof(ivec3) == 12");
static_assert(sizeof(i64vec3) == 24, "sizeof(i64vec3) == 24");

#if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS
static_assert(sizeof(f16vec3) == 6, "sizeof(f16vec3) == 6");
#endif
static_assert(sizeof(vec3) == 12, "sizeof(vec3) == 12");
static_assert(sizeof(dvec3) == 24, "sizeof(dvec3) == 24");

/*
 * 4-element vectors
 */

template <typename T>
struct vec_t<T,4>
  : public swizzle_ops::base<T>
{
    static int const count = 4;
    typedef T element;
    typedef vec_t<T,4> type;

    /* Default constructor, copy constructor, and destructor */
    inline constexpr vec_t() : x(), y(), z(), w() {}
    inline constexpr vec_t(vec_t<T,4> const &v)
      : x(v.x), y(v.y), z(v.z), w(v.w) {}
    inline ~vec_t() { x.~T(); y.~T(); z.~T(); w.~T(); }

    /* Implicit constructor for swizzling */
    template<int SWIZZLE>
    inline constexpr vec_t(vec_t<T, 4, SWIZZLE> const &v)
      : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}

    /* Explicit constructor for type conversion */
    template<typename U, int SWIZZLE>
    explicit inline constexpr vec_t(vec_t<U, 4, SWIZZLE> const &v)
      : x(T(v[0])), y(T(v[1])), z(T(v[2])), w(T(v[3])) {}

    /* Constructor for initializer_list. We need these ugly
     * loops until C++ lets us initialize m_data directly. */
    inline vec_t(std::initializer_list<element> const &list)
    {
        auto l = list.begin();
        for (int i = 0; i < count && l != list.end(); ++i, ++l)
            m_data[i] = *l;
        for (int i = (int)list.size(); i < count; ++i)
            m_data[i] = T(0);
    }

    /* Various explicit constructors */
    explicit inline constexpr vec_t(T X)
      : x(X), y(X), z(X), w(X) {}
    explicit inline constexpr vec_t(T X, T Y, T Z, T W)
      : x(X), y(Y), z(Z), w(W) {}
    explicit inline constexpr vec_t(vec_t<T,2> XY, T Z, T W)
      : x(XY.x), y(XY.y), z(Z), w(W) {}
    explicit inline constexpr vec_t(T X, vec_t<T,2> YZ, T W)
      : x(X), y(YZ.x), z(YZ.y), w(W) {}
    explicit inline constexpr vec_t(T X, T Y, vec_t<T,2> ZW)
      : x(X), y(Y), z(ZW.x), w(ZW.y) {}
    explicit inline constexpr vec_t(vec_t<T,2> XY, vec_t<T,2> ZW)
      : x(XY.x), y(XY.y), z(ZW.x), w(ZW.y) {}
    explicit inline constexpr vec_t(vec_t<T,3> XYZ, T W)
      : x(XYZ.x), y(XYZ.y), z(XYZ.z), w(W) {}
    explicit inline constexpr vec_t(T X, vec_t<T,3> YZW)
      : x(X), y(YZW.x), z(YZW.y), w(YZW.z) {}

    /* Factory for base axis vectors, e.g. [1,0,0,…,0] */
    static inline vec_t<T,4> axis(int i)
    {
        ASSERT(i >= 0);
        ASSERT(i < 4);
        return vec_t<T,4>(T(i == 0), T(i == 1), T(i == 2), T(i == 3));
    }

    LOL_COMMON_MEMBER_OPS(x)

    static const vec_t<T,4> zero;
    static const vec_t<T,4> axis_x;
    static const vec_t<T,4> axis_y;
    static const vec_t<T,4> axis_z;
    static const vec_t<T,4> axis_w;

    template<typename U>
    friend std::ostream &operator<<(std::ostream &stream, vec_t<U,4> const &v);

    union
    {
        struct { T x, y, z, w; };
        struct { T r, g, b, a; };
        struct { T s, t, p, q; };

#if !_DOXYGEN_SKIP_ME
        vec_t<T,2,9000> const xx, rr, ss;
        vec_t<T,2,9001> _____ xy, rg, st;
        vec_t<T,2,9002> _____ xz, rb, sp;
        vec_t<T,2,9003> _____ xw, ra, sq;
        vec_t<T,2,9010> _____ yx, gr, ts;
        vec_t<T,2,9011> const yy, gg, tt;
        vec_t<T,2,9012> _____ yz, gb, tp;
        vec_t<T,2,9013> _____ yw, ga, tq;
        vec_t<T,2,9020> _____ zx, br, ps;
        vec_t<T,2,9021> _____ zy, bg, pt;
        vec_t<T,2,9022> const zz, bb, pp;
        vec_t<T,2,9023> _____ zw, ba, pq;
        vec_t<T,2,9030> _____ wx, ar, qs;
        vec_t<T,2,9031> _____ wy, ag, qt;
        vec_t<T,2,9032> _____ wz, ab, qp;
        vec_t<T,2,9033> const ww, aa, qq;

        vec_t<T,3,90000> const xxx, rrr, sss;
        vec_t<T,3,90001> const xxy, rrg, sst;
        vec_t<T,3,90002> const xxz, rrb, ssp;
        vec_t<T,3,90003> const xxw, rra, ssq;
        vec_t<T,3,90010> const xyx, rgr, sts;
        vec_t<T,3,90011> const xyy, rgg, stt;
        vec_t<T,3,90012> _____ xyz, rgb, stp;
        vec_t<T,3,90013> _____ xyw, rga, stq;
        vec_t<T,3,90020> const xzx, rbr, sps;
        vec_t<T,3,90021> _____ xzy, rbg, spt;
        vec_t<T,3,90022> const xzz, rbb, spp;
        vec_t<T,3,90023> _____ xzw, rba, spq;
        vec_t<T,3,90030> const xwx, rar, sqs;
        vec_t<T,3,90031> _____ xwy, rag, sqt;
        vec_t<T,3,90032> _____ xwz, rab, sqp;
        vec_t<T,3,90033> const xww, raa, sqq;
        vec_t<T,3,90100> const yxx, grr, tss;
        vec_t<T,3,90101> const yxy, grg, tst;
        vec_t<T,3,90102> _____ yxz, grb, tsp;
        vec_t<T,3,90103> _____ yxw, gra, tsq;
        vec_t<T,3,90110> const yyx, ggr, tts;
        vec_t<T,3,90111> const yyy, ggg, ttt;
        vec_t<T,3,90112> const yyz, ggb, ttp;
        vec_t<T,3,90113> const yyw, gga, ttq;
        vec_t<T,3,90120> _____ yzx, gbr, tps;
        vec_t<T,3,90121> const yzy, gbg, tpt;
        vec_t<T,3,90122> const yzz, gbb, tpp;
        vec_t<T,3,90123> _____ yzw, gba, tpq;
        vec_t<T,3,90130> _____ ywx, gar, tqs;
        vec_t<T,3,90131> const ywy, gag, tqt;
        vec_t<T,3,90132> _____ ywz, gab, tqp;
        vec_t<T,3,90133> const yww, gaa, tqq;
        vec_t<T,3,90200> const zxx, brr, pss;
        vec_t<T,3,90201> _____ zxy, brg, pst;
        vec_t<T,3,90202> const zxz, brb, psp;
        vec_t<T,3,90203> _____ zxw, bra, psq;
        vec_t<T,3,90210> _____ zyx, bgr, pts;
        vec_t<T,3,90211> const zyy, bgg, ptt;
        vec_t<T,3,90212> const zyz, bgb, ptp;
        vec_t<T,3,90213> _____ zyw, bga, ptq;
        vec_t<T,3,90220> const zzx, bbr, pps;
        vec_t<T,3,90221> const zzy, bbg, ppt;
        vec_t<T,3,90222> const zzz, bbb, ppp;
        vec_t<T,3,90223> const zzw, bba, ppq;
        vec_t<T,3,90230> _____ zwx, bar, pqs;
        vec_t<T,3,90231> _____ zwy, bag, pqt;
        vec_t<T,3,90232> const zwz, bab, pqp;
        vec_t<T,3,90233> const zww, baa, pqq;
        vec_t<T,3,90300> const wxx, arr, qss;
        vec_t<T,3,90301> _____ wxy, arg, qst;
        vec_t<T,3,90302> _____ wxz, arb, qsp;
        vec_t<T,3,90303> const wxw, ara, qsq;
        vec_t<T,3,90310> _____ wyx, agr, qts;
        vec_t<T,3,90311> const wyy, agg, qtt;
        vec_t<T,3,90312> _____ wyz, agb, qtp;
        vec_t<T,3,90313> const wyw, aga, qtq;
        vec_t<T,3,90320> _____ wzx, abr, qps;
        vec_t<T,3,90321> _____ wzy, abg, qpt;
        vec_t<T,3,90322> const wzz, abb, qpp;
        vec_t<T,3,90323> const wzw, aba, qpq;
        vec_t<T,3,90330> const wwx, aar, qqs;
        vec_t<T,3,90331> const wwy, aag, qqt;
        vec_t<T,3,90332> const wwz, aab, qqp;
        vec_t<T,3,90333> const www, aaa, qqq;

        vec_t<T,4,900000> const xxxx, rrrr, ssss;
        vec_t<T,4,900001> const xxxy, rrrg, ssst;
        vec_t<T,4,900002> const xxxz, rrrb, sssp;
        vec_t<T,4,900003> const xxxw, rrra, sssq;
        vec_t<T,4,900010> const xxyx, rrgr, ssts;
        vec_t<T,4,900011> const xxyy, rrgg, sstt;
        vec_t<T,4,900012> const xxyz, rrgb, sstp;
        vec_t<T,4,900013> const xxyw, rrga, sstq;
        vec_t<T,4,900020> const xxzx, rrbr, ssps;
        vec_t<T,4,900021> const xxzy, rrbg, sspt;
        vec_t<T,4,900022> const xxzz, rrbb, sspp;
        vec_t<T,4,900023> const xxzw, rrba, sspq;
        vec_t<T,4,900030> const xxwx, rrar, ssqs;
        vec_t<T,4,900031> const xxwy, rrag, ssqt;
        vec_t<T,4,900032> const xxwz, rrab, ssqp;
        vec_t<T,4,900033> const xxww, rraa, ssqq;
        vec_t<T,4,900100> const xyxx, rgrr, stss;
        vec_t<T,4,900101> const xyxy, rgrg, stst;
        vec_t<T,4,900102> const xyxz, rgrb, stsp;
        vec_t<T,4,900103> const xyxw, rgra, stsq;
        vec_t<T,4,900110> const xyyx, rggr, stts;
        vec_t<T,4,900111> const xyyy, rggg, sttt;
        vec_t<T,4,900112> const xyyz, rggb, sttp;
        vec_t<T,4,900113> const xyyw, rgga, sttq;
        vec_t<T,4,900120> const xyzx, rgbr, stps;
        vec_t<T,4,900121> const xyzy, rgbg, stpt;
        vec_t<T,4,900122> const xyzz, rgbb, stpp;
        vec_t<T,4,900123> _____ xyzw, rgba, stpq;
        vec_t<T,4,900130> const xywx, rgar, stqs;
        vec_t<T,4,900131> const xywy, rgag, stqt;
        vec_t<T,4,900132> _____ xywz, rgab, stqp;
        vec_t<T,4,900133> const xyww, rgaa, stqq;
        vec_t<T,4,900200> const xzxx, rbrr, spss;
        vec_t<T,4,900201> const xzxy, rbrg, spst;
        vec_t<T,4,900202> const xzxz, rbrb, spsp;
        vec_t<T,4,900203> const xzxw, rbra, spsq;
        vec_t<T,4,900210> const xzyx, rbgr, spts;
        vec_t<T,4,900211> const xzyy, rbgg, sptt;
        vec_t<T,4,900212> const xzyz, rbgb, sptp;
        vec_t<T,4,900213> _____ xzyw, rbga, sptq;
        vec_t<T,4,900220> const xzzx, rbbr, spps;
        vec_t<T,4,900221> const xzzy, rbbg, sppt;
        vec_t<T,4,900222> const xzzz, rbbb, sppp;
        vec_t<T,4,900223> const xzzw, rbba, sppq;
        vec_t<T,4,900230> const xzwx, rbar, spqs;
        vec_t<T,4,900231> _____ xzwy, rbag, spqt;
        vec_t<T,4,900232> const xzwz, rbab, spqp;
        vec_t<T,4,900233> const xzww, rbaa, spqq;
        vec_t<T,4,900300> const xwxx, rarr, sqss;
        vec_t<T,4,900301> const xwxy, rarg, sqst;
        vec_t<T,4,900302> const xwxz, rarb, sqsp;
        vec_t<T,4,900303> const xwxw, rara, sqsq;
        vec_t<T,4,900310> const xwyx, ragr, sqts;
        vec_t<T,4,900311> const xwyy, ragg, sqtt;
        vec_t<T,4,900312> _____ xwyz, ragb, sqtp;
        vec_t<T,4,900313> const xwyw, raga, sqtq;
        vec_t<T,4,900320> const xwzx, rabr, sqps;
        vec_t<T,4,900321> _____ xwzy, rabg, sqpt;
        vec_t<T,4,900322> const xwzz, rabb, sqpp;
        vec_t<T,4,900323> const xwzw, raba, sqpq;
        vec_t<T,4,900330> const xwwx, raar, sqqs;
        vec_t<T,4,900331> const xwwy, raag, sqqt;
        vec_t<T,4,900332> const xwwz, raab, sqqp;
        vec_t<T,4,900333> const xwww, raaa, sqqq;
        vec_t<T,4,901000> const yxxx, grrr, tsss;
        vec_t<T,4,901001> const yxxy, grrg, tsst;
        vec_t<T,4,901002> const yxxz, grrb, tssp;
        vec_t<T,4,901003> const yxxw, grra, tssq;
        vec_t<T,4,901010> const yxyx, grgr, tsts;
        vec_t<T,4,901011> const yxyy, grgg, tstt;
        vec_t<T,4,901012> const yxyz, grgb, tstp;
        vec_t<T,4,901013> const yxyw, grga, tstq;
        vec_t<T,4,901020> const yxzx, grbr, tsps;
        vec_t<T,4,901021> const yxzy, grbg, tspt;
        vec_t<T,4,901022> const yxzz, grbb, tspp;
        vec_t<T,4,901023> _____ yxzw, grba, tspq;
        vec_t<T,4,901030> const yxwx, grar, tsqs;
        vec_t<T,4,901031> const yxwy, grag, tsqt;
        vec_t<T,4,901032> _____ yxwz, grab, tsqp;
        vec_t<T,4,901033> const yxww, graa, tsqq;
        vec_t<T,4,901100> const yyxx, ggrr, ttss;
        vec_t<T,4,901101> const yyxy, ggrg, ttst;
        vec_t<T,4,901102> const yyxz, ggrb, ttsp;
        vec_t<T,4,901103> const yyxw, ggra, ttsq;
        vec_t<T,4,901110> const yyyx, gggr, ttts;
        vec_t<T,4,901111> const yyyy, gggg, tttt;
        vec_t<T,4,901112> const yyyz, gggb, tttp;
        vec_t<T,4,901113> const yyyw, ggga, tttq;
        vec_t<T,4,901120> const yyzx, ggbr, ttps;
        vec_t<T,4,901121> const yyzy, ggbg, ttpt;
        vec_t<T,4,901122> const yyzz, ggbb, ttpp;
        vec_t<T,4,901123> const yyzw, ggba, ttpq;
        vec_t<T,4,901130> const yywx, ggar, ttqs;
        vec_t<T,4,901131> const yywy, ggag, ttqt;
        vec_t<T,4,901132> const yywz, ggab, ttqp;
        vec_t<T,4,901133> const yyww, ggaa, ttqq;
        vec_t<T,4,901200> const yzxx, gbrr, tpss;
        vec_t<T,4,901201> const yzxy, gbrg, tpst;
        vec_t<T,4,901202> const yzxz, gbrb, tpsp;
        vec_t<T,4,901203> _____ yzxw, gbra, tpsq;
        vec_t<T,4,901210> const yzyx, gbgr, tpts;
        vec_t<T,4,901211> const yzyy, gbgg, tptt;
        vec_t<T,4,901212> const yzyz, gbgb, tptp;
        vec_t<T,4,901213> const yzyw, gbga, tptq;
        vec_t<T,4,901220> const yzzx, gbbr, tpps;
        vec_t<T,4,901221> const yzzy, gbbg, tppt;
        vec_t<T,4,901222> const yzzz, gbbb, tppp;
        vec_t<T,4,901223> const yzzw, gbba, tppq;
        vec_t<T,4,901230> _____ yzwx, gbar, tpqs;
        vec_t<T,4,901231> const yzwy, gbag, tpqt;
        vec_t<T,4,901232> const yzwz, gbab, tpqp;
        vec_t<T,4,901233> const yzww, gbaa, tpqq;
        vec_t<T,4,901300> const ywxx, garr, tqss;
        vec_t<T,4,901301> const ywxy, garg, tqst;
        vec_t<T,4,901302> _____ ywxz, garb, tqsp;
        vec_t<T,4,901303> const ywxw, gara, tqsq;
        vec_t<T,4,901310> const ywyx, gagr, tqts;
        vec_t<T,4,901311> const ywyy, gagg, tqtt;
        vec_t<T,4,901312> const ywyz, gagb, tqtp;
        vec_t<T,4,901313> const ywyw, gaga, tqtq;
        vec_t<T,4,901320> _____ ywzx, gabr, tqps;
        vec_t<T,4,901321> const ywzy, gabg, tqpt;
        vec_t<T,4,901322> const ywzz, gabb, tqpp;
        vec_t<T,4,901323> const ywzw, gaba, tqpq;
        vec_t<T,4,901330> const ywwx, gaar, tqqs;
        vec_t<T,4,901331> const ywwy, gaag, tqqt;
        vec_t<T,4,901332> const ywwz, gaab, tqqp;
        vec_t<T,4,901333> const ywww, gaaa, tqqq;
        vec_t<T,4,902000> const zxxx, brrr, psss;
        vec_t<T,4,902001> const zxxy, brrg, psst;
        vec_t<T,4,902002> const zxxz, brrb, pssp;
        vec_t<T,4,902003> const zxxw, brra, pssq;
        vec_t<T,4,902010> const zxyx, brgr, psts;
        vec_t<T,4,902011> const zxyy, brgg, pstt;
        vec_t<T,4,902012> const zxyz, brgb, pstp;
        vec_t<T,4,902013> _____ zxyw, brga, pstq;
        vec_t<T,4,902020> const zxzx, brbr, psps;
        vec_t<T,4,902021> const zxzy, brbg, pspt;
        vec_t<T,4,902022> const zxzz, brbb, pspp;
        vec_t<T,4,902023> const zxzw, brba, pspq;
        vec_t<T,4,902030> const zxwx, brar, psqs;
        vec_t<T,4,902031> _____ zxwy, brag, psqt;
        vec_t<T,4,902032> const zxwz, brab, psqp;
        vec_t<T,4,902033> const zxww, braa, psqq;
        vec_t<T,4,902100> const zyxx, bgrr, ptss;
        vec_t<T,4,902101> const zyxy, bgrg, ptst;
        vec_t<T,4,902102> const zyxz, bgrb, ptsp;
        vec_t<T,4,902103> _____ zyxw, bgra, ptsq;
        vec_t<T,4,902110> const zyyx, bggr, ptts;
        vec_t<T,4,902111> const zyyy, bggg, pttt;
        vec_t<T,4,902112> const zyyz, bggb, pttp;
        vec_t<T,4,902113> const zyyw, bgga, pttq;
        vec_t<T,4,902120> const zyzx, bgbr, ptps;
        vec_t<T,4,902121> const zyzy, bgbg, ptpt;
        vec_t<T,4,902122> const zyzz, bgbb, ptpp;
        vec_t<T,4,902123> const zyzw, bgba, ptpq;
        vec_t<T,4,902130> _____ zywx, bgar, ptqs;
        vec_t<T,4,902131> const zywy, bgag, ptqt;
        vec_t<T,4,902132> const zywz, bgab, ptqp;
        vec_t<T,4,902133> const zyww, bgaa, ptqq;
        vec_t<T,4,902200> const zzxx, bbrr, ppss;
        vec_t<T,4,902201> const zzxy, bbrg, ppst;
        vec_t<T,4,902202> const zzxz, bbrb, ppsp;
        vec_t<T,4,902203> const zzxw, bbra, ppsq;
        vec_t<T,4,902210> const zzyx, bbgr, ppts;
        vec_t<T,4,902211> const zzyy, bbgg, pptt;
        vec_t<T,4,902212> const zzyz, bbgb, pptp;
        vec_t<T,4,902213> const zzyw, bbga, pptq;
        vec_t<T,4,902220> const zzzx, bbbr, ppps;
        vec_t<T,4,902221> const zzzy, bbbg, pppt;
        vec_t<T,4,902222> const zzzz, bbbb, pppp;
        vec_t<T,4,902223> const zzzw, bbba, pppq;
        vec_t<T,4,902230> const zzwx, bbar, ppqs;
        vec_t<T,4,902231> const zzwy, bbag, ppqt;
        vec_t<T,4,902232> const zzwz, bbab, ppqp;
        vec_t<T,4,902233> const zzww, bbaa, ppqq;
        vec_t<T,4,902300> const zwxx, barr, pqss;
        vec_t<T,4,902301> _____ zwxy, barg, pqst;
        vec_t<T,4,902302> const zwxz, barb, pqsp;
        vec_t<T,4,902303> const zwxw, bara, pqsq;
        vec_t<T,4,902310> _____ zwyx, bagr, pqts;
        vec_t<T,4,902311> const zwyy, bagg, pqtt;
        vec_t<T,4,902312> const zwyz, bagb, pqtp;
        vec_t<T,4,902313> const zwyw, baga, pqtq;
        vec_t<T,4,902320> const zwzx, babr, pqps;
        vec_t<T,4,902321> const zwzy, babg, pqpt;
        vec_t<T,4,902322> const zwzz, babb, pqpp;
        vec_t<T,4,902323> const zwzw, baba, pqpq;
        vec_t<T,4,902330> const zwwx, baar, pqqs;
        vec_t<T,4,902331> const zwwy, baag, pqqt;
        vec_t<T,4,902332> const zwwz, baab, pqqp;
        vec_t<T,4,902333> const zwww, baaa, pqqq;
        vec_t<T,4,903000> const wxxx, arrr, qsss;
        vec_t<T,4,903001> const wxxy, arrg, qsst;
        vec_t<T,4,903002> const wxxz, arrb, qssp;
        vec_t<T,4,903003> const wxxw, arra, qssq;
        vec_t<T,4,903010> const wxyx, argr, qsts;
        vec_t<T,4,903011> const wxyy, argg, qstt;
        vec_t<T,4,903012> _____ wxyz, argb, qstp;
        vec_t<T,4,903013> const wxyw, arga, qstq;
        vec_t<T,4,903020> const wxzx, arbr, qsps;
        vec_t<T,4,903021> _____ wxzy, arbg, qspt;
        vec_t<T,4,903022> const wxzz, arbb, qspp;
        vec_t<T,4,903023> const wxzw, arba, qspq;
        vec_t<T,4,903030> const wxwx, arar, qsqs;
        vec_t<T,4,903031> const wxwy, arag, qsqt;
        vec_t<T,4,903032> const wxwz, arab, qsqp;
        vec_t<T,4,903033> const wxww, araa, qsqq;
        vec_t<T,4,903100> const wyxx, agrr, qtss;
        vec_t<T,4,903101> const wyxy, agrg, qtst;
        vec_t<T,4,903102> _____ wyxz, agrb, qtsp;
        vec_t<T,4,903103> const wyxw, agra, qtsq;
        vec_t<T,4,903110> const wyyx, aggr, qtts;
        vec_t<T,4,903111> const wyyy, aggg, qttt;
        vec_t<T,4,903112> const wyyz, aggb, qttp;
        vec_t<T,4,903113> const wyyw, agga, qttq;
        vec_t<T,4,903120> _____ wyzx, agbr, qtps;
        vec_t<T,4,903121> const wyzy, agbg, qtpt;
        vec_t<T,4,903122> const wyzz, agbb, qtpp;
        vec_t<T,4,903123> const wyzw, agba, qtpq;
        vec_t<T,4,903130> const wywx, agar, qtqs;
        vec_t<T,4,903131> const wywy, agag, qtqt;
        vec_t<T,4,903132> const wywz, agab, qtqp;
        vec_t<T,4,903133> const wyww, agaa, qtqq;
        vec_t<T,4,903200> const wzxx, abrr, qpss;
        vec_t<T,4,903201> _____ wzxy, abrg, qpst;
        vec_t<T,4,903202> const wzxz, abrb, qpsp;
        vec_t<T,4,903203> const wzxw, abra, qpsq;
        vec_t<T,4,903210> _____ wzyx, abgr, qpts;
        vec_t<T,4,903211> const wzyy, abgg, qptt;
        vec_t<T,4,903212> const wzyz, abgb, qptp;
        vec_t<T,4,903213> const wzyw, abga, qptq;
        vec_t<T,4,903220> const wzzx, abbr, qpps;
        vec_t<T,4,903221> const wzzy, abbg, qppt;
        vec_t<T,4,903222> const wzzz, abbb, qppp;
        vec_t<T,4,903223> const wzzw, abba, qppq;
        vec_t<T,4,903230> const wzwx, abar, qpqs;
        vec_t<T,4,903231> const wzwy, abag, qpqt;
        vec_t<T,4,903232> const wzwz, abab, qpqp;
        vec_t<T,4,903233> const wzww, abaa, qpqq;
        vec_t<T,4,903300> const wwxx, aarr, qqss;
        vec_t<T,4,903301> const wwxy, aarg, qqst;
        vec_t<T,4,903302> const wwxz, aarb, qqsp;
        vec_t<T,4,903303> const wwxw, aara, qqsq;
        vec_t<T,4,903310> const wwyx, aagr, qqts;
        vec_t<T,4,903311> const wwyy, aagg, qqtt;
        vec_t<T,4,903312> const wwyz, aagb, qqtp;
        vec_t<T,4,903313> const wwyw, aaga, qqtq;
        vec_t<T,4,903320> const wwzx, aabr, qqps;
        vec_t<T,4,903321> const wwzy, aabg, qqpt;
        vec_t<T,4,903322> const wwzz, aabb, qqpp;
        vec_t<T,4,903323> const wwzw, aaba, qqpq;
        vec_t<T,4,903330> const wwwx, aaar, qqqs;
        vec_t<T,4,903331> const wwwy, aaag, qqqt;
        vec_t<T,4,903332> const wwwz, aaab, qqqp;
        vec_t<T,4,903333> const wwww, aaaa, qqqq;
#endif

        T m_data[count];
    };
};

static_assert(sizeof(i8vec4) == 4, "sizeof(i8vec4) == 4");
static_assert(sizeof(i16vec4) == 8, "sizeof(i16vec4) == 8");
static_assert(sizeof(ivec4) == 16, "sizeof(ivec4) == 16");
static_assert(sizeof(i64vec4) == 32, "sizeof(i64vec4) == 32");

#if LOL_FEATURE_CXX11_UNRESTRICTED_UNIONS
static_assert(sizeof(f16vec4) == 8, "sizeof(f16vec4) == 8");
#endif
static_assert(sizeof(vec4) == 16, "sizeof(vec4) == 16");
static_assert(sizeof(dvec4) == 32, "sizeof(dvec4) == 32");

/*
 * vec_t *(scalar, vec_t)
 */

template<typename T, int N, int SWIZZLE>
static inline typename std::enable_if<SWIZZLE != FULL_SWIZZLE, vec_t<T,N>>::type
operator *(T const &val, vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = val * a[i];
    return ret;
}

/*
 * vec_t min|max|fmod(vec_t, vec_t|scalar)
 * vec_t min|max|fmod(scalar, vec_t)
 */

#define LOL_SWIZZLE_V_VV_FUN(fun) \
    template<typename T, int N, int SWIZZLE1, int SWIZZLE2> \
    inline vec_t<T,N> fun(vec_t<T,N,SWIZZLE1> const &a, \
                          vec_t<T,N,SWIZZLE2> const &b) \
    { \
        using lol::fun; \
        vec_t<T,N> ret; \
        for (int i = 0; i < N; ++i) \
            ret[i] = fun(a[i], b[i]); \
        return ret; \
    } \
    \
    template<typename T, int N, int SWIZZLE> \
    inline vec_t<T,N> fun(vec_t<T,N,SWIZZLE> const &a, T const &b) \
    { \
        using lol::fun; \
        vec_t<T,N> ret; \
        for (int i = 0; i < N; ++i) \
            ret[i] = fun(a[i], b); \
        return ret; \
    } \
    \
    template<typename T, int N, int SWIZZLE> \
    inline vec_t<T,N> fun(T const &a, vec_t<T,N,SWIZZLE> const &b) \
    { \
        using lol::fun; \
        vec_t<T,N> ret; \
        for (int i = 0; i < N; ++i) \
            ret[i] = fun(a, b[i]); \
        return ret; \
    }

LOL_SWIZZLE_V_VV_FUN(min)
LOL_SWIZZLE_V_VV_FUN(max)
LOL_SWIZZLE_V_VV_FUN(fmod)

#undef LOL_SWIZZLE_V_VV_FUN

/*
 * vec_t clamp(vec_t, vec_t, vec_t)
 * vec_t clamp(vec_t, scalar, vec_t)
 * vec_t clamp(vec_t, vec_t, scalar)
 * vec_t clamp(vec_t, scalar, scalar)
 */

template<typename T, int N, int SWIZZLE1, int SWIZZLE2, int SWIZZLE3>
static inline vec_t<T,N> clamp(vec_t<T,N,SWIZZLE1> const &x,
                               vec_t<T,N,SWIZZLE2> const &a,
                               vec_t<T,N,SWIZZLE3> const &b)
{
    return max(min(x, b), a);
}

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline vec_t<T,N> clamp(vec_t<T,N,SWIZZLE1> const &x,
                               T const &a,
                               vec_t<T,N,SWIZZLE2> const &b)
{
    return max(min(x, b), a);
}

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline vec_t<T,N> clamp(vec_t<T,N,SWIZZLE1> const &x,
                               vec_t<T,N,SWIZZLE2> const &a,
                               T const &b)
{
    return max(min(x, b), a);
}

template<typename T, int N, int SWIZZLE1>
static inline vec_t<T,N> clamp(vec_t<T,N,SWIZZLE1> const &x,
                               T const &a,
                               T const &b)
{
    return max(min(x, b), a);
}

/*
 * vec_t mix(vec_t, vec_t, vec_t)
 * vec_t mix(vec_t, vec_t, scalar)
 */

template<typename T, int N, int SWIZZLE1, int SWIZZLE2, int SWIZZLE3>
static inline vec_t<T,N> mix(vec_t<T,N,SWIZZLE1> const &x,
                             vec_t<T,N,SWIZZLE2> const &y,
                             vec_t<T,N,SWIZZLE3> const &a)
{
    return x + a * (y - x);
}

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline vec_t<T,N> mix(vec_t<T,N,SWIZZLE1> const &x,
                             vec_t<T,N,SWIZZLE2> const &y,
                             T const &a)
{
    return x + a * (y - x);
}

/*
 * Some GLSL-like functions.
 */

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline T dot(vec_t<T,N,SWIZZLE1> const &a,
                    vec_t<T,N,SWIZZLE2> const &b)
{
    T ret(0);
    for (int i = 0; i < N; ++i)
        ret += a[i] * b[i];
    return ret;
}

template<typename T, int N, int SWIZZLE>
static inline T sqlength(vec_t<T,N,SWIZZLE> const &a)
{
    return dot(a, a);
}

template<typename T, int N, int SWIZZLE>
static inline T length(vec_t<T,N,SWIZZLE> const &a)
{
    /* FIXME: this is not very nice */
    return T(sqrt((double)sqlength(a)));
}

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline vec_t<T,N> lerp(vec_t<T,N,SWIZZLE1> const &a,
                              vec_t<T,N,SWIZZLE2> const &b,
                              T const &s)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = a[i] + s * (b[i] - a[i]);
    return ret;
}

template<typename T, int N, int SWIZZLE1, int SWIZZLE2>
static inline T distance(vec_t<T,N,SWIZZLE1> const &a,
                         vec_t<T,N,SWIZZLE2> const &b)
{
    return length(a - b);
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> fract(vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = fract(a[i]);
    return ret;
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> saturate(vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = saturate(a[i]);
    return ret;
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> normalize(vec_t<T,N,SWIZZLE> const &a)
{
    T norm = T(length(a));
    return norm ? a / norm : vec_t<T,N>(T(0));
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> abs(vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = lol::abs(a[i]);
    return ret;
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> degrees(vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = lol::degrees(a[i]);
    return ret;
}

template<typename T, int N, int SWIZZLE>
static inline vec_t<T,N> radians(vec_t<T,N,SWIZZLE> const &a)
{
    vec_t<T,N> ret;
    for (int i = 0; i < N; ++i)
        ret[i] = lol::radians(a[i]);
    return ret;
}

/*
 * C++11 iterators
 */

template<typename T, int N, int SWIZZLE>
class vec_const_iter
{
public:
    inline vec_const_iter(vec_t<T,N,SWIZZLE> const &vec, int pos)
      : m_vec(vec),
        m_pos(pos)
    { }

    inline bool operator !=(vec_const_iter<T,N,SWIZZLE> const & that) const
    {
        return m_pos != that.m_pos;
    }

    template<int S = SWIZZLE>
    inline typename std::enable_if<S != FULL_SWIZZLE, T const &>::type
    operator *() const
    {
        return m_vec[m_pos];
    }

    template<int S = SWIZZLE>
    inline typename std::enable_if<S == FULL_SWIZZLE, T const &>::type
    operator *() const
    {
        return m_vec[m_pos];
    }

    inline vec_const_iter<T,N,SWIZZLE> & operator ++()
    {
        ++m_pos;
        return *this;
    }

private:
    vec_t<T,N,SWIZZLE> const &m_vec;
    int m_pos;
};

/* FIXME: do we need a non-const iterator? Looks almost useless to me,
 * and a lot of hassle with swizzling. */
template<typename T, int N, int SWIZZLE>
inline vec_const_iter<T,N,SWIZZLE> begin(vec_t<T,N,SWIZZLE> const &a)
{
    return vec_const_iter<T,N,SWIZZLE>(a, 0);
}

template<typename T, int N, int SWIZZLE>
inline vec_const_iter<T,N,SWIZZLE> end(vec_t<T,N,SWIZZLE> const &a)
{
    return vec_const_iter<T,N,SWIZZLE>(a, N);
}

/*
 * Constants
 */

template<typename T, int N>
vec_t<T,N> const vec_t<T,N>::zero = vec_t<T,N>(T(0));
template<typename T>
vec_t<T,2> const vec_t<T,2>::zero = vec_t<T,2>(T(0));
template<typename T>
vec_t<T,3> const vec_t<T,3>::zero = vec_t<T,3>(T(0));
template<typename T>
vec_t<T,4> const vec_t<T,4>::zero = vec_t<T,4>(T(0));

template<typename T>
vec_t<T,2> const vec_t<T,2>::axis_x = vec_t<T,2>(T(1), T(0));
template<typename T>
vec_t<T,2> const vec_t<T,2>::axis_y = vec_t<T,2>(T(0), T(1));

template<typename T>
vec_t<T,3> const vec_t<T,3>::axis_x = vec_t<T,3>(T(1), T(0), T(0));
template<typename T>
vec_t<T,3> const vec_t<T,3>::axis_y = vec_t<T,3>(T(0), T(1), T(0));
template<typename T>
vec_t<T,3> const vec_t<T,3>::axis_z = vec_t<T,3>(T(0), T(0), T(1));

template<typename T>
vec_t<T,4> const vec_t<T,4>::axis_x = vec_t<T,4>(T(1), T(0), T(0), T(0));
template<typename T>
vec_t<T,4> const vec_t<T,4>::axis_y = vec_t<T,4>(T(0), T(1), T(0), T(0));
template<typename T>
vec_t<T,4> const vec_t<T,4>::axis_z = vec_t<T,4>(T(0), T(0), T(1), T(0));
template<typename T>
vec_t<T,4> const vec_t<T,4>::axis_w = vec_t<T,4>(T(0), T(0), T(0), T(1));

#if !LOL_FEATURE_CXX11_CONSTEXPR
#undef constexpr
#endif

} /* namespace lol */

