#pragma once
#include <algorithm>
#include <cstdint>
#include <iostream>

template <typename T>
struct vec2 {
	T x, y;

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

	static const vec2<T> zero;
	static const vec2<T> one;

	vec2<T> operator+(vec2<T> a) const { return vec2(x + a.x, y + a.y); }
	vec2<T> operator-(vec2<T> a) const { return vec2(x - a.x, y - a.y); }
	vec2<T> operator*(vec2<T> a) const { return vec2(x * a.x, y * a.y); }
	vec2<T> operator/(vec2<T> a) const { return vec2(x / a.x, y / a.y); }

	vec2<T> operator+(T a) const { return vec2(x + a, y + a); }
	vec2<T> operator-(T a) const { return vec2(x - a, y - a); }
	vec2<T> operator*(T a) const { return vec2(x * a, y * a); }
	vec2<T> operator/(T a) const { return vec2(x / a, y / a); }

	bool operator==(vec2<T> const& other) const {
		return x == other.x && y == other.y;
	}

	static vec2<T> min(vec2<T> a, vec2<T> b);
	static vec2<T> max(vec2<T> a, vec2<T> b);

	int32_t size() const;
};

using vec2i = vec2<int32_t>;
using vec2f = vec2<float>;

template <typename T>
vec2<T> vec2<T>::min(vec2<T> a, vec2<T> b) {
	return vec2<T>(std::min(a.x, b.x), std::min(a.y, b.y));
}

template <typename T>
vec2<T> vec2<T>::max(vec2<T> a, vec2<T> b) {
	return vec2<T>(std::max(a.x, b.x), std::max(a.y, b.y));
}

template <typename T>
int32_t vec2<T>::size() const {
	return x * y;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, vec2<T> const& v) {
	return os << "vec2(" << v.x << "," << v.y << ")";
}

template <typename T> const vec2<T> vec2<T>::zero(0, 0);
template <typename T> const vec2<T> vec2<T>::one(1, 1);
