//
// 64 bit
//

union Vec2F64 {
    struct {
        double x, y;
    };
    struct {
        double w, h;
    };
    double e[2];
};

union Vec3F64 {
    struct {
        double x, y, z;
    };
    struct {
        double r, g, b;
    };
    struct {
        double h, s, l;
    };
    struct {
        Vec2F64 xy;
        double floor;
    };
    struct {
        double _x1;
        Vec2F64 yz;
    };
    double e[3];
};

union Vec4F64 {
    struct {
        double x, y, z, w;
    };
    struct {
        double r, g, b, a;
    };
    struct {
        double h, s, l, a;
    };
    struct {
        Vec3F64 rgb;
        double _i1;
    };
    struct {
        Vec3F64 xyz;
        double _i2;
    };
    struct {
        Vec2F64 xy;
        Vec2F64 zw;
    };


    double e[4];
};

union Vec2I64 {
    struct {
        int64_t x, y;
    };
    int64_t e[2];
};

union Vec3I64 {
    struct {
        int64_t x;
        int64_t y;
        int64_t z;
    };
    struct {
        int64_t r, g, b;
    };
    struct {
        int64_t z_[2];
        int64_t floor;
    };
    int64_t e[3];
};

union Vec4I64 {
    struct {
        int64_t x, y, z, w;
    };
    struct {
        int64_t r, g, b, a;
    };
    int64_t e[4];
};

struct Rect2F64 {
    union {
        struct {
            double x0;
            double y0;
            double x1;
            double y1;
        };
        struct {
            Vec2F64 min;
            Vec2F64 max;
        };
    };
};

struct Rect3F64 {
    Vec3F64 min;
    Vec3F64 max;
};

// clang-format off
/*--#

for vec in ["Vec2F64", "Vec3F64", "Vec4F64"]:
        v = vec.lower()
        z = "(double)a.z," if vec == "Vec3F64" or vec == "Vec4F64" else ""
        w = "(double)a.w," if                  vec == "Vec4F64" else ""
        print(f"inline {vec} {vec}ITo{vec}({vec}I a) {{ return {{ (double)a.x, (double)a.y, {z} {w} }}; }}")
        if z != "": z = ",-a.z"
        if w != "": w = ",-a.w"
        print(f"inline {vec} operator-({vec} a) {{ return {{ -a.x, -a.y {z} {w} }}; }}")

for op in ["*", "/", "+", "-"]:
        for vec in ["Vec2F64", "Vec3F64", "Vec4F64"]:
                x = f"a.x {op} b.x,"
                y = f"a.y {op} b.y,"
                z = f"a.z {op} b.z," if vec == "Vec3F64" or vec == "Vec4F64" else ""
                w = f"a.w {op} b.w," if                  vec == "Vec4F64" else ""

                print(f"inline {vec} operator{op}({vec} a, {vec} b) {{ return {{ {x} {y} {z} {w} }}; }}")
                print(f"inline {vec} &operator{op}=({vec} &a, {vec} b) {{ a = a {op} b; return a; }}")


                x = f"a.x {op} b,"
                y = f"a.y {op} b,"
                z = f"a.z {op} b," if vec == "Vec3F64" or vec == "Vec4F64" else ""
                w = f"a.w {op} b," if                  vec == "Vec4F64" else ""

                print(f"inline {vec} operator{op}({vec} a, double b) {{ return {{ {x} {y} {z} {w} }}; }}")
                print(f"inline {vec} operator{op}(double b, {vec} a) {{ return {{ {x} {y} {z} {w} }}; }}")
                print(f"inline {vec} &operator{op}=({vec} &a, double b) {{ a = a {op} b; return a; }}")
*/
inline Vec2F64 Vec2IToVec2(Vec2I64 a) { return { (double)a.x, (double)a.y,   }; }
inline Vec3F64 Vec3IToVec3(Vec3I64 a) { return { (double)a.x, (double)a.y, (double)a.z,  }; }
inline Vec4F64 Vec4IToVec4(Vec4I64 a) { return { (double)a.x, (double)a.y, (double)a.z, (double)a.w, }; }
inline Vec2F64 operator-(Vec2F64 a) { return { -a.x, -a.y   }; }
inline Vec3F64 operator-(Vec3F64 a) { return { -a.x, -a.y ,-a.z  }; }
inline Vec4F64 operator-(Vec4F64 a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
inline Vec2F64 operator*(Vec2F64 a, Vec2F64 b) { return { a.x * b.x, a.y * b.y,   }; }
inline Vec2F64 &operator*=(Vec2F64 &a, Vec2F64 b) { a = a * b; return a; }
inline Vec2F64 operator*(Vec2F64 a, double b) { return { a.x * b, a.y * b,   }; }
inline Vec2F64 operator*(double b, Vec2F64 a) { return { a.x * b, a.y * b,   }; }
inline Vec2F64 &operator*=(Vec2F64 &a, double b) { a = a * b; return a; }
inline Vec3F64 operator*(Vec3F64 a, Vec3F64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z,  }; }
inline Vec3F64 &operator*=(Vec3F64 &a, Vec3F64 b) { a = a * b; return a; }
inline Vec3F64 operator*(Vec3F64 a, double b) { return { a.x * b, a.y * b, a.z * b,  }; }
inline Vec3F64 operator*(double b, Vec3F64 a) { return { a.x * b, a.y * b, a.z * b,  }; }
inline Vec3F64 &operator*=(Vec3F64 &a, double b) { a = a * b; return a; }
inline Vec4F64 operator*(Vec4F64 a, Vec4F64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
inline Vec4F64 &operator*=(Vec4F64 &a, Vec4F64 b) { a = a * b; return a; }
inline Vec4F64 operator*(Vec4F64 a, double b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
inline Vec4F64 operator*(double b, Vec4F64 a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
inline Vec4F64 &operator*=(Vec4F64 &a, double b) { a = a * b; return a; }
inline Vec2F64 operator/(Vec2F64 a, Vec2F64 b) { return { a.x / b.x, a.y / b.y,   }; }
inline Vec2F64 &operator/=(Vec2F64 &a, Vec2F64 b) { a = a / b; return a; }
inline Vec2F64 operator/(Vec2F64 a, double b) { return { a.x / b, a.y / b,   }; }
inline Vec2F64 operator/(double b, Vec2F64 a) { return { b / a.x, b / a.y,   }; }
inline Vec2F64 &operator/=(Vec2F64 &a, double b) { a = a / b; return a; }
inline Vec3F64 operator/(Vec3F64 a, Vec3F64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z,  }; }
inline Vec3F64 &operator/=(Vec3F64 &a, Vec3F64 b) { a = a / b; return a; }
inline Vec3F64 operator/(Vec3F64 a, double b) { return { a.x / b, a.y / b, a.z / b,  }; }
inline Vec3F64 operator/(double b, Vec3F64 a) { return { b / a.x, b / a.y, b / a.z,  }; }
inline Vec3F64 &operator/=(Vec3F64 &a, double b) { a = a / b; return a; }
inline Vec4F64 operator/(Vec4F64 a, Vec4F64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
inline Vec4F64 &operator/=(Vec4F64 &a, Vec4F64 b) { a = a / b; return a; }
inline Vec4F64 operator/(Vec4F64 a, double b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
inline Vec4F64 operator/(double b, Vec4F64 a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
inline Vec4F64 &operator/=(Vec4F64 &a, double b) { a = a / b; return a; }
inline Vec2F64 operator+(Vec2F64 a, Vec2F64 b) { return { a.x + b.x, a.y + b.y,   }; }
inline Vec2F64 &operator+=(Vec2F64 &a, Vec2F64 b) { a = a + b; return a; }
inline Vec2F64 operator+(Vec2F64 a, double b) { return { a.x + b, a.y + b,   }; }
inline Vec2F64 operator+(double b, Vec2F64 a) { return { a.x + b, a.y + b,   }; }
inline Vec2F64 &operator+=(Vec2F64 &a, double b) { a = a + b; return a; }
inline Vec3F64 operator+(Vec3F64 a, Vec3F64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z,  }; }
inline Vec3F64 &operator+=(Vec3F64 &a, Vec3F64 b) { a = a + b; return a; }
inline Vec3F64 operator+(Vec3F64 a, double b) { return { a.x + b, a.y + b, a.z + b,  }; }
inline Vec3F64 operator+(double b, Vec3F64 a) { return { a.x + b, a.y + b, a.z + b,  }; }
inline Vec3F64 &operator+=(Vec3F64 &a, double b) { a = a + b; return a; }
inline Vec4F64 operator+(Vec4F64 a, Vec4F64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
inline Vec4F64 &operator+=(Vec4F64 &a, Vec4F64 b) { a = a + b; return a; }
inline Vec4F64 operator+(Vec4F64 a, double b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
inline Vec4F64 operator+(double b, Vec4F64 a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
inline Vec4F64 &operator+=(Vec4F64 &a, double b) { a = a + b; return a; }
inline Vec2F64 operator-(Vec2F64 a, Vec2F64 b) { return { a.x - b.x, a.y - b.y,   }; }
inline Vec2F64 &operator-=(Vec2F64 &a, Vec2F64 b) { a = a - b; return a; }
inline Vec2F64 operator-(Vec2F64 a, double b) { return { a.x - b, a.y - b,   }; }
inline Vec2F64 operator-(double b, Vec2F64 a) { return { b - a.x, b - a.y,   }; }
inline Vec2F64 &operator-=(Vec2F64 &a, double b) { a = a - b; return a; }
inline Vec3F64 operator-(Vec3F64 a, Vec3F64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z,  }; }
inline Vec3F64 &operator-=(Vec3F64 &a, Vec3F64 b) { a = a - b; return a; }
inline Vec3F64 operator-(Vec3F64 a, double b) { return { a.x - b, a.y - b, a.z - b,  }; }
inline Vec3F64 operator-(double b, Vec3F64 a) { return { b - a.x, b - a.y, b - a.z,  }; }
inline Vec3F64 &operator-=(Vec3F64 &a, double b) { a = a - b; return a; }
inline Vec4F64 operator-(Vec4F64 a, Vec4F64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
inline Vec4F64 &operator-=(Vec4F64 &a, Vec4F64 b) { a = a - b; return a; }
inline Vec4F64 operator-(Vec4F64 a, double b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
inline Vec4F64 operator-(double b, Vec4F64 a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
inline Vec4F64 &operator-=(Vec4F64 &a, double b) { a = a - b; return a; }
/*END*/

inline Vec2I64 operator-(Vec2I64 a) { return { -a.x, -a.y   }; }
inline Vec3I64 operator-(Vec3I64 a) { return { -a.x, -a.y ,-a.z  }; }
inline Vec4I64 operator-(Vec4I64 a) { return { -a.x, -a.y ,-a.z ,-a.w }; }
inline Vec2I64 operator*(Vec2I64 a, Vec2I64 b) { return { a.x * b.x, a.y * b.y,   }; }
inline Vec2I64 &operator*=(Vec2I64 &a, Vec2I64 b) { a = a * b; return a; }
inline Vec2I64 operator*(Vec2I64 a, int64_t b) { return { a.x * b, a.y * b,   }; }
inline Vec2I64 operator*(int64_t b, Vec2I64 a) { return { a.x * b, a.y * b,   }; }
inline Vec2I64 &operator*=(Vec2I64 &a, int64_t b) { a = a * b; return a; }
inline Vec3I64 operator*(Vec3I64 a, Vec3I64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z,  }; }
inline Vec3I64 &operator*=(Vec3I64 &a, Vec3I64 b) { a = a * b; return a; }
inline Vec3I64 operator*(Vec3I64 a, int64_t b) { return { a.x * b, a.y * b, a.z * b,  }; }
inline Vec3I64 operator*(int64_t b, Vec3I64 a) { return { a.x * b, a.y * b, a.z * b,  }; }
inline Vec3I64 &operator*=(Vec3I64 &a, int64_t b) { a = a * b; return a; }
inline Vec4I64 operator*(Vec4I64 a, Vec4I64 b) { return { a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w, }; }
inline Vec4I64 &operator*=(Vec4I64 &a, Vec4I64 b) { a = a * b; return a; }
inline Vec4I64 operator*(Vec4I64 a, int64_t b) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
inline Vec4I64 operator*(int64_t b, Vec4I64 a) { return { a.x * b, a.y * b, a.z * b, a.w * b, }; }
inline Vec4I64 &operator*=(Vec4I64 &a, int64_t b) { a = a * b; return a; }
inline Vec2I64 operator/(Vec2I64 a, Vec2I64 b) { return { a.x / b.x, a.y / b.y,   }; }
inline Vec2I64 &operator/=(Vec2I64 &a, Vec2I64 b) { a = a / b; return a; }
inline Vec2I64 operator/(Vec2I64 a, int64_t b) { return { a.x / b, a.y / b,   }; }
inline Vec2I64 operator/(int64_t b, Vec2I64 a) { return { b / a.x, b / a.y,   }; }
inline Vec2I64 &operator/=(Vec2I64 &a, int64_t b) { a = a / b; return a; }
inline Vec3I64 operator/(Vec3I64 a, Vec3I64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z,  }; }
inline Vec3I64 &operator/=(Vec3I64 &a, Vec3I64 b) { a = a / b; return a; }
inline Vec3I64 operator/(Vec3I64 a, int64_t b) { return { a.x / b, a.y / b, a.z / b,  }; }
inline Vec3I64 operator/(int64_t b, Vec3I64 a) { return { b / a.x, b / a.y, b / a.z,  }; }
inline Vec3I64 &operator/=(Vec3I64 &a, int64_t b) { a = a / b; return a; }
inline Vec4I64 operator/(Vec4I64 a, Vec4I64 b) { return { a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w, }; }
inline Vec4I64 &operator/=(Vec4I64 &a, Vec4I64 b) { a = a / b; return a; }
inline Vec4I64 operator/(Vec4I64 a, int64_t b) { return { a.x / b, a.y / b, a.z / b, a.w / b, }; }
inline Vec4I64 operator/(int64_t b, Vec4I64 a) { return { b / a.x, b / a.y, b / a.z, b / a.w, }; }
inline Vec4I64 &operator/=(Vec4I64 &a, int64_t b) { a = a / b; return a; }
inline Vec2I64 operator+(Vec2I64 a, Vec2I64 b) { return { a.x + b.x, a.y + b.y,   }; }
inline Vec2I64 &operator+=(Vec2I64 &a, Vec2I64 b) { a = a + b; return a; }
inline Vec2I64 operator+(Vec2I64 a, int64_t b) { return { a.x + b, a.y + b,   }; }
inline Vec2I64 operator+(int64_t b, Vec2I64 a) { return { a.x + b, a.y + b,   }; }
inline Vec2I64 &operator+=(Vec2I64 &a, int64_t b) { a = a + b; return a; }
inline Vec3I64 operator+(Vec3I64 a, Vec3I64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z,  }; }
inline Vec3I64 &operator+=(Vec3I64 &a, Vec3I64 b) { a = a + b; return a; }
inline Vec3I64 operator+(Vec3I64 a, int64_t b) { return { a.x + b, a.y + b, a.z + b,  }; }
inline Vec3I64 operator+(int64_t b, Vec3I64 a) { return { a.x + b, a.y + b, a.z + b,  }; }
inline Vec3I64 &operator+=(Vec3I64 &a, int64_t b) { a = a + b; return a; }
inline Vec4I64 operator+(Vec4I64 a, Vec4I64 b) { return { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w, }; }
inline Vec4I64 &operator+=(Vec4I64 &a, Vec4I64 b) { a = a + b; return a; }
inline Vec4I64 operator+(Vec4I64 a, int64_t b) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
inline Vec4I64 operator+(int64_t b, Vec4I64 a) { return { a.x + b, a.y + b, a.z + b, a.w + b, }; }
inline Vec4I64 &operator+=(Vec4I64 &a, int64_t b) { a = a + b; return a; }
inline Vec2I64 operator-(Vec2I64 a, Vec2I64 b) { return { a.x - b.x, a.y - b.y,   }; }
inline Vec2I64 &operator-=(Vec2I64 &a, Vec2I64 b) { a = a - b; return a; }
inline Vec2I64 operator-(Vec2I64 a, int64_t b) { return { a.x - b, a.y - b,   }; }
inline Vec2I64 operator-(int64_t b, Vec2I64 a) { return { b - a.x, b - a.y,   }; }
inline Vec2I64 &operator-=(Vec2I64 &a, int64_t b) { a = a - b; return a; }
inline Vec3I64 operator-(Vec3I64 a, Vec3I64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z,  }; }
inline Vec3I64 &operator-=(Vec3I64 &a, Vec3I64 b) { a = a - b; return a; }
inline Vec3I64 operator-(Vec3I64 a, int64_t b) { return { a.x - b, a.y - b, a.z - b,  }; }
inline Vec3I64 operator-(int64_t b, Vec3I64 a) { return { b - a.x, b - a.y, b - a.z,  }; }
inline Vec3I64 &operator-=(Vec3I64 &a, int64_t b) { a = a - b; return a; }
inline Vec4I64 operator-(Vec4I64 a, Vec4I64 b) { return { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w, }; }
inline Vec4I64 &operator-=(Vec4I64 &a, Vec4I64 b) { a = a - b; return a; }
inline Vec4I64 operator-(Vec4I64 a, int64_t b) { return { a.x - b, a.y - b, a.z - b, a.w - b, }; }
inline Vec4I64 operator-(int64_t b, Vec4I64 a) { return { b - a.x, b - a.y, b - a.z, b - a.w, }; }
inline Vec4I64 &operator-=(Vec4I64 &a, int64_t b) { a = a - b; return a; }

inline Rect2F64 Rect2_Center(Vec2F64 center, Vec2F64 halfdim) { return {center.x - halfdim.x, center.y - halfdim.y, center.x + halfdim.x, center.y + halfdim.y}; }
inline Vec2F64 CalcSize(Rect2F64 r) { return { r.max.x - r.min.x, r.max.y - r.min.y }; }
inline Rect2F64 Rect2_Size(double x, double y, double w, double h) { return { x, y, x + w, y + h }; }
inline Rect2F64 Rect2_Size(Vec2F64 pos, Vec2F64 size) { return { pos.x, pos.y, pos.x + size.w, pos.y + size.h }; }
inline Rect2F64 Rect2_MinMax(Vec2F64 p0, Vec2F64 p1) { return {p0.x, p0.y, p1.x, p1.y }; }
inline Rect2F64 ShrinkByHalfSize(Rect2F64 rect, Vec2F64 half_size) { return {rect.min.x + half_size.x, rect.min.y + half_size.y, rect.max.x - half_size.x, rect.max.y - half_size.y}; }
inline Rect2F64 ExpandByHalfSize(Rect2F64 rect, Vec2F64 half_size) { return {rect.min.x - half_size.x, rect.min.y - half_size.y, rect.max.x + half_size.x, rect.max.y + half_size.y}; }

inline Rect2F64 Rect2_AdjustMinMax(Vec2F64 p0, Vec2F64 p1) {
    Rect2F64 result = {
        Minimum(p0.x, p1.x), Minimum(p0.y, p1.y),
        Maximum(p0.x, p1.x), Maximum(p0.y, p1.y),
    };
    return result;
}

inline double GetXSize(Rect2F64 r) { return r.max.x - r.min.x; }
inline double GetYSize(Rect2F64 r) { return r.max.y - r.min.y; }
inline Vec2F64 GetSize(Rect2F64 r) { return r.max - r.min; }

inline double Lerp(double from, double to, double t) {return (1-t)*from + t*to;}
inline Vec2F64 Lerp(Vec2F64 from, Vec2F64 to, double t) {return (1-t)*from + t*to;}
inline Vec3F64 Lerp(Vec3F64 from, Vec3F64 to, double t) {return (1-t)*from + t*to;}
inline Vec4F64 Lerp(Vec4F64 from, Vec4F64 to, double t) {return (1-t)*from + t*to;}

inline Vec2 Vec2F64ToVec2(Vec2F64 a) {return Vec2{(float)a.x, (float)a.y};}
inline Rect2 Rect2F64ToRect2(Rect2F64 a) { return Rect2{(float)a.min.x, (float)a.min.y, (float)a.max.x, (float)a.max.y}; }
