diff --git a/externals/mcl/.clang-format b/externals/mcl/.clang-format
new file mode 100644
index 00000000..5c5555ea
--- /dev/null
+++ b/externals/mcl/.clang-format
@@ -0,0 +1,218 @@
+---
+Language:        Cpp
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveMacros: None
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: None
+AlignConsecutiveDeclarations: None
+AlignConsecutiveMacros: None
+AlignEscapedNewlines: Right
+AlignOperands:   AlignAfterOperator
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros:
+  - __capability
+BinPackArguments: true
+BinPackParameters: false
+BitFieldColonSpacing: Both
+BraceWrapping:
+  AfterCaseLabel:  false
+  AfterClass:      false
+  AfterControlStatement: Never
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  AfterExternBlock: false
+  BeforeCatch:     false
+  BeforeElse:      false
+  BeforeLambdaBody: false
+  BeforeWhile:     false
+  IndentBraces:    false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: false
+  SplitEmptyNamespace: false
+BreakBeforeBinaryOperators: All
+BreakBeforeBraces: Custom
+BreakBeforeConceptDeclarations: true
+BreakBeforeTernaryOperators: true
+BreakBeforeInheritanceComma: false
+BreakConstructorInitializersBeforeComma: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     0
+CommentPragmas:  '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat:   false
+# EmptyLineAfterAccessModifier: Leave
+EmptyLineBeforeAccessModifier: Always
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+  - foreach
+  - Q_FOREACH
+  - BOOST_FOREACH
+IncludeBlocks:   Regroup
+IncludeCategories:
+  - Regex:           '^<mach/'
+    Priority:        1
+    SortPriority:    0
+    CaseSensitive:   false
+  - Regex:           '^<windows.h>'
+    Priority:        1
+    SortPriority:    0
+    CaseSensitive:   false
+  - Regex:           '(^<signal.h>)|(^<sys/ucontext.h>)|(^<ucontext.h>)'
+    Priority:        1
+    SortPriority:    0
+    CaseSensitive:   false
+  - Regex:           '^<([^\.])*>$'
+    Priority:        2
+    SortPriority:    0
+    CaseSensitive:   false
+  - Regex:           '^<.*\.'
+    Priority:        3
+    SortPriority:    0
+    CaseSensitive:   false
+  - Regex:           '.*'
+    Priority:        4
+    SortPriority:    0
+    CaseSensitive:   false
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IncludeIsMainSourceRegex: ''
+# IndentAccessModifiers: false
+IndentCaseBlocks: false
+IndentCaseLabels: false
+IndentExternBlock: NoIndent
+IndentGotoLabels: false
+IndentPPDirectives: AfterHash
+IndentRequires:  false
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+# InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+NamespaceMacros:
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PenaltyIndentedWhitespace: 0
+PointerAlignment: Left
+RawStringFormats:
+  - Language:        Cpp
+    Delimiters:
+      - cc
+      - CC
+      - cpp
+      - Cpp
+      - CPP
+      - 'c++'
+      - 'C++'
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+  - Language:        TextProto
+    Delimiters:
+      - pb
+      - PB
+      - proto
+      - PROTO
+    EnclosingFunctions:
+      - EqualsProto
+      - EquivToProto
+      - PARSE_PARTIAL_TEXT_PROTO
+      - PARSE_TEST_PROTO
+      - PARSE_TEXT_PROTO
+      - ParseTextOrDie
+      - ParseTextProtoOrDie
+      - ParseTestProto
+      - ParsePartialTestProto
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+ReflowComments:  true
+# ShortNamespaceLines: 5
+SortIncludes:    true
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles:  false
+SpacesInConditionalStatement: false
+SpacesInCStyleCastParentheses: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: false
+# SpacesInLineCommentPrefix: -1
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Latest
+StatementAttributeLikeMacros:
+  - Q_EMIT
+StatementMacros:
+  - Q_UNUSED
+  - QT_REQUIRE_VERSION
+TabWidth:        4
+TypenameMacros:
+UseCRLF:         false
+UseTab:          Never
+WhitespaceSensitiveMacros:
+  - STRINGIZE
+  - PP_STRINGIZE
+  - BOOST_PP_STRINGIZE
+  - NS_SWIFT_NAME
+  - CF_SWIFT_NAME
+  - FCODE
+  - ICODE
+...
+
diff --git a/externals/mcl/.gitignore b/externals/mcl/.gitignore
new file mode 100644
index 00000000..547b4ec7
--- /dev/null
+++ b/externals/mcl/.gitignore
@@ -0,0 +1,2 @@
+*build*/
+.DS_Store
diff --git a/externals/mcl/CMakeLists.txt b/externals/mcl/CMakeLists.txt
new file mode 100644
index 00000000..c1c28f61
--- /dev/null
+++ b/externals/mcl/CMakeLists.txt
@@ -0,0 +1,126 @@
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+include(GNUInstallDirs)
+
+project(mcl LANGUAGES CXX VERSION 0.1.3)
+
+# Project options
+option(MCL_WARNINGS_AS_ERRORS "Warnings as errors" ON)
+
+# Default to a Release build
+if (NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+    message(STATUS "Defaulting to a Release build")
+endif()
+
+# Set hard requirements for C++
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+# Warn on CMake API deprecations
+set(CMAKE_WARN_DEPRECATED ON)
+
+# Disable in-source builds
+set(CMAKE_DISABLE_SOURCE_CHANGES ON)
+set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
+if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
+    message(SEND_ERROR "In-source builds are not allowed.")
+endif()
+
+# Add the module directory to the list of paths
+list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
+
+# Compiler flags
+if (MSVC)
+    set(MCL_CXX_FLAGS
+        /std:c++latest
+        /experimental:external
+        /external:W0
+        /external:anglebrackets
+        /W4
+        /w44263 # Non-virtual member function hides base class virtual function
+        /w44265 # Class has virtual functions, but destructor is not virtual
+        /w44456 # Declaration of 'var' hides previous local declaration
+        /w44457 # Declaration of 'var' hides function parameter
+        /w44458 # Declaration of 'var' hides class member
+        /w44459 # Declaration of 'var' hides global definition
+        /w44946 # Reinterpret-cast between related types
+        /wd4592 # Symbol will be dynamically initialized (implementation limitation)
+        /permissive- # Stricter C++ standards conformance
+        /MP
+        /Zi
+        /Zo
+        /EHsc
+        /Zc:externConstexpr # Allows external linkage for variables declared "extern constexpr", as the standard permits.
+        /Zc:inline          # Omits inline functions from object-file output.
+        /Zc:throwingNew     # Assumes new (without std::nothrow) never returns null.
+        /volatile:iso       # Use strict standard-abiding volatile semantics
+        /bigobj             # Increase number of sections in .obj files
+        /DNOMINMAX)
+
+    if (MCL_WARNINGS_AS_ERRORS)
+        list(APPEND MCL_CXX_FLAGS /WX)
+    endif()
+
+    if (CMAKE_VS_PLATFORM_TOOLSET MATCHES "LLVM-vs[0-9]+")
+        list(APPEND MCL_CXX_FLAGS
+             -Qunused-arguments
+             -Wno-missing-braces)
+    endif()
+else()
+    set(MCL_CXX_FLAGS
+        -Wall
+        -Wextra
+        -Wcast-qual
+        -pedantic
+        -pedantic-errors
+        -Wfatal-errors
+        -Wno-missing-braces)
+
+    if (MCL_WARNINGS_AS_ERRORS)
+        list(APPEND MCL_CXX_FLAGS -Werror)
+    endif()
+endif()
+
+# Dependencies
+
+if (NOT TARGET Catch2::Catch2)
+    find_package(Catch2 QUIET)
+endif()
+
+if (NOT TARGET fmt::fmt)
+    find_package(fmt REQUIRED)
+endif()
+
+# Project files
+
+add_subdirectory(src)
+if (TARGET Catch2::Catch2)
+    add_subdirectory(tests)
+endif()
+
+# Install instructions
+
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+install(TARGETS mcl EXPORT mclTargets)
+install(EXPORT mclTargets
+    NAMESPACE merry::
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
+)
+
+configure_package_config_file(CMakeModules/mclConfig.cmake.in
+    mclConfig.cmake
+    INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
+)
+write_basic_package_version_file(mclConfigVersion.cmake
+    COMPATIBILITY SameMajorVersion
+)
+install(FILES
+    "${CMAKE_CURRENT_BINARY_DIR}/mclConfig.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/mclConfigVersion.cmake"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
+)
+
+install(DIRECTORY include/ TYPE INCLUDE FILES_MATCHING PATTERN "*.hpp")
diff --git a/externals/mcl/CMakeModules/CreateTargetDirectoryGroups.cmake b/externals/mcl/CMakeModules/CreateTargetDirectoryGroups.cmake
new file mode 100644
index 00000000..175899e7
--- /dev/null
+++ b/externals/mcl/CMakeModules/CreateTargetDirectoryGroups.cmake
@@ -0,0 +1,17 @@
+# This function should be passed a name of an existing target. It will automatically generate
+# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
+# one in the filesystem.
+function(create_target_directory_groups target_name)
+    # Place any files that aren't in the source list in a separate group so that they don't get in
+    # the way.
+    source_group("Other Files" REGULAR_EXPRESSION ".")
+
+    get_target_property(target_sources "${target_name}" SOURCES)
+
+    foreach(file_name IN LISTS target_sources)
+        get_filename_component(dir_name "${file_name}" PATH)
+        # Group names use '\' as a separator even though the entire rest of CMake uses '/'...
+        string(REPLACE "/" "\\" group_name "${dir_name}")
+        source_group("${group_name}" FILES "${file_name}")
+    endforeach()
+endfunction()
diff --git a/externals/mcl/CMakeModules/DetectArchitecture.cmake b/externals/mcl/CMakeModules/DetectArchitecture.cmake
new file mode 100644
index 00000000..1fbd5e9d
--- /dev/null
+++ b/externals/mcl/CMakeModules/DetectArchitecture.cmake
@@ -0,0 +1,56 @@
+include(CheckSymbolExists)
+
+function(detect_architecture symbol arch)
+    if (NOT DEFINED ARCHITECTURE)
+        set(CMAKE_REQUIRED_QUIET YES)
+        check_symbol_exists("${symbol}" "" DETECT_ARCHITECTURE_${arch})
+        unset(CMAKE_REQUIRED_QUIET)
+
+        if (DETECT_ARCHITECTURE_${arch})
+            set(ARCHITECTURE "${arch}" PARENT_SCOPE)
+        endif()
+
+        unset(DETECT_ARCHITECTURE_${arch} CACHE)
+    endif()
+endfunction()
+
+detect_architecture("__ARM64__" arm64)
+detect_architecture("__aarch64__" arm64)
+detect_architecture("_M_ARM64" arm64)
+
+detect_architecture("__arm__" arm32)
+detect_architecture("__TARGET_ARCH_ARM" arm32)
+detect_architecture("_M_ARM" arm32)
+
+detect_architecture("__x86_64" x86_64)
+detect_architecture("__x86_64__" x86_64)
+detect_architecture("__amd64" x86_64)
+detect_architecture("_M_X64" x86_64)
+
+detect_architecture("__i386" x86_32)
+detect_architecture("__i386__" x86_32)
+detect_architecture("_M_IX86" x86_32)
+
+detect_architecture("__ia64" ia64)
+detect_architecture("__ia64__" ia64)
+detect_architecture("_M_IA64" ia64)
+
+detect_architecture("__mips" mips)
+detect_architecture("__mips__" mips)
+detect_architecture("_M_MRX000" mips)
+
+detect_architecture("__ppc64__" ppc64)
+detect_architecture("__powerpc64__" ppc64)
+
+detect_architecture("__ppc__" ppc32)
+detect_architecture("__ppc" ppc32)
+detect_architecture("__powerpc__" ppc32)
+detect_architecture("_ARCH_COM" ppc32)
+detect_architecture("_ARCH_PWR" ppc32)
+detect_architecture("_ARCH_PPC" ppc32)
+detect_architecture("_M_MPPC" ppc32)
+detect_architecture("_M_PPC" ppc32)
+
+detect_architecture("__riscv" riscv)
+
+detect_architecture("__EMSCRIPTEN__" wasm)
diff --git a/externals/mcl/CMakeModules/mclConfig.cmake.in b/externals/mcl/CMakeModules/mclConfig.cmake.in
new file mode 100644
index 00000000..8c9ad12a
--- /dev/null
+++ b/externals/mcl/CMakeModules/mclConfig.cmake.in
@@ -0,0 +1,5 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+check_required_components(@PROJECT_NAME@)
diff --git a/externals/mcl/LICENSE b/externals/mcl/LICENSE
new file mode 100644
index 00000000..614ef755
--- /dev/null
+++ b/externals/mcl/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 merryhime
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/externals/mcl/README b/externals/mcl/README
new file mode 100644
index 00000000..55feaceb
--- /dev/null
+++ b/externals/mcl/README
@@ -0,0 +1,17 @@
+
+                                                    oooo
+                                                    `888
+                        ooo. .oo.  .oo.    .ooooo.   888
+                        `888P"Y88bP"Y88b  d88' `"Y8  888
+                         888   888   888  888        888
+                         888   888   888  888   .o8  888
+                        o888o o888o o888o `Y8bod8P' o888o
+
+
+  .-.-.   .-.-.   .-.-.   .-.-.   .-.-.   .-.-.   .-.-.   .-.-.   .-.-.   .-.-.
+ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \
+`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`-'   `-`
+
+A collection of C++20 utilities which is common to a number of merry's projects.
+
+MIT licensed.
diff --git a/externals/mcl/include/mcl/assert.hpp b/externals/mcl/include/mcl/assert.hpp
new file mode 100644
index 00000000..3b9377a7
--- /dev/null
+++ b/externals/mcl/include/mcl/assert.hpp
@@ -0,0 +1,61 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <stdexcept>
+#include <type_traits>
+
+#include <fmt/format.h>
+
+#include "mcl/hint/assume.hpp"
+
+namespace mcl::detail {
+
+[[noreturn]] void assert_terminate_impl(fmt::string_view msg, fmt::format_args args);
+
+template<typename... Ts>
+[[noreturn]] void assert_terminate(fmt::string_view msg, Ts... args) {
+    assert_terminate_impl(msg, fmt::make_format_args(args...));
+}
+
+}  // namespace mcl::detail
+
+#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
+
+#define ASSERT(expr)                                                     \
+    [&] {                                                                \
+        if (std::is_constant_evaluated()) {                              \
+            if (!(expr)) {                                               \
+                throw std::logic_error{"ASSERT failed at compile time"}; \
+            }                                                            \
+        } else {                                                         \
+            if (!(expr)) [[unlikely]] {                                  \
+                ::mcl::detail::assert_terminate(#expr);                  \
+            }                                                            \
+        }                                                                \
+    }()
+
+#define ASSERT_MSG(expr, ...)                                                     \
+    [&] {                                                                         \
+        if (std::is_constant_evaluated()) {                                       \
+            if (!(expr)) {                                                        \
+                throw std::logic_error{"ASSERT_MSG failed at compile time"};      \
+            }                                                                     \
+        } else {                                                                  \
+            if (!(expr)) [[unlikely]] {                                           \
+                ::mcl::detail::assert_terminate(#expr "\nMessage: " __VA_ARGS__); \
+            }                                                                     \
+        }                                                                         \
+    }()
+
+#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false\nMessage: " __VA_ARGS__)
+
+#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS)
+#    define DEBUG_ASSERT(expr) ASSUME(expr)
+#    define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
+#else
+#    define DEBUG_ASSERT(expr) ASSERT(expr)
+#    define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
+#endif
diff --git a/externals/mcl/include/mcl/bit/bit_count.hpp b/externals/mcl/include/mcl/bit/bit_count.hpp
new file mode 100644
index 00000000..c08abd00
--- /dev/null
+++ b/externals/mcl/include/mcl/bit/bit_count.hpp
@@ -0,0 +1,54 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <bitset>
+
+#include "mcl/bitsizeof.hpp"
+#include "mcl/concepts/bit_integral.hpp"
+#include "mcl/stdint.hpp"
+
+namespace mcl::bit {
+
+template<BitIntegral T>
+inline size_t count_ones(T x) {
+    return std::bitset<bitsizeof<T>>(x).count();
+}
+
+template<BitIntegral T>
+constexpr size_t count_leading_zeros(T x) {
+    size_t result = bitsizeof<T>;
+    while (x != 0) {
+        x >>= 1;
+        result--;
+    }
+    return result;
+}
+
+template<BitIntegral T>
+constexpr int highest_set_bit(T x) {
+    int result = -1;
+    while (x != 0) {
+        x >>= 1;
+        result++;
+    }
+    return result;
+}
+
+template<BitIntegral T>
+constexpr size_t lowest_set_bit(T x) {
+    if (x == 0) {
+        return bitsizeof<T>;
+    }
+
+    size_t result = 0;
+    while ((x & 1) == 0) {
+        x >>= 1;
+        result++;
+    }
+    return result;
+}
+
+}  // namespace mcl::bit
diff --git a/externals/mcl/include/mcl/bit/bit_field.hpp b/externals/mcl/include/mcl/bit/bit_field.hpp
new file mode 100644
index 00000000..e4fca8ef
--- /dev/null
+++ b/externals/mcl/include/mcl/bit/bit_field.hpp
@@ -0,0 +1,203 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/assert.hpp"
+#include "mcl/bitsizeof.hpp"
+#include "mcl/concepts/bit_integral.hpp"
+#include "mcl/stdint.hpp"
+
+namespace mcl::bit {
+
+/// Create a mask with `count` number of one bits.
+template<size_t count, BitIntegral T>
+constexpr T ones() {
+    static_assert(count <= bitsizeof<T>, "count larger than bitsize of T");
+
+    if constexpr (count == 0) {
+        return 0;
+    } else {
+        return static_cast<T>(~static_cast<T>(0)) >> (bitsizeof<T> - count);
+    }
+}
+
+/// Create a mask with `count` number of one bits.
+template<BitIntegral T>
+constexpr T ones(size_t count) {
+    ASSERT_MSG(count <= bitsizeof<T>, "count larger than bitsize of T");
+
+    if (count == 0) {
+        return 0;
+    }
+    return ~static_cast<T>(0) >> (bitsizeof<T> - count);
+}
+
+/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
+template<size_t begin_bit, size_t end_bit, BitIntegral T>
+constexpr T mask() {
+    static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
+    static_assert(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
+    static_assert(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
+
+    return ones<end_bit - begin_bit + 1, T>() << begin_bit;
+}
+
+/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
+template<BitIntegral T>
+constexpr T mask(size_t begin_bit, size_t end_bit) {
+    ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
+    ASSERT_MSG(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
+    ASSERT_MSG(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
+
+    return ones<T>(end_bit - begin_bit + 1) << begin_bit;
+}
+
+/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
+template<size_t begin_bit, size_t end_bit, BitIntegral T>
+constexpr T get_bits(T value) {
+    constexpr T m = mask<begin_bit, end_bit, T>();
+    return (value & m) >> begin_bit;
+}
+
+/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
+template<BitIntegral T>
+constexpr T get_bits(size_t begin_bit, size_t end_bit, T value) {
+    const T m = mask<T>(begin_bit, end_bit);
+    return (value & m) >> begin_bit;
+}
+
+/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
+template<size_t begin_bit, size_t end_bit, BitIntegral T>
+constexpr T clear_bits(T value) {
+    constexpr T m = mask<begin_bit, end_bit, T>();
+    return value & ~m;
+}
+
+/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
+template<BitIntegral T>
+constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value) {
+    const T m = mask<T>(begin_bit, end_bit);
+    return value & ~m;
+}
+
+/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
+template<size_t begin_bit, size_t end_bit, BitIntegral T>
+constexpr T set_bits(T value, T new_bits) {
+    constexpr T m = mask<begin_bit, end_bit, T>();
+    return (value & ~m) | ((new_bits << begin_bit) & m);
+}
+
+/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
+template<BitIntegral T>
+constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits) {
+    const T m = mask<T>(begin_bit, end_bit);
+    return (value & ~m) | ((new_bits << begin_bit) & m);
+}
+
+/// Extract bit at bit_position from value of type T.
+template<size_t bit_position, BitIntegral T>
+constexpr bool get_bit(T value) {
+    constexpr T m = mask<bit_position, bit_position, T>();
+    return (value & m) != 0;
+}
+
+/// Extract bit at bit_position from value of type T.
+template<BitIntegral T>
+constexpr bool get_bit(size_t bit_position, T value) {
+    const T m = mask<T>(bit_position, bit_position);
+    return (value & m) != 0;
+}
+
+/// Clears bit at bit_position of value of type T.
+template<size_t bit_position, BitIntegral T>
+constexpr T clear_bit(T value) {
+    constexpr T m = mask<bit_position, bit_position, T>();
+    return value & ~m;
+}
+
+/// Clears bit at bit_position of value of type T.
+template<BitIntegral T>
+constexpr T clear_bit(size_t bit_position, T value) {
+    const T m = mask<T>(bit_position, bit_position);
+    return value & ~m;
+}
+
+/// Modifies bit at bit_position of value of type T.
+template<size_t bit_position, BitIntegral T>
+constexpr T set_bit(T value, bool new_bit) {
+    constexpr T m = mask<bit_position, bit_position, T>();
+    return (value & ~m) | (new_bit ? m : static_cast<T>(0));
+}
+
+/// Modifies bit at bit_position of value of type T.
+template<BitIntegral T>
+constexpr T set_bit(size_t bit_position, T value, bool new_bit) {
+    const T m = mask<T>(bit_position, bit_position);
+    return (value & ~m) | (new_bit ? m : static_cast<T>(0));
+}
+
+/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
+template<size_t bit_count, BitIntegral T>
+constexpr T sign_extend(T value) {
+    static_assert(bit_count != 0, "cannot sign-extend zero-sized value");
+
+    constexpr T m = ones<bit_count, T>();
+    if (get_bit<bit_count - 1, T>(value)) {
+        return value | ~m;
+    }
+    return value;
+}
+
+/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
+template<BitIntegral T>
+constexpr T sign_extend(size_t bit_count, T value) {
+    ASSERT_MSG(bit_count != 0, "cannot sign-extend zero-sized value");
+
+    const T m = ones<T>(bit_count);
+    if (get_bit<T>(bit_count - 1, value)) {
+        return value | ~m;
+    }
+    return value;
+}
+
+/// Replicate an element across a value of type T.
+template<size_t element_size, BitIntegral T>
+constexpr T replicate_element(T value) {
+    static_assert(element_size <= bitsizeof<T>, "element_size is too large");
+    static_assert(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
+
+    if constexpr (element_size == bitsizeof<T>) {
+        return value;
+    } else {
+        return replicate_element<element_size * 2, T>(static_cast<T>(value | (value << element_size)));
+    }
+}
+
+/// Replicate an element of type U across a value of type T.
+template<BitIntegral U, BitIntegral T>
+constexpr T replicate_element(T value) {
+    static_assert(bitsizeof<U> <= bitsizeof<T>, "element_size is too large");
+
+    return replicate_element<bitsizeof<U>, T>(value);
+}
+
+/// Replicate an element across a value of type T.
+template<BitIntegral T>
+constexpr T replicate_element(size_t element_size, T value) {
+    ASSERT_MSG(element_size <= bitsizeof<T>, "element_size is too large");
+    ASSERT_MSG(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
+
+    if (element_size == bitsizeof<T>) {
+        return value;
+    }
+    return replicate_element<T>(static_cast<T>(value | (value << element_size)), element_size * 2);
+}
+
+template<BitIntegral T>
+constexpr bool most_significant_bit(T value) {
+    return get_bit<bitsizeof<T> - 1, T>(value);
+}
+
+}  // namespace mcl::bit
diff --git a/externals/mcl/include/mcl/bit/rotate.hpp b/externals/mcl/include/mcl/bit/rotate.hpp
new file mode 100644
index 00000000..649e5e22
--- /dev/null
+++ b/externals/mcl/include/mcl/bit/rotate.hpp
@@ -0,0 +1,31 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/bitsizeof.hpp"
+#include "mcl/concepts/bit_integral.hpp"
+#include "mcl/stdint.hpp"
+
+namespace mcl::bit {
+
+template<BitIntegral T>
+constexpr T rotate_right(T x, size_t amount) {
+    amount %= bitsizeof<T>;
+    if (amount == 0) {
+        return x;
+    }
+    return static_cast<T>((x >> amount) | (x << (bitsizeof<T> - amount)));
+}
+
+template<BitIntegral T>
+constexpr T rotate_left(T x, size_t amount) {
+    amount %= bitsizeof<T>;
+    if (amount == 0) {
+        return x;
+    }
+    return static_cast<T>((x << amount) | (x >> (bitsizeof<T> - amount)));
+}
+
+}  // namespace mcl::bit
diff --git a/externals/mcl/include/mcl/bit/swap.hpp b/externals/mcl/include/mcl/bit/swap.hpp
new file mode 100644
index 00000000..0df6bac4
--- /dev/null
+++ b/externals/mcl/include/mcl/bit/swap.hpp
@@ -0,0 +1,50 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/concepts/bit_integral.hpp"
+
+namespace mcl::bit {
+
+constexpr u16 swap_bytes_16(u16 value) {
+    return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
+}
+
+constexpr u32 swap_bytes_32(u32 value) {
+    return ((value & 0xff000000u) >> 24)
+         | ((value & 0x00ff0000u) >> 8)
+         | ((value & 0x0000ff00u) << 8)
+         | ((value & 0x000000ffu) << 24);
+}
+
+constexpr u64 swap_bytes_64(u64 value) {
+    return ((value & 0xff00000000000000ull) >> 56)
+         | ((value & 0x00ff000000000000ull) >> 40)
+         | ((value & 0x0000ff0000000000ull) >> 24)
+         | ((value & 0x000000ff00000000ull) >> 8)
+         | ((value & 0x00000000ff000000ull) << 8)
+         | ((value & 0x0000000000ff0000ull) << 24)
+         | ((value & 0x000000000000ff00ull) << 40)
+         | ((value & 0x00000000000000ffull) << 56);
+}
+
+constexpr u32 swap_halves_32(u32 value) {
+    return ((value & 0xffff0000u) >> 16)
+         | ((value & 0x0000ffffu) << 16);
+}
+
+constexpr u64 swap_halves_64(u64 value) {
+    return ((value & 0xffff000000000000ull) >> 48)
+         | ((value & 0x0000ffff00000000ull) >> 16)
+         | ((value & 0x00000000ffff0000ull) << 16)
+         | ((value & 0x000000000000ffffull) << 48);
+}
+
+constexpr u64 swap_words_64(u64 value) {
+    return ((value & 0xffffffff00000000ull) >> 32)
+         | ((value & 0x00000000ffffffffull) << 32);
+}
+
+}  // namespace mcl::bit
diff --git a/externals/mcl/include/mcl/bit_cast.hpp b/externals/mcl/include/mcl/bit_cast.hpp
new file mode 100644
index 00000000..cfa8860a
--- /dev/null
+++ b/externals/mcl/include/mcl/bit_cast.hpp
@@ -0,0 +1,36 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstring>
+#include <type_traits>
+
+namespace mcl {
+
+/// Reinterpret objects of one type as another by bit-casting between object representations.
+template<class Dest, class Source>
+inline Dest bit_cast(const Source& source) noexcept {
+    static_assert(sizeof(Dest) == sizeof(Source), "size of destination and source objects must be equal");
+    static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
+    static_assert(std::is_trivially_copyable_v<Source>, "source type must be trivially copyable");
+
+    std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
+    std::memcpy(&dest, &source, sizeof(dest));
+    return reinterpret_cast<Dest&>(dest);
+}
+
+/// Reinterpret objects of any arbitrary type as another type by bit-casting between object representations.
+/// Note that here we do not verify if source pointed to by source_ptr has enough bytes to read from.
+template<class Dest, class SourcePtr>
+inline Dest bit_cast_pointee(const SourcePtr source_ptr) noexcept {
+    static_assert(sizeof(SourcePtr) == sizeof(void*), "source pointer must have size of a pointer");
+    static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
+
+    std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
+    std::memcpy(&dest, bit_cast<void*>(source_ptr), sizeof(dest));
+    return reinterpret_cast<Dest&>(dest);
+}
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/bitsizeof.hpp b/externals/mcl/include/mcl/bitsizeof.hpp
new file mode 100644
index 00000000..5d28f595
--- /dev/null
+++ b/externals/mcl/include/mcl/bitsizeof.hpp
@@ -0,0 +1,15 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <climits>
+#include <cstddef>
+
+namespace mcl {
+
+template<typename T>
+constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T);
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/concepts/bit_integral.hpp b/externals/mcl/include/mcl/concepts/bit_integral.hpp
new file mode 100644
index 00000000..caddc059
--- /dev/null
+++ b/externals/mcl/include/mcl/concepts/bit_integral.hpp
@@ -0,0 +1,16 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/concepts/is_any_of.hpp"
+#include "mcl/stdint.hpp"
+
+namespace mcl {
+
+/// Integral upon which bit operations can be safely performed.
+template<typename T>
+concept BitIntegral = IsAnyOf<T, u8, u16, u32, u64, uptr, size_t>;
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/concepts/is_any_of.hpp b/externals/mcl/include/mcl/concepts/is_any_of.hpp
new file mode 100644
index 00000000..3e6593db
--- /dev/null
+++ b/externals/mcl/include/mcl/concepts/is_any_of.hpp
@@ -0,0 +1,14 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/concepts/same_as.hpp"
+
+namespace mcl {
+
+template<typename T, typename... U>
+concept IsAnyOf = (SameAs<T, U> || ...);
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/concepts/same_as.hpp b/externals/mcl/include/mcl/concepts/same_as.hpp
new file mode 100644
index 00000000..7aaaa501
--- /dev/null
+++ b/externals/mcl/include/mcl/concepts/same_as.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl {
+
+namespace detail {
+
+template<typename T, typename U>
+concept SameHelper = std::is_same_v<T, U>;
+
+}  // namespace detail
+
+template<typename T, typename U>
+concept SameAs = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/container/intrusive_list.hpp b/externals/mcl/include/mcl/container/intrusive_list.hpp
new file mode 100644
index 00000000..b757f214
--- /dev/null
+++ b/externals/mcl/include/mcl/container/intrusive_list.hpp
@@ -0,0 +1,378 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include "mcl/assert.hpp"
+
+namespace mcl {
+
+template<typename T>
+class intrusive_list;
+template<typename T>
+class intrusive_list_iterator;
+
+template<typename T>
+class intrusive_list_node {
+public:
+    bool is_sentinel() const {
+        return is_sentinel_;
+    }
+
+protected:
+    intrusive_list_node* next = nullptr;
+    intrusive_list_node* prev = nullptr;
+    bool is_sentinel_ = false;
+
+    friend class intrusive_list<T>;
+    friend class intrusive_list_iterator<T>;
+    friend class intrusive_list_iterator<const T>;
+};
+
+template<typename T>
+class intrusive_list_sentinel final : public intrusive_list_node<T> {
+    using intrusive_list_node<T>::next;
+    using intrusive_list_node<T>::prev;
+    using intrusive_list_node<T>::is_sentinel_;
+
+public:
+    intrusive_list_sentinel() {
+        next = this;
+        prev = this;
+        is_sentinel_ = true;
+    }
+};
+
+template<typename T>
+class intrusive_list_iterator {
+public:
+    using iterator_category = std::bidirectional_iterator_tag;
+    using difference_type = std::ptrdiff_t;
+    using value_type = T;
+    using pointer = value_type*;
+    using const_pointer = const value_type*;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+
+    // If value_type is const, we want "const intrusive_list_node<value_type>", not "intrusive_list_node<const value_type>"
+    using node_type = std::conditional_t<std::is_const<value_type>::value,
+                                         const intrusive_list_node<std::remove_const_t<value_type>>,
+                                         intrusive_list_node<value_type>>;
+    using node_pointer = node_type*;
+    using node_reference = node_type&;
+
+    intrusive_list_iterator() = default;
+    intrusive_list_iterator(const intrusive_list_iterator& other) = default;
+    intrusive_list_iterator& operator=(const intrusive_list_iterator& other) = default;
+
+    explicit intrusive_list_iterator(node_pointer list_node)
+            : node(list_node) {
+    }
+    explicit intrusive_list_iterator(pointer data)
+            : node(data) {
+    }
+    explicit intrusive_list_iterator(reference data)
+            : node(&data) {
+    }
+
+    intrusive_list_iterator& operator++() {
+        node = node->next;
+        return *this;
+    }
+    intrusive_list_iterator& operator--() {
+        node = node->prev;
+        return *this;
+    }
+    intrusive_list_iterator operator++(int) {
+        intrusive_list_iterator it(*this);
+        ++*this;
+        return it;
+    }
+    intrusive_list_iterator operator--(int) {
+        intrusive_list_iterator it(*this);
+        --*this;
+        return it;
+    }
+
+    bool operator==(const intrusive_list_iterator& other) const {
+        return node == other.node;
+    }
+    bool operator!=(const intrusive_list_iterator& other) const {
+        return !operator==(other);
+    }
+
+    reference operator*() const {
+        DEBUG_ASSERT(!node->is_sentinel());
+        return static_cast<reference>(*node);
+    }
+    pointer operator->() const {
+        return std::addressof(operator*());
+    }
+
+    node_pointer AsNodePointer() const {
+        return node;
+    }
+
+private:
+    friend class intrusive_list<T>;
+    node_pointer node = nullptr;
+};
+
+template<typename T>
+class intrusive_list {
+public:
+    using difference_type = std::ptrdiff_t;
+    using size_type = std::size_t;
+    using value_type = T;
+    using pointer = value_type*;
+    using const_pointer = const value_type*;
+    using reference = value_type&;
+    using const_reference = const value_type&;
+    using iterator = intrusive_list_iterator<value_type>;
+    using const_iterator = intrusive_list_iterator<const value_type>;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    /**
+     * Inserts a node at the given location indicated by an iterator.
+     *
+     * @param location The location to insert the node.
+     * @param new_node The node to add.
+     */
+    iterator insert(iterator location, pointer new_node) {
+        return insert_before(location, new_node);
+    }
+
+    /**
+     * Inserts a node at the given location, moving the previous
+     * node occupant ahead of the one inserted.
+     *
+     * @param location The location to insert the new node.
+     * @param new_node The node to insert into the list.
+     */
+    iterator insert_before(iterator location, pointer new_node) {
+        auto existing_node = location.AsNodePointer();
+
+        new_node->next = existing_node;
+        new_node->prev = existing_node->prev;
+        existing_node->prev->next = new_node;
+        existing_node->prev = new_node;
+
+        return iterator(new_node);
+    }
+
+    /**
+     * Inserts a new node into the list ahead of the position indicated.
+     *
+     * @param position Location to insert the node in front of.
+     * @param new_node The node to be inserted into the list.
+     */
+    iterator insert_after(iterator position, pointer new_node) {
+        if (empty())
+            return insert(begin(), new_node);
+
+        return insert(++position, new_node);
+    }
+
+    /**
+     * Add an entry to the start of the list.
+     * @param node Node to add to the list.
+     */
+    void push_front(pointer node) {
+        insert(begin(), node);
+    }
+
+    /**
+     * Add an entry to the end of the list
+     * @param node Node to add to the list.
+     */
+    void push_back(pointer node) {
+        insert(end(), node);
+    }
+
+    /**
+     * Erases the node at the front of the list.
+     * @note Must not be called on an empty list.
+     */
+    void pop_front() {
+        DEBUG_ASSERT(!empty());
+        erase(begin());
+    }
+
+    /**
+     * Erases the node at the back of the list.
+     * @note Must not be called on an empty list.
+     */
+    void pop_back() {
+        DEBUG_ASSERT(!empty());
+        erase(--end());
+    }
+
+    /**
+     * Removes a node from this list
+     * @param it An iterator that points to the node to remove from list.
+     */
+    pointer remove(iterator& it) {
+        DEBUG_ASSERT(it != end());
+
+        pointer node = &*it++;
+
+        node->prev->next = node->next;
+        node->next->prev = node->prev;
+#if !defined(NDEBUG)
+        node->next = nullptr;
+        node->prev = nullptr;
+#endif
+
+        return node;
+    }
+
+    /**
+     * Removes a node from this list
+     * @param it A constant iterator that points to the node to remove from list.
+     */
+    pointer remove(const iterator& it) {
+        iterator copy = it;
+        return remove(copy);
+    }
+
+    /**
+     * Removes a node from this list.
+     * @param node A pointer to the node to remove.
+     */
+    pointer remove(pointer node) {
+        return remove(iterator(node));
+    }
+
+    /**
+     * Removes a node from this list.
+     * @param node A reference to the node to remove.
+     */
+    pointer remove(reference node) {
+        return remove(iterator(node));
+    }
+
+    /**
+     * Is this list empty?
+     * @returns true if there are no nodes in this list.
+     */
+    bool empty() const {
+        return root->next == root.get();
+    }
+
+    /**
+     * Gets the total number of elements within this list.
+     * @return the number of elements in this list.
+     */
+    size_type size() const {
+        return static_cast<size_type>(std::distance(begin(), end()));
+    }
+
+    /**
+     * Retrieves a reference to the node at the front of the list.
+     * @note Must not be called on an empty list.
+     */
+    reference front() {
+        DEBUG_ASSERT(!empty());
+        return *begin();
+    }
+
+    /**
+     * Retrieves a constant reference to the node at the front of the list.
+     * @note Must not be called on an empty list.
+     */
+    const_reference front() const {
+        DEBUG_ASSERT(!empty());
+        return *begin();
+    }
+
+    /**
+     * Retrieves a reference to the node at the back of the list.
+     * @note Must not be called on an empty list.
+     */
+    reference back() {
+        DEBUG_ASSERT(!empty());
+        return *--end();
+    }
+
+    /**
+     * Retrieves a constant reference to the node at the back of the list.
+     * @note Must not be called on an empty list.
+     */
+    const_reference back() const {
+        DEBUG_ASSERT(!empty());
+        return *--end();
+    }
+
+    // Iterator interface
+    iterator begin() { return iterator(root->next); }
+    const_iterator begin() const { return const_iterator(root->next); }
+    const_iterator cbegin() const { return begin(); }
+
+    iterator end() { return iterator(root.get()); }
+    const_iterator end() const { return const_iterator(root.get()); }
+    const_iterator cend() const { return end(); }
+
+    reverse_iterator rbegin() { return reverse_iterator(end()); }
+    const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+    const_reverse_iterator crbegin() const { return rbegin(); }
+
+    reverse_iterator rend() { return reverse_iterator(begin()); }
+    const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+    const_reverse_iterator crend() const { return rend(); }
+
+    /**
+     * Erases a node from the list, indicated by an iterator.
+     * @param it The iterator that points to the node to erase.
+     */
+    iterator erase(iterator it) {
+        remove(it);
+        return it;
+    }
+
+    /**
+     * Erases a node from this list.
+     * @param node A pointer to the node to erase from this list.
+     */
+    iterator erase(pointer node) {
+        return erase(iterator(node));
+    }
+
+    /**
+     * Erases a node from this list.
+     * @param node A reference to the node to erase from this list.
+     */
+    iterator erase(reference node) {
+        return erase(iterator(node));
+    }
+
+    /**
+     * Exchanges contents of this list with another list instance.
+     * @param other The other list to swap with.
+     */
+    void swap(intrusive_list& other) noexcept {
+        root.swap(other.root);
+    }
+
+private:
+    std::shared_ptr<intrusive_list_node<T>> root = std::make_shared<intrusive_list_sentinel<T>>();
+};
+
+/**
+ * Exchanges contents of an intrusive list with another intrusive list.
+ * @tparam T The type of data being kept track of by the lists.
+ * @param lhs The first list.
+ * @param rhs The second list.
+ */
+template<typename T>
+void swap(intrusive_list<T>& lhs, intrusive_list<T>& rhs) noexcept {
+    lhs.swap(rhs);
+}
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/hint/assume.hpp b/externals/mcl/include/mcl/hint/assume.hpp
new file mode 100644
index 00000000..ac1873f8
--- /dev/null
+++ b/externals/mcl/include/mcl/hint/assume.hpp
@@ -0,0 +1,13 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#if defined(__clang) || defined(__GNUC__)
+#    define ASSUME(expr) [&] { if (!(expr)) __builtin_unreachable(); }()
+#elif defined(_MSC_VER)
+#    define ASSUME(expr) __assume(expr)
+#else
+#    define ASSUME(expr)
+#endif
diff --git a/externals/mcl/include/mcl/iterator/reverse.hpp b/externals/mcl/include/mcl/iterator/reverse.hpp
new file mode 100644
index 00000000..eb21b9e0
--- /dev/null
+++ b/externals/mcl/include/mcl/iterator/reverse.hpp
@@ -0,0 +1,34 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <iterator>
+
+namespace mcl::iterator {
+namespace detail {
+
+template<typename T>
+struct reverse_adapter {
+    T& iterable;
+
+    constexpr auto begin() {
+        using namespace std;
+        return rbegin(iterable);
+    }
+
+    constexpr auto end() {
+        using namespace std;
+        return rend(iterable);
+    }
+};
+
+}  // namespace detail
+
+template<typename T>
+constexpr detail::reverse_adapter<T> reverse(T&& iterable) {
+    return detail::reverse_adapter<T>{iterable};
+}
+
+}  // namespace mcl::iterator
diff --git a/externals/mcl/include/mcl/macro/anonymous_variable.hpp b/externals/mcl/include/mcl/macro/anonymous_variable.hpp
new file mode 100644
index 00000000..09cd48a2
--- /dev/null
+++ b/externals/mcl/include/mcl/macro/anonymous_variable.hpp
@@ -0,0 +1,13 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <mcl/macro/concatenate_tokens.hpp>
+
+#ifdef __COUNTER__
+#    define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __COUNTER__)
+#else
+#    define ANONYMOUS_VARIABLE(str) CONCATENATE_TOKENS(str, __LINE__)
+#endif
diff --git a/externals/mcl/include/mcl/macro/architecture.hpp b/externals/mcl/include/mcl/macro/architecture.hpp
new file mode 100644
index 00000000..3251d829
--- /dev/null
+++ b/externals/mcl/include/mcl/macro/architecture.hpp
@@ -0,0 +1,40 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#if defined(__ARM64__) || defined(__aarch64__) || defined(_M_ARM64)
+#    define MCL_ARCHITECTURE arm64
+#    define MCL_ARCHITECTURE_ARM64 1
+#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM)
+#    define MCL_ARCHITECTURE arm32
+#    define MCL_ARCHITECTURE_ARM32 1
+#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
+#    define MCL_ARCHITECTURE x86_64
+#    define MCL_ARCHITECTURE_X86_64 1
+#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#    define MCL_ARCHITECTURE x86_32
+#    define MCL_ARCHITECTURE_X86_32 1
+#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
+#    define MCL_ARCHITECTURE ia64
+#    define MCL_ARCHITECTURE_IA64 1
+#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000)
+#    define MCL_ARCHITECTURE mips
+#    define MCL_ARCHITECTURE_MIPS 1
+#elif defined(__ppc64__) || defined(__powerpc64__)
+#    define MCL_ARCHITECTURE ppc64
+#    define MCL_ARCHITECTURE_PPC64 1
+#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_M_MPPC) || defined(_M_PPC)
+#    define MCL_ARCHITECTURE ppc32
+#    define MCL_ARCHITECTURE_PPC32 1
+#elif defined(__riscv)
+#    define MCL_ARCHITECTURE riscv
+#    define MCL_ARCHITECTURE_RISCV 1
+#elif defined(__EMSCRIPTEN__)
+#    define MCL_ARCHITECTURE wasm
+#    define MCL_ARCHITECTURE_WASM 1
+#else
+#    define MCL_ARCHITECTURE generic
+#    define MCL_ARCHITECTURE_GENERIC 1
+#endif
diff --git a/externals/mcl/include/mcl/macro/concatenate_tokens.hpp b/externals/mcl/include/mcl/macro/concatenate_tokens.hpp
new file mode 100644
index 00000000..3555b23c
--- /dev/null
+++ b/externals/mcl/include/mcl/macro/concatenate_tokens.hpp
@@ -0,0 +1,8 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#define CONCATENATE_TOKENS(x, y) CONCATENATE_TOKENS_IMPL(x, y)
+#define CONCATENATE_TOKENS_IMPL(x, y) x##y
diff --git a/externals/mcl/include/mcl/mp/metafunction/apply.hpp b/externals/mcl/include/mcl/mp/metafunction/apply.hpp
new file mode 100644
index 00000000..e2c17141
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metafunction/apply.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<template<class...> class F, class L>
+struct apply_impl;
+
+template<template<class...> class F, template<class...> class LT, class... Es>
+struct apply_impl<F, LT<Es...>> {
+    using type = F<Es...>;
+};
+
+}  // namespace detail
+
+/// Invokes metafunction F where the arguments are all the members of list L
+template<template<class...> class F, class L>
+using apply = typename detail::apply_impl<F, L>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metafunction/bind.hpp b/externals/mcl/include/mcl/mp/metafunction/bind.hpp
new file mode 100644
index 00000000..2701fc95
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metafunction/bind.hpp
@@ -0,0 +1,18 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+/// Binds the first sizeof...(A) arguments of metafunction F with arguments A
+template<template<class...> class F, class... As>
+struct bind {
+    template<class... Rs>
+    using type = F<As..., Rs...>;
+};
+
+}  // namespace mcl::mp
+
+#define MCL_MP_BIND(...) ::mcl::mp::bind<__VA_ARGS__>::template type
diff --git a/externals/mcl/include/mcl/mp/metafunction/identity.hpp b/externals/mcl/include/mcl/mp/metafunction/identity.hpp
new file mode 100644
index 00000000..737f9ce1
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metafunction/identity.hpp
@@ -0,0 +1,22 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class T>
+struct identity_impl {
+    using type = T;
+};
+
+}  // namespace detail
+
+/// Identity metafunction
+template<class T>
+using identity = typename identity_impl<T>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metafunction/map.hpp b/externals/mcl/include/mcl/mp/metafunction/map.hpp
new file mode 100644
index 00000000..13fcaecd
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metafunction/map.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<template<class...> class F, class L>
+struct map_impl;
+
+template<template<class...> class F, template<class...> class LT, class... Es>
+struct map_impl<F, LT<Es...>> {
+    using type = LT<F<Es>...>;
+};
+
+}  // namespace detail
+
+/// Applies each element of list L to metafunction F
+template<template<class...> class F, class L>
+using map = typename detail::map_impl<F, L>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/bit_and.hpp b/externals/mcl/include/mcl/mp/metavalue/bit_and.hpp
new file mode 100644
index 00000000..e4a98e21
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/bit_and.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Bitwise and of metavalues Vs
+template<class... Vs>
+using bit_and = lift_value<(Vs::value & ...)>;
+
+/// Bitwise and of metavalues Vs
+template<class... Vs>
+constexpr auto bit_and_v = (Vs::value & ...);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/bit_not.hpp b/externals/mcl/include/mcl/mp/metavalue/bit_not.hpp
new file mode 100644
index 00000000..5d31956d
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/bit_not.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Bitwise not of metavalue V
+template<class V>
+using bit_not = lift_value<~V::value>;
+
+/// Bitwise not of metavalue V
+template<class V>
+constexpr auto bit_not_v = ~V::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/bit_or.hpp b/externals/mcl/include/mcl/mp/metavalue/bit_or.hpp
new file mode 100644
index 00000000..812edfb2
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/bit_or.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Bitwise or of metavalues Vs
+template<class... Vs>
+using bit_or = lift_value<(Vs::value | ...)>;
+
+/// Bitwise or of metavalues Vs
+template<class... Vs>
+constexpr auto bit_or_v = (Vs::value | ...);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/bit_xor.hpp b/externals/mcl/include/mcl/mp/metavalue/bit_xor.hpp
new file mode 100644
index 00000000..9c543d41
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/bit_xor.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Bitwise xor of metavalues Vs
+template<class... Vs>
+using bit_xor = lift_value<(Vs::value ^ ...)>;
+
+/// Bitwise xor of metavalues Vs
+template<class... Vs>
+constexpr auto bit_xor_v = (Vs::value ^ ...);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/conjunction.hpp b/externals/mcl/include/mcl/mp/metavalue/conjunction.hpp
new file mode 100644
index 00000000..71a8f67a
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/conjunction.hpp
@@ -0,0 +1,42 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/logic_if.hpp"
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class...>
+struct conjunction_impl;
+
+template<>
+struct conjunction_impl<> {
+    using type = false_type;
+};
+
+template<class V>
+struct conjunction_impl<V> {
+    using type = V;
+};
+
+template<class V1, class... Vs>
+struct conjunction_impl<V1, Vs...> {
+    using type = logic_if<V1, typename conjunction_impl<Vs...>::type, V1>;
+};
+
+}  // namespace detail
+
+/// Conjunction of metavalues Vs with short-circuiting and type preservation.
+template<class... Vs>
+using conjunction = typename detail::conjunction_impl<Vs...>::type;
+
+/// Conjunction of metavalues Vs with short-circuiting and type preservation.
+template<class... Vs>
+constexpr auto conjunction_v = conjunction<Vs...>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/disjunction.hpp b/externals/mcl/include/mcl/mp/metavalue/disjunction.hpp
new file mode 100644
index 00000000..679a44f4
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/disjunction.hpp
@@ -0,0 +1,42 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/logic_if.hpp"
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class...>
+struct disjunction_impl;
+
+template<>
+struct disjunction_impl<> {
+    using type = false_type;
+};
+
+template<class V>
+struct disjunction_impl<V> {
+    using type = V;
+};
+
+template<class V1, class... Vs>
+struct disjunction_impl<V1, Vs...> {
+    using type = logic_if<V1, V1, typename disjunction_impl<Vs...>::type>;
+};
+
+}  // namespace detail
+
+/// Disjunction of metavalues Vs with short-circuiting and type preservation.
+template<class... Vs>
+using disjunction = typename detail::disjunction_impl<Vs...>::type;
+
+/// Disjunction of metavalues Vs with short-circuiting and type preservation.
+template<class... Vs>
+constexpr auto disjunction_v = disjunction<Vs...>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/lift_value.hpp b/externals/mcl/include/mcl/mp/metavalue/lift_value.hpp
new file mode 100644
index 00000000..bd5df322
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/lift_value.hpp
@@ -0,0 +1,15 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+namespace mcl::mp {
+
+/// Lifts a value into a type (a metavalue)
+template<auto V>
+using lift_value = std::integral_constant<decltype(V), V>;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/logic_and.hpp b/externals/mcl/include/mcl/mp/metavalue/logic_and.hpp
new file mode 100644
index 00000000..81c40a45
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/logic_and.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+/// Logical conjunction of metavalues Vs without short-circuiting or type presevation.
+template<class... Vs>
+using logic_and = bool_value<(true && ... && Vs::value)>;
+
+/// Logical conjunction of metavalues Vs without short-circuiting or type presevation.
+template<class... Vs>
+constexpr bool logic_and_v = (true && ... && Vs::value);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/logic_if.hpp b/externals/mcl/include/mcl/mp/metavalue/logic_if.hpp
new file mode 100644
index 00000000..b300d267
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/logic_if.hpp
@@ -0,0 +1,21 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+/// Conditionally select between types T and F based on boolean metavalue V
+template<class V, class T, class F>
+using logic_if = std::conditional_t<bool(V::value), T, F>;
+
+/// Conditionally select between metavalues T and F based on boolean metavalue V
+template<class V, class TV, class FV>
+constexpr auto logic_if_v = logic_if<V, TV, FV>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/logic_not.hpp b/externals/mcl/include/mcl/mp/metavalue/logic_not.hpp
new file mode 100644
index 00000000..a7a6aee9
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/logic_not.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+/// Logical negation of metavalue V.
+template<class V>
+using logic_not = bool_value<!bool(V::value)>;
+
+/// Logical negation of metavalue V.
+template<class V>
+constexpr bool logic_not_v = !bool(V::value);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/logic_or.hpp b/externals/mcl/include/mcl/mp/metavalue/logic_or.hpp
new file mode 100644
index 00000000..3b46fa57
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/logic_or.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+/// Logical disjunction of metavalues Vs without short-circuiting or type presevation.
+template<class... Vs>
+using logic_or = bool_value<(false || ... || Vs::value)>;
+
+/// Logical disjunction of metavalues Vs without short-circuiting or type presevation.
+template<class... Vs>
+constexpr bool logic_or_v = (false || ... || Vs::value);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/product.hpp b/externals/mcl/include/mcl/mp/metavalue/product.hpp
new file mode 100644
index 00000000..d40c0ce3
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/product.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Product of metavalues Vs
+template<class... Vs>
+using product = lift_value<(Vs::value * ...)>;
+
+/// Product of metavalues Vs
+template<class... Vs>
+constexpr auto product_v = (Vs::value * ...);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/sum.hpp b/externals/mcl/include/mcl/mp/metavalue/sum.hpp
new file mode 100644
index 00000000..7111c02c
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/sum.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Sum of metavalues Vs
+template<class... Vs>
+using sum = lift_value<(Vs::value + ...)>;
+
+/// Sum of metavalues Vs
+template<class... Vs>
+constexpr auto sum_v = (Vs::value + ...);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/value.hpp b/externals/mcl/include/mcl/mp/metavalue/value.hpp
new file mode 100644
index 00000000..693169d6
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/value.hpp
@@ -0,0 +1,30 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+namespace mcl::mp {
+
+/// A metavalue (of type VT and value v).
+template<class VT, VT v>
+using value = std::integral_constant<VT, v>;
+
+/// A metavalue of type size_t (and value v).
+template<size_t v>
+using size_value = value<size_t, v>;
+
+/// A metavalue of type bool (and value v). (Aliases to std::bool_constant.)
+template<bool v>
+using bool_value = value<bool, v>;
+
+/// true metavalue (Aliases to std::true_type).
+using true_type = bool_value<true>;
+
+/// false metavalue (Aliases to std::false_type).
+using false_type = bool_value<false>;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/value_cast.hpp b/externals/mcl/include/mcl/mp/metavalue/value_cast.hpp
new file mode 100644
index 00000000..3b88ed7d
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/value_cast.hpp
@@ -0,0 +1,15 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+namespace mcl::mp {
+
+/// Casts a metavalue from one type to another
+template<class T, class V>
+using value_cast = std::integral_constant<T, static_cast<T>(V::value)>;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/metavalue/value_equal.hpp b/externals/mcl/include/mcl/mp/metavalue/value_equal.hpp
new file mode 100644
index 00000000..a10e07f0
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/metavalue/value_equal.hpp
@@ -0,0 +1,15 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+namespace mcl::mp {
+
+/// Do two metavalues contain the same value?
+template<class V1, class V2>
+using value_equal = std::bool_constant<V1::value == V2::value>;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/misc/argument_count.hpp b/externals/mcl/include/mcl/mp/misc/argument_count.hpp
new file mode 100644
index 00000000..5a30f498
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/misc/argument_count.hpp
@@ -0,0 +1,19 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/lift_value.hpp"
+
+namespace mcl::mp {
+
+/// Metafunction that returns the number of arguments it has
+template<typename... Ts>
+using argument_count = lift_value<sizeof...(Ts)>;
+
+/// Metafunction that returns the number of arguments it has
+template<typename... Ts>
+constexpr auto argument_count_v = sizeof...(Ts);
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/append.hpp b/externals/mcl/include/mcl/mp/typelist/append.hpp
new file mode 100644
index 00000000..838b9c94
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/append.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class... L>
+struct append_impl;
+
+template<template<class...> class LT, class... E1s, class... E2s>
+struct append_impl<LT<E1s...>, E2s...> {
+    using type = LT<E1s..., E2s...>;
+};
+
+}  // namespace detail
+
+/// Append items E to list L
+template<class L, class... Es>
+using append = typename detail::append_impl<L, Es...>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/cartesian_product.hpp b/externals/mcl/include/mcl/mp/typelist/cartesian_product.hpp
new file mode 100644
index 00000000..1b378c6f
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/cartesian_product.hpp
@@ -0,0 +1,47 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metafunction/bind.hpp"
+#include "mcl/mp/metafunction/map.hpp"
+#include "mcl/mp/typelist/append.hpp"
+#include "mcl/mp/typelist/concat.hpp"
+#include "mcl/mp/typelist/list.hpp"
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class... Ls>
+struct cartesian_product_impl;
+
+template<class RL>
+struct cartesian_product_impl<RL> {
+    using type = RL;
+};
+
+template<template<class...> class LT, class... REs, class... E2s>
+struct cartesian_product_impl<LT<REs...>, LT<E2s...>> {
+    using type = concat<
+        map<MCL_MP_BIND(append, REs), list<E2s...>>...>;
+};
+
+template<class RL, class L2, class L3, class... Ls>
+struct cartesian_product_impl<RL, L2, L3, Ls...> {
+    using type = typename cartesian_product_impl<
+        typename cartesian_product_impl<RL, L2>::type,
+        L3,
+        Ls...>::type;
+};
+
+}  // namespace detail
+
+/// Produces the cartesian product of a set of lists
+/// For example:
+/// cartesian_product<list<A, B>, list<D, E>> == list<list<A, D>, list<A, E>, list<B, D>, list<B, E>
+template<typename L1, typename... Ls>
+using cartesian_product = typename detail::cartesian_product_impl<map<list, L1>, Ls...>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/concat.hpp b/externals/mcl/include/mcl/mp/typelist/concat.hpp
new file mode 100644
index 00000000..cfdefbc1
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/concat.hpp
@@ -0,0 +1,94 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/typelist/list.hpp"
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class... Ls>
+struct concat_impl;
+
+template<>
+struct concat_impl<> {
+    using type = list<>;
+};
+
+template<class L>
+struct concat_impl<L> {
+    using type = L;
+};
+
+template<template<class...> class LT, class... E1s, class... E2s, class... Ls>
+struct concat_impl<LT<E1s...>, LT<E2s...>, Ls...> {
+    using type = typename concat_impl<LT<E1s..., E2s...>, Ls...>::type;
+};
+
+template<template<class...> class LT,
+         class... E1s,
+         class... E2s,
+         class... E3s,
+         class... E4s,
+         class... E5s,
+         class... E6s,
+         class... E7s,
+         class... E8s,
+         class... E9s,
+         class... E10s,
+         class... E11s,
+         class... E12s,
+         class... E13s,
+         class... E14s,
+         class... E15s,
+         class... E16s,
+         class... Ls>
+struct concat_impl<
+    LT<E1s...>,
+    LT<E2s...>,
+    LT<E3s...>,
+    LT<E4s...>,
+    LT<E5s...>,
+    LT<E6s...>,
+    LT<E7s...>,
+    LT<E8s...>,
+    LT<E9s...>,
+    LT<E10s...>,
+    LT<E11s...>,
+    LT<E12s...>,
+    LT<E13s...>,
+    LT<E14s...>,
+    LT<E15s...>,
+    LT<E16s...>,
+    Ls...> {
+    using type = typename concat_impl<
+        LT<
+            E1s...,
+            E2s...,
+            E3s...,
+            E4s...,
+            E5s...,
+            E6s...,
+            E7s...,
+            E8s...,
+            E9s...,
+            E10s...,
+            E11s...,
+            E12s...,
+            E13s...,
+            E14s...,
+            E15s...,
+            E16s...>,
+        Ls...>::type;
+};
+
+}  // namespace detail
+
+/// Concatenate lists together
+template<class... Ls>
+using concat = typename detail::concat_impl<Ls...>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/contains.hpp b/externals/mcl/include/mcl/mp/typelist/contains.hpp
new file mode 100644
index 00000000..f8d1fbf8
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/contains.hpp
@@ -0,0 +1,23 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl::mp {
+
+/// Does list L contain an element which is same as type T?
+template<class L, class T>
+struct contains;
+
+template<template<class...> class LT, class... Ts, class T>
+struct contains<LT<Ts...>, T>
+        : bool_value<(false || ... || std::is_same_v<Ts, T>)> {};
+
+/// Does list L contain an element which is same as type T?
+template<class L, class T>
+constexpr bool contains_v = contains<L, T>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/drop.hpp b/externals/mcl/include/mcl/mp/typelist/drop.hpp
new file mode 100644
index 00000000..9c7be44b
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/drop.hpp
@@ -0,0 +1,33 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<size_t N, class L>
+struct drop_impl;
+
+template<size_t N, template<class...> class LT>
+struct drop_impl<N, LT<>> {
+    using type = LT<>;
+};
+
+template<size_t N, template<class...> class LT, class E1, class... Es>
+struct drop_impl<N, LT<E1, Es...>> {
+    using type = std::conditional_t<N == 0, LT<E1, Es...>, typename drop_impl<N - 1, LT<Es...>>::type>;
+};
+
+}  // namespace detail
+
+/// Drops the first N elements of list L
+template<std::size_t N, class L>
+using drop = typename detail::drop_impl<N, L>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/get.hpp b/externals/mcl/include/mcl/mp/typelist/get.hpp
new file mode 100644
index 00000000..a2ec7461
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/get.hpp
@@ -0,0 +1,18 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <tuple>
+
+#include "mcl/mp/metafunction/apply.hpp"
+
+namespace mcl::mp {
+
+/// Get element I from list L
+template<std::size_t I, class L>
+using get = std::tuple_element_t<I, apply<std::tuple, L>>;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/head.hpp b/externals/mcl/include/mcl/mp/typelist/head.hpp
new file mode 100644
index 00000000..63062825
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/head.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class L>
+struct head_impl;
+
+template<template<class...> class LT, class E1, class... Es>
+struct head_impl<LT<E1, Es...>> {
+    using type = E1;
+};
+
+}  // namespace detail
+
+/// Gets the tail/cdr/all-but-the-first-element of list L
+template<class L>
+using head = typename detail::head_impl<L>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/length.hpp b/externals/mcl/include/mcl/mp/typelist/length.hpp
new file mode 100644
index 00000000..d5949604
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/length.hpp
@@ -0,0 +1,20 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metafunction/apply.hpp"
+#include "mcl/mp/misc/argument_count.hpp"
+
+namespace mcl::mp {
+
+/// Length of list L
+template<class L>
+using length = apply<argument_count, L>;
+
+/// Length of list L
+template<class L>
+constexpr auto length_v = length<L>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/lift_sequence.hpp b/externals/mcl/include/mcl/mp/typelist/lift_sequence.hpp
new file mode 100644
index 00000000..ba2617b8
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/lift_sequence.hpp
@@ -0,0 +1,29 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <type_traits>
+
+#include "mcl/mp/typelist/list.hpp"
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class VL>
+struct lift_sequence_impl;
+
+template<class T, template<class, T...> class VLT, T... values>
+struct lift_sequence_impl<VLT<T, values...>> {
+    using type = list<std::integral_constant<T, values>...>;
+};
+
+}  // namespace detail
+
+/// Lifts values in value list VL to create a type list.
+template<class VL>
+using lift_sequence = typename detail::lift_sequence_impl<VL>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/list.hpp b/externals/mcl/include/mcl/mp/typelist/list.hpp
new file mode 100644
index 00000000..48348ec7
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/list.hpp
@@ -0,0 +1,13 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+/// Contains a list of types
+template<class... E>
+struct list {};
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/lower_to_tuple.hpp b/externals/mcl/include/mcl/mp/typelist/lower_to_tuple.hpp
new file mode 100644
index 00000000..a9007f3b
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/lower_to_tuple.hpp
@@ -0,0 +1,24 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <tuple>
+
+namespace mcl::mp {
+
+/// Converts a list of metavalues to a tuple.
+template<class L>
+struct lower_to_tuple;
+
+template<template<class...> class LT, class... Es>
+struct lower_to_tuple<LT<Es...>> {
+    static constexpr auto value = std::make_tuple(static_cast<typename Es::value_type>(Es::value)...);
+};
+
+/// Converts a list of metavalues to a tuple.
+template<class L>
+constexpr auto lower_to_tuple_v = lower_to_tuple<L>::value;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/prepend.hpp b/externals/mcl/include/mcl/mp/typelist/prepend.hpp
new file mode 100644
index 00000000..df9aedd8
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/prepend.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class... L>
+struct prepend_impl;
+
+template<template<class...> class LT, class... E1s, class... E2s>
+struct prepend_impl<LT<E1s...>, E2s...> {
+    using type = LT<E2s..., E1s...>;
+};
+
+}  // namespace detail
+
+/// Prepend items E to list L
+template<class L, class... Es>
+using prepend = typename detail::prepend_impl<L, Es...>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/mp/typelist/tail.hpp b/externals/mcl/include/mcl/mp/typelist/tail.hpp
new file mode 100644
index 00000000..54f14c12
--- /dev/null
+++ b/externals/mcl/include/mcl/mp/typelist/tail.hpp
@@ -0,0 +1,25 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+namespace mcl::mp {
+
+namespace detail {
+
+template<class L>
+struct tail_impl;
+
+template<template<class...> class LT, class E1, class... Es>
+struct tail_impl<LT<E1, Es...>> {
+    using type = LT<Es...>;
+};
+
+}  // namespace detail
+
+/// Gets the first type of list L
+template<class L>
+using tail = typename detail::tail_impl<L>::type;
+
+}  // namespace mcl::mp
diff --git a/externals/mcl/include/mcl/scope_exit.hpp b/externals/mcl/include/mcl/scope_exit.hpp
new file mode 100644
index 00000000..d6d11ed2
--- /dev/null
+++ b/externals/mcl/include/mcl/scope_exit.hpp
@@ -0,0 +1,85 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <exception>
+#include <type_traits>
+#include <utility>
+
+#include <mcl/macro/anonymous_variable.hpp>
+
+namespace mcl::detail {
+
+struct scope_exit_tag {};
+struct scope_fail_tag {};
+struct scope_success_tag {};
+
+template<typename Function>
+class scope_exit final {
+public:
+    explicit scope_exit(Function&& fn)
+            : function(std::move(fn)) {}
+    ~scope_exit() noexcept {
+        function();
+    }
+
+private:
+    Function function;
+};
+
+template<typename Function>
+class scope_fail final {
+public:
+    explicit scope_fail(Function&& fn)
+            : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
+    ~scope_fail() noexcept {
+        if (std::uncaught_exceptions() > exception_count) {
+            function();
+        }
+    }
+
+private:
+    Function function;
+    int exception_count;
+};
+
+template<typename Function>
+class scope_success final {
+public:
+    explicit scope_success(Function&& fn)
+            : function(std::move(fn)), exception_count(std::uncaught_exceptions()) {}
+    ~scope_success() {
+        if (std::uncaught_exceptions() <= exception_count) {
+            function();
+        }
+    }
+
+private:
+    Function function;
+    int exception_count;
+};
+
+// We use ->* here as it has the highest precedence of the operators we can use.
+
+template<typename Function>
+auto operator->*(scope_exit_tag, Function&& function) {
+    return scope_exit<std::decay_t<Function>>{std::forward<Function>(function)};
+}
+
+template<typename Function>
+auto operator->*(scope_fail_tag, Function&& function) {
+    return scope_fail<std::decay_t<Function>>{std::forward<Function>(function)};
+}
+
+template<typename Function>
+auto operator->*(scope_success_tag, Function&& function) {
+    return scope_success<std::decay_t<Function>>{std::forward<Function>(function)};
+}
+
+}  // namespace mcl::detail
+
+#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(MCL_SCOPE_EXIT_VAR_) = ::mcl::detail::scope_exit_tag{}->*[&]() noexcept
+#define SCOPE_FAIL auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_fail_tag{}->*[&]() noexcept
+#define SCOPE_SUCCESS auto ANONYMOUS_VARIABLE(MCL_SCOPE_FAIL_VAR_) = ::mcl::detail::scope_success_tag{}->*[&]()
diff --git a/externals/mcl/include/mcl/stdint.hpp b/externals/mcl/include/mcl/stdint.hpp
new file mode 100644
index 00000000..37b82a2b
--- /dev/null
+++ b/externals/mcl/include/mcl/stdint.hpp
@@ -0,0 +1,27 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+
+using u8 = std::uint8_t;
+using u16 = std::uint16_t;
+using u32 = std::uint32_t;
+using u64 = std::uint64_t;
+using uptr = std::uintptr_t;
+
+using s8 = std::int8_t;
+using s16 = std::int16_t;
+using s32 = std::int32_t;
+using s64 = std::int64_t;
+using sptr = std::intptr_t;
+
+using size_t = std::size_t;
+
+using f32 = float;
+using f64 = double;
+static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide");
+static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide");
diff --git a/externals/mcl/include/mcl/type_traits/function_info.hpp b/externals/mcl/include/mcl/type_traits/function_info.hpp
new file mode 100644
index 00000000..2ac804ae
--- /dev/null
+++ b/externals/mcl/include/mcl/type_traits/function_info.hpp
@@ -0,0 +1,70 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include <cstddef>
+#include <tuple>
+
+#include "mcl/mp/typelist/list.hpp"
+
+namespace mcl {
+
+template<class F>
+struct function_info : function_info<decltype(&F::operator())> {};
+
+template<class R, class... As>
+struct function_info<R(As...)> {
+    using return_type = R;
+    using parameter_list = mp::list<As...>;
+    static constexpr std::size_t parameter_count = sizeof...(As);
+
+    using equivalent_function_type = R(As...);
+
+    template<std::size_t I>
+    struct parameter {
+        static_assert(I < parameter_count, "Non-existent parameter");
+        using type = std::tuple_element_t<I, std::tuple<As...>>;
+    };
+};
+
+template<class R, class... As>
+struct function_info<R (*)(As...)> : function_info<R(As...)> {};
+
+template<class C, class R, class... As>
+struct function_info<R (C::*)(As...)> : function_info<R(As...)> {
+    using class_type = C;
+
+    using equivalent_function_type_with_class = R(C*, As...);
+};
+
+template<class C, class R, class... As>
+struct function_info<R (C::*)(As...) const> : function_info<R(As...)> {
+    using class_type = C;
+
+    using equivalent_function_type_with_class = R(C*, As...);
+};
+
+template<class F>
+constexpr size_t parameter_count_v = function_info<F>::parameter_count;
+
+template<class F>
+using parameter_list = typename function_info<F>::parameter_list;
+
+template<class F, std::size_t I>
+using get_parameter = typename function_info<F>::template parameter<I>::type;
+
+template<class F>
+using equivalent_function_type = typename function_info<F>::equivalent_function_type;
+
+template<class F>
+using equivalent_function_type_with_class = typename function_info<F>::equivalent_function_type_with_class;
+
+template<class F>
+using return_type = typename function_info<F>::return_type;
+
+template<class F>
+using class_type = typename function_info<F>::class_type;
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/type_traits/integer_of_size.hpp b/externals/mcl/include/mcl/type_traits/integer_of_size.hpp
new file mode 100644
index 00000000..e37995ea
--- /dev/null
+++ b/externals/mcl/include/mcl/type_traits/integer_of_size.hpp
@@ -0,0 +1,48 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/stdint.hpp"
+
+namespace mcl {
+
+namespace detail {
+
+template<size_t size>
+struct integer_of_size_impl {};
+
+template<>
+struct integer_of_size_impl<8> {
+    using unsigned_type = u8;
+    using signed_type = s8;
+};
+
+template<>
+struct integer_of_size_impl<16> {
+    using unsigned_type = u16;
+    using signed_type = s16;
+};
+
+template<>
+struct integer_of_size_impl<32> {
+    using unsigned_type = u32;
+    using signed_type = s32;
+};
+
+template<>
+struct integer_of_size_impl<64> {
+    using unsigned_type = u64;
+    using signed_type = s64;
+};
+
+}  // namespace detail
+
+template<size_t size>
+using unsigned_integer_of_size = typename detail::integer_of_size_impl<size>::unsigned_type;
+
+template<size_t size>
+using signed_integer_of_size = typename detail::integer_of_size_impl<size>::signed_type;
+
+}  // namespace mcl
diff --git a/externals/mcl/include/mcl/type_traits/is_instance_of_template.hpp b/externals/mcl/include/mcl/type_traits/is_instance_of_template.hpp
new file mode 100644
index 00000000..8a7fc33a
--- /dev/null
+++ b/externals/mcl/include/mcl/type_traits/is_instance_of_template.hpp
@@ -0,0 +1,22 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "mcl/mp/metavalue/value.hpp"
+
+namespace mcl {
+
+/// Is type T an instance of template class C?
+template<template<class...> class, class>
+struct is_instance_of_template : mp::false_type {};
+
+template<template<class...> class C, class... As>
+struct is_instance_of_template<C, C<As...>> : mp::true_type {};
+
+/// Is type T an instance of template class C?
+template<template<class...> class C, class T>
+constexpr bool is_instance_of_template_v = is_instance_of_template<C, T>::value;
+
+}  // namespace mcl
diff --git a/externals/mcl/src/CMakeLists.txt b/externals/mcl/src/CMakeLists.txt
new file mode 100644
index 00000000..a25392b3
--- /dev/null
+++ b/externals/mcl/src/CMakeLists.txt
@@ -0,0 +1,69 @@
+add_library(mcl
+    ../include/mcl/assert.hpp
+    ../include/mcl/bit/bit_count.hpp
+    ../include/mcl/bit/bit_field.hpp
+    ../include/mcl/bit/rotate.hpp
+    ../include/mcl/bit/swap.hpp
+    ../include/mcl/bit_cast.hpp
+    ../include/mcl/bitsizeof.hpp
+    ../include/mcl/concepts/bit_integral.hpp
+    ../include/mcl/concepts/is_any_of.hpp
+    ../include/mcl/concepts/same_as.hpp
+    ../include/mcl/container/intrusive_list.hpp
+    ../include/mcl/hint/assume.hpp
+    ../include/mcl/iterator/reverse.hpp
+    ../include/mcl/macro/anonymous_variable.hpp
+    ../include/mcl/macro/architecture.hpp
+    ../include/mcl/macro/concatenate_tokens.hpp
+    ../include/mcl/mp/metafunction/apply.hpp
+    ../include/mcl/mp/metafunction/bind.hpp
+    ../include/mcl/mp/metafunction/identity.hpp
+    ../include/mcl/mp/metafunction/map.hpp
+    ../include/mcl/mp/metavalue/bit_and.hpp
+    ../include/mcl/mp/metavalue/bit_not.hpp
+    ../include/mcl/mp/metavalue/bit_or.hpp
+    ../include/mcl/mp/metavalue/bit_xor.hpp
+    ../include/mcl/mp/metavalue/conjunction.hpp
+    ../include/mcl/mp/metavalue/disjunction.hpp
+    ../include/mcl/mp/metavalue/lift_value.hpp
+    ../include/mcl/mp/metavalue/logic_and.hpp
+    ../include/mcl/mp/metavalue/logic_if.hpp
+    ../include/mcl/mp/metavalue/logic_not.hpp
+    ../include/mcl/mp/metavalue/logic_or.hpp
+    ../include/mcl/mp/metavalue/product.hpp
+    ../include/mcl/mp/metavalue/sum.hpp
+    ../include/mcl/mp/metavalue/value.hpp
+    ../include/mcl/mp/metavalue/value_cast.hpp
+    ../include/mcl/mp/metavalue/value_equal.hpp
+    ../include/mcl/mp/misc/argument_count.hpp
+    ../include/mcl/mp/typelist/append.hpp
+    ../include/mcl/mp/typelist/cartesian_product.hpp
+    ../include/mcl/mp/typelist/concat.hpp
+    ../include/mcl/mp/typelist/contains.hpp
+    ../include/mcl/mp/typelist/drop.hpp
+    ../include/mcl/mp/typelist/get.hpp
+    ../include/mcl/mp/typelist/head.hpp
+    ../include/mcl/mp/typelist/length.hpp
+    ../include/mcl/mp/typelist/lift_sequence.hpp
+    ../include/mcl/mp/typelist/list.hpp
+    ../include/mcl/mp/typelist/lower_to_tuple.hpp
+    ../include/mcl/mp/typelist/prepend.hpp
+    ../include/mcl/mp/typelist/tail.hpp
+    ../include/mcl/scope_exit.hpp
+    ../include/mcl/stdint.hpp
+    ../include/mcl/type_traits/function_info.hpp
+    ../include/mcl/type_traits/integer_of_size.hpp
+    ../include/mcl/type_traits/is_instance_of_template.hpp
+    assert.cpp
+)
+target_include_directories(mcl
+    PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+target_compile_options(mcl PRIVATE ${MCL_CXX_FLAGS})
+target_link_libraries(mcl PUBLIC $<BUILD_INTERFACE:fmt::fmt>)
+add_library(merry::mcl ALIAS mcl)
+
+include(CreateTargetDirectoryGroups)
+create_target_directory_groups(mcl)
diff --git a/externals/mcl/src/assert.cpp b/externals/mcl/src/assert.cpp
new file mode 100644
index 00000000..e30697e2
--- /dev/null
+++ b/externals/mcl/src/assert.cpp
@@ -0,0 +1,20 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#include "mcl/assert.hpp"
+
+#include <cstdio>
+#include <exception>
+
+#include <fmt/format.h>
+
+namespace mcl::detail {
+
+[[noreturn]] void assert_terminate_impl(fmt::string_view msg, fmt::format_args args) {
+    fmt::print(stderr, "assertion failed: ");
+    fmt::vprint(stderr, msg, args);
+    std::terminate();
+}
+
+}  // namespace mcl::detail
diff --git a/externals/mcl/tests/CMakeLists.txt b/externals/mcl/tests/CMakeLists.txt
new file mode 100644
index 00000000..1b60518d
--- /dev/null
+++ b/externals/mcl/tests/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_executable(mcl-tests
+    main.cpp
+    mp/metavalue_tests.cpp
+    mp/typelist_tests.cpp
+    type_traits/type_traits_tests.cpp
+)
+target_include_directories(mcl-tests PUBLIC .)
+target_compile_options(mcl-tests PRIVATE ${STAMINA_CXX_FLAGS})
+target_link_libraries(mcl-tests PRIVATE Catch2::Catch2 mcl)
+
+include(CTest)
+include(Catch)
+catch_discover_tests(mcl-tests)
+enable_testing()
diff --git a/externals/mcl/tests/main.cpp b/externals/mcl/tests/main.cpp
new file mode 100644
index 00000000..14b471b4
--- /dev/null
+++ b/externals/mcl/tests/main.cpp
@@ -0,0 +1,6 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#define CATCH_CONFIG_MAIN
+#include "catch2/catch.hpp"
diff --git a/externals/mcl/tests/mp/metavalue_tests.cpp b/externals/mcl/tests/mp/metavalue_tests.cpp
new file mode 100644
index 00000000..b7d763dc
--- /dev/null
+++ b/externals/mcl/tests/mp/metavalue_tests.cpp
@@ -0,0 +1,90 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#include <type_traits>
+
+#include <mcl/mp/metavalue/bit_and.hpp>
+#include <mcl/mp/metavalue/bit_not.hpp>
+#include <mcl/mp/metavalue/bit_or.hpp>
+#include <mcl/mp/metavalue/bit_xor.hpp>
+#include <mcl/mp/metavalue/conjunction.hpp>
+#include <mcl/mp/metavalue/disjunction.hpp>
+#include <mcl/mp/metavalue/lift_value.hpp>
+#include <mcl/mp/metavalue/logic_and.hpp>
+#include <mcl/mp/metavalue/logic_not.hpp>
+#include <mcl/mp/metavalue/logic_or.hpp>
+#include <mcl/mp/metavalue/product.hpp>
+#include <mcl/mp/metavalue/sum.hpp>
+#include <mcl/mp/metavalue/value.hpp>
+#include <mcl/mp/metavalue/value_cast.hpp>
+#include <mcl/mp/metavalue/value_equal.hpp>
+
+using namespace mcl::mp;
+
+// bit_and
+
+static_assert(bit_and<lift_value<3>, lift_value<1>>::value == 1);
+
+// bit_not
+
+static_assert(bit_not<lift_value<0>>::value == ~0);
+
+// bit_or
+
+static_assert(bit_or<lift_value<1>, lift_value<3>>::value == 3);
+
+// bit_xor
+
+static_assert(bit_xor<lift_value<1>, lift_value<3>>::value == 2);
+
+// conjunction
+
+static_assert(std::is_same_v<conjunction<std::true_type>, std::true_type>);
+static_assert(std::is_same_v<conjunction<std::true_type, lift_value<0>>, lift_value<0>>);
+static_assert(std::is_same_v<conjunction<std::true_type, lift_value<42>, std::true_type>, std::true_type>);
+
+// disjunction
+
+static_assert(std::is_same_v<disjunction<std::true_type>, std::true_type>);
+static_assert(std::is_same_v<disjunction<std::false_type, lift_value<0>>, lift_value<0>>);
+static_assert(std::is_same_v<disjunction<std::false_type, lift_value<42>, std::true_type>, lift_value<42>>);
+
+// lift_value
+
+static_assert(std::is_same_v<lift_value<3>, std::integral_constant<int, 3>>);
+static_assert(std::is_same_v<lift_value<false>, std::false_type>);
+
+// logic_and
+
+static_assert(std::is_same_v<logic_and<>, std::true_type>);
+static_assert(std::is_same_v<logic_and<std::true_type>, std::true_type>);
+static_assert(std::is_same_v<logic_and<lift_value<1>>, std::true_type>);
+static_assert(std::is_same_v<logic_and<std::true_type, std::false_type>, std::false_type>);
+
+// logic_not
+
+static_assert(std::is_same_v<logic_not<std::false_type>, std::true_type>);
+
+// logic_or
+
+static_assert(std::is_same_v<logic_or<>, std::false_type>);
+static_assert(std::is_same_v<logic_or<std::true_type>, std::true_type>);
+static_assert(std::is_same_v<logic_or<lift_value<0>>, std::false_type>);
+static_assert(std::is_same_v<logic_or<std::true_type, std::false_type>, std::true_type>);
+
+// product
+
+static_assert(product<lift_value<1>, lift_value<2>, lift_value<3>, lift_value<4>>::value == 24);
+
+// sum
+
+static_assert(sum<lift_value<1>, lift_value<2>, lift_value<3>, lift_value<4>>::value == 10);
+
+// value_cast
+
+static_assert(std::is_same_v<value_cast<int, std::true_type>, std::integral_constant<int, 1>>);
+
+// value_equal
+
+static_assert(std::is_same_v<value_equal<std::true_type, std::integral_constant<int, 1>>, std::true_type>);
diff --git a/externals/mcl/tests/mp/typelist_tests.cpp b/externals/mcl/tests/mp/typelist_tests.cpp
new file mode 100644
index 00000000..4f67dc4e
--- /dev/null
+++ b/externals/mcl/tests/mp/typelist_tests.cpp
@@ -0,0 +1,104 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <mcl/mp/metavalue/value.hpp>
+#include <mcl/mp/typelist/append.hpp>
+#include <mcl/mp/typelist/cartesian_product.hpp>
+#include <mcl/mp/typelist/concat.hpp>
+#include <mcl/mp/typelist/contains.hpp>
+#include <mcl/mp/typelist/drop.hpp>
+#include <mcl/mp/typelist/get.hpp>
+#include <mcl/mp/typelist/head.hpp>
+#include <mcl/mp/typelist/length.hpp>
+#include <mcl/mp/typelist/lift_sequence.hpp>
+#include <mcl/mp/typelist/list.hpp>
+#include <mcl/mp/typelist/lower_to_tuple.hpp>
+#include <mcl/mp/typelist/prepend.hpp>
+#include <mcl/mp/typelist/tail.hpp>
+
+using namespace mcl::mp;
+
+// append
+
+static_assert(std::is_same_v<append<list<int, bool>, double>, list<int, bool, double>>);
+static_assert(std::is_same_v<append<list<>, int, int>, list<int, int>>);
+
+// cartesian_product
+
+static_assert(
+    std::is_same_v<
+        cartesian_product<list<int, bool>, list<double, float>, list<char, unsigned>>,
+        list<
+            list<int, double, char>,
+            list<int, double, unsigned>,
+            list<int, float, char>,
+            list<int, float, unsigned>,
+            list<bool, double, char>,
+            list<bool, double, unsigned>,
+            list<bool, float, char>,
+            list<bool, float, unsigned>>>);
+
+// concat
+
+static_assert(std::is_same_v<concat<list<int, bool>, list<double>>, list<int, bool, double>>);
+static_assert(std::is_same_v<concat<list<>, list<int>, list<int>>, list<int, int>>);
+
+// contains
+
+static_assert(contains_v<list<int>, int>);
+static_assert(!contains_v<list<>, int>);
+static_assert(!contains_v<list<double>, int>);
+static_assert(contains_v<list<double, int>, int>);
+
+// drop
+
+static_assert(std::is_same_v<list<>, drop<3, list<int, int>>>);
+static_assert(std::is_same_v<list<>, drop<3, list<int, int, int>>>);
+static_assert(std::is_same_v<list<int>, drop<3, list<int, int, int, int>>>);
+static_assert(std::is_same_v<list<double>, drop<3, list<int, int, int, double>>>);
+static_assert(std::is_same_v<list<int, double, bool>, drop<0, list<int, double, bool>>>);
+
+// get
+
+static_assert(std::is_same_v<get<0, list<int, double>>, int>);
+static_assert(std::is_same_v<get<1, list<int, double>>, double>);
+
+// head
+
+static_assert(std::is_same_v<head<list<int, double>>, int>);
+static_assert(std::is_same_v<head<list<int>>, int>);
+
+// length
+
+static_assert(length_v<list<>> == 0);
+static_assert(length_v<list<int>> == 1);
+static_assert(length_v<list<int, int, int>> == 3);
+
+// lift_sequence
+
+static_assert(
+    std::is_same_v<
+        lift_sequence<std::make_index_sequence<3>>,
+        list<size_value<0>, size_value<1>, size_value<2>>>);
+
+// lower_to_tuple
+
+static_assert(lower_to_tuple_v<list<size_value<0>, size_value<1>, size_value<2>>> == std::tuple<std::size_t, std::size_t, std::size_t>(0, 1, 2));
+static_assert(lower_to_tuple_v<list<std::true_type, std::false_type>> == std::make_tuple(true, false));
+
+// prepend
+
+static_assert(std::is_same_v<prepend<list<int, int>, double>, list<double, int, int>>);
+static_assert(std::is_same_v<prepend<list<>, double>, list<double>>);
+static_assert(std::is_same_v<prepend<list<int>, double, bool>, list<double, bool, int>>);
+
+// tail
+
+static_assert(std::is_same_v<tail<list<int, double>>, list<double>>);
+static_assert(std::is_same_v<tail<list<int>>, list<>>);
diff --git a/externals/mcl/tests/type_traits/type_traits_tests.cpp b/externals/mcl/tests/type_traits/type_traits_tests.cpp
new file mode 100644
index 00000000..b1a3ed7a
--- /dev/null
+++ b/externals/mcl/tests/type_traits/type_traits_tests.cpp
@@ -0,0 +1,39 @@
+// This file is part of the mcl project.
+// Copyright (c) 2022 merryhime
+// SPDX-License-Identifier: MIT
+
+#include <tuple>
+#include <type_traits>
+
+#include <mcl/type_traits/function_info.hpp>
+#include <mcl/type_traits/is_instance_of_template.hpp>
+
+using namespace mcl;
+
+// function_info
+
+struct Bar {
+    int frob(double a) { return a; }
+};
+
+static_assert(parameter_count_v<void()> == 0);
+static_assert(parameter_count_v<void(int, int, int)> == 3);
+static_assert(std::is_same_v<get_parameter<void (*)(bool, int, double), 2>, double>);
+static_assert(std::is_same_v<equivalent_function_type<void (*)(bool, int, double)>, void(bool, int, double)>);
+static_assert(std::is_same_v<return_type<void (*)(bool, int, double)>, void>);
+static_assert(std::is_same_v<equivalent_function_type<decltype(&Bar::frob)>, int(double)>);
+static_assert(std::is_same_v<class_type<decltype(&Bar::frob)>, Bar>);
+
+// is_instance_of_template
+
+template<class, class...>
+class Foo {};
+
+template<class, class>
+class Pair {};
+
+static_assert(is_instance_of_template_v<std::tuple, std::tuple<int, bool>>);
+static_assert(!is_instance_of_template_v<std::tuple, bool>);
+static_assert(is_instance_of_template_v<Foo, Foo<bool>>);
+static_assert(is_instance_of_template_v<Pair, Pair<bool, int>>);
+static_assert(!is_instance_of_template_v<Pair, Foo<bool, int>>);