commit 62272807dcebbec96c2fbabc172c36e0eccaeae3
Author: janis <janis@nirgendwo.xyz>
Date:   Thu Jun 23 17:26:19 2022 +0100

    initial commit

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dcc32a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+compile_commands.json
+.cache/
+target/
diff --git a/Alloy.toml b/Alloy.toml
new file mode 100644
index 0000000..33db694
--- /dev/null
+++ b/Alloy.toml
@@ -0,0 +1,16 @@
+[package]
+name = "utils"
+authors = []
+standard = "c++2b"
+version = "0.1.0"
+
+[lib]
+cc-flags = ["-DNOMINMAX", "-D_HAS_CXX20=1"]
+exclude-sources = ["src/main.cc"]
+reparent-headers = true
+name = "utils"
+
+[[bin]]
+cc-flags = ["-DNOMINMAX", "-g", "-gcodeview"]
+ld-flags = ["-g"]
+name = "test"
diff --git a/include/assert.hpp b/include/assert.hpp
new file mode 100644
index 0000000..cd448fb
--- /dev/null
+++ b/include/assert.hpp
@@ -0,0 +1,120 @@
+#pragma once
+
+#if defined(UTIL_ASSERT_FORMAT)
+#include "print.hpp"
+#endif
+
+#ifdef assert
+#pragma push_macro("assert")
+#undef assert
+#define UTIL_ASSERT_UNDEF
+#endif
+
+#include <stdio.h>
+#include "move.hpp"
+#include "source_location.hpp"
+#include "type_traits.hpp"
+
+namespace util {
+template <class From, class To>
+struct is_convertible
+    : std::integral_constant<
+          bool,
+          (decltype(detail::test_returnable<To>(0))::value &&
+           decltype(detail::test_implicitly_convertible<From, To>(0))::value) ||
+              (is_void<From>::value && is_void<To>::value)> {};
+
+template <typename From, typename To>
+inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
+
+template <typename From>
+concept boolean_testable_impl = is_convertible_v<From, bool> && requires {
+  static_cast<bool>(declval<From>());
+};
+
+template <typename T>
+concept boolean_testable = requires(T &&t) {
+  { !static_cast<T &&>(t) } -> boolean_testable_impl;
+};
+
+template <typename A, typename B>
+concept weakly_equality_comparable_with = requires (const remove_reference_t<A>& a, const remove_reference_t<B>& b) {
+    {a == b} -> boolean_testable;
+    {a != b} -> boolean_testable;
+    {b == a} -> boolean_testable;
+    {b == a} -> boolean_testable;
+};
+
+template <typename T>
+concept equality_comparable = weakly_equality_comparable_with<T, T>;
+
+template <typename A, typename B>
+concept equality_comparable_with = weakly_equality_comparable_with<A, B>;
+
+template <boolean_testable B>
+inline constexpr auto assert(B&& b, const source_location& source = source_location::current()) ->void {
+    if (!b) [[unlikely]] {
+
+        #if defined (UTIL_ASSERT_FORMAT)
+        if constexpr (is_formattable_v<B>) {
+            print(std::format("Assertion failed: {}\n", b));
+            print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        } else {
+            print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        }
+        #else
+        printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column());
+        #endif
+    }
+}
+
+template <typename A, typename B>
+requires (equality_comparable_with<A, B>)
+inline constexpr auto assert_eq(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void {
+    if (rhs != lhs) [[unlikely]] {
+        #if defined (UTIL_ASSERT_FORMAT)
+        if constexpr (is_formattable_v<A> && is_formattable_v<B>) {
+            print(std::format("Assertion failed: {} != {}\n", rhs, lhs));
+            print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        } else {
+            print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        }
+        #else
+        printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column());
+        #endif
+    }
+}
+
+template <equality_comparable T>
+inline constexpr auto assert_eq(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void {
+    assert_eq<T,T>(FWD(rhs), FWD(lhs),source);
+}
+
+
+template <typename A, typename B>
+requires (equality_comparable_with<A, B>)
+inline constexpr auto assert_ne(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void {
+    if (rhs == lhs) [[unlikely]] {
+        #if defined (UTIL_ASSERT_FORMAT)
+        if constexpr (is_formattable_v<A> && is_formattable_v<B>) {
+            print(std::format("Assertion failed: {} == {}\n", rhs, lhs));
+            print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        } else {
+            print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column()));
+        }
+        #else
+        printf("Assertion failed in function %s at %s:%d,%d", source.function_name(), source.file_name(), source.line(), source.column());
+        #endif
+    }
+}
+
+template <equality_comparable T>
+inline constexpr auto assert_ne(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void {
+    assert_ne<T, T>(FWD(rhs), FWD(lhs), source);
+}
+} // namespace util
+
+#ifdef UTIL_ASSERT_UNDEF
+#pragma pop_macro("assert")
+#undef UTIL_ASSERT_UNDEF
+#endif
\ No newline at end of file
diff --git a/include/concepts/container.hpp b/include/concepts/container.hpp
new file mode 100644
index 0000000..b1bcddf
--- /dev/null
+++ b/include/concepts/container.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <concepts>
+#include <ranges>
+
+namespace util {
+template <typename C>
+concept reservable = requires(C &c, typename C::size_type i) {
+  {c.reserve(i)};
+};
+
+template <typename C>
+concept back_insertible = requires(C &c,
+                                   const std::ranges::range_value_t<C> &t) {
+  { std::back_inserter(c) } -> std::same_as<std::back_insert_iterator<C>>;
+  {c.push_back(t)};
+};
+
+template <typename C>
+concept front_insertible = requires(C &c,
+                                    const std::ranges::range_value_t<C> &t) {
+  { std::front_inserter(c) } -> std::same_as<std::front_insert_iterator<C>>;
+  {c.push_front(t)};
+};
+
+template <typename C>
+concept insertible = requires(C &c, std::ranges::iterator_t<C> i,
+                              const std::ranges::range_value_t<C> &t) {
+  { std::inserter(c, i) } -> std::same_as<std::insert_iterator<C>>;
+  {c.insert(i, t)};
+};
+template <typename C, typename... Ts>
+concept emplaceable = requires(C &c, Ts... t) {
+  {c.emplace(std::move(t...))};
+};
+
+template <typename C, typename... Ts>
+concept back_emplaceable = requires(C &c, Ts... t) {
+  {c.emplace_back(std::move(t...))};
+};
+} // namespace util
\ No newline at end of file
diff --git a/include/concepts/traits.hpp b/include/concepts/traits.hpp
new file mode 100644
index 0000000..479e41f
--- /dev/null
+++ b/include/concepts/traits.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <compare>
+#include <concepts>
+#include <type_traits>
+
+namespace util {
+
+template <typename T>
+concept Number = std::integral<T> || std::floating_point<T>;
+
+template <auto T, auto U>
+requires std::equality_comparable_with<decltype(T), decltype(T)>
+struct is_equal : public std::integral_constant<bool, T == U> {
+};
+
+template <auto T, auto U>
+inline constexpr auto is_equal_v = is_equal<T, U>::value;
+
+template <auto T, auto U>
+requires std::three_way_comparable_with<decltype(T), decltype(T)>
+struct cmp {
+  constexpr static std::partial_ordering ordering = T <=> U;
+};
+
+template <auto T, auto U> inline constexpr auto cmp_v = cmp<T, U>::ordering;
+
+template <std::partial_ordering ordering, auto T, auto U>
+concept Compare = is_equal_v<cmp_v<T, U>, ordering>;
+
+template <auto T, auto U>
+concept Equal = is_equal_v<T, U>;
+
+template <typename T, typename... Pack>
+concept all_same_as = (std::same_as<T, Pack> && ...);
+
+template <typename T, typename U>
+concept explicit_convertible_to = requires(const T ct, T t, const U &cu) {
+  {t = cu};
+  { ct.operator U() } -> std::same_as<U>;
+}
+|| std::same_as<T, U>;
+
+template <typename From, typename... To>
+concept explicit_convertible_to_any_of =
+    (explicit_convertible_to<std::remove_cv_t<From>, To> || ...);
+
+template <typename From, typename... To>
+struct is_explicit_convertible_to_any_of
+    : std::bool_constant<explicit_convertible_to_any_of<From, To...>> {};
+
+template <typename T>
+concept explicit_convertible_to_integral =
+    std::integral<T> || explicit_convertible_to_any_of < std::remove_cv_t<T>,
+bool, char, signed char, unsigned char, wchar_t,
+#ifdef __cpp_char8_t
+    char8_t,
+#endif // __cpp_char8_t
+    char16_t, char32_t, short, unsigned short, int, unsigned int, long,
+    unsigned long, long long, unsigned long long > ;
+
+template <typename T>
+concept explicit_convertible_to_floating_point = std::floating_point<T> ||
+    explicit_convertible_to_any_of<T, float, double, long double>;
+
+template <typename From, typename To>
+struct is_explicit_convertible
+    : std::bool_constant<explicit_convertible_to<From, To>> {};
+
+template <typename From, typename To>
+inline constexpr bool is_explicit_convertible_v =
+    is_explicit_convertible<From, To>::value;
+
+template <typename Fn, typename R, typename... Args>
+concept with_return_type = requires(Fn fn, Args... args) {
+  { fn(args...) } -> std::same_as<R>;
+};
+
+} // namespace util
\ No newline at end of file
diff --git a/include/enum.hpp b/include/enum.hpp
new file mode 100644
index 0000000..e536ed8
--- /dev/null
+++ b/include/enum.hpp
@@ -0,0 +1,158 @@
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace util {
+namespace bitflag_operators {
+
+template <typename E>
+concept enum_type = std::is_enum_v<E>;
+
+template <enum_type E> auto operator&(const E &rhs, const E &lhs) -> E {
+  return static_cast<E>(underlying_type_cast(rhs) & underlying_type_cast(lhs));
+}
+
+template <enum_type E> auto operator&=(E &rhs, const E &lhs) -> E & {
+  rhs = rhs & lhs;
+  return rhs;
+}
+
+template <enum_type E> auto operator|(const E &rhs, const E &lhs) -> E {
+  return static_cast<E>(underlying_type_cast(rhs) | underlying_type_cast(lhs));
+}
+
+template <enum_type E> auto operator|=(E &rhs, const E &lhs) -> E & {
+  rhs = rhs | lhs;
+  return rhs;
+}
+
+template <enum_type E> auto operator^(const E &rhs, const E &lhs) -> E {
+  return static_cast<E>(underlying_type_cast(rhs) ^ underlying_type_cast(lhs));
+}
+
+template <enum_type E> auto operator^=(E &rhs, const E &lhs) -> E & {
+  rhs = rhs ^ lhs;
+  return rhs;
+}
+
+template <enum_type E> auto operator~(const E &rhs) -> E {
+  return static_cast<E>(~underlying_type_cast(rhs));
+}
+
+template <enum_type E> auto is_empty(const E &value) -> bool {
+  return std::to_underlying(value) == 0;
+}
+
+} // namespace bitflag_operators
+
+using bitflag_operators::enum_type;
+
+template <typename E>
+auto underlying_type_cast(const E &value) -> std::underlying_type_t<E>
+requires std::is_enum_v<E> {
+  return static_cast<std::underlying_type_t<E>>(value);
+}
+
+template <typename E>
+requires std::is_enum_v<E>
+class EnumFlag {
+  E flags;
+
+public:
+  using U = std::underlying_type_t<E>;
+  using value_type = U;
+
+  EnumFlag() : flags(static_cast<E>(U(0))) {}
+  EnumFlag(const U &value) : flags(static_cast<E>(value)) {}
+  EnumFlag(const E &value) : flags(value) {}
+
+  auto operator=(const U &value) -> EnumFlag & {
+    flags = static_cast<E>(value);
+    return *this;
+  }
+  auto operator=(const E &value) -> EnumFlag & {
+    flags = value;
+    return *this;
+  }
+
+  auto as_underlying_type() const -> U { return static_cast<U>(flags); }
+  auto as_enum_type() const -> E { return flags; }
+
+  /// `union` is reserved
+  auto unison(const EnumFlag &other) const -> EnumFlag { return this | other; }
+
+  auto intersection(const EnumFlag &other) const -> EnumFlag {
+    return *this & other;
+  }
+
+  auto contains(const EnumFlag &other) const -> bool {
+    return intersection(other).as_underlying_type() ==
+           other.as_underlying_type();
+  }
+
+  auto intersects(const EnumFlag &other) const -> bool {
+    return intersection().as_underlying_type() != 0;
+  }
+
+  auto add(const EnumFlag &other) -> EnumFlag & {
+    *this |= other;
+    return *this;
+  }
+
+  auto remove(const EnumFlag &other) -> EnumFlag & {
+    *this &= ~other;
+    return *this;
+  }
+
+  operator U() const { return as_underlying_type(); }
+  operator E() const { return as_enum_type(); }
+
+  // auto operator==(const EnumFlag& other) const {
+  //   return as_underlying_type() == other.as_underlying_type();
+  // }
+
+  auto operator<=>(const EnumFlag &other) const {
+    return other.as_underlying_type() <=> other.as_underlying_type();
+  }
+
+  // operator bool() const { return as_underlying_type() != 0; }
+
+  auto operator|=(const EnumFlag &other) {
+    flags = static_cast<E>(as_underlying_type() | other.as_underlying_type());
+  }
+
+  auto operator|(const EnumFlag &other) const -> EnumFlag {
+    auto flag = *this;
+    flag |= other;
+
+    return flag;
+  }
+
+  auto operator&=(const EnumFlag &other) {
+    flags = static_cast<E>(as_underlying_type() & other.as_underlying_type());
+  }
+
+  auto operator&(const EnumFlag &other) const -> EnumFlag {
+    auto flag = *this;
+    flag &= other;
+
+    return flag;
+  }
+
+  auto operator~() const -> EnumFlag {
+    return static_cast<E>(~as_underlying_type());
+  }
+
+  auto operator^=(const EnumFlag &other) {
+    flags = static_cast<E>(as_underlying_type() ^ other.as_underlying_type());
+  }
+
+  auto operator^(const EnumFlag &other) const -> EnumFlag {
+    auto flag = *this;
+    flag ^= other;
+
+    return flag;
+  }
+};
+} // namespace util
\ No newline at end of file
diff --git a/include/format.hpp b/include/format.hpp
new file mode 100644
index 0000000..d8c74cf
--- /dev/null
+++ b/include/format.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "move.hpp"
+#include "type_traits.hpp"
+#include <format>
+#include <string_view>
+
+namespace util {
+
+template <typename... Ts>
+inline constexpr auto format(std::string_view fmt, Ts&&... args)
+    -> std::string {
+return std::vformat(fmt, std::make_format_args(std::forward<Ts>(args)...));
+}
+
+template <typename T, typename Context>
+using has_formatter =
+    std::is_constructible<typename Context::template formatter_type<T>>;
+
+template <typename T>
+    using is_formattable = has_formatter<T, std::format_context>;
+
+    template <typename T>
+    inline constexpr bool is_formattable_v = is_formattable<T>::value;
+
+    template <typename T>
+    concept formattable = is_formattable_v<T>;
+}  // namespace util
+
+#include <optional>
+
+template<util::formattable F>
+struct std::formatter<std::optional<F>> : public std::formatter<F> {
+  template <typename FormatContext>
+  auto format(const std::optional<F>& option, FormatContext& ctx)
+      -> decltype(ctx.out()) {
+        auto fmt = ctx.out();
+
+        if (option.has_value()) {
+            fmt = std::format_to(fmt, "Some(");
+            ctx.advance_to(fmt);
+            fmt = std::formatter<F>::format(option.value(), ctx);
+            *fmt++ = ')';
+        } else {
+            fmt = std::format_to(fmt, "None");
+        }
+
+        return fmt;
+      }
+};
\ No newline at end of file
diff --git a/include/iterator.hpp b/include/iterator.hpp
new file mode 100644
index 0000000..949acfa
--- /dev/null
+++ b/include/iterator.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <tuple>
+
+namespace util {
+
+template <int I, class... Ts>
+decltype(auto) get(Ts&&... ts) {
+  return std::get<I>(std::forward_as_tuple(ts...));
+}
+
+template <class Iterator>
+class Range {
+  Iterator m_begin, m_end;
+
+ public:
+  Range(const Iterator _begin, const Iterator _end)
+      : m_begin(_begin), m_end(_end) {}
+
+  Range(const std::pair<const Iterator, const Iterator>& t) {
+    m_begin = t.first;
+    m_end   = t.second;
+  }
+
+  template <typename... Args>
+  Range(Args&&... args) : m_begin(get<0>(args...)), m_end(get<1>(args...)) {}
+
+  auto begin() -> Iterator { return m_begin; }
+  auto begin() const -> const Iterator { return m_begin; }
+
+  auto end() -> Iterator { return m_end; }
+  auto end() const -> const Iterator { return m_end; }
+};
+
+}  // namespace util
diff --git a/include/math.hpp b/include/math.hpp
new file mode 100644
index 0000000..0b9fec2
--- /dev/null
+++ b/include/math.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "types.hpp"
+#include <concepts>
+#include <numbers>
+
+
+namespace util {
+template <std::floating_point N> auto radians(N degrees) -> N {
+  return (degrees * std::numbers::pi_v<N>) / static_cast<N>(180);
+}
+
+template <std::floating_point N> auto degrees(N radians) -> N {
+  return (radians * static_cast<N>(180)) / std::numbers::pi_v<N>;
+}
+} // namespace util
\ No newline at end of file
diff --git a/include/move.hpp b/include/move.hpp
new file mode 100644
index 0000000..ed0c28e
--- /dev/null
+++ b/include/move.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "type_traits.hpp"
+
+// static_cast to rvalue reference
+#define MOV(...) \
+  static_cast<remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
+
+// static_cast to identity
+// The extra && aren't necessary as discussed above, but make it more robust in case it's used with a non-reference.
+#define FWD(...) \
+  static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
\ No newline at end of file
diff --git a/include/option.hpp b/include/option.hpp
new file mode 100644
index 0000000..8051df1
--- /dev/null
+++ b/include/option.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace util {
+    template<typename T>
+    class Option {
+        public:
+
+
+private:
+bool m_has_value;
+T m_value;
+    };
+}
\ No newline at end of file
diff --git a/include/print.hpp b/include/print.hpp
new file mode 100644
index 0000000..509a6ce
--- /dev/null
+++ b/include/print.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <iostream>
+#include <string_view>
+#include <utility>
+#include "format.hpp"
+
+namespace util {
+
+template <typename... Ts>
+inline constexpr auto print(std::string_view fmt, Ts&&... args)
+    ->void{
+        const auto str = util::format(fmt, std::forward<Ts>(args)...);
+        std::cout.write(str.c_str(), str.size());
+}
+
+template <typename... Ts>
+inline constexpr auto println(std::string_view fmt, Ts&&... args)
+    ->void{
+        print(fmt, std::forward<Ts>(args)...);
+        std::cout.put('\n');
+}
+
+}
\ No newline at end of file
diff --git a/include/ranges/collect.hpp b/include/ranges/collect.hpp
new file mode 100644
index 0000000..b52df72
--- /dev/null
+++ b/include/ranges/collect.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "../concepts/container.hpp"
+#include <algorithm>
+#include <ranges>
+#include <unordered_set>
+#include <vector>
+
+
+namespace util {
+namespace rg = std::ranges;
+namespace detail {
+template <typename Collection, rg::input_range R>
+constexpr auto collect_range(R &&r) -> Collection {
+  Collection c;
+  if constexpr (rg::sized_range<R> && util::reservable<Collection>) {
+    c.reserve(r.size());
+  }
+
+  if constexpr (util::back_insertible<Collection>) {
+    rg::move(r, std::back_inserter(c));
+  } else if constexpr (util::back_emplaceable<Collection,
+                                              decltype(*r.begin())>) {
+    for (auto &&e : r) {
+      c.emplace_back(std::move(e));
+    }
+  } else if constexpr (util::emplaceable<Collection, decltype(*r.begin())>) {
+    for (auto &&e : r) {
+      c.emplace(std::move(e));
+    }
+  } 
+
+  return c;
+}
+
+template <typename Collection> struct collect_range_adaptor {
+  template <rg::input_range R> constexpr auto operator()(R &&r) const {
+    return collect_range<Collection>(std::forward<R>(r));
+  }
+};
+
+template <typename Collection, rg::viewable_range R>
+constexpr auto operator|(R &&r,
+                         const collect_range_adaptor<Collection> &adaptor) {
+  return adaptor(std::forward<R>(r));
+}
+} // namespace detail
+
+namespace views {
+
+template <typename Collection>
+inline detail::collect_range_adaptor<Collection> collect;
+}
+
+template <typename Collection, rg::range R>
+
+constexpr auto collect(R &&r) -> Collection {
+  return r | views::collect<Collection>;
+}
+
+template <typename Range>
+constexpr auto to_vector(Range r)
+    -> std::vector<rg::range_value_t<decltype(r)>> {
+  std::vector<rg::range_value_t<decltype(r)>> v;
+
+  if constexpr (rg::sized_range<decltype(r)>) {
+    v.reserve(rg::size(r));
+  }
+
+  std::ranges::copy(r, std::back_inserter(v));
+
+  return v;
+}
+
+template <typename Range>
+constexpr auto to_unordered_set(Range r)
+    -> std::unordered_set<std::ranges::range_value_t<decltype(r)>> {
+  std::unordered_set<std::ranges::range_value_t<decltype(r)>> v(r.begin(),
+                                                                r.end());
+  return v;
+}
+
+} // namespace util
\ No newline at end of file
diff --git a/include/ranges/drop_last.hpp b/include/ranges/drop_last.hpp
new file mode 100644
index 0000000..70df1ed
--- /dev/null
+++ b/include/ranges/drop_last.hpp
@@ -0,0 +1,68 @@
+#pragma once
+#include <ranges>
+
+namespace util {
+namespace rg = std::ranges;
+
+template <rg::sized_range R>
+requires rg::view<R>
+class drop_last_view : public rg::view_interface<drop_last_view<R>> {
+private:
+  R base_{};
+  std::iter_difference_t<rg::iterator_t<R>> count_{};
+  rg::iterator_t<R> iter_{std::begin(base_)};
+
+public:
+  drop_last_view() = default;
+
+  constexpr drop_last_view(R base,
+                           std::iter_difference_t<rg::iterator_t<R>> count)
+      : base_(base), count_(count), iter_(std::begin(base_)) {}
+
+  constexpr R base() const & { return base_; }
+  constexpr R base() && { return std::move(base_); }
+
+  constexpr auto begin() const { return iter_; }
+  constexpr auto end() const { return std::end(base_) - count_; }
+
+  constexpr auto size() const requires rg::sized_range<const R> {
+    const auto s = rg::size(base_);
+    const auto c = static_cast<decltype(s)>(count_);
+    return s < c ? 0 : s - c;
+  }
+};
+
+namespace detail {
+struct drop_last_range_adaptor_closure {
+  std::size_t count_;
+  constexpr drop_last_range_adaptor_closure(std::size_t count)
+      : count_(count) {}
+
+  template <rg::viewable_range R> constexpr auto operator()(R &&r) const {
+    return drop_last_view(std::forward<R>(r), count_);
+  }
+};
+
+struct drop_last_range_adaptor {
+  template <rg::viewable_range R>
+  constexpr auto operator()(R &&r,
+                            std::iter_difference_t<rg::iterator_t<R>> count) {
+    return drop_last_view(std::forward<R>(r), count);
+  }
+
+  constexpr auto operator()(std::size_t count) {
+    return drop_last_range_adaptor_closure(count);
+  }
+};
+
+template <rg::viewable_range R>
+constexpr auto operator|(R &&r, drop_last_range_adaptor_closure const &a) {
+  return a(std::forward<R>(r));
+}
+} // namespace detail
+
+namespace views {
+inline detail::drop_last_range_adaptor drop_last;
+} // namespace views
+
+} // namespace util
\ No newline at end of file
diff --git a/include/ranges/enumerate.hpp b/include/ranges/enumerate.hpp
new file mode 100644
index 0000000..2f9f478
--- /dev/null
+++ b/include/ranges/enumerate.hpp
@@ -0,0 +1,91 @@
+#pragma once
+#include "types.hpp"
+#include <ranges>
+
+
+namespace util {
+namespace rg = std::ranges;
+
+template <rg::range R> struct enumerate_iterator_t : rg::iterator_t<R> {
+  using base = rg::iterator_t<R>;
+  using enumerator_type = std::size_t;
+  using value_type = std::pair<enumerator_type, rg::range_value_t<R>>;
+  using reference =
+      std::pair<enumerator_type, std::remove_cv_t<rg::range_reference_t<R>>>;
+
+  enumerator_type enumerator{};
+
+  enumerate_iterator_t() = default;
+  enumerate_iterator_t(const base &b) : base{b}, enumerator(0) {}
+
+  auto operator<=>(const enumerate_iterator_t &other) const {
+    return static_cast<const base &>(*this) <=>
+           static_cast<const base &>(other);
+  }
+
+  auto inc_enumerator() -> void { enumerator++; }
+
+  auto operator++(int) -> enumerate_iterator_t {
+    const auto result = static_cast<base &>(*this)++;
+    inc_enumerator();
+    return result;
+  }
+
+  auto operator++() -> enumerate_iterator_t & {
+    inc_enumerator();
+    ++static_cast<base &>(*this);
+    return (*this);
+  }
+
+  auto operator*() -> reference {
+    return reference(enumerator, *static_cast<base &>(*this));
+  }
+  auto operator*() const -> reference {
+    return reference(enumerator, *static_cast<const base &>(*this));
+  }
+};
+
+template <rg::range R>
+class enumerate_view : public rg::view_interface<enumerate_view<R>> {
+  R base_{};
+  isize enumerator{};
+  enumerate_iterator_t<R> iter_{std::begin(base_)};
+
+public:
+  enumerate_view() = default;
+
+  constexpr enumerate_view(R base) : base_(base), iter_(std::begin(base_)) {}
+
+  constexpr R base() const & { return base_; }
+  constexpr R base() && { return std::move(base_); }
+
+  constexpr auto begin() const { return iter_; }
+  constexpr auto end() const { return std::end(base_); }
+};
+
+namespace detail {
+struct enumerate_range_adaptor_closure {
+  template <rg::viewable_range R> constexpr auto operator()(R &&r) const {
+    return enumerate_view(std::forward<R>(r));
+  }
+};
+
+struct enumerate_range_adaptor {
+  template <rg::viewable_range R> constexpr auto operator()(R &&r) {
+    return enumerate_view(std::forward<R>(r));
+  }
+
+  constexpr auto operator()() { return enumerate_range_adaptor_closure(); }
+};
+
+template <rg::viewable_range R>
+constexpr auto operator|(R &&r, enumerate_range_adaptor_closure const &a) {
+  return a(std::forward<R>(r));
+}
+} // namespace detail
+
+namespace views {
+inline detail::enumerate_range_adaptor_closure enumerate;
+} // namespace views
+
+} // namespace util
\ No newline at end of file
diff --git a/include/ranges/first.hpp b/include/ranges/first.hpp
new file mode 100644
index 0000000..338e8b0
--- /dev/null
+++ b/include/ranges/first.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <optional>
+#include <ranges>
+#include <xutility>
+
+
+namespace util {
+namespace rg = std::ranges;
+namespace detail {
+
+template <rg::input_range R>
+constexpr auto get_first(R &&r) -> std::optional<rg::range_value_t<R>> {
+  if (rg::begin(r) == rg::end(r)) {
+    return std::nullopt;
+  } else {
+    return *rg::begin(r);
+  }
+}
+
+struct first_range_adaptor {
+  template <rg::input_range R> constexpr auto operator()(R &&r) const {
+    return get_first(std::forward<R>(r));
+  }
+};
+
+template <rg::viewable_range R>
+constexpr auto operator|(R &&r, const first_range_adaptor &adaptor) {
+  return adaptor(std::forward<R>(r));
+}
+} // namespace detail
+
+namespace views {
+
+inline detail::first_range_adaptor first;
+}
+
+template <rg::range R>
+constexpr auto get_first(R &&r) -> std::optional<rg::range_value_t<R>>{
+  return r | views::first;
+}
+} // namespace util
\ No newline at end of file
diff --git a/include/ranges/generated.hpp b/include/ranges/generated.hpp
new file mode 100644
index 0000000..ac92904
--- /dev/null
+++ b/include/ranges/generated.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <concepts>
+#include <cstddef>
+#include <ranges>
+#include <xutility>
+
+
+namespace util {
+namespace rg = std::ranges;
+
+namespace detail {
+
+template <std::invocable F> struct generated_iterator {
+  using value_type = decltype(std::declval<F>()());
+  using reference = value_type &;
+  using difference_type = std::ptrdiff_t;
+
+  value_type value{};
+  F generator{};
+
+  generated_iterator() = default;
+  generated_iterator(const F &gen) : generator(gen), value(gen()) {}
+  generated_iterator(F &&gen) : generator(std::move(gen)), value(gen()) {}
+
+  auto generate_new_value() -> void { value = generator(); }
+
+  constexpr auto operator==(const generated_iterator &other) const {
+    return false;
+  }
+
+  auto operator++(int) -> generated_iterator {
+    const auto result = *this;
+    ++(*this);
+    return result;
+  }
+
+  auto operator++() -> generated_iterator & {
+    generate_new_value();
+    return (*this);
+  }
+
+  auto operator*() { return value; }
+  auto operator*() const { return value; }
+};
+} // namespace detail
+
+template <std::invocable F>
+class generated_view : rg::view_interface<generated_view<F>> {
+  F generator;
+
+public:
+  generated_view() = default;
+  constexpr generated_view(F &&generator) : generator(generator) {}
+
+  constexpr auto begin() const { return detail::generated_iterator(generator); }
+  constexpr auto end() const { return std::unreachable_sentinel; }
+};
+
+namespace detail {
+
+struct generated_range_adaptor_closure {
+  template <std::invocable F> constexpr auto operator()(F &&fn) const {
+    return generated_view(std::forward<F>(fn));
+  }
+};
+
+struct generated_range_adaptor {
+  template <std::invocable F> constexpr auto operator()(F &&r) {
+    return generated_view(std::forward<F>(r));
+  }
+
+  constexpr auto operator()() { return generated_range_adaptor_closure(); }
+};
+} // namespace detail
+
+namespace views {
+
+inline detail::generated_range_adaptor_closure from;
+}
+
+} // namespace util
\ No newline at end of file
diff --git a/include/result.hpp b/include/result.hpp
new file mode 100644
index 0000000..d6a7143
--- /dev/null
+++ b/include/result.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <optional>
+
+namespace util {
+class Result {};
+
+using result = Result;
+using expected = Result;
+} // namespace util
\ No newline at end of file
diff --git a/include/source_location.hpp b/include/source_location.hpp
new file mode 100644
index 0000000..31b60a4
--- /dev/null
+++ b/include/source_location.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "types.hpp"
+
+namespace util {
+
+struct source_location {
+  _NODISCARD static constexpr source_location
+  current(const uint_least32_t _Line_ = __builtin_LINE(),
+          const uint_least32_t _Column_ = __builtin_COLUMN(),
+          const char *const _File_ = __builtin_FILE(),
+          const char *const _Function_ = __builtin_FUNCTION()) noexcept {
+    source_location _Result;
+    _Result._Line = _Line_;
+    _Result._Column = _Column_;
+    _Result._File = _File_;
+    _Result._Function = _Function_;
+    return _Result;
+  }
+
+  _NODISCARD_CTOR constexpr source_location() noexcept = default;
+
+  _NODISCARD constexpr uint_least32_t line() const noexcept { return _Line; }
+  _NODISCARD constexpr uint_least32_t column() const noexcept {
+    return _Column;
+  }
+  _NODISCARD constexpr const char *file_name() const noexcept { return _File; }
+  _NODISCARD constexpr const char *function_name() const noexcept {
+    return _Function;
+  }
+
+private:
+  uint_least32_t _Line{};
+  uint_least32_t _Column{};
+  const char *_File = "";
+  const char *_Function = "";
+};
+}
\ No newline at end of file
diff --git a/include/step_function.hpp b/include/step_function.hpp
new file mode 100644
index 0000000..dcde6fb
--- /dev/null
+++ b/include/step_function.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "assert.hpp"
+#include "step_function.hpp"
+#include "types.hpp"
+#include <chrono>
+#include <functional>
+#include <type_traits>
+#include "time.hpp"
+
+enum class ReturnTag {
+    Repeat,
+    Next,
+    Previous,
+    Goto,
+};
+
+struct Return {
+    ReturnTag tag;
+    union {
+        isize index;
+    };
+};
+
+using namespace std::chrono_literals;
+
+class Stepped {
+
+using duration_type = std::chrono::system_clock::duration;
+    using step_return_type = std::pair<Return, duration_type>;
+    using step_type = std::function<step_return_type()>;
+
+    std::vector<step_type> m_steps;
+
+    isize m_current_step;
+    duration_type m_current_wait_duration;
+    util::Delta m_delta;
+
+    auto inc_step() -> void;
+    auto dec_step() -> void;
+
+    public:
+    Stepped() : m_current_step(0), m_steps(), m_delta(), m_current_wait_duration(0ms) {}
+    
+    auto add_step(const step_type& step) -> void;
+    auto run() -> void;
+    auto reset() -> void;
+
+
+};
+
+/// IMPLEMENTATION
+
+using namespace std::chrono_literals;
+
+auto Stepped::inc_step() -> void {
+    if (++m_current_step >= m_steps.size()) {
+        m_current_step = 0;
+    }
+}
+
+auto Stepped::dec_step() -> void {
+    if (--m_current_step < 0) {
+        m_current_step = m_steps.size() - 1;
+    }
+}
+
+auto Stepped::add_step(const step_type &step) -> void {
+    m_steps.push_back(step);
+}
+
+auto Stepped::reset() -> void {
+    m_current_step = 0;
+    m_current_wait_duration = 0ms;
+}
+
+auto Stepped::run() -> void {
+    if (m_delta.has_elapsed(m_current_wait_duration)) {
+        util::assert(m_current_step < m_steps.size());
+
+        auto&& [result, next_wait_duration] = m_steps[m_current_step]();
+
+        switch (result.tag) {
+        case ReturnTag::Repeat:
+        break;
+        case ReturnTag::Next:
+        inc_step();
+          break;
+        case ReturnTag::Previous:
+        dec_step();
+          break;
+        case ReturnTag::Goto: {
+        util::assert(result.index < m_steps.size() && result.index >= 0);
+       m_current_step = result.index;
+          break;
+        }
+        }
+
+        m_current_wait_duration = next_wait_duration;
+    }
+}
\ No newline at end of file
diff --git a/include/string.hpp b/include/string.hpp
new file mode 100644
index 0000000..31b9cab
--- /dev/null
+++ b/include/string.hpp
@@ -0,0 +1,151 @@
+#pragma once
+
+#include "types.hpp"
+#include <cctype>
+#include <limits>
+#include <string_view>
+#include <type_traits>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+#endif
+
+#include <algorithm>
+#include <locale>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace util {
+#ifdef _WIN32
+inline std::string to_string(std::wstring_view wstr) {
+  const auto size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(),
+                                        nullptr, 0, nullptr, nullptr);
+
+  std::string str(size, 0);
+
+  WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr.length(), str.data(),
+                      str.length(), nullptr, nullptr);
+
+  return str;
+}
+
+inline std::wstring to_wstring(std::string_view str) {
+  const auto size =
+      MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), nullptr, 0);
+
+  std::wstring wstr(size, 0);
+
+  MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), wstr.data(),
+                      wstr.length());
+
+  return wstr;
+}
+#endif
+
+template <typename A>
+std::basic_string<A> to_lower(std::basic_string_view<A> view) {
+  std::basic_string<A> out(view.size(), '\0');
+
+  std::transform(
+      std::begin(view), std::end(view), std::begin(out),
+      [](typename std::basic_string<A>::value_type c) {
+        return std::use_facet<
+                   std::ctype<typename std::basic_string<A>::value_type>>(
+                   std::locale())
+            .tolower(c);
+      });
+
+  return out;
+}
+
+template <typename CharT>
+constexpr bool string_eq_nocase(std::basic_string_view<CharT> rh,
+                                std::basic_string_view<CharT> lh) {
+  const auto rh_lower = to_lower<CharT>(rh);
+  const auto lh_lower = to_lower<CharT>(lh);
+
+  return rh_lower == lh_lower;
+}
+
+template <typename CharT>
+auto split_with(const std::basic_string<CharT> &str, CharT delimiter)
+    -> std::vector<std::basic_string<CharT>> {
+  std::vector<std::basic_string<CharT>> parts;
+  std::basic_string<CharT> part;
+
+  const auto start = str.find_first_not_of(delimiter);
+  const auto end = str.find_last_not_of(delimiter);
+  std::basic_istringstream<CharT> stream(
+      str.substr(start, end == std::string::npos ? end : end + 1));
+
+  while (std::getline(stream, part, delimiter))
+    parts.push_back(part);
+
+  return parts;
+}
+
+#include <concepts>
+
+template <std::integral I>
+inline constexpr auto from_char_radix(const char &c, I radix = 10) -> I {
+  I value = 0;
+
+  if (std::isdigit(c)) {
+    value = c - '0';
+  } else if (std::isalpha(c)) {
+    if (std::isupper(c)) {
+      value = c + 10 - 'A';
+    } else {
+      value = c + 10 - 'a';
+    }
+  }
+
+  return value < radix ? value : std::numeric_limits<I>::max();
+}
+
+template <std::integral I>
+inline constexpr auto from_string_view_radix(std::string_view view,
+                                             I radix = 10) -> I {
+  auto negative = false;
+  if (view.starts_with('-')) {
+    negative = true;
+    view.remove_prefix(1);
+  } else if (view.starts_with('+')) {
+    negative = false;
+    view.remove_prefix(1);
+  }
+
+  I value = 0;
+  const auto max = std::numeric_limits<I>::max();
+
+  for (auto &&c : view) {
+    const auto i = from_char_radix<I>(c, radix);
+
+    if (i >= radix) {
+      return max;
+    }
+
+    if (value > max / radix) {
+      return max;
+    }
+
+    value *= radix;
+
+    if (i > max - value) {
+      return max;
+    }
+
+    value += i;
+  }
+
+  if (negative && std::is_signed_v<I>) {
+    return -value;
+  } else {
+    return value;
+  }
+}
+
+} // namespace util
diff --git a/include/tagged_union.hpp b/include/tagged_union.hpp
new file mode 100644
index 0000000..df38656
--- /dev/null
+++ b/include/tagged_union.hpp
@@ -0,0 +1,124 @@
+#pragma once
+
+#include "assert.hpp"
+#include "enum.hpp"
+#include "tagged_union.hpp"
+#include "type_traits.hpp"
+#include "types.hpp"
+#include <memory>
+#include <optional>
+#include <concepts>
+#include <type_traits>
+#include <vector>
+
+namespace util {
+namespace detail {
+template <int N, typename... Ts>
+using nth_type = typename std::tuple_element<N, std::tuple<Ts...>>::type;
+
+template <typename> consteval std::size_t locate(std::size_t ind) {
+  return static_cast<std::size_t>(-1);
+}
+
+template <typename IndexedType, typename T, typename... Ts>
+consteval std::size_t locate(std::size_t ind = 0) {
+  if (std::is_same<IndexedType, T>::value) {
+    return ind;
+  } else {
+    return locate<IndexedType, Ts...>(ind + 1);
+  }
+}
+
+template <typename T, typename... Ts>
+struct index_of
+    : std::integral_constant<std::size_t, locate<T, std::tuple<Ts...>>> {};
+
+template <typename T, typename... Ts>
+    inline constexpr std::size_t index_of_v = index_of<T, Ts...>::value;
+
+
+template <typename IndexedType, typename...> struct is_in : std::false_type {};
+
+template <typename IndexedType, typename Item, typename... Items>
+struct is_in<IndexedType, Item, Items...>
+    : std::integral_constant<bool, std::is_same<IndexedType, Item>::value ||
+                                       is_in<IndexedType, Items...>::value> {};
+
+
+template <typename Item, typename... Items>
+concept in_types = is_in<std::size_t, Item, Items...>::value;
+
+
+template<typename T>
+inline constexpr auto max_all_inner(T arg) -> T {
+    return arg;
+}
+
+template<typename T, typename... Ts>
+inline constexpr auto max_all_inner(T arg, Ts... args) -> decltype(max_all_inner(arg)){
+    return std::max(arg, max_all_inner(args...));
+}
+
+template<typename... Ts>
+inline constexpr auto max_all(Ts... args) -> usize {
+    return max_all_inner(args...);
+}
+
+template <typename... Ts> class variadic_union {
+  std::vector<u8> m_data;
+  std::optional<usize> m_type_index;
+
+public:
+  variadic_union() : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(std::nullopt) {}
+
+  template<std::size_t I, typename... Us>
+  variadic_union(Us&&... args) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) {
+    *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = detail::nth_type<I, Ts...>{std::forward<Us>(args)...};
+  }
+
+  template<std::size_t I, typename U>
+  requires (std::same_as<U, detail::nth_type<I, Ts...>>)
+  variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) {
+    *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = value;
+  }
+
+  template<std::size_t I, typename U>
+  requires (std::same_as<U, detail::nth_type<I, Ts...>>)
+  variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(I) {
+    *reinterpret_cast<detail::nth_type<I, Ts...>*>(m_data.data()) = std::move(value);
+  }
+
+  template<typename U>
+  requires (in_types<U, Ts...>)
+  variadic_union(U&& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v<U, Ts...>) {
+    *reinterpret_cast<U*>(m_data.data()) = std::move(value);
+  }
+
+  template<typename U>
+  requires (in_types<U, Ts...>)
+  variadic_union(const U& value) : m_data(detail::max_all(sizeof(Ts)...)), m_type_index(index_of_v<U, Ts...>) {
+    *reinterpret_cast<U*>(m_data.data()) = value;
+  }
+
+template<typename U>
+constexpr auto get() -> U& {
+    util::assert_eq(m_type_index.value(), index_of_v<U, Ts...>);
+
+    return reinterpret_cast<U*>(m_data.data());
+}
+};
+
+} // namespace detail
+
+#define NTH(Is, Ts) typename detail::nth_type<, Ts...> element_##I;
+
+template <util::enum_type Tag, typename... Ts> class TaggedUnion {
+    Tag tag;
+    detail::variadic_union<Ts...> inner;
+public:
+template <typename U>
+  requires (detail::in_types<U, Ts...>)
+TaggedUnion(const Tag& tag, const U& value) : tag(tag), inner(detail::variadic_union(value)) {}
+
+};
+} // namespace util
\ No newline at end of file
diff --git a/include/thread.hpp b/include/thread.hpp
new file mode 100644
index 0000000..76bb7c7
--- /dev/null
+++ b/include/thread.hpp
@@ -0,0 +1,164 @@
+#pragma once
+
+#include "types.hpp"
+#include <algorithm>
+#include <optional>
+#include <string_view>
+#include <vector>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <Windows.h>
+
+#include <TlHelp32.h>
+
+namespace util {
+namespace threads {
+
+auto get_all_threads(u32 pid) -> std::vector<u32>;
+
+auto get_all_threads() -> std::vector<u32>;
+auto get_other_threads() -> std::vector<u32>;
+
+auto suspend_threads(const std::vector<u32> &thread_ids) -> std::vector<u32>;
+
+auto resume_threads(const std::vector<u32> &thread_ids) -> bool;
+
+/// suspends all threads, only then executes function, and resumes threads
+/// returns `nullopt` if not all threads were suspended.
+template <typename T, typename F>
+auto with_suspended_threads(F &&fn) -> std::optional<T> {
+  auto threads = get_other_threads();
+
+  auto suspended_threads = suspend_threads(threads);
+
+  const auto suspended_all_threads = suspended_threads.size() == threads.size();
+
+  std::optional<T> result = std::nullopt;
+  if (suspended_all_threads) {
+    result = fn();
+  }
+
+  return result;
+}
+
+} // namespace threads
+
+namespace processes {
+auto get_process_id(std::string_view name) -> std::optional<u32>;
+auto get_process_id(std::wstring_view name) -> std::optional<u32>;
+} // namespace processes
+
+/// impl
+
+namespace threads {
+inline auto get_all_threads(u32 pid) -> std::vector<u32> {
+  std::vector<u32> threads;
+  const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+
+  if (snapshot != INVALID_HANDLE_VALUE) {
+    THREADENTRY32 t;
+    t.dwSize = sizeof(t);
+    if (Thread32First(snapshot, &t)) {
+      if (t.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
+                          sizeof(t.th32OwnerProcessID)) {
+        if (t.th32OwnerProcessID == pid) {
+          threads.push_back(t.th32ThreadID);
+        }
+      }
+    }
+  }
+
+  return threads;
+}
+
+inline auto get_all_threads() -> std::vector<u32> {
+  return get_all_threads(GetCurrentProcessId());
+}
+
+inline auto get_other_threads() -> std::vector<u32> {
+  auto threads = get_all_threads(GetCurrentProcessId());
+
+  threads.erase(
+      std::remove_if(threads.begin(), threads.end(),
+                     [](auto &&id) { return id == GetCurrentThreadId(); }),
+      threads.end());
+
+  return threads;
+}
+
+inline auto resume_threads(const std::vector<u32> &thread_ids) -> bool {
+  // return false if any threads failed to resume
+  return std::count_if(thread_ids.begin(), thread_ids.end(), [](auto &&id) {
+           if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) {
+             const auto result = ResumeThread(handle);
+             CloseHandle(handle);
+
+             return result != (u32)-1;
+           }
+           return false;
+         }) == thread_ids.size();
+}
+
+inline auto suspend_threads(const std::vector<u32> &thread_ids)
+    -> std::vector<u32> {
+  std::vector<u32> suspended_threads;
+  for (auto &&id : thread_ids) {
+    if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) {
+      if (SuspendThread(handle) != (u32)-1) {
+        suspended_threads.push_back(id);
+      }
+      CloseHandle(handle);
+    }
+  }
+  return suspended_threads;
+}
+} // namespace threads
+
+namespace processes {
+
+inline auto get_process_id(std::string_view name) -> std::optional<u32> {
+  auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+  std::optional<u32> result = std::nullopt;
+
+  PROCESSENTRY32 process_entry;
+  if (Process32First(snapshot, &process_entry)) {
+    do {
+      const auto process_name = std::string_view(process_entry.szExeFile);
+      if (name == process_name) {
+        result = process_entry.th32ProcessID;
+      }
+    } while (Process32Next(snapshot, &process_entry));
+  }
+
+  CloseHandle(snapshot);
+
+  return result;
+}
+
+inline auto get_process_id(std::wstring_view name) -> std::optional<u32> {
+  auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+  std::optional<u32> result = std::nullopt;
+
+  PROCESSENTRY32W process_entry;
+  if (Process32FirstW(snapshot, &process_entry)) {
+    do {
+      const auto process_name = std::wstring_view(process_entry.szExeFile);
+      if (name == process_name) {
+        result = process_entry.th32ProcessID;
+      }
+    } while (Process32NextW(snapshot, &process_entry));
+  }
+
+  CloseHandle(snapshot);
+
+  return result;
+}
+
+} // namespace processes
+
+} // namespace util
+#endif
\ No newline at end of file
diff --git a/include/time.hpp b/include/time.hpp
new file mode 100644
index 0000000..82ddd98
--- /dev/null
+++ b/include/time.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "types.hpp"
+#include <chrono>
+#include <ratio>
+
+namespace util {
+using std::chrono::system_clock;
+using namespace std::chrono_literals;
+
+template <typename F>
+auto time_of(F &&fn) -> std::chrono::duration<f64, std::milli> {
+  const auto start = std::chrono::system_clock::now();
+  fn();
+  const auto end = std::chrono::system_clock::now();
+
+  return {end - start};
+}
+
+class Delta {
+  system_clock::time_point last;
+
+public:
+  Delta() : last(system_clock::now()) {}
+
+  template <typename Rep, typename Ratio>
+  inline auto has_elapsed(const std::chrono::duration<Rep, Ratio> &duration)
+      -> bool {
+    const auto now = system_clock::now();
+
+    if (duration < (now - last)) {
+      last = now;
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  template <typename Rep = usize, typename Ratio = std::milli>
+  inline auto elapsed() -> std::chrono::duration<Rep, Ratio> {
+    const auto now = system_clock::now();
+    const auto duration =
+        std::chrono::duration_cast<std::chrono::duration<Rep, Ratio>>(now -
+                                                                      last);
+    last = now;
+
+    return duration;
+  }
+
+  inline auto has_elapsed_s(f64 seconds) -> bool {
+    return has_elapsed(std::chrono::duration<f64>(seconds));
+  }
+
+  inline auto has_elapsed_ms(i64 milliseconds) -> bool {
+    return has_elapsed(std::chrono::duration<i64, std::milli>(milliseconds));
+  }
+
+  inline auto elapsed_ms() -> std::chrono::duration<isize, std::milli> {
+    const auto now = system_clock::now();
+    const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+        system_clock::now() - last);
+    last = now;
+    return duration;
+  }
+
+  inline auto elapsed_s() -> std::chrono::duration<f64> {
+    const auto now = system_clock::now();
+    const auto duration =
+        std::chrono::duration<f64>(system_clock::now() - last);
+    last = now;
+    return duration;
+  }
+};
+} // namespace util
diff --git a/include/type_traits.hpp b/include/type_traits.hpp
new file mode 100644
index 0000000..5e2e032
--- /dev/null
+++ b/include/type_traits.hpp
@@ -0,0 +1,78 @@
+#pragma once
+
+namespace util {
+
+
+template <typename T, T V>
+struct integral_constant {static constexpr T value = V;};
+
+struct true_type : integral_constant<bool, true> {};
+struct false_type : integral_constant<bool, false> {};
+
+inline constexpr bool true_type_v = true_type::value;
+inline constexpr bool false_type_v = false_type::value;
+
+namespace detail {
+template <class T> struct type_identity {
+  using type = T;
+}; // or use std::type_identity (since C++20)
+
+template <class T> // Note that `cv void&` is a substitution failure
+auto try_add_lvalue_reference(int) -> type_identity<T &>;
+template <class T> // Handle T = cv void case
+auto try_add_lvalue_reference(...) -> type_identity<T>;
+
+template <class T> auto try_add_rvalue_reference(int) -> type_identity<T &&>;
+template <class T> auto try_add_rvalue_reference(...) -> type_identity<T>;
+
+} // namespace detail
+
+template <class T>
+struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {
+};
+
+template <class T>
+struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {
+};
+
+template <class T>
+using add_rvalue_reference_t = typename add_rvalue_reference<T>::type;
+
+template <class T> add_rvalue_reference_t<T> declval() noexcept {
+  #pragma clang diagnostic push
+  static_assert(false, "Calling declval is ill-formed, see N4892 [declval]/2.");
+  #pragma clang diagnostic pop
+}
+
+namespace detail {
+
+template <class T>
+auto test_returnable(int)
+    -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
+template <class> auto test_returnable(...) -> false_type;
+
+template <class From, class To>
+auto test_implicitly_convertible(int)
+    -> decltype(void(declval<void (&)(To)>()(declval<From>())),
+                true_type{});
+template <class, class>
+auto test_implicitly_convertible(...) -> false_type;
+
+} // namespace detail
+
+template <typename T>
+struct is_void : false_type{};
+
+template<>
+struct is_void<void> : true_type {};
+
+template <typename T>
+inline constexpr bool is_void_v = is_void<T>::value;
+
+template< class T > struct remove_reference      { typedef T type; };
+template< class T > struct remove_reference<T&>  { typedef T type; };
+template< class T > struct remove_reference<T&&> { typedef T type; };
+
+template< class T >
+using remove_reference_t = typename remove_reference<T>::type;
+}
\ No newline at end of file
diff --git a/include/types.hpp b/include/types.hpp
new file mode 100644
index 0000000..41713c4
--- /dev/null
+++ b/include/types.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+
+using byte = unsigned char;
+
+using u8 = uint8_t;
+using i8 = int8_t;
+
+using u16 = uint16_t;
+using i16 = int16_t;
+
+using u32 = uint32_t;
+using i32 = int32_t;
+
+using u64 = uint64_t;
+using i64 = int64_t;
+
+using usize = std::size_t;
+using isize = std::ptrdiff_t;
+
+using f32 = float;
+using f64 = double;
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..418cd5c
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,50 @@
+#include "format.hpp"
+#include "print.hpp"
+#include "ranges/first.hpp"
+#include "ranges/generated.hpp"
+#include "tagged_union.hpp"
+#include <cstdlib>
+#include <iostream>
+#include <stdio.h>
+
+#define UTIL_ASSERT_FORMAT
+#include "assert.hpp"
+#include "ranges/collect.hpp"
+#include <ranges>
+#include <set>
+#include <vector>
+#include <string_view>
+
+using namespace std::string_view_literals;
+
+int main(int argc, const char *argv[]) {
+  printf("Hello, Alloy!\n");
+  auto vec = std::vector<int>({1, 2, 3, 4, 5});
+
+  auto range = vec | std::views::filter([](auto &i) { return i % 2; });
+  auto infinite_range = util::views::from([] {return 1;}) | std::views::take(5);
+
+  auto random = util::views::from([] {
+          return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"
+              [std::rand() %
+               std::strlen(
+                   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY")];
+        }) |
+        std::views::take(10) | util::views::collect<std::string>;
+
+auto first = random | util::views::first;
+
+util::println("{}", first);
+util::println("{}", random);
+
+  for (auto&& i : infinite_range) {
+    util::print("{}\n", i);
+  }
+
+util::print("hello {}\n", "world");
+
+  util::assert_eq("asdf"sv, "nsdf"sv);
+
+  const auto set = util::collect<std::set<int>>(range);
+  return 1;
+}
\ No newline at end of file