diff --git a/CMakeLists.txt b/CMakeLists.txt
index b130c84102eb591a7ff16272863cfd837a91ea50..c01b1c12e8e42228b4429679218b776b4011fa06 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,6 +62,7 @@ add_subdirectory( vendor/header_only )
 
 # 3rd party integrations
 add_subdirectory( integrations/bullet )
+add_subdirectory( integrations/entt )
 
 
 ## G++ enable insane checks
diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml
index c1258bf2c631b5bb898b0053a62d484f25ecf40b..d31c9660c236e39cb971dd8e663451388227c379 100644
--- a/demo/data/rollball.yml
+++ b/demo/data/rollball.yml
@@ -8,11 +8,16 @@ prefabs:
         shape:
           type: box
           scale: [1.0, 5.0, 41]
-      phys::Body:
-        type: static
-        shape:
-          type: box
-          extents: [0.5, 2.5, 20.5]
+      gfx::material:
+        ambient: [0.0215, 0.1754, 0.0215]
+        diffuse: [1, 1, 1]
+        specular: [0.0633, 0.727811, 0.633]
+        shininess: 16
+#      phys::Body:
+#        type: static
+#        shape:
+#          type: box
+#          extents: [0.5, 2.5, 20.5]
   # Wall Z shorter to avoid z-fighting
   - name: "wallZ"
     components:
@@ -22,11 +27,16 @@ prefabs:
         shape:
           type: box
           scale: [39, 5, 1]
-      phys::Body:
-        type: static
-        shape:
-          type: box
-          extents: [ 19.5, 2.5, 0.5 ]
+      gfx::material:
+        ambient: [0.0215, 0.1754, 0.0215]
+        diffuse: [1, 1, 1]
+        specular: [0.0633, 0.727811, 0.633]
+        shininess: 16
+#      phys::Body:
+#        type: static
+#        shape:
+#          type: box
+#          extents: [ 19.5, 2.5, 0.5 ]
   - name: "floor"
     components:
       Transform:
@@ -35,11 +45,16 @@ prefabs:
         shape:
           type: box # we don't (currently) support planes...
           scale: [39, 0.5, 39]
-      phys::Body:
-        type: static
-        shape:
-          type: box # we don't (currently) support planes...
-          extents: [19.5, 0.25, 19.5]
+      gfx::material:
+        ambient: [0.0215, 0.1754, 0.0215]
+        diffuse: [1, 1, 1]
+        specular: [0.0633, 0.727811, 0.633]
+        shininess: 16
+#      phys::Body:
+#        type: static
+#        shape:
+#          type: box # we don't (currently) support planes...
+#          extents: [19.5, 0.25, 19.5]
   - name: player
     components:
       Transform:
@@ -52,10 +67,10 @@ prefabs:
         diffuse: [0.4, 0.4, 0.4]
         specular: [0.774597,0.774597,0.774597]
         shininess: 16
-      phys::Body:
-        shape:
-          type: sphere
-          radius: 1
+#      phys::Body:
+#        shape:
+#          type: sphere
+#          radius: 1
   - name: collectable
     components:
       Transform:
@@ -68,9 +83,9 @@ prefabs:
         diffuse: [1, 1, 1]
         specular: [0.0633, 0.727811, 0.633]
         shininess: 16
-      phys::Body:
-        type: kinematic
-        shape:
-          type: box
-      phys::Callbacks:
-      phys::Cache:
\ No newline at end of file
+#      phys::Body:
+#        type: kinematic
+#        shape:
+#          type: box
+#      phys::Callbacks:
+#      phys::Cache:
\ No newline at end of file
diff --git a/demo/demo/GameScene.cpp b/demo/demo/GameScene.cpp
index 9fee528217c7ca5a62f316b6f9ad9c303b55d8df..165c4b99fa881fc35509e415d8fde2d99615efde 100644
--- a/demo/demo/GameScene.cpp
+++ b/demo/demo/GameScene.cpp
@@ -23,31 +23,31 @@
 //
 
 #include "GameScene.h"
+#include "fggl/entity/loader/loader.hpp"
 
 camera_type cam_mode = cam_free;
 
-static void placeObject(fggl::ecs3::World& world, fggl::ecs::entity_t parent, fggl::ecs::entity_t prototype, glm::vec3 targetPos) {
-	auto obj = world.copy(prototype);
-	auto result = world.get<fggl::math::Transform>(obj);
+static void placeObject(fggl::entity::EntityManager& world, fggl::entity::EntityID floor, fggl::entity::EntityFactory* factory, fggl::util::GUID prototype, glm::vec3 targetPos) {
+	auto obj = factory->create(prototype, world);
+	auto& result = world.get<fggl::math::Transform>(obj);
 
 	int xPos = (int)targetPos.x;
 	int zPos = (int)targetPos.z * -1;
 
 	// figure out the floor height
-	auto heightMap = world.get<fggl::data::HeightMap>(parent);
-	targetPos.y = heightMap->getValue(xPos, zPos); // TODO should really be the gradient at the required point
-
-	result->origin( targetPos );
+	auto heightMap = world.get<fggl::data::HeightMap>(floor);
+	targetPos.y = heightMap.getValue(xPos, zPos); // TODO should really be the gradient at the required point
+	result.origin( targetPos );
 }
 
-static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) {
-	auto cameras = ecs.findMatching<fggl::gfx::Camera>();
-	fggl::ecs3::entity_t cam = cameras[0];
+static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) {
+	auto cameras = ecs.find<fggl::gfx::Camera>();
+	auto cam = cameras[0];
 
 	auto camTransform = ecs.get<fggl::math::Transform>(cam);
 	auto camComp = ecs.get<fggl::gfx::Camera>(cam);
 
-	const glm::vec3 dir = ( camTransform->origin() - camComp->target );
+	const glm::vec3 dir = ( camTransform.origin() - camComp.target );
 	const glm::vec3 forward = glm::normalize( dir );
 
 	// scroll wheel
@@ -55,7 +55,7 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp
 	float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y );
 	if ( (glm::length( dir ) < 25.0f && delta < 0.0f) || (glm::length( dir ) > 2.5f && delta > 0.0f) )
 		motion -= (forward * delta);
-	camTransform->origin( camTransform->origin() + motion );
+	camTransform.origin( camTransform.origin() + motion );
 
 	if ( cam_mode == cam_arcball || input.mouse.down( fggl::input::MouseButton::MIDDLE ) ) {
 		fggl::input::process_arcball(ecs, input, cam);
@@ -65,35 +65,31 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp
 	fggl::input::process_edgescroll( ecs, input, cam );
 }
 
-static void setupCamera(fggl::ecs3::World& world) {
-	auto prototype = world.create(false);
+static void setupCamera(fggl::entity::EntityManager& world) {
+	auto prototype = world.create();
 
 	// setup camera position/transform
-	auto* transform = world.add<fggl::math::Transform>(prototype);
-	if ( transform != nullptr) {
-		transform->origin(glm::vec3(10.0f, 3.0f, 10.0f));
-	}
+	auto& transform = world.add<fggl::math::Transform>(prototype);
+	transform.origin(glm::vec3(10.0f, 3.0f, 10.0f));
 
 	// setup camera components
 	world.add<fggl::gfx::Camera>(prototype);
-	auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
-	if ( cameraKeys != nullptr ) {
-		cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W);
-		cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S);
-		cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A);
-		cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D);
-		cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q);
-		cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
-	}
+	auto& cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
+	cameraKeys.forward = glfwGetKeyScancode(GLFW_KEY_W);
+	cameraKeys.backward = glfwGetKeyScancode(GLFW_KEY_S);
+	cameraKeys.left = glfwGetKeyScancode(GLFW_KEY_A);
+	cameraKeys.right = glfwGetKeyScancode(GLFW_KEY_D);
+	cameraKeys.rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q);
+	cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
 }
 
-static fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) {
-	fggl::ecs3::entity_t terrain;
+static fggl::entity::EntityID setupTerrain(fggl::entity::EntityManager& world) {
+	fggl::entity::EntityID terrain;
 	{
-		terrain = world.create(false);
+		terrain = world.create();
 
-		auto* camTf = world.add<fggl::math::Transform>(terrain);
-		camTf->origin( glm::vec3(-128.0f, 0.0f, 128.0f) );
+		auto& camTf = world.add<fggl::math::Transform>(terrain);
+		camTf.origin( glm::vec3(-128.0f, 0.0f, 128.0f) );
 
 		//auto terrainData = m_world.get<fggl::data::HeightMap>(terrain);
 		fggl::data::HeightMap terrainData{};
@@ -102,34 +98,35 @@ static fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) {
 		const siv::PerlinNoise::seed_type seed = 123456U;
 		const siv::PerlinNoise perlin{ seed };
 
-		for (int z = 0; z < fggl::data::heightMaxZ; ++z) {
-			for (int x = 0; x < fggl::data::heightMaxX; ++x) {
+		for (std::size_t z = 0; z < fggl::data::heightMaxZ; ++z) {
+			for (std::size_t x = 0; x < fggl::data::heightMaxX; ++x) {
 				const double noise = perlin.octave2D_11( (x * 0.01), (z * 0.01) , 4) * 10.f;
 				terrainData.setValue(x, z, (float)noise);
 			}
 		}
-		world.set<fggl::data::HeightMap>(terrain, &terrainData);
+
+	//	world.set<fggl::data::HeightMap>(terrain, &terrainData);
 	}
 	return terrain;
 }
 
-static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world) {
-	auto& types = world.types();
+static fggl::entity::EntityID setupEnvironment(fggl::entity::EntityManager& world) {
 	setupCamera(world);
 	return setupTerrain(world);
 }
 
-static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) {
-	auto& types = world.types();
-	fggl::ecs3::entity_t bunker;
+static auto BUNKER_PROTOTYPE = "bunker"_fid;
+
+static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) {
 	{
-		bunker = world.create(true);
-		world.add(bunker, types.find(fggl::math::Transform::name));
+		auto bunkerSpec = fggl::entity::EntitySpec{};
+		bunkerSpec.components[fggl::util::make_guid("transform")] = {};
+
+		fggl::entity::ComponentSpec procMesh{};
+		procMesh.set<std::string>("shader", "phong");
 
 		// mesh
 		int nSections = 2;
-		constexpr float HALF_PI = M_PI / 2.0f;
-		constexpr char shader[] = "phong";
 
 		fggl::data::Mesh mesh;
 		for (int j=-(nSections/2); j<=nSections/2; j++) {
@@ -138,10 +135,10 @@ static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) {
 			const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset );
 			const auto leftSlope = fggl::math::modelMatrix(
 				glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset,
-				glm::vec3( 0.0f, -HALF_PI, 0.0f) );
+				glm::vec3( 0.0f, -fggl::math::HALF_PI, 0.0f) );
 			const auto rightSlope = fggl::math::modelMatrix(
 				glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
-				glm::vec3( 0.0f, HALF_PI, 0.0f) );
+				glm::vec3( 0.0f, fggl::math::HALF_PI, 0.0f) );
 
 			fggl::data::make_cube( mesh, cubeMat );
 			fggl::data::make_slope( mesh, leftSlope );
@@ -149,17 +146,21 @@ static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) {
 		}
 		mesh.removeDups();
 
-		fggl::data::StaticMesh staticMesh{mesh, shader};
-		world.set<fggl::data::StaticMesh>(bunker, &staticMesh);
+		procMesh.set<std::vector<fggl::data::Vertex>>("vertexData", mesh.vertexList());
+		procMesh.set<std::vector<unsigned int>>("indexData", mesh.indexList());
+		bunkerSpec.components[fggl::util::make_guid("procedural_mesh")] = procMesh;
+
+		factory->define(BUNKER_PROTOTYPE, bunkerSpec);
 	}
-	return bunker;
 }
 
 void GameScene::setup() {
 	m_canvas.size( fggl::math::vec2(0,0), fggl::math::vec2(100, 100));
 
+	auto* entityFactory = m_owner.service<fggl::entity::EntityFactory>();
+
 	auto terrain = setupEnvironment(world());
-	auto bunkerPrototype = setupBunkerPrototype(world());
+	setupBunkerPrototype(entityFactory);
 
 	// create building prototype
 	int nCubes = 3;
@@ -167,14 +168,12 @@ void GameScene::setup() {
 		glm::vec3 location;
 		location.x = i * 6.f + 1.0f;
 		location.z = -5.0f + 1.0f;
-		placeObject(world(), terrain, bunkerPrototype, location);
+		placeObject(world(), terrain, entityFactory, BUNKER_PROTOTYPE, location);
 	}
 }
 
 void GameScene::update() {
 	Game::update();
-
-	auto& inputSystem = input();
 	process_camera(world(), input());
 }
 
diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp
index a09e849aa03e51d94e2fa8d96c05bc25c99c4a49..24106bb9de13a7ff6055d965716fe0a5ab5452a5 100644
--- a/demo/demo/main.cpp
+++ b/demo/demo/main.cpp
@@ -26,7 +26,7 @@
 #endif
 
 #include "fggl/fggl.hpp"
-#include "fggl/ecs/component_fwd.hpp"
+#include "fggl/entity/module.hpp"
 
 #include "fggl/audio/openal/audio.hpp"
 
@@ -84,6 +84,7 @@ int main(int argc, const char* argv[]) {
 	moduleManager.use<fggl::gfx::OpenGL4>();
 	moduleManager.use<fggl::display::GLFW>();
 	moduleManager.use<fggl::assets::AssetFolders>();
+	moduleManager.use<fggl::entity::ECS>();
 	moduleManager.resolve();
 
 	// create the application
@@ -99,10 +100,10 @@ int main(int argc, const char* argv[]) {
 
 	// load a bunch of modules to provide game functionality
 	//app.use<fggl::ecs3::ecsTypes>();
-	app.use<fggl::gfx::SceneUtils>();
+	/*app.use<fggl::gfx::SceneUtils>();
 	#ifdef FGGL_MODULE_BULLET
 		app.use<FGGL_MODULE_BULLET>();
-	#endif
+	#endif*/
 
 	// our test states
 	setup_menu(app);
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index 9967e3091f8b54475a6c2bfd65ea9e786c05b8bc..88cc9269175199e4af3d489b866edfbb5b338063 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -23,83 +23,65 @@
 #include "fggl/gfx/phong.hpp"
 
 #include "fggl/input/camera_input.hpp"
-#include "fggl/ecs3/prototype/loader.hpp"
-#include "fggl/debug/draw.hpp"
-
-#include <array>
-
-struct Prefabs {
-	fggl::ecs3::entity_t collectable;
-	fggl::ecs3::entity_t player;
-};
 
-static void setup_prefabs(fggl::data::Storage* storage, fggl::ecs3::World& world, Prefabs& prefabs) {
-	fggl::ecs3::load_prototype_file(world, *storage, "rollball.yml");
-
-	{
-		// player (cube because my sphere function doesn't exist yet
-		prefabs.player = world.findPrototype("player");
-		world.add<fggl::phys::Dynamics>(prefabs.player);
-	}
+#include "fggl/entity/entity.hpp"
+#include "fggl/entity/loader/loader.hpp"
 
-	{
-		// collectable
-		prefabs.collectable = world.findPrototype("collectable");
+#include "fggl/debug/draw.hpp"
 
-		// we need both of these for callbacks to trigger.
-		world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable);
-		world.add<fggl::phys::CollisionCache>(prefabs.collectable);
-	}
+#include <array>
 
-}
+static const fggl::util::GUID playerPrefab = "player"_fid;
+static const fggl::util::GUID collectablePrefab = "collectable"_fid;
+static const fggl::util::GUID WallNPrefab = "wallX"_fid;
+static const fggl::util::GUID WallEPrefab = "wallZ"_fid;
+static const fggl::util::GUID floorPrefab = "floor"_fid;
 
-static void setup_camera(fggl::ecs3::World& world) {
-	auto prototype = world.create(false);
+static void setup_camera(fggl::entity::EntityManager& world) {
+	auto prototype = world.create();
 
 	// setup camera position/transform
-	auto* transform = world.add<fggl::math::Transform>(prototype);
-	if ( transform != nullptr) {
-		transform->origin(glm::vec3(10.0f, 3.0f, 10.0f));
-	}
+	auto& transform = world.add<fggl::math::Transform>(prototype);
+	transform.origin(glm::vec3(10.0f, 3.0f, 10.0f));
 
 	// setup camera components
 	world.add<fggl::gfx::Camera>(prototype);
 }
 
-static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, const fggl::math::vec2& size, demo::RollState& state) {
+static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world, fggl::entity::EntityFactory* factory, const fggl::math::vec2& size, demo::RollState& state) {
 	{
-		auto northWall = world.createFromPrototype("wallX");
-		auto* transform = world.get<fggl::math::Transform>(northWall);
-		transform->origin({size.x/2, 0.0F, 0.0F});
+		auto northWall = factory->create(WallNPrefab, world);
+		auto& transform = world.get<fggl::math::Transform>(northWall);
+		transform.origin({size.x/2, 0.0F, 0.0F});
 	}
 
 	{
-		auto southWall = world.createFromPrototype("wallX");
-		auto* transform = world.get<fggl::math::Transform>(southWall);
-		transform->origin({-size.x/2, 0.0F, 0.0F});
+		auto southWall = factory->create(WallNPrefab, world);
+		auto& transform = world.get<fggl::math::Transform>(southWall);
+		transform.origin({-size.x/2, 0.0F, 0.0F});
 	}
 
 	{
-		auto westWall = world.createFromPrototype("wallZ");
-		auto* transform = world.get<fggl::math::Transform>(westWall);
-		transform->origin({0.0F, 0.0F, -size.y/2});
+		auto westWall = factory->create(WallEPrefab, world);
+		auto& transform = world.get<fggl::math::Transform>(westWall);
+		transform.origin({0.0F, 0.0F, -size.y/2});
 	}
 
 	{
-		auto eastWall = world.createFromPrototype("wallZ");
-		auto* transform = world.get<fggl::math::Transform>(eastWall);
-		transform->origin({0.0F, 0.0F, size.y/2});
+		auto eastWall = factory->create(WallEPrefab, world);
+		auto& transform = world.get<fggl::math::Transform>(eastWall);
+		transform.origin({0.0F, 0.0F, size.y/2});
 	}
 
 	{
-		auto floor = world.createFromPrototype("floor");
-		auto *transform = world.get<fggl::math::Transform>(floor);
-		transform->origin({0.0F, -2.5F, 0.0F});
+		auto floor = factory->create(floorPrefab, world);
+		auto& transform = world.get<fggl::math::Transform>(floor);
+		transform.origin({0.0F, -2.5F, 0.0F});
 	}
 
 	{
 		// player just starts off as the prefab dictates
-		state.player = world.createFromPrototype("player");
+		state.player = factory->create(playerPrefab, world);
 	}
 
 	{
@@ -113,16 +95,16 @@ static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, const fg
 		// build the collectables
 		int collectCount = 0;
 		for (auto& pos : collectPos) {
-			auto collectable = world.createFromPrototype("collectable");
-			auto* transform = world.get<fggl::math::Transform>(collectable);
-			transform->origin(pos);
+			auto collectable = factory->create(collectablePrefab, world);
+			auto& transform = world.get<fggl::math::Transform>(collectable);
+			transform.origin(pos);
 
 			state.collectables[collectCount++] = collectable;
 		}
 	}
 
 	// ensure the state is clean
-	state.closestPickup = fggl::ecs::NULL_ENTITY;
+	state.closestPickup = fggl::entity::INVALID;
 	state.mode = demo::DebugMode::NORMAL;
 	state.time = 0.0F;
 
@@ -144,26 +126,28 @@ namespace demo {
 	void RollBall::activate() {
 		Game::activate();
 
-		Prefabs prefabs{};
-		setup_prefabs(m_owner.service<fggl::data::Storage>(), world(), prefabs);
+		fggl::debug::log(fggl::debug::Level::info, "RollBall::activate()");
+
+		auto* assetLoader = m_owner.service<fggl::assets::Loader>();
+		assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET);
 
 		// collectable callbacks
-		auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
+		/*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
 		collectableCallbacks->onEnter = [this](auto ourEntity, auto theirEntity) {
 			if ( theirEntity == state.player) {
 				//if ( ourEntity == state.closestPickup ) {
 				//	// we're the closest pickup and we're about to get killed, so need to not be.
 				//	state.closestPickup = fggl::ecs::NULL_ENTITY;
 				//}
-				this->world().destroy(ourEntity);
+				world().destroy(ourEntity);
 			}
-		};
+		};*/
 
 		// actual scene objects
 		setup_camera(world());
 
 		// create a 20x20 grid
-		setup_environment(world(), WORLD_SIZE, state);
+		setup_environment(world(), m_owner.service<fggl::entity::EntityFactory>(), WORLD_SIZE, state);
 	}
 
 	fggl::math::vec3 calc_move_vector(const fggl::input::Input& input) {
@@ -197,7 +181,7 @@ namespace demo {
 
 		auto& input = this->input();
 
-			if ( state.player != fggl::ecs3::NULL_ENTITY ) {
+			if ( state.player != fggl::entity::INVALID ) {
 				auto &world = this->world();
 
 				// mode selection
@@ -209,26 +193,26 @@ namespace demo {
 				auto force = calc_move_vector(input);
 				if ( force != fggl::math::VEC3_ZERO ) {
 					force = glm::normalize(force) * MOVE_FORCE;
-					auto *dynamics = world.get<fggl::phys::Dynamics>(state.player);
-					dynamics->force += force;
+					auto& dynamics = world.get<fggl::phys::Dynamics>(state.player);
+					dynamics.force += force;
 				}
 
 				// track player position with camera
 				{
-					auto cameras = world.findMatching<fggl::gfx::Camera>();
-					fggl::ecs3::entity_t cam = cameras[0];
-					auto *camComp = world.get<fggl::gfx::Camera>(cam);
-					camComp->target = world.get<fggl::math::Transform>(state.player)->origin();
+					auto cameras = world.find<fggl::gfx::Camera>();
+					auto cam = cameras[0];
+					auto& camComp = world.get<fggl::gfx::Camera>(cam);
+					camComp.target = world.get<fggl::math::Transform>(state.player).origin();
 
-					auto *camTransform = world.get<fggl::math::Transform>(cam);
-					camTransform->origin( camComp->target + cameraOffset );
+					auto& camTransform = world.get<fggl::math::Transform>(cam);
+					camTransform.origin( camComp.target + cameraOffset );
 				}
 
 				// distance mode
 				if ( state.mode == DebugMode::DISTANCE ) {
 					closestPickup(world);
 				} else {
-					if ( state.closestPickup != fggl::ecs::NULL_ENTITY ) {
+					if ( state.closestPickup != fggl::entity::INVALID ) {
 						if ( world.alive(state.closestPickup) ){
 							auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup);
 							if (renderer != nullptr) {
@@ -236,7 +220,7 @@ namespace demo {
 							}
 						}
 
-						state.closestPickup = fggl::ecs::NULL_ENTITY;
+						state.closestPickup = fggl::entity::INVALID;
 					}
 				}
 
@@ -246,18 +230,18 @@ namespace demo {
 		}
 	}
 
-	void RollBall::closestPickup(fggl::ecs3::World &world) {
+	void RollBall::closestPickup(fggl::entity::EntityManager &world) {
 		float closestDistance = FLT_MAX;
-		fggl::ecs::entity_t closestEntity = fggl::ecs::NULL_ENTITY;
-		auto playerPos = world.get<fggl::math::Transform>(state.player)->origin();
+		auto closestEntity = fggl::entity::INVALID;
+		auto playerPos = world.get<fggl::math::Transform>(state.player).origin();
 
 		for ( const auto& pickup : state.collectables ) {
 			if ( !world.alive(pickup) ) {
 				continue;
 			}
 
-			auto* transform = world.get<fggl::math::Transform>(pickup);
-			auto distance = glm::distance(transform->origin(), playerPos);
+			auto& transform = world.get<fggl::math::Transform>(pickup);
+			auto distance = glm::distance(transform.origin(), playerPos);
 			if ( distance < closestDistance) {
 				closestDistance = distance;
 				closestEntity = pickup;
@@ -275,7 +259,7 @@ namespace demo {
 
 			// now, deal with the new closest
 			state.closestPickup = closestEntity;
-			if (closestEntity != fggl::ecs::NULL_ENTITY) {
+			if (closestEntity != fggl::entity::INVALID) {
 				auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup);
 				if (renderer != nullptr) {
 					renderer->diffuse = COLOUR_BLUE;
@@ -284,18 +268,18 @@ namespace demo {
 		}
 
 		// closest pickup should face the player
-		if ( state.closestPickup != fggl::ecs::NULL_ENTITY) {
-			auto* transform = world.get<fggl::math::Transform>(state.closestPickup);
-			auto* playerTransform = world.get<fggl::math::Transform>( state.player );
-			transform->lookAt(playerTransform->origin());
+		if ( state.closestPickup != fggl::entity::INVALID) {
+			auto& transform = world.get<fggl::math::Transform>(state.closestPickup);
+			auto& playerTransform = world.get<fggl::math::Transform>( state.player );
+			transform.lookAt(playerTransform.origin());
 
 			// lazy, so using the debug draw line for this bit
-			dd::line( &playerTransform->origin()[0], &transform->origin()[0], dd::colors::White );
+			dd::line( &playerTransform.origin()[0], &transform.origin()[0], dd::colors::White );
 		}
 
 	}
 
-	void RollBall::spinCubes(fggl::ecs3::World& world, float deltaTime) {
+	void RollBall::spinCubes(fggl::entity::EntityManager& world, float deltaTime) {
 		// rotation
 		for ( const auto& entity : state.collectables ) {
 			if ( !world.alive(entity) || entity == state.closestPickup ) {
@@ -303,8 +287,8 @@ namespace demo {
 			}
 
 			// rotate the cubes
-			auto* transform = world.get<fggl::math::Transform>(entity);
-			transform->rotateEuler(  COLLECTABLE_ROTATION * deltaTime );
+			auto& transform = world.get<fggl::math::Transform>(entity);
+			transform.rotateEuler( COLLECTABLE_ROTATION * deltaTime );
 		}
 
 	}
diff --git a/demo/demo/topdown.cpp b/demo/demo/topdown.cpp
index 25f7a239a7d11217244bb59a85c47ec7af47cdd7..c424dc3652050555d7a87e19f5be1a526f521553 100644
--- a/demo/demo/topdown.cpp
+++ b/demo/demo/topdown.cpp
@@ -21,35 +21,36 @@
 #include "fggl/data/storage.hpp"
 #include "fggl/gfx/camera.hpp"
 #include "fggl/input/camera_input.hpp"
-#include "fggl/ecs3/prototype/loader.hpp"
+#include "fggl/entity/loader/loader.hpp"
+
+static const fggl::util::GUID collectablePrefab = "collectable"_fid;
+static const fggl::util::GUID WallNPrefab = "wallN"_fid;
+static const fggl::util::GUID WallEPrefab = "wallE"_fid;
+static const fggl::util::GUID floorPrefab = "floor"_fid;
 
 namespace demo {
 
-	static void create_topdown_camera(fggl::ecs3::World& world) {
-		auto prototype = world.create(false);
+	static void create_topdown_camera(fggl::entity::EntityManager& world) {
+		auto prototype = world.create();
 
 		// setup camera position/transform
-		auto* transform = world.add<fggl::math::Transform>(prototype);
-		if ( transform != nullptr) {
-			transform->origin(glm::vec3(10.0f, 50.0f, 10.0f));
-		}
+		auto& transform = world.add<fggl::math::Transform>(prototype);
+		transform.origin(glm::vec3(10.0f, 50.0f, 10.0f));
 
 		// setup camera components
-		auto* camera = world.add<fggl::gfx::Camera>(prototype);
-		camera->target = glm::vec3(0.0f, 0.0f, 0.0f);
-
-		auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
-		if ( cameraKeys != nullptr ) {
-			cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W);
-			cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S);
-			cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A);
-			cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D);
-			cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q);
-			cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
-		}
+		auto& camera = world.add<fggl::gfx::Camera>(prototype);
+		camera.target = glm::vec3(0.0f, 0.0f, 0.0f);
+
+		auto& cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype);
+		cameraKeys.forward = glfwGetKeyScancode(GLFW_KEY_W);
+		cameraKeys.backward = glfwGetKeyScancode(GLFW_KEY_S);
+		cameraKeys.left = glfwGetKeyScancode(GLFW_KEY_A);
+		cameraKeys.right = glfwGetKeyScancode(GLFW_KEY_D);
+		cameraKeys.rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q);
+		cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E);
 	}
 
-	static void place_cover_boxes(fggl::ecs3::World& world) {
+	static void place_cover_boxes(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
 		std::array<fggl::math::vec3,8> boxPos = {{
 													 {-10.0F, 0.0F, -10.0F},
 													 {-10.0F, 0.0F,  10.0F},
@@ -61,52 +62,51 @@ namespace demo {
 													 {  0.0F, 0.0F,  10.0F},
 												 }};
 		for (auto pos : boxPos) {
-			auto box = world.createFromPrototype("collectable");
-			auto* transform = world.get<fggl::math::Transform>(box);
-			transform->origin(pos);
+			auto box = factory->create(collectablePrefab, world);
+			auto& transform = world.get<fggl::math::Transform>(box);
+			transform.origin(pos);
 		}
 	}
 
-	static void build_arena(fggl::ecs3::World& world) {
+	static void build_arena(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
 		{
-			auto floor = world.createFromPrototype("floor");
-			auto* transform = world.get<fggl::math::Transform>(floor);
-			transform->origin({0.0F, -2.5F, 0.0F});
-			fggl::debug::log("created floor: {}", floor);
+			auto floor = factory->create(floorPrefab, world);
+			auto& transform = world.get<fggl::math::Transform>(floor);
+			transform.origin({0.0F, -2.5F, 0.0F});
+			//fggl::debug::log("created floor: {}", floor);
 		}
 
 		fggl::math::vec2 size{40.0F, 40.0F};
 		for (auto side : {-1.0F, 1.0F})
 		{
 			{
-				auto northWall = world.createFromPrototype("wallX");
-				auto *transform = world.get<fggl::math::Transform>(northWall);
-				transform->origin({size.x / 2 * side, 0.0F, 0.0F});
+				auto obj = factory->create(WallNPrefab, world);
+				auto& transform = world.get<fggl::math::Transform>(obj);
+				transform.origin({size.x / 2 * side, 0.0F, 0.0F});
 			}
 			{
-				auto westWall = world.createFromPrototype("wallZ");
-				auto* transform = world.get<fggl::math::Transform>(westWall);
-				transform->origin({0.0F, 0.0F, size.y/2 * side });
+				auto obj = factory->create(WallEPrefab, world);
+				auto& transform = world.get<fggl::math::Transform>(obj);
+				transform.origin({0.0F, 0.0F, size.y/2 * side });
 			}
 		}
 
-		place_cover_boxes(world);
+		place_cover_boxes(factory, world);
 	}
 
-static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) {
-	auto cameras = ecs.findMatching<fggl::gfx::Camera>();
+static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) {
+	auto cameras = ecs.find<fggl::gfx::Camera>();
 	if ( !cameras.empty() ) {
-		fggl::ecs3::entity_t cam = cameras[0];
+		auto cam = cameras[0];
 		fggl::input::process_scroll(ecs, input, cam);
 		fggl::input::process_freecam(ecs, input, cam);
 		fggl::input::process_edgescroll(ecs, input, cam);
 	}
 }
 
-static void populate_sample_level(fggl::ecs3::World& world) {
+static void populate_sample_level(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) {
 	create_topdown_camera(world);
-
-	build_arena(world);
+	build_arena(factory, world);
 }
 
 TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) {
@@ -116,11 +116,11 @@ TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) {
 void TopDown::activate() {
 	Game::activate();
 
-	auto* storage = m_owner.service<fggl::data::Storage>();
-	fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml");
+	auto* factory = m_owner.service<fggl::entity::EntityFactory>();
+	//fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml");
 
 	// create a sample level
-	populate_sample_level(world());
+	populate_sample_level(factory, world());
 }
 
 void TopDown::update() {
@@ -133,12 +133,12 @@ void TopDown::update() {
 }
 
 void TopDown::pick_object() {
-	auto cameras = world().findMatching<fggl::gfx::Camera>();
+	auto cameras = world().find<fggl::gfx::Camera>();
 	if ( cameras.empty() ) {
 		return;
 	}
 
-	fggl::ecs3::entity_t cam = cameras[0];
+	auto cam = cameras[0];
 
 	fggl::math::vec2 position {
 		input().mouse.axis(fggl::input::MouseAxis::X),
@@ -147,8 +147,8 @@ void TopDown::pick_object() {
 
 	auto ray = fggl::gfx::get_camera_ray(world(), cam, position);
 	auto hit = phys().raycast(ray);
-	if ( hit != fggl::ecs3::NULL_ENTITY) {
-		fggl::debug::log("hit: {}", hit);
+	if ( hit != fggl::entity::INVALID) {
+		//fggl::debug::log("hit: {}", hit);
 	} else {
 		fggl::debug::log("no hit");
 	}
diff --git a/demo/include/rollball.hpp b/demo/include/rollball.hpp
index 1934a67a1afc000a4c9ac433faac13b04a96504c..d18bb4bac235b7b5525a07b1e2d4da347d07b71d 100644
--- a/demo/include/rollball.hpp
+++ b/demo/include/rollball.hpp
@@ -36,14 +36,14 @@ namespace demo {
 	};
 
 	struct RollState {
-		fggl::ecs::entity_t player = fggl::ecs::NULL_ENTITY;
-		fggl::ecs::entity_t closestPickup;
+		fggl::entity::EntityID player = fggl::entity::INVALID;
+		fggl::entity::EntityID closestPickup;
 		DebugMode mode = DebugMode::NORMAL;
 
-		std::array<fggl::ecs3::entity_t, 3> collectables {
-			fggl::ecs::NULL_ENTITY,
-			fggl::ecs::NULL_ENTITY,
-			fggl::ecs::NULL_ENTITY };
+		std::array<fggl::entity::EntityID, 3> collectables {
+			fggl::entity::INVALID,
+			fggl::entity::INVALID,
+			fggl::entity::INVALID };
 		float time = 0.0f;
 	};
 
@@ -63,8 +63,8 @@ namespace demo {
 			RollState state;
 			fggl::math::vec3 cameraOffset = {-15.0F, 15.0F, 0.0F};
 
-			void closestPickup(fggl::ecs3::World& world);
-			void spinCubes(fggl::ecs3::World& world, float dt);
+			void closestPickup(fggl::entity::EntityManager& world);
+			void spinCubes(fggl::entity::EntityManager& world, float dt);
 	};
 
 }
diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt
index d7c20d199fcb7ff0c09edae4876fc100753f7d62..64fa463ae42b8301ca3b3440b6d43d408aa2e08d 100644
--- a/fggl/CMakeLists.txt
+++ b/fggl/CMakeLists.txt
@@ -43,12 +43,6 @@ target_sources(${PROJECT_NAME}
         data/procedural.cpp
         data/heightmap.cpp
 
-        ecs/ecs.cpp
-        ecs3/module/module.cpp
-        ecs3/fast/Container.cpp
-        ecs3/prototype/loader.cpp
-        ecs3/prototype/world.cpp
-
         scenes/menu.cpp
         scenes/game.cpp
 
@@ -88,6 +82,7 @@ add_subdirectory(debug)
 
 # platform integrations
 add_subdirectory(platform)
+add_subdirectory(entity)
 
 ##
 # begin windows support
diff --git a/fggl/app.cpp b/fggl/app.cpp
index b2b4ee4b72f10f5bb57d6c7343c0a41e6047d6af..406fbc757a50bb42246e045b46c7587052d84e63 100644
--- a/fggl/app.cpp
+++ b/fggl/app.cpp
@@ -16,21 +16,15 @@
 #include <memory>
 
 #include <spdlog/spdlog.h>
-
 #include <fggl/app.hpp>
-#include <fggl/ecs3/types.hpp>
-#include "fggl/ecs/component_fwd.hpp"
-#include "fggl/ecs3/module/module.hpp"
 
 namespace fggl {
 
     App::App(modules::Manager* services, const Identifer& name) : App::App( services, name, name ) {
     }
 
-    App::App(modules::Manager* services, const Identifer& name, const Identifer& folder ) :
+    App::App(modules::Manager* services, const Identifer& /*name*/, const Identifer& /*folder*/ ) :
         m_running(true),
-        m_types(std::make_unique<ecs3::TypeRegistry>()),
-        m_modules(std::make_unique<ecs3::ModuleManager>(*m_types)),
 		m_window(nullptr),
         m_states(),
 		m_subsystems(services){}
@@ -58,14 +52,14 @@ namespace fggl {
 			if ( windowing != nullptr) {
 				windowing->pollEvents();
 			}
-            m_modules->onUpdate();
+            //m_modules->onUpdate();
 
 			auto& state = m_states.active();
 			state.update();
 
             // window rendering to frame buffer
             if ( m_window != nullptr ) {
-				m_modules->onFrameStart();
+			//	m_modules->onFrameStart();
 				m_window->frameStart();
 
                 // get draw instructions
@@ -73,7 +67,7 @@ namespace fggl {
                 state.render(graphics);
 
                 m_window->frameEnd();
-                m_modules->onFrameEnd();
+              //  m_modules->onFrameEnd();
 
 				m_running = m_running && !m_window->wantClose();
             }
diff --git a/fggl/data/heightmap.cpp b/fggl/data/heightmap.cpp
index 4f401f17848230e0c7d93add456b2595c4bb786c..2122f081b0d16cf2003434f76d409a340cb283b6 100644
--- a/fggl/data/heightmap.cpp
+++ b/fggl/data/heightmap.cpp
@@ -83,7 +83,7 @@ namespace fggl::data {
         delete[] triNormals;
     }
 
-    void generateHeightMesh(const data::HeightMap *heights, data::Mesh &mesh) {
+    void generateHeightMesh(const data::HeightMap& heights, data::Mesh &mesh) {
 
         // step 1: convert height data into vertex locations
         const int numElms = data::heightMaxX * data::heightMaxZ;
@@ -92,7 +92,7 @@ namespace fggl::data {
 		// iterate the
         for (std::size_t x = 0; x < data::heightMaxX; x++) {
             for (std::size_t z = 0; z < data::heightMaxZ; z++) {
-                float level = heights->getValue(x, z);
+                float level = heights.getValue(x, z);
                 auto xPos = float(x);
                 auto zPos = float(z);
 
diff --git a/fggl/ecs/ecs.cpp b/fggl/ecs/ecs.cpp
deleted file mode 100644
index cda2001366735ceab99023376f1c868b53837513..0000000000000000000000000000000000000000
--- a/fggl/ecs/ecs.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include <fggl/ecs/ecs.hpp>
-
-#include <iostream>
-
-using namespace fggl::ecs;
-
-Archetype::Archetype(const archToken_t& token ) : type(token) {
-	for ( archToken_t::size_type i = 0; i < token.size(); ++i ) {
-		data.push_back( new unsigned char[default_cap] );
-		dataSizes.push_back( default_cap );
-	}
-}
-
-ECS::ECS() : m_entityIDCounter(1) {}
-
-ECS::~ECS() {
-	for( Archetype* arch : m_archetypes ) {
-		for ( std::size_t i=0; i < arch->type.size(); ++i ) {
-			const ComponentBase* const comp = m_componentMap[arch->type[i]];
-			const std::size_t& size = comp->size();
-
-			for ( std::size_t e=0; e<arch->entities.size(); ++e ) {
-				comp->destroy(&arch->data[i][e*size]);
-			}
-			delete[] arch->data[i];
-		}
-		delete arch;
-	}
-
-	for( componentmap_t::value_type& p : m_componentMap )
-		delete p.second;
-}
-
-entity_t ECS::getNewID() {
-	return m_entityIDCounter++;
-}
-
-entity_t ECS::createEntity( ) {
-	auto eid = getNewID();
-
-	Record dummy;
-	dummy.archetype = nullptr;
-	dummy.index = 0;
-	m_entityArchtypes[ eid ] = dummy;
-
-	return eid;
-}
-
-Archetype* ECS::getArchetype(const archToken_t& id) {
-	for ( auto* arch : m_archetypes ) {
-		if ( arch->type == id ) {
-			return arch;
-		}
-	}
-
-	// didn't exist, need to make one
-	Archetype* arch = new Archetype(id);
-	m_archetypes.push_back( arch );
-
-	return arch;
-}
diff --git a/fggl/ecs3/fast/Container.cpp b/fggl/ecs3/fast/Container.cpp
deleted file mode 100644
index 182df0ae4cd0094947c7c7e63d8119a8188115e6..0000000000000000000000000000000000000000
--- a/fggl/ecs3/fast/Container.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 23/10/2021.
-//
-
-#include <fggl/ecs3/fast/Container.hpp>
-
-namespace fggl::ecs3 {
-
-    std::ostream& operator<<(std::ostream& out, RecordIdentifier const& curr) {
-        out << "record(";
-        for (std::size_t i=0; i<curr.count; i++) {
-            out << i;
-            if ( i != curr.count-1) {
-                out << ",";
-            }
-        }
-        out << ")";
-        return out;
-    }
-
-
-    std::size_t Container::create() {
-        ensure(m_size + 1);
-        auto pos = m_size++;
-
-        // setup entry
-        for (std::size_t i = 0; i < m_identifier.count; ++i) {
-            auto compMeta = m_types.meta(m_identifier.types[i]);
-            auto offset = offsets[i] + (pos * compMeta->size());
-
-            auto compPtr = backingStore + offset;
-            compMeta->construct(compPtr);
-        }
-        return pos;
-    }
-
-    void Container::remove(std::size_t pos) {
-        // cleanup entry
-        for (std::size_t i = 0; i < m_identifier.count; ++i) {
-            auto compMeta = m_types.meta(m_identifier.types[i]);
-            auto offset = offsets[i] + (pos * compMeta->size());
-
-            auto compPtr = backingStore + offset;
-            compMeta->destroy(compPtr);
-        }
-
-        // if we're not the last one, swap places
-        if (pos != m_size - 1) {
-            move(pos, *this, m_size - 1);
-        }
-
-        m_size--;
-    }
-
-    void Container::move(std::size_t newPos, Container &oldContainer, std::size_t oldPos) {
-        for (std::size_t i = 0; i < m_identifier.count; ++i) {
-            auto thisComp = m_identifier.types[i];
-            auto compMeta = m_types.meta(thisComp);
-
-            auto newPtr = backingStore + (offsets[i] + (newPos * compMeta->size()));
-            auto oldPtr = oldContainer.data_raw(thisComp) + (oldPos * compMeta->size());
-            compMeta->move(oldPtr, newPtr);
-            compMeta->destroy(oldPtr);
-        }
-    }
-
-    std::size_t Container::expand(Container &other, std::size_t otherPos, component_type_t newComp) {
-        ensure(m_size + 1);
-        auto pos = m_size++;
-
-        for (std::size_t i = 0; i < m_identifier.count; ++i) {
-            auto thisComp = m_identifier.types[i];
-            auto compMeta = m_types.meta(thisComp);
-
-            auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size()));
-            if (newComp != thisComp) {
-                // we're moving the component
-                auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size());
-                compMeta->move(oldPtr, newPtr);
-            } else {
-                // this is the new comp
-                compMeta->construct(newPtr);
-            }
-        }
-
-        // remove the old entity
-        other.remove(otherPos);
-        return pos;
-    }
-
-    void Container::contract(Container &other, std::size_t otherPos) {
-        ensure(m_size + 1);
-        auto pos = m_size++;
-
-        move(pos, other, otherPos);
-
-        for (std::size_t i = 0; i < m_identifier.count; ++i) {
-            auto thisComp = m_identifier.types[i];
-            auto compMeta = m_types.meta(thisComp);
-
-            auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size()));
-            auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size());
-            compMeta->move(oldPtr, newPtr);
-        }
-
-        other.remove(otherPos);
-    }
-
-    void Container::ensure(std::size_t size) {
-        if (size < m_capacity) {
-            return;
-        }
-
-        std::size_t required = 0;
-        for (std::size_t i = 0; i < m_identifier.count; i++) {
-            auto meta = m_types.meta(m_identifier.types[i]);
-            required += meta->size() * size;
-        }
-
-        auto *newBacking = new unsigned char[required];
-        std::size_t newOffsets[RecordIdentifier::MAX_COMPS];
-        std::size_t currOffset = 0;
-
-        // bulk copy routine
-        for (std::size_t cid = 0; cid < m_identifier.count; ++cid) {
-            auto compMeta = m_types.meta(m_identifier.types[cid]);
-            newOffsets[cid] = currOffset;
-
-            compMeta->bulkMove(
-                    backingStore + offsets[cid],
-                    newBacking + newOffsets[cid],
-                    m_size);
-
-            currOffset += size * compMeta->size();
-        }
-
-        // swap the backing store and cleanup the old one
-        auto *tmp = backingStore;
-        backingStore = newBacking;
-        delete[] tmp;
-
-        // update size info
-        std::memcpy(offsets, newOffsets, m_identifier.count * sizeof(std::size_t));
-        m_capacity = size;
-    }
-
-}
diff --git a/fggl/ecs3/prototype/loader.cpp b/fggl/ecs3/prototype/loader.cpp
deleted file mode 100644
index 5c5ca6fd37143d8dd4785b7f7ca8b6a46d2f5cd4..0000000000000000000000000000000000000000
--- a/fggl/ecs3/prototype/loader.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-#include "fggl/ecs3/prototype/loader.hpp"
-
-namespace fggl::ecs3 {
-
-	void load_prototype_node( ecs3::World& world, const YAML::Node& node) {
-		auto prototype = world.create(true);
-		auto& typeReg = world.types();
-
-		// store the prototype's name for future use
-		auto* nameComp = world.get<ecs3::EntityMeta>( prototype );
-		nameComp->typeName = node["name"].as<std::string>();
-
-		// grab the components and iterate them
-		if ( node["components"] ) {
-			world.createFromSpec( prototype, node["components"]);
-		}
-	}
-
-	void load_prototype_file( ecs3::World& world, data::Storage& storage, const std::string& name ) {
-		auto path = storage.resolvePath( data::Data, name );
-		if ( !std::filesystem::exists(path) ) {
-			debug::log(debug::Level::warning, "prototype file not found: {}", path.string());
-			return;
-		}
-
-		auto root = YAML::LoadFile( path.string().c_str() );
-		for (const auto& node : root["prefabs"] ) {
-			load_prototype_node(world, node);
-		}
-	}
-
-}
-
-namespace fggl::ecs {
-
-	static void process_mesh(data::Mesh& out, const YAML::Node& shape) {
-		auto transform = data::OFFSET_NONE;
-		if ( shape["offset"] ) {
-			auto scaleFactor = shape["offset"].as<math::vec3>();
-			transform = glm::translate(transform,  scaleFactor);
-		}
-
-		if ( shape["scale"] ) {
-			auto scaleFactor = shape["scale"].as<math::vec3>();
-			transform = glm::scale(transform,  scaleFactor);
-		}
-
-		// now the shape itself
-		auto shapeType = shape["type"].as<std::string>();
-		if ( shapeType == SHAPE_BOX_VALUE ) {
-			data::make_cube(out, transform);
-		} else if ( shapeType == SHAPE_SPHERE_VALUE ) {
-			int stacks = shape["stacks"].as<int>(DEFAULT_STACKS);
-			int slices = shape["slices"].as<int>(DEFAULT_SLICES);
-			data::make_sphere(out, transform, stacks, slices);
-		}
-	}
-
-	template<>
-	bool restore_config(math::Transform* comp, const YAML::Node& node) {
-		comp->origin(node["origin"].as<math::vec3>( math::VEC3_ZERO ));
-		comp->euler(node["rotation"].as<math::vec3>(math::VEC3_ZERO));
-		comp->scale(node["scale"].as<math::vec3>(math::VEC3_ONES));
-		return true;
-	}
-
-	template<>
-	bool restore_config(gfx::PhongMaterial* comp, const YAML::Node& node) {
-		comp->diffuse = node["diffuse"].as<math::vec3>( gfx::DEFAULT_DIFFUSE );
-		comp->ambient = node["ambient"].as<math::vec3>( gfx::DEFAULT_AMBIENT );
-		comp->specular = node["specular"].as<math::vec3>( gfx::DEFAULT_SPECULAR );
-		comp->shininess = node["shininess"].as<float>( gfx::DEFAULT_SHININESS );
-		return true;
-	}
-
-	template<>
-	bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node) {
-		if ( !node["pipeline"] ) {
-			return false;
-		}
-
-		meshComp->pipeline = node["pipeline"].as<std::string>();
-
-		if ( node["shape"] ) {
-			data::Mesh mesh;
-			if (node["shape"].IsSequence()) {
-				// is a composite shape
-				for (const auto &shapeNode : node["shape"]) {
-					process_mesh(mesh, shapeNode);
-				}
-			} else {
-				// is a basic shape
-				process_mesh(mesh, node["shape"]);
-			}
-
-			// optimise and load
-			mesh.removeDups();
-			meshComp->mesh = mesh;
-		} else {
-			return false;
-		}
-
-		return true;
-	}
-
-	template<>
-	bool restore_config(phys::RigidBody* body, const YAML::Node& node) {
-		body->type = node["type"].as<fggl::phys::BodyType>(fggl::phys::BodyType::DYNAMIC);
-		if ( body->type == fggl::phys::BodyType::STATIC) {
-			body->mass = phys::MASS_STATIC;
-		} else {
-			body->mass = node["mass"].as<float>( phys::MASS_DEFAULT );
-		}
-
-		// shape detection
-		if ( node["shape"] ) {
-			auto type = node["shape"]["type"].as< std::string >();
-
-			if ( type == SHAPE_BOX_VALUE ) {
-				// assume unit box if extents are missing
-				auto extents = node["shape"]["extents"].as<math::vec3>(phys::UNIT_EXTENTS);
-				body->shape = new phys::Box(extents);
-			} else if ( type == SHAPE_SPHERE_VALUE ) {
-				auto radius = node["shape"]["radius"].as<float>(0.5F);
-				body->shape = new phys::Sphere(radius);
-			} else {
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	template<>
-	bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node) {
-		// TODO implement callback scripting
-		return true;
-	}
-
-	template<>
-	bool restore_config(phys::CollisionCache* cache, const YAML::Node& node){
-		// probably nothing needed here. Ideally, this should be automatically added when callbacks are.
-		return true;
-	}
-}
diff --git a/fggl/entity/CMakeLists.txt b/fggl/entity/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d1ab8fe68c683cb013bf9878117713459d9eb81c
--- /dev/null
+++ b/fggl/entity/CMakeLists.txt
@@ -0,0 +1,5 @@
+target_sources(fggl
+    PRIVATE
+        loader/loader.cpp
+        module.cpp
+)
diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62750d21ee7ef9b951e55a14bec84c8f4c8d4023
--- /dev/null
+++ b/fggl/entity/loader/loader.cpp
@@ -0,0 +1,55 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 24/07/22.
+//
+
+#include "fggl/entity/loader/loader.hpp"
+#include "fggl/debug/logging.hpp"
+
+namespace fggl::entity {
+
+	assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data) {
+		auto* filePath = std::get<assets::AssetPath*>(data);
+
+		// We need to process the prototypes, and load them into the asset system.
+		auto nodes = YAML::LoadAllFromFile( filePath->c_str() );
+		for (const auto& node : nodes) {
+			auto prefabs = node["prefabs"];
+			for ( const auto& prefab : prefabs ) {
+				auto name = prefab["name"].as<fggl::util::GUID>();
+
+				#ifndef NDEBUG
+					debug::log(debug::Level::info, "found prefab: {}", fggl::util::guidToString(name) );
+				#endif
+
+				// setup the components
+				EntitySpec entity{};
+				for (const auto& compEntry : prefab["components"]) {
+					auto compId = compEntry.first.as<fggl::util::GUID>();
+
+					ComponentSpec compSpec{};
+					compSpec.config = compEntry.second;
+					entity.components[compId] = compSpec;
+				}
+
+				factory->define( name, entity );
+			}
+		}
+
+		return nullptr;
+	}
+
+} // namespace fggl::entity
\ No newline at end of file
diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab79b1b211d3864e9c1f5848c69b5ea8bf195661
--- /dev/null
+++ b/fggl/entity/module.cpp
@@ -0,0 +1,47 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// stateless component factories that are probably safe
+//
+
+#include "fggl/entity/module.hpp"
+#include "fggl/math/types.hpp"
+
+namespace fggl::entity {
+
+	void make_transform(const entity::ComponentSpec& spec, EntityManager& manager, const entity::EntityID entity){
+		manager.add<math::Transform>(entity);
+	}
+
+	void install_component_factories(entity::EntityFactory* factory) {
+		factory->bind(math::Transform::guid, make_transform);
+	}
+
+	bool entity_svc_factory(modules::ModuleService service, modules::Services& services) {
+		if ( service == EntityFactory::service) {
+			auto* factory = services.create<EntityFactory>();
+			install_component_factories(factory);
+
+			// we are responsible for prefabs...
+			auto* assetLoader = services.get<assets::Loader>();
+			assetLoader->setFactory(PROTOTYPE_ASSET, [factory](const assets::AssetGUID& a, assets::AssetData b) {
+				return load_prototype(factory, a, b); }, assets::LoadType::PATH );
+
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory ECS::factory = entity_svc_factory;
+}
\ No newline at end of file
diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
index 8b45eedef6824d58c00d9da781bb1fde298409e1..722a232f9c9c935291ec99a6c82c4a449aabb903 100644
--- a/fggl/gfx/ogl/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -194,24 +194,24 @@ namespace fggl::gfx {
 		m_canvasRenderer->render(m_canvasPipeline, paint);
 	}
 
-	void OpenGL4Backend::drawScene(ecs3::World &world) {
+	void OpenGL4Backend::drawScene(entity::EntityManager &world) {
 		if ( m_modelRenderer ) {
 			m_modelRenderer->render(world);
 		}
 
 		if ( m_debugRenderer ) {
-			auto cameras = world.findMatching<gfx::Camera>();
+			auto cameras = world.find<gfx::Camera>();
 			if ( cameras.empty() ) {
 				return;
 			}
 
 			auto cameraEnt = cameras.front();
 
-			auto* const camTransform = world.get<math::Transform>(cameraEnt);
-			auto* const camComp = world.get<gfx::Camera>(cameraEnt);
+			const auto& camTransform = world.get<math::Transform>(cameraEnt);
+			const auto& camComp = world.get<gfx::Camera>(cameraEnt);
 
-			const math::mat4 projectionMatrix = glm::perspective(camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane);
-			const math::mat4 viewMatrix = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() );
+			const math::mat4 projectionMatrix = glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane);
+			const math::mat4 viewMatrix = glm::lookAt( camTransform.origin(), camComp.target, camTransform.up() );
 			m_debugRenderer->mvpMatrix = projectionMatrix * viewMatrix;
 
 			dd::flush();
@@ -222,79 +222,4 @@ namespace fggl::gfx {
 		glViewport(0, 0, width, height);
 	}
 
-	// TODO(webpigeon): this shouldn't be hard-coded
-	constexpr glm::vec3 DEFAULT_LIGHTPOS = glm::vec3(20.0F, 20.0F, 15.0F);
-
-	void GlMeshRenderer::render(fggl::ecs3::World &ecs, ecs3::entity_t camera, float dt) {
-		if (camera == ecs::NULL_ENTITY) {
-			debug::warning("tried to render a scene, but no camera exists!");
-			return;
-		}
-
-		auto entities = ecs.findMatching<GlRenderToken>();
-		if (entities.empty()) {
-			debug::warning("asked to render, but no entities are renderable");
-			return;
-		}
-
-		total += dt;
-
-		glEnable(GL_CULL_FACE);
-		glCullFace(GL_BACK);
-
-		glEnable(GL_DEPTH_TEST);
-
-		// camera logic
-		auto *const camTransform = ecs.get<math::Transform>(camera);
-		auto *const camComp = ecs.get<gfx::Camera>(camera);
-		glm::mat4 proj = glm::perspective(camComp->fov, camComp->aspectRatio,
-										  camComp->nearPlane, camComp->farPlane);
-		glm::mat4 view =
-			glm::lookAt(camTransform->origin(), camComp->target, camTransform->up());
-
-		// lighting
-		glm::vec3 lightPos = DEFAULT_LIGHTPOS;
-
-		// TODO(webpigeon): better performance if grouped by vao first
-		// TODO(webpigeon): the nvidia performance presentation said I shouldn't use uniforms for large data
-		for (auto &entity : entities) {
-			const auto &transform = ecs.get<fggl::math::Transform>(entity);
-			const auto &mesh = ecs.get<GlRenderToken>(entity);
-
-			glm::mat4 model = transform->model();
-			//	    model = glm::rotate(model, glm::radians(total/2048.0f * 360.0f),
-			//glm::vec3(0.0f,1.0f,0.0f));
-
-			auto shader = mesh->pipeline;
-			glUseProgram(shader);
-
-			glUniformMatrix4fv(glGetUniformLocation(shader, "model"), 1, GL_FALSE,
-							   glm::value_ptr(model));
-			glUniformMatrix4fv(glGetUniformLocation(shader, "view"), 1, GL_FALSE,
-							   glm::value_ptr(view));
-			glUniformMatrix4fv(glGetUniformLocation(shader, "projection"), 1, GL_FALSE,
-							   glm::value_ptr(proj));
-
-			// lighting
-			GLint lightID = glGetUniformLocation(shader, "lightPos");
-			if (lightID != -1) {
-				glUniform3fv(lightID, 1, glm::value_ptr(lightPos));
-			}
-
-			glBindVertexArray(mesh->vao);
-
-			if (mesh->renderType == GlRenderType::triangle_strip) {
-				glEnable(GL_PRIMITIVE_RESTART);
-				glPrimitiveRestartIndex(mesh->restartVertex);
-			}
-			glDrawElements(mesh->renderType, mesh->idxSize, GL_UNSIGNED_INT,
-						   reinterpret_cast<void *>(mesh->idxOffset));
-			if (mesh->renderType == GlRenderType::triangle_strip) {
-				glDisable(GL_PRIMITIVE_RESTART);
-			}
-		}
-
-		glBindVertexArray(0);
-	}
-
 }  // namespace fggl::gfx
diff --git a/fggl/gfx/ogl4/CMakeLists.txt b/fggl/gfx/ogl4/CMakeLists.txt
index 3b0954f68c4e5684f794b4842b8e6cdac7efbd59..93a61b15202c27edb630dcb029c154b9ba35a6fb 100644
--- a/fggl/gfx/ogl4/CMakeLists.txt
+++ b/fggl/gfx/ogl4/CMakeLists.txt
@@ -2,7 +2,9 @@
 # Sources
 target_sources(fggl
     PRIVATE
+		setup.cpp
 		canvas.cpp
 		models.cpp
 		debug.cpp
+		module.cpp
 )
diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp
index 62b3e0d952f6b7faa18395e45c7d26c0f8be6d94..bfed052ff445c5e5e53a5e0ebc2fe6fc721e1134 100644
--- a/fggl/gfx/ogl4/models.cpp
+++ b/fggl/gfx/ogl4/models.cpp
@@ -50,74 +50,74 @@ namespace fggl::gfx::ogl4 {
 		return elementBuffer;
 	}
 
-	static void setupComponent(StaticModel* modelComp, std::shared_ptr<ogl::Shader>& shader, data::Mesh& mesh) {
+	static void setupComponent(StaticModel& modelComp, std::shared_ptr<ogl::Shader>& shader, data::Mesh& mesh) {
 		auto vao = std::make_shared< ogl::VertexArray >();
 		auto meshBuffer = setupArrayBuffer(vao, mesh.vertexList());
 		auto elementBuffer = setupIndexBuffer(vao, mesh.indexList());
 
 		// set up the element attributes
-		modelComp->vao = vao;
-		modelComp->vertexData = meshBuffer;
-		modelComp->elements = elementBuffer;
-		modelComp->pipeline = shader;
-		modelComp->elementCount = mesh.indexCount();
-		modelComp->drawType = ogl::Primative::TRIANGLE;
+		modelComp.vao = vao;
+		modelComp.vertexData = meshBuffer;
+		modelComp.elements = elementBuffer;
+		modelComp.pipeline = shader;
+		modelComp.elementCount = mesh.indexCount();
+		modelComp.drawType = ogl::Primative::TRIANGLE;
 	}
 
-	void StaticModelRenderer::resolveModels(ecs3::World &world) {
+	void StaticModelRenderer::resolveModels(entity::EntityManager &world) {
 		// FIXME: this needs something reactive or performance will suck.
-		auto renderables = world.findMatching<data::StaticMesh>();
-		for (auto& renderable : renderables){
-			auto currModel = world.tryGet<StaticModel>( renderable );
+		auto renderables = world.find<data::StaticMesh>();
+		for (const auto& renderable : renderables){
+			auto* currModel = world.tryGet<StaticModel>( renderable );
 			if ( currModel != nullptr ){
 				continue;
 			}
 
-			auto* meshComp = world.get<data::StaticMesh>(renderable);
-			auto* modelComp = world.add<StaticModel>(renderable);
+			auto& meshComp = world.get<data::StaticMesh>(renderable);
+			auto& modelComp = world.add<StaticModel>(renderable);
 
 			auto shader = m_phong;
 			try {
-				shader = std::make_shared<ogl::Shader>( m_shaders->get( meshComp->pipeline ) );
+				shader = std::make_shared<ogl::Shader>( m_shaders->get( meshComp.pipeline ) );
 			} catch ( std::out_of_range& e) {
-				debug::log(debug::Level::warning, "Could not find shader: {}", meshComp->pipeline);
+				debug::log(debug::Level::warning, "Could not find shader: {}", meshComp.pipeline);
 			}
 
-			setupComponent(modelComp, shader, meshComp->mesh);
+			setupComponent(modelComp, shader, meshComp.mesh);
 
 			// no active model, we need to resolve/load one.
-			spdlog::info("looks like {} needs a static mesh", renderable);
+			//spdlog::info("looks like {} needs a static mesh", renderable);
 		}
 
 		// terrain
-		auto terrain = world.findMatching<data::HeightMap>();
+		auto terrain = world.find<data::HeightMap>();
 		for (auto& renderable : terrain){
 			auto currModel = world.tryGet<StaticModel>( renderable );
 			if ( currModel != nullptr ){
 				continue;
 			}
 
-			auto* heightmap = world.get<data::HeightMap>(renderable);
+			auto& heightmap = world.get<data::HeightMap>(renderable);
 			data::Mesh heightMapMesh{};
 			data::generateHeightMesh(heightmap, heightMapMesh);
 
-			auto* modelComp = world.add<StaticModel>(renderable);
+			auto& modelComp = world.add<StaticModel>(renderable);
 			setupComponent(modelComp, m_phong, heightMapMesh);
 
 			// we know this is a triangle strip with a restart vertex...
 			// FIXME the model should be telling us this...
-			modelComp->drawType = ogl::Primative::TRIANGLE_STRIP;
-			modelComp->restartIndex = heightMapMesh.restartVertex;
+			modelComp.drawType = ogl::Primative::TRIANGLE_STRIP;
+			modelComp.restartIndex = heightMapMesh.restartVertex;
 
 			// no active model, we need to resolve/load one.
-			spdlog::info("looks like heightmap, {} needs a static mesh", renderable);
+			//spdlog::info("looks like heightmap, {} needs a static mesh", renderable);
 		}
 	}
 
-	void StaticModelRenderer::renderModelsForward(const ecs3::World &world) {
+	void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world) {
 
 		// fetch cameras we will need to render with
-		auto cameras = world.findMatching<gfx::Camera>();
+		auto cameras = world.find<gfx::Camera>();
 
 		// if there are no cameras, we can't do anything...
 		if ( cameras.size() == 0) {
@@ -137,29 +137,29 @@ namespace fggl::gfx::ogl4 {
 			glEnable( GL_DEPTH_TEST );
 
 			// set-up camera matrices
-			auto* const camTransform = world.get<math::Transform>(cameraEnt);
-			auto* const camComp = world.get<gfx::Camera>(cameraEnt);
+			const auto& camTransform = world.get<math::Transform>(cameraEnt);
+			const auto& camComp = world.get<gfx::Camera>(cameraEnt);
 
-			const math::mat4 projectionMatrix = glm::perspective(camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane);
-			const math::mat4 viewMatrix = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() );
+			const math::mat4 projectionMatrix = glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane);
+			const math::mat4 viewMatrix = glm::lookAt( camTransform.origin(), camComp.target, camTransform.up() );
 
 			// TODO lighting needs to not be this...
 			math::vec3 lightPos{0.0f, 10.0f, 0.0f};
 			std::shared_ptr<ogl::Shader> shader = nullptr;
 
-			auto renderables = world.findMatching<StaticModel>();
+			auto renderables = world.find<StaticModel>();
 			for ( const auto& entity : renderables ){
 				// ensure that the model pipeline actually exists...
-				StaticModel* model = world.get<StaticModel>(entity);
-				if ( model->pipeline == nullptr ) {
+				const StaticModel& model = world.get<StaticModel>(entity);
+				if ( model.pipeline == nullptr ) {
 					spdlog::warn("shader was null, aborting render");
 					continue;
 				}
 
 				// check if we switched shaders
-				if ( shader != model->pipeline ) {
+				if ( shader != model.pipeline ) {
 					// new shader - need to re-send the view and projection matrices
-					shader = model->pipeline;
+					shader = model.pipeline;
 					shader->use();
 					if ( shader->hasUniform("projection") ) {
 						shader->setUniformMtx(shader->uniform("view"), viewMatrix);
@@ -168,14 +168,14 @@ namespace fggl::gfx::ogl4 {
 				}
 
 				// set model transform
-				auto* transform = world.get<math::Transform>(entity);
+				const auto& transform = world.get<math::Transform>(entity);
 				if (shader->hasUniform("model")) {
-					shader->setUniformMtx(shader->uniform("model"), transform->model());
+					shader->setUniformMtx(shader->uniform("model"), transform.model());
 				} else {
-					shader->setUniformMtx(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform->model());
-					shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform->model());
+					shader->setUniformMtx(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform.model());
+					shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform.model());
 
-					auto normalMatrix = glm::mat3(glm::transpose(inverse(transform->model())));
+					auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model())));
 					shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix);
 				}
 
@@ -195,14 +195,14 @@ namespace fggl::gfx::ogl4 {
 
 					if (!local) {
 						lightPos = glm::normalize(lightPos);
-						auto viewDir = glm::normalize(camTransform->origin() - transform->origin());
+						auto viewDir = glm::normalize(camTransform.origin() - transform.origin());
 						auto halfVector = glm::normalize(lightPos + viewDir);
 						shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector );
 						shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
 						shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
 					} else {
-						auto camModelView = (viewMatrix * camTransform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
-						auto modelModelView = (viewMatrix * transform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+						auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+						auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
 						math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
 						shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
 
@@ -214,40 +214,37 @@ namespace fggl::gfx::ogl4 {
 				}
 
 				// material detection with fallback
-				auto* material = &gfx::DEFAULT_MATERIAL;
-				if ( world.has<PhongMaterial>(entity) ) {
-					material = world.get<PhongMaterial>(entity);
-				}
+				const auto& material = world.get<PhongMaterial>(entity);
 
 				if ( shader->hasUniform("material.ambient") ) {
-					shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
-					shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
-					shader->setUniformF(shader->uniform("material.specular"), material->specular);
-					shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
+					shader->setUniformF(shader->uniform("material.ambient"), material.ambient);
+					shader->setUniformF(shader->uniform("material.diffuse"), material.diffuse);
+					shader->setUniformF(shader->uniform("material.specular"), material.specular);
+					shader->setUniformF(shader->uniform("material.shininess"), material.shininess);
 				} else {
-					shader->setUniformF(shader->uniform("materials[0].emission"), material->emission);
-					shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient);
-					shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse);
-					shader->setUniformF(shader->uniform("materials[0].specular"), material->specular);
-					shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess);
+					shader->setUniformF(shader->uniform("materials[0].emission"), material.emission);
+					shader->setUniformF(shader->uniform("materials[0].ambient"), material.ambient);
+					shader->setUniformF(shader->uniform("materials[0].diffuse"), material.diffuse);
+					shader->setUniformF(shader->uniform("materials[0].specular"), material.specular);
+					shader->setUniformF(shader->uniform("materials[0].shininess"), material.shininess);
 				}
 
 				if ( shader->hasUniform("lightPos")) {
 					shader->setUniformF( shader->uniform("lightPos"), lightPos);
 				}
 
-				auto vao = model->vao;
+				auto vao = model.vao;
 				vao->bind();
 
-				model->vertexData->bind();
+				model.vertexData->bind();
 
-				if ( model->restartIndex != NO_RESTART_IDX) {
+				if ( model.restartIndex != NO_RESTART_IDX) {
 					glEnable(GL_PRIMITIVE_RESTART);
-					glPrimitiveRestartIndex(model->restartIndex);
+					glPrimitiveRestartIndex(model.restartIndex);
 				}
-				auto elements = model->elements.get();
-				vao->drawElements( *elements, model->drawType, model->elementCount);
-				if ( model->restartIndex != NO_RESTART_IDX) {
+				auto elements = model.elements.get();
+				vao->drawElements( *elements, model.drawType, model.elementCount);
+				if ( model.restartIndex != NO_RESTART_IDX) {
 					glDisable(GL_PRIMITIVE_RESTART);
 				}
 			}
diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aad07e8c91bb5672d189f400b6b54c8b718fabb9
--- /dev/null
+++ b/fggl/gfx/ogl4/module.cpp
@@ -0,0 +1,103 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 24/07/22.
+//
+
+#include "fggl/gfx/ogl4/module.hpp"
+#include "fggl/gfx/phong.hpp"
+#include "fggl/data/procedural.hpp"
+
+#include <string>
+
+namespace fggl::gfx {
+
+	constexpr int DEFAULT_STACKS = 16;
+	constexpr int DEFAULT_SLICES = 16;
+	constexpr const char* SHAPE_SPHERE{"sphere"};
+	constexpr const char* SHAPE_BOX{"box"};
+
+	static void process_shape(const YAML::Node& node, data::Mesh& mesh) {
+		auto transform = data::OFFSET_NONE;
+		if ( node["offset"] ) {
+			auto offset = node["offset"].as<math::vec3>();
+			transform = glm::translate(transform, offset);
+		}
+
+		if ( node["scale"] ) {
+			auto offset = node["scale"].as<math::vec3>();
+			transform = glm::scale(transform, offset);
+		}
+
+		// now the shape itself
+		auto type = node["type"].as<std::string>();
+		if ( type == SHAPE_BOX ) {
+			data::make_cube(mesh, transform);
+		} else if ( type == SHAPE_SPHERE ) {
+			int stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS);
+			int slices = node["slices"].as<uint32_t>(DEFAULT_SLICES);
+			data::make_sphere(mesh, transform, stacks, slices);
+		} else {
+			debug::log(debug::Level::warning, "unknown shape type requested: {}", type);
+		}
+	}
+
+	void attach_mesh(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) {
+		auto& meshComp = manager.add<data::StaticMesh>(id);
+		meshComp.pipeline = spec.get<std::string>("pipeline", "");
+
+		if ( spec.has("shape") ) {
+			// procedural mesh
+			data::Mesh mesh;
+			if ( spec.config["shape"].IsSequence() ) {
+				for( const auto& node : spec.config["shape"] ) {
+					process_shape(node, mesh);
+				}
+			} else {
+				process_shape(spec.config["shape"], mesh);
+			}
+			mesh.removeDups();
+			meshComp.mesh = mesh;
+		}
+	}
+
+	void attach_material(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) {
+		auto& mat = manager.add<gfx::PhongMaterial>(id);
+	}
+
+	void attach_light(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) {
+		auto& light = manager.add<gfx::Light>(id);
+	}
+
+	bool ogl4_factory(modules::ModuleService service, modules::Services& services) {
+		if (service == WindowGraphics::service) {
+			// setup the thing responsible for graphics
+			auto* storage = services.get<data::Storage>();
+			auto* fontLibrary = services.get<gui::FontLibrary>();
+			services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, fontLibrary);
+
+			// register as responsible for creating rendering components
+			auto* entityFactory = services.get<entity::EntityFactory>();
+			entityFactory->bind(data::StaticMesh::guid, attach_mesh);
+			entityFactory->bind(gfx::PhongMaterial::guid, attach_material);
+			entityFactory->bind(gfx::Light::guid, attach_light);
+
+			return true;
+		}
+		return false;
+	}
+	const modules::ServiceFactory OpenGL4::factory = ogl4_factory;
+
+} // namespace fggl::gfX::ogl4
\ No newline at end of file
diff --git a/fggl/ecs3/prototype/world.cpp b/fggl/gfx/ogl4/setup.cpp
similarity index 75%
rename from fggl/ecs3/prototype/world.cpp
rename to fggl/gfx/ogl4/setup.cpp
index 4b7c98674e6493991a7ba047114cea8120350018..cf1a07e947018b38707bec3c250507a7bbd3d3bf 100644
--- a/fggl/ecs3/prototype/world.cpp
+++ b/fggl/gfx/ogl4/setup.cpp
@@ -13,7 +13,15 @@
  */
 
 //
-// Created by webpigeon on 23/10/2021.
+// Created by webpigeon on 24/07/22.
 //
 
-#include <fggl/ecs3/prototype/world.hpp>
+#include "fggl/gfx/ogl4/setup.hpp"
+
+namespace fggl::gfx::ogl4 {
+
+	Graphics *WindowGraphics::create(display::Window &window) {
+		return new OpenGL4Backend(m_storage, m_fonts);
+	}
+
+}
\ No newline at end of file
diff --git a/fggl/input/camera_input.cpp b/fggl/input/camera_input.cpp
index e0b04140ad97d30ec5ff7975fdcdfd6466d35fb9..6c6dbeefba5ac9ed8b4780487b995bc1c02953c5 100644
--- a/fggl/input/camera_input.cpp
+++ b/fggl/input/camera_input.cpp
@@ -16,7 +16,6 @@
 // Created by webpigeon on 20/11/2021.
 //
 
-#include <fggl/ecs3/ecs.hpp>
 #include <fggl/input/input.hpp>
 
 #include <fggl/gfx/camera.hpp>
@@ -24,15 +23,15 @@
 
 namespace fggl::input {
 
-    void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
+    void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
         // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
-        auto *camTransform = ecs.get<fggl::math::Transform>(cam);
-        auto *camComp = ecs.get<fggl::gfx::Camera>(cam);
+        auto& camTransform = ecs.get<fggl::math::Transform>(cam);
+        auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
         auto &mouse = input.mouse;
 
-        glm::vec4 position(camTransform->origin(), 1.0f);
-        glm::vec4 pivot(camComp->target, 1.0f);
-        glm::mat4 view = glm::lookAt(camTransform->origin(), camComp->target, camTransform->up());
+        glm::vec4 position(camTransform.origin(), 1.0f);
+        glm::vec4 pivot(camComp.target, 1.0f);
+        glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up());
         glm::vec3 viewDir = -glm::transpose(view)[2];
         glm::vec3 rightDir = glm::transpose(view)[0];
 
@@ -51,52 +50,52 @@ namespace fggl::input {
         rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir);
         glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot;
 
-        camTransform->origin(finalPos);
+        camTransform.origin(finalPos);
     }
 
-	void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom, float maxZoom){
-		auto* camTransform = ecs.get<fggl::math::Transform>(cam);
-		auto* camComp = ecs.get<fggl::gfx::Camera>(cam);
+	void process_scroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam, float minZoom, float maxZoom){
+		auto& camTransform = ecs.get<fggl::math::Transform>(cam);
+		auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
 
-		const glm::vec3 dir = ( camTransform->origin() - camComp->target );
+		const glm::vec3 dir = ( camTransform.origin() - camComp.target );
 		const glm::vec3 forward = glm::normalize( dir );
 
 		glm::vec3 motion(0.0F);
 		float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y );
 		if ( (glm::length( dir ) < maxZoom && delta < 0.0f) || (glm::length( dir ) > minZoom && delta > 0.0f) ) {
 			motion -= (forward * delta);
-			camTransform->origin(camTransform->origin() + motion);
+			camTransform.origin(camTransform.origin() + motion);
 		}
 	}
 
-    void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
+    void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
         float rotationValue = 0.0f;
         glm::vec3 translation(0.0f);
 
         auto &keyboard = input.keyboard;
-        auto* settings = ecs.get<FreeCamKeys>(cam);
+        auto& settings = ecs.get<FreeCamKeys>(cam);
 
         // calculate rotation (user input)
-        if (keyboard.down(settings->rotate_cw)) {
+        if (keyboard.down(settings.rotate_cw)) {
             rotationValue = ROT_SPEED;
-        } else if (keyboard.down(settings->rotate_ccw)) {
+        } else if (keyboard.down(settings.rotate_ccw)) {
             rotationValue = -ROT_SPEED;
         }
 
         // calculate movement (user input)
-        if (keyboard.down(settings->forward)) {
+        if (keyboard.down(settings.forward)) {
             translation -= fggl::math::RIGHT;
         }
 
-        if (keyboard.down(settings->backward)) {
+        if (keyboard.down(settings.backward)) {
             translation += fggl::math::RIGHT;
         }
 
-        if (keyboard.down(settings->right)) {
+        if (keyboard.down(settings.right)) {
             translation += fggl::math::FORWARD;
         }
 
-        if (keyboard.down(settings->left)) {
+        if (keyboard.down(settings.left)) {
             translation -= fggl::math::FORWARD;
         }
 
@@ -104,8 +103,8 @@ namespace fggl::input {
         auto camTransform = ecs.get<fggl::math::Transform>(cam);
         auto camComp = ecs.get<fggl::gfx::Camera>(cam);
 
-        glm::vec4 position(camTransform->origin(), 1.0f);
-        glm::vec4 pivot(camComp->target, 1.0f);
+        glm::vec4 position(camTransform.origin(), 1.0f);
+        glm::vec4 pivot(camComp.target, 1.0f);
 
         // apply movement
         if (translation != glm::vec3(0.0f)) {
@@ -126,11 +125,11 @@ namespace fggl::input {
             position = (rotation * (position - pivot)) + pivot;
         }
 
-        camTransform->origin(position);
-        camComp->target = pivot;
+        camTransform.origin(position);
+        camComp.target = pivot;
     }
 
-    void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) {
+    void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) {
         glm::vec3 translation(0.0f);
 
         auto &mouse = input.mouse;
@@ -153,11 +152,11 @@ namespace fggl::input {
         }
 
         // apply rotation/movement
-        auto camTransform = ecs.get<fggl::math::Transform>(cam);
-        auto camComp = ecs.get<fggl::gfx::Camera>(cam);
+        auto& camTransform = ecs.get<fggl::math::Transform>(cam);
+        auto& camComp = ecs.get<fggl::gfx::Camera>(cam);
 
-        glm::vec4 position(camTransform->origin(), 1.0f);
-        glm::vec4 pivot(camComp->target, 1.0f);
+        glm::vec4 position(camTransform.origin(), 1.0f);
+        glm::vec4 pivot(camComp.target, 1.0f);
 
         // apply movement
         if (translation != glm::vec3(0.0f)) {
@@ -173,7 +172,7 @@ namespace fggl::input {
         }
 
         // move camera
-        camTransform->origin(position);
-        camComp->target = pivot;
+        camTransform.origin(position);
+        camComp.target = pivot;
     }
 }
diff --git a/fggl/platform/linux/paths.cpp b/fggl/platform/linux/paths.cpp
index 92f1a1a5f31f96a888b5f9401ab5a19f535e987c..d53cc7b7096bd1c3af7aa1eec6e365ebfcd8034e 100644
--- a/fggl/platform/linux/paths.cpp
+++ b/fggl/platform/linux/paths.cpp
@@ -83,7 +83,7 @@ namespace fggl::platform {
 		// check system paths
 		for ( const auto& path : paths.dataDirs ) {
 			auto fullPath = path / relPath;
-			debug::log(debug::Level::warning, "Checking data path: {}, {}", fullPath.c_str(), std::filesystem::exists(fullPath));
+			debug::log(debug::Level::debug, "Checking data path: {}, {}", fullPath.c_str(), std::filesystem::exists(fullPath));
 			if ( std::filesystem::exists(fullPath) ) {
 				return fullPath;
 			}
@@ -92,6 +92,7 @@ namespace fggl::platform {
 		// if debug mode, try CWD as well.
 		auto debugPath = std::filesystem::current_path() / "data" / relPath;
 		if ( std::filesystem::exists(debugPath) ) {
+			debug::log(debug::Level::debug, "Checking debug path: {}, {}", debugPath.c_str(), std::filesystem::exists(debugPath));
 			return debugPath;
 		}
 
diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp
index f5f7746f02758e7893dad62f3b0fef53b0705f55..5774f23a19d4bfd2e8d18f5ad89e8953cda28c11 100644
--- a/fggl/scenes/game.cpp
+++ b/fggl/scenes/game.cpp
@@ -32,7 +32,7 @@ namespace fggl::scenes {
 		fggl::AppState::activate();
 
 		// setup the scene
-		m_world = std::make_unique<ecs3::World>(*m_owner.registry());
+		m_world = std::make_unique<entity::EntityManager>();
 
 		#ifdef FGGL_MODULE_BULLET
 			// FIXME this ties bullet to the game state - which shouldn't be the case
@@ -59,7 +59,7 @@ namespace fggl::scenes {
 			m_phys->step();
 		}
 
-		m_world->reapEntities();
+		//m_world->reapEntities();
 	}
 
 	void Game::render(fggl::gfx::Graphics &gfx) {
diff --git a/fggl/util/guid.cpp b/fggl/util/guid.cpp
index f2f908a080a174290ee6039470cc789fe88936c0..ee0025bc480968d0af66b5a9cb2cfad5ced3aff0 100644
--- a/fggl/util/guid.cpp
+++ b/fggl/util/guid.cpp
@@ -44,9 +44,9 @@ namespace fggl::util {
 		auto tableValue = guidTable.find(guid);
 		if (tableValue != guidTable.end()) {
 			return tableValue->second;
-		} else {
-			return "FGGL_UNKNOWN_GUID";
 		}
+		// it's not in the table...
+		return "UNKNOWN_GUID("+std::to_string(guid.get())+")";
 	}
 
 }
diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp
index e89b254a5d96296939c41b7342e1ad177a690756..b66846e69cafb2d996eaf0ce3e21e845aa7986c0 100644
--- a/include/fggl/app.hpp
+++ b/include/fggl/app.hpp
@@ -22,8 +22,6 @@
 #include <memory>
 #include <unordered_map>
 
-#include <fggl/ecs3/types.hpp>
-#include "fggl/ecs3/module/module.hpp"
 #include "fggl/display/glfw/window.hpp"
 #include <fggl/gfx/paint.hpp>
 #include <fggl/util/states.hpp>
@@ -108,12 +106,6 @@ namespace fggl {
 				return m_states.put<T>(name, *this);
 			}
 
-			template<typename T, typename... Args>
-			T &use(Args &&...args) {
-				auto ptr = m_modules->load<T>(args...);
-				return *ptr;
-			}
-
 			inline void change_state(const Identifer &name) {
 				m_expectedScene = name;
 				/*m_states.active().deactivate();
@@ -135,10 +127,6 @@ namespace fggl {
 				}
 			}
 
-			inline ecs3::TypeRegistry* registry() {
-				return m_types.get();
-			}
-
 			inline bool running() const {
 				return m_running;
 			}
@@ -149,8 +137,6 @@ namespace fggl {
 
 		private:
 			bool m_running;
-			std::unique_ptr<ecs3::TypeRegistry> m_types;
-			std::unique_ptr<ecs3::ModuleManager> m_modules;
 			display::Window* m_window;
 			AppMachine m_states;
 			Identifer m_expectedScene;
diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp
index be85581b13aa1d9da64a987f9edb86a613aef866..044b7a22a4c249075a9ed7e4e4ba376918f7e86c 100644
--- a/include/fggl/assets/loader.hpp
+++ b/include/fggl/assets/loader.hpp
@@ -45,7 +45,8 @@ namespace fggl::assets {
 	class Loader {
 		public:
 			constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::Loader");
-			explicit inline Loader(data::Storage* storage)  : m_storage(storage), m_parent(nullptr) {}
+
+			explicit inline Loader(data::Storage* storage) : m_storage(storage) {}
 			explicit Loader(Loader* parent, data::Storage* storage) : m_parent(parent), m_storage(storage) {};
 
 			// no move, no copy.
@@ -65,16 +66,18 @@ namespace fggl::assets {
 				m_requests.push(ResourceRequest{guid, type});
 			}
 
-			void load(const AssetGUID guid, const AssetType& type) {
+			void load(const AssetGUID& guid, const AssetType& type) {
 				auto path = m_storage->resolvePath(data::StorageType::Data, guid);
 
 				auto& config = m_factories.at(type);
 				switch (config.second) {
 					case LoadType::DIRECT:
 						// TODO we load the data into main memory and give a pointer to it.
+						debug::log(debug::Level::error, "Tried to load direct asset - no one wrote that yet!");
 						break;
 					case LoadType::STAGED:
 						// TODO we load the data into temp memory and give a pointer to it.
+						debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!");
 						break;
 					case LoadType::PATH:
 						config.first(guid, AssetData(&path));
diff --git a/include/fggl/data/heightmap.hpp b/include/fggl/data/heightmap.hpp
index 312659ef6d7557e7db855df5006c6ea2200ac16b..d541368fd48b88fc6fd0c778c5842aff78e33a81 100644
--- a/include/fggl/data/heightmap.hpp
+++ b/include/fggl/data/heightmap.hpp
@@ -54,7 +54,7 @@ namespace fggl::data {
 		return x * zMax + z;
 	}
 
-	void generateHeightMesh(const data::HeightMap *heights, data::Mesh &mesh);
+	void generateHeightMesh(const data::HeightMap& heights, data::Mesh &mesh);
 }
 
 #endif //FGGL_DATA_HEIGHTMAP_HPP
diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp
index 3944a8af3151a3cb6c3953c8622421de6c619b05..900b1e698440695f958c88e3b251a0f050e58c55 100644
--- a/include/fggl/data/model.hpp
+++ b/include/fggl/data/model.hpp
@@ -37,7 +37,8 @@ namespace fggl::data {
 			return {
 				pos,
 				ILLEGAL_NORMAL,
-				DEFAULT_COLOUR
+				DEFAULT_COLOUR,
+				{0.0F, 0.0F}
 			};
 		}
 	};
@@ -161,6 +162,8 @@ namespace fggl::data {
 
 	struct StaticMesh {
 		constexpr static const char name[] = "StaticMesh";
+		constexpr static const util::GUID guid = util::make_guid("StaticMesh");
+
 		data::Mesh mesh;
 		std::string pipeline;
 
diff --git a/include/fggl/ds/placeholder.hpp b/include/fggl/ds/placeholder.hpp
index ab9d9f77baaf34cec86326871e52daf0919a6fac..a4623a19aa6eb40e99426466a82ac2a30b04873a 100644
--- a/include/fggl/ds/placeholder.hpp
+++ b/include/fggl/ds/placeholder.hpp
@@ -32,7 +32,7 @@ namespace fggl::ds {
 			using WeakRef = std::size_t;
 			constexpr static WeakRef BAD_INDEX = 0;
 
-			bool valid(WeakRef idx) const {
+			inline bool valid(WeakRef idx) const {
 				return m_data.find(idx) != m_data.end();
 			}
 
@@ -67,8 +67,6 @@ namespace fggl::ds {
 			std::size_t m_nextIdx = 1;
 	};
 
-	template<typename T, std::size_t N>
-	using SlotMap = FakeSlotMap<T, N>;
 
 } // namespace fggl::ds
 
diff --git a/fggl/ecs3/module/module.cpp b/include/fggl/ds/slot_map.hpp
similarity index 70%
rename from fggl/ecs3/module/module.cpp
rename to include/fggl/ds/slot_map.hpp
index 88cee8822b0186756e665e9b088ba6131a5a66d3..56443f50f412c8e34de149bf1df7efb24f92a10b 100644
--- a/fggl/ecs3/module/module.cpp
+++ b/include/fggl/ds/slot_map.hpp
@@ -13,15 +13,19 @@
  */
 
 //
-// Created by webpigeon on 23/10/2021.
+// Created by webpigeon on 23/07/22.
 //
 
-#include "fggl/ecs3/module/module.hpp"
+#ifndef FGGL_DS_SLOT_MAP_HPP
+#define FGGL_DS_SLOT_MAP_HPP
 
-namespace fggl::ecs3 {
+#include "fggl/ds/placeholder.hpp"
 
-    // default empty implementions
-    void Module::onUpdate() {}
-    void Module::onFrameStart() {}
-    void Module::onFrameEnd() {}
-}
+namespace fggl::ds {
+
+	template<typename T, std::size_t N>
+	using SlotMap = FakeSlotMap<T, N>;
+
+} // namespace fggl::ds
+
+#endif //FGGL_DS_SLOT_MAP_HPP
diff --git a/include/fggl/ecs/component.hpp b/include/fggl/ecs/component.hpp
deleted file mode 100644
index 46c382c1950db935f87afd141065d2e5d86a6b6f..0000000000000000000000000000000000000000
--- a/include/fggl/ecs/component.hpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS_COMPONENT_HPP
-#define FGGL_ECS_COMPONENT_HPP
-
-#include "utility.hpp"
-#include "fggl/debug/logging.hpp"
-
-#include <cassert>
-#include <cstring>
-#include <string>
-#include <new>
-#include <utility>
-#include <iostream>
-
-#include <typeinfo>
-
-#include <vector>
-#include <unordered_map>
-#include "yaml-cpp/yaml.h"
-
-namespace fggl::ecs {
-
-	template<typename T>
-	bool restore_config(T* comp, const YAML::Node& node);
-
-	class ComponentBase {
-		public:
-			using data_t = unsigned char;
-
-			virtual ~ComponentBase() {};
-
-			// in place
-			virtual void destroy(data_t *data) const = 0;
-			virtual void move(data_t *src, data_t *dest) const = 0;
-			virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) = 0;
-			virtual void construct(data_t *data) const = 0;
-
-			// virtual
-			virtual void *construct() const = 0;
-			virtual void *restore(const YAML::Node& config) const = 0;
-
-			virtual void *copyConstruct(const void *src) = 0;
-
-			virtual const char *name() const = 0;
-			virtual const component_type_t id() const = 0;
-
-			virtual std::size_t size() const = 0;
-	};
-
-	template<class C>
-	class Component : public ComponentBase {
-		public:
-			virtual void destroy(data_t *data) const override {
-				C *location = std::launder(reinterpret_cast<C *>(data));
-				location->~C();
-			}
-
-			virtual const char *name() const override {
-				return C::name;
-			}
-
-			virtual const component_type_t id() const {
-				return Component<C>::typeID();
-			}
-
-			virtual void construct(unsigned char *data) const override {
-				new(data) C();
-			}
-
-			virtual void* restore(const YAML::Node& config) const override {
-				C* ptr = new C();
-				bool restored = restore_config<C>(ptr, config);
-				if ( !restored ) {
-					debug::error("error restoring {}", C::name);
-					assert( false && "failed to restore configuration when loading type!" );
-				}
-				return ptr;
-			}
-
-			void *copyConstruct(const void *src) override {
-				const C *srcPtr = (C *) src;
-				return new C(*srcPtr);
-			}
-
-			void *construct() const override {
-				return new C();
-			}
-
-			virtual void move(data_t *src, data_t *dest) const override {
-				assert(src != nullptr);
-				assert(dest != nullptr);
-				new(&dest[0]) C(std::move(*reinterpret_cast<C *>(src)));
-			}
-
-			virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) {
-				if (std::is_trivially_copyable<C>::value) {
-					std::memcpy(dest, src, count * size());
-				} else {
-					unsigned char *srcPtr = src;
-					unsigned char *destPtr = dest;
-
-					for (std::size_t i = 0; i < count; ++i) {
-						new(destPtr) C(std::move(*reinterpret_cast<C *>(srcPtr)));
-						srcPtr += sizeof(C);
-						destPtr += sizeof(C);
-					}
-				}
-			}
-
-			virtual std::size_t size() const {
-				return sizeof(C);
-			}
-
-			static component_type_t typeID() {
-				return TypeIdGenerator<ComponentBase>::GetNewID<C>();
-			}
-	};
-
-}
-
-#endif
diff --git a/include/fggl/ecs/component_fwd.hpp b/include/fggl/ecs/component_fwd.hpp
deleted file mode 100644
index 22c1dafb5414abc6d348065be6040b4695391bf0..0000000000000000000000000000000000000000
--- a/include/fggl/ecs/component_fwd.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 22/07/22.
-//
-
-#ifndef FGGL_ECS_COMPONENT_FWD_HPP
-#define FGGL_ECS_COMPONENT_FWD_HPP
-
-#include "fggl/ecs/component.hpp"
-
-#include "fggl/math/types.hpp"
-#include "fggl/gfx/phong.hpp"
-#include "fggl/data/model.hpp"
-#include "fggl/phys/types.hpp"
-
-#ifdef __GNUC__
-	#include <cxxabi.h>
-#endif
-
-namespace fggl::ecs {
-
-	template<typename T>
-	bool restore_config(T* comp, const YAML::Node& node) {
-		char* realName;
-
-		#ifdef __GNUC__
-		int status;
-		realName = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
-		#endif
-
-		debug::log(debug::Level::warning, "restore_config is not implemented for {}", realName);
-		return false;
-	}
-
-	template<>
-	bool restore_config(math::Transform* comp, const YAML::Node& node);
-
-	template<>
-	bool restore_config(gfx::PhongMaterial* comp, const YAML::Node& node);
-
-	template<>
-	bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node);
-
-	template<>
-	bool restore_config(phys::RigidBody* body, const YAML::Node& node);
-
-	template<>
-	bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node);
-
-	template<>
-	bool restore_config(phys::CollisionCache* cache, const YAML::Node& node);
-
-} // namespace fggl::ecs
-
-#endif //FGGL_ECS_COMPONENT_FWD_HPP
diff --git a/include/fggl/ecs/ecs.hpp b/include/fggl/ecs/ecs.hpp
deleted file mode 100644
index d7fce80f0a8dc94b930c03f767a3f2dd0fdb18c1..0000000000000000000000000000000000000000
--- a/include/fggl/ecs/ecs.hpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS_ECS_HPP
-#define FGGL_ECS_ECS_HPP
-
-#include "utility.hpp"
-#include "component.hpp"
-
-#include <iostream>
-#include <algorithm>
-#include <cassert>
-#include <string>
-#include <vector>
-#include <unordered_map>
-
-namespace fggl::ecs {
-	using archToken_t = std::vector<component_type_t>;
-
-	struct Archetype {
-		constexpr static unsigned int default_cap = 0;
-		const archToken_t type;
-		std::vector<ComponentBase::data_t *> data;
-		std::vector<std::size_t> dataSizes;
-		std::vector<entity_t> entities;
-
-		Archetype(const archToken_t &type_a);
-
-		inline archToken_t create(const component_type_t cid) const {
-			assert(!contains(cid));
-
-			// create the new type
-			auto newType = type;
-			newType.push_back(cid);
-			std::sort(newType.begin(), newType.end());
-			return newType;
-		}
-
-		inline bool contains(const component_type_t cid) const {
-			return (std::find(type.begin(), type.end(), cid) != type.end());
-		}
-	};
-
-	class ECS {
-			struct Record {
-				Archetype *archetype;
-				std::size_t index;
-			};
-			using componentmap_t = std::unordered_map<component_type_t, ComponentBase *>;
-			using entitymap_t = std::unordered_map<entity_t, Record>;
-			using archetype_t = std::vector<Archetype *>;
-
-		public:
-			ECS();
-			~ECS();
-
-			entity_t getNewID();
-			entity_t createEntity();
-			void removeEntity(const entity_t eid);
-
-			template<class C>
-			void registerComponent() {
-				component_type_t type = Component<C>::typeID();
-				if (m_componentMap.find(type) != m_componentMap.end())
-					return;
-				m_componentMap.emplace(type, new Component<C>);
-			}
-
-			template<class C>
-			bool isComponentRegistered() {
-				component_type_t type = Component<C>::typeID();
-				return (m_componentMap.find(type) != m_componentMap.end());
-			}
-
-			template<class C, typename... Args>
-			C *addComponent(const entity_t &id, Args &&... args) {
-				component_type_t type = Component<C>::typeID();
-				assert(isComponentRegistered<C>());
-
-				Record &record = m_entityArchtypes[id];
-				Archetype *oldArch = record.archetype;
-				C *newComp = nullptr;
-				Archetype *newArch = nullptr;
-
-				if (!oldArch) {
-					archToken_t newID(1, type);
-					const ComponentBase *const newCompType = m_componentMap[type];
-
-					// fetch type
-					newArch = getArchetype(newID);
-					assert(newArch->type.size() == 1);
-
-					// calculate if we have enouph space to allocate
-					std::size_t emplacementPos = ensureCapacity(newArch, 0, newCompType);
-					newComp = new(&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...);
-				} else {
-					// check if the arch contains the component
-					if (oldArch->contains(type)) {
-						return nullptr;
-					}
-
-					// create a new archetype with the component
-					auto newID = oldArch->create(type);
-					newArch = getArchetype(newID);
-
-					// relocate the old data to the new archetype
-					for (std::size_t j = 0; j < newID.size(); ++j) {
-						const component_type_t compType = newID[j];
-						const ComponentBase *const comp = m_componentMap.at(compType);
-
-
-						// TODO this seems a little suspect - surely we could allocate all blocks at once?
-						// if per component the arrays could become out of sync...
-						int newOffset = ensureCapacity(newArch, j, comp);
-						int oldIdx = getComponentIdx(oldArch, compType);
-
-						if (oldIdx != -1) {
-							assert(oldArch->contains(compType));
-							const std::size_t compSize = comp->size();
-							const std::size_t oldOffset = record.index * compSize;
-
-							comp->move(&oldArch->data[oldIdx][oldOffset],
-									   &newArch->data[j][newOffset]);
-							comp->destroy(&oldArch->data[oldIdx][oldOffset]);
-						} else {
-							assert(!oldArch->contains(compType));
-							newComp = new(&newArch->data[j][newOffset])
-								C(std::forward<Args>(args)...);
-						}
-					}
-
-					// ensure the old archetype is still contigious
-					const int lastEnt = oldArch->entities.size() - 1;
-					if (lastEnt != record.index) {
-						for (std::size_t i = 0; i < oldArch->type.size(); ++i) {
-							const component_type_t typeID = oldArch->type[i];
-							const ComponentBase *const comp = m_componentMap[typeID];
-							const std::size_t &compSize = comp->size();
-
-							// shift the empty record to the end of the list
-							std::size_t slotOffset = record.index * compSize;
-							std::size_t lastOffset = lastEnt * compSize;
-
-							// if we're not the last entity, swap
-							if (slotOffset != lastOffset) {
-								comp->move(&oldArch->data[i][lastOffset],
-										   &oldArch->data[i][slotOffset]);
-								comp->destroy(&oldArch->data[i][lastOffset]);
-							}
-						}
-
-						// fix the position
-						oldArch->entities[record.index] = oldArch->entities[lastEnt];
-					}
-					oldArch->entities.pop_back();
-				}
-
-				// register the new data with the new archetype
-				newArch->entities.push_back(id);
-				record.index = newArch->entities.size() - 1;
-				record.archetype = newArch;
-				return newComp;
-			}
-
-			template<class C>
-			void removeComponent(const entity_t &entityId);
-
-			template<class C>
-			bool hasComponent(const entity_t &entityId) const {
-				const component_type_t componentID = Component<C>::typeID();
-				const auto *arch = m_entityArchtypes.at(entityId).archetype;
-				if (arch == nullptr) {
-					return false;
-				}
-				return (std::find(arch->type.begin(), arch->type.end(), componentID) !=
-					arch->type.end());
-			}
-
-			template<class C>
-			C *getComponent(const entity_t &entityId) {
-				assert(hasComponent<C>(entityId));
-				const auto type = Component<C>::typeID();
-
-				const ComponentBase *const newComp = m_componentMap[type];
-				const auto record = m_entityArchtypes.at(entityId);
-				const auto *arch = record.archetype;
-
-				// JWR: linear search... seems a little suspect, they're ordered after all
-				for (std::size_t i = 0; i < arch->type.size(); ++i) {
-					if (arch->type[i] == type) {
-						return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()]));
-					}
-				}
-
-				return nullptr;
-			}
-
-			template<class C>
-			const C *getComponent(const entity_t &entityId) const {
-				assert(hasComponent<C>(entityId));
-				const auto type = Component<C>::typeID();
-
-				const ComponentBase *const newComp = m_componentMap.at(type);
-				const auto record = m_entityArchtypes.at(entityId);
-				const auto *arch = record.archetype;
-
-				// JWR: linear search... seems a little suspect, they're ordered after all
-				for (std::size_t i = 0; i < arch->type.size(); ++i) {
-					if (arch->type[i] == type) {
-						return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()]));
-					}
-				}
-
-				return nullptr;
-			}
-
-			template<class... Cs>
-			std::vector<entity_t> getEntityWith() const {
-				// construct the key
-				archToken_t key;
-				(key.push_back(Component<Cs>::typeID()), ...);
-
-				// entities
-				std::vector<entity_t> entities;
-				for (Archetype *arch : m_archetypes) {
-					if (std::includes(arch->type.begin(), arch->type.end(), key.begin(), key.end())) {
-						if (!arch->entities.empty()) {
-							entities.insert(entities.begin(), arch->entities.begin(), arch->entities.end());
-						}
-					}
-				}
-
-				return entities;
-			}
-
-		private:
-			entitymap_t m_entityArchtypes;
-			archetype_t m_archetypes;
-			entity_t m_entityIDCounter;
-			componentmap_t m_componentMap;
-			Archetype *getArchetype(const archToken_t &id);
-
-			inline std::string arch2str(Archetype *arch) {
-				std::string str;
-				for (const auto &type : arch->type) {
-					str += std::to_string(type);
-				}
-				return str;
-			}
-
-			inline std::size_t ensureCapacity(Archetype *arch, const int idx, const ComponentBase *const comp) {
-				const std::size_t &compSize = comp->size();
-				std::size_t currSize = arch->entities.size() * compSize;
-				std::size_t newSize = currSize + compSize;
-
-				std::size_t cap = arch->dataSizes[idx];
-
-				if (newSize > arch->dataSizes[idx]) {
-					arch->dataSizes[idx] *= 2;
-					arch->dataSizes[idx] += compSize;
-
-					// copy data over
-					unsigned char *newData = new unsigned char[arch->dataSizes[idx]];
-					for (std::size_t e = 0; e < arch->entities.size(); ++e) {
-						const int offset = e * compSize;
-						comp->move(&arch->data[idx][offset], &newData[offset]);
-						comp->destroy(&arch->data[idx][offset]);
-					}
-
-					// free the old data and swap the pointers
-					delete[] arch->data[idx];
-					arch->data[idx] = newData;
-				}
-
-				return currSize;
-			}
-
-			inline int getComponentIdx(const Archetype *arch, const component_type_t goal) {
-				// JWR could do binary search for speedup
-				for (std::size_t i = 0; i < arch->type.size(); ++i) {
-					if (arch->type[i] == goal)
-						return i;
-				}
-				return -1;
-			}
-
-	};
-
-}
-
-#endif
diff --git a/include/fggl/ecs/utility.hpp b/include/fggl/ecs/utility.hpp
deleted file mode 100644
index ebcc051e6625a73c80d726308fb2531f74df781e..0000000000000000000000000000000000000000
--- a/include/fggl/ecs/utility.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS_UTILITY_HPP
-#define FGGL_ECS_UTILITY_HPP
-
-#include <cstdint>
-
-namespace fggl::ecs {
-
-	using IDType = std::uint32_t;
-	using entity_t = IDType;
-	using component_type_t = IDType;
-
-	constexpr IDType NULL_ENTITY = 0;
-
-	template<class T>
-	class TypeIdGenerator {
-		private:
-			static IDType m_count;
-		public:
-			template<class U>
-			static const IDType GetNewID() {
-				static const IDType idCounter = m_count++;
-				return idCounter;
-			}
-	};
-
-	template<class T> IDType TypeIdGenerator<T>::m_count = 1;
-}
-
-#endif
diff --git a/include/fggl/ecs3/ecs.hpp b/include/fggl/ecs3/ecs.hpp
deleted file mode 100644
index 5f681515d635740dc44e37e53ed4cac315ff3066..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/ecs.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS3_ECS_HPP
-#define FGGL_ECS3_ECS_HPP
-
-#include "fggl/ecs3/module/module.hpp"
-#include <fggl/ecs3/prototype/world.hpp>
-#include <fggl/math/types.hpp>
-
-namespace fggl::ecs3 {
-
-	using World = prototype::World;
-
-	class ecsTypes : public Module {
-
-		public:
-			inline std::string name() const override {
-				return "ecs::core";
-			}
-
-			inline void onLoad(ModuleManager& manager, TypeRegistry& types) override {
-			}
-	};
-
-}
-
-#endif
\ No newline at end of file
diff --git a/include/fggl/ecs3/fast/Container.hpp b/include/fggl/ecs3/fast/Container.hpp
deleted file mode 100644
index 3516ab30637f1f22d6c2a7cafb1b0bc369bb1de5..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/fast/Container.hpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 23/10/2021.
-//
-
-#ifndef FGGL_ECS3_FAST_CONTAINER_HPP
-#define FGGL_ECS3_FAST_CONTAINER_HPP
-
-#include <cstdint>
-#include <cstdarg>
-#include <cassert>
-
-#include <fggl/ecs3/types.hpp>
-
-namespace fggl::ecs3 {
-
-
-	class Container {
-		public:
-			const RecordIdentifier m_identifier;
-
-			Container(const TypeRegistry &reg, RecordIdentifier id) :
-                m_identifier(id),
-				m_types(reg),
-				backingStore(nullptr),
-                m_size(0),
-                m_capacity(0)  {};
-
-			~Container() {
-				delete[] backingStore;
-			}
-
-			std::size_t create();
-
-			void remove(std::size_t pos);
-
-			std::size_t expand(Container &other, std::size_t otherPos, component_type_t newComp);
-
-			void contract(Container &other, std::size_t otherPos);
-
-			void ensure(std::size_t size);
-
-			inline unsigned char *data_raw(component_type_t type) {
-				auto seek_id = m_identifier.idx(type);
-
-				// you asked for something I don't contain...
-				if (seek_id == m_identifier.count) {
-					std::cerr << "asked for " << type << " from " << m_identifier << std::endl;
-					assert(seek_id != m_identifier.count);
-					return nullptr;
-				}
-
-				// figure out the offset
-				return backingStore + offsets[seek_id];
-			}
-
-			template<typename T>
-			inline T *data() {
-				auto comp_id = Component<T>::typeID();
-				return (T *) data_raw(comp_id);
-			}
-
-			template<typename T>
-			T *set(std::size_t entity, T *compData) {
-				auto *comps = data<T>();
-				auto entityPos = idx(entity);
-
-				auto compMeta = m_types.template meta<T>();
-
-				unsigned char *usrPtr = (unsigned char *) &comps[entityPos];
-				compMeta->destroy(usrPtr);
-				compMeta->move((unsigned char *) compData, usrPtr);
-
-				return &comps[entityPos];
-			}
-
-			[[nodiscard]]
-			inline std::size_t size() const {
-				return m_size;
-			}
-
-			inline std::size_t idx(entity_t entity) {
-				auto *entityData = data<EntityMeta>();
-				for (std::size_t i = 0; i < m_size; i++) {
-					if (entityData[i].id == entity) {
-						return i;
-					}
-				}
-				return m_size;
-			}
-
-		private:
-			const TypeRegistry &m_types;
-			unsigned char *backingStore;
-			std::size_t offsets[RecordIdentifier::MAX_COMPS]{};
-			std::size_t m_size;
-			std::size_t m_capacity;
-
-			void move(std::size_t newPos, Container &oldContainer, std::size_t oldPos);
-	};
-
-}
-
-#endif //FGGL_ECS3_FAST_CONTAINER_HPP
diff --git a/include/fggl/ecs3/fast/ecs.hpp b/include/fggl/ecs3/fast/ecs.hpp
deleted file mode 100644
index 6161b6b26737a7ffdc1198bdad6f25241ee60b2e..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/fast/ecs.hpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS3_FAST_ECS_HPP
-#define FGGL_ECS3_FAST_ECS_HPP
-
-#include <cstddef>
-#include <cstdarg>
-#include <cassert>
-#include <cstring>
-
-#include <map>
-#include <algorithm>
-#include <iostream>
-#include <type_traits>
-
-#include <fggl/ecs3/utils.hpp>
-#include <fggl/ecs3/types.hpp>
-#include <fggl/ecs3/fast/Container.hpp>
-
-namespace fggl::ecs3::fast {
-
-	using entity_t = unsigned int;
-	constexpr entity_t NULL_ENTITY = 0;
-
-	class World {
-		public:
-			explicit World(TypeRegistry &reg) : m_registry(reg), m_last(NULL_ENTITY) {}
-
-			entity_t create() {
-				auto next = m_last++;
-
-				auto arch = make_id(1, Component<EntityMeta>::typeID());
-				auto &container = getContainer(arch);
-
-				m_entities[next] = container.m_identifier;
-
-				auto pos = container.create();
-				auto *entityMeta = container.data<EntityMeta>();
-				entityMeta[pos].id = next;
-
-				return next;
-			}
-
-			void remove(entity_t entity) {
-				auto arch = m_entities.at(entity);
-				auto container = m_records.at(arch);
-
-				auto entPos = container.idx(entity);
-				container.remove(entPos);
-			}
-
-			inline Container &getContainer(RecordIdentifier &arch) {
-				try {
-					return m_records.at(arch);
-				} catch (std::out_of_range &e) {
-					auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch}));
-					return v.first->second;
-				}
-			}
-
-			template<typename T>
-			T *add(const entity_t entity) {
-				auto currArch = m_entities.at(entity);
-				auto newArch = currArch.with<T>();
-				m_entities[entity] = newArch;
-
-				auto &oldContainer = m_records.at(currArch);
-				auto &newContainer = getContainer(newArch);
-
-				auto oldPos = oldContainer.idx(entity);
-
-				auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID());
-				auto *data = newContainer.template data<T>();
-				return &data[newPos];
-			}
-
-			template<typename T>
-			T *set(const entity_t entity, T *record) {
-				auto currArch = m_entities.at(entity);
-
-				// check we already have that component type...
-				if (currArch.idx(Component<T>::typeID()) == currArch.count) {
-					add<T>(entity);
-					currArch = m_entities.at(entity);
-				}
-
-				auto &container = m_records.at(currArch);
-				auto pos = container.idx(entity);
-				return container.set<T>(pos, record);
-			}
-
-			template<typename T>
-			T *get(entity_t entity) {
-				auto currArch = m_entities.at(entity);
-				auto pos = currArch.idx(entity);
-				auto &container = m_records.at(currArch);
-
-				auto *data = container.template data<T>();
-				return &data[pos];
-			}
-
-			template<typename T>
-			const T *get(entity_t entity) const {
-				auto currArch = m_entities.at(entity);
-				auto pos = currArch.idx(entity);
-				auto &container = m_records.at(currArch);
-
-				auto *data = container.template data<T>();
-				return &data[pos];
-			}
-
-			template<typename T>
-			void remove(entity_t entity) {
-				auto currArch = m_entities.at(entity);
-				auto newArch = currArch.without<T>();
-
-				auto &oldContainer = m_records[currArch];
-				auto &newContainer = m_records[newArch];
-
-				auto oldPos = oldContainer.idx(entity);
-				auto newPos = newContainer.create();
-
-				m_records[newArch].contract(newPos, oldContainer, oldPos);
-			}
-
-			template<typename... T>
-			std::vector<entity_t> findMatching() const {
-				return {};
-			}
-
-		private:
-			TypeRegistry &m_registry;
-			std::map<RecordIdentifier, Container> m_records;
-			std::map<entity_t, RecordIdentifier> m_entities;
-			entity_t m_last{};
-	};
-
-}
-
-#endif
diff --git a/include/fggl/ecs3/module/module.hpp b/include/fggl/ecs3/module/module.hpp
deleted file mode 100644
index 13300b7b27351623e4dcf1fad6c52c12210ddcda..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/module/module.hpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 23/10/2021.
-//
-
-#ifndef FGGL_ECS3_MODULE_MODULE_HPP
-#define FGGL_ECS3_MODULE_MODULE_HPP
-
-#include <string>
-#include <map>
-#include <memory>
-
-#include <fggl/ecs3/types.hpp>
-
-namespace fggl::ecs3 {
-
-	class Module {
-		public:
-			virtual ~Module() = default;
-
-			[[nodiscard]] virtual std::string name() const = 0;
-
-			virtual void onLoad(ModuleManager &manager, TypeRegistry &tr) {};
-
-			virtual void onUpdate();
-			virtual void onFrameStart();
-			virtual void onFrameEnd();
-	};
-
-	class ModuleManager {
-		public:
-			explicit ModuleManager(TypeRegistry &types) : m_types(types), m_modules() {}
-
-			~ModuleManager() = default;
-
-			template<typename C, typename... Args>
-			std::shared_ptr<C> load(Args &... args) {
-				auto ptr = std::make_shared<C>(args...);
-				m_modules[ptr->name()] = ptr;
-
-				ptr->onLoad(*this, m_types);
-				//spdlog::info("loaded ECS module: {}", ptr->name());
-				return ptr;
-			}
-
-			template<typename C>
-			void onAdd(const callback_t &cb) {
-				m_types.callbackAdd(Component<C>::typeID(), cb);
-			}
-
-			void onUpdate() {
-				for (auto &[id, ptr] : m_modules) {
-					ptr->onUpdate();
-				}
-			}
-
-			void onFrameStart() {
-				for (auto &[id, ptr] : m_modules) {
-					ptr->onFrameStart();
-				}
-			}
-
-			void onFrameEnd() {
-				for (auto &[id, ptr] : m_modules) {
-					ptr->onFrameEnd();
-				}
-			}
-
-		private:
-			TypeRegistry &m_types;
-			std::map<std::string, std::shared_ptr<Module>> m_modules;
-	};
-
-}
-
-#endif //FGGL_ECS3_MODULE_MODULE_HPP
diff --git a/include/fggl/ecs3/prototype/loader.hpp b/include/fggl/ecs3/prototype/loader.hpp
deleted file mode 100644
index 89fe334390cc0f9bed586a41907bc862126bf1ab..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/prototype/loader.hpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 04/06/22.
-//
-
-#ifndef FGGL_ECS3_PROTOTYPE_LOADER_HPP
-#define FGGL_ECS3_PROTOTYPE_LOADER_HPP
-
-#include "yaml-cpp/yaml.h"
-
-#include "fggl/math/types.hpp"
-#include "fggl/phys/types.hpp"
-#include "fggl/gfx/phong.hpp"
-
-#include "fggl/data/storage.hpp"
-#include "fggl/data/model.hpp"
-#include "fggl/data/procedural.hpp"
-
-#include "fggl/ecs3/ecs.hpp"
-#include "fggl/ecs/component_fwd.hpp"
-
-namespace fggl::ecs3 {
-
-	void load_prototype_node( ecs3::World& world, const YAML::Node& node);
-	void load_prototype_file( ecs3::World& world, data::Storage& storage, const std::string& name );
-
-} // namespace fggl::ecs3
-
-namespace YAML {
-
-	template<>
-	struct convert<fggl::math::vec3> {
-		static Node encode(const fggl::math::vec3& rhs){
-			Node node;
-			node.push_back(rhs.x);
-			node.push_back(rhs.y);
-			node.push_back(rhs.z);
-			return node;
-		}
-
-		static bool decode(const Node& node, fggl::math::vec3& rhs) {
-			if ( !node.IsSequence() || node.size() != 3) {
-				return false;
-			}
-
-			rhs.x = node[0].as<float>();
-			rhs.y = node[1].as<float>();
-			rhs.z = node[2].as<float>();
-			return true;
-		}
-	};
-
-	template<>
-	struct convert<fggl::math::quat> {
-		static Node encode(const fggl::math::quat& rhs){
-			Node node;
-			node.push_back(rhs.x);
-			node.push_back(rhs.y);
-			node.push_back(rhs.z);
-			node.push_back(rhs.w);
-			return node;
-		}
-
-		static bool decode(const Node& node, fggl::math::quat& rhs) {
-			if ( !node.IsSequence() || node.size() != 3) {
-				return false;
-			}
-
-			rhs.x = node[0].as<float>();
-			rhs.y = node[1].as<float>();
-			rhs.z = node[2].as<float>();
-			rhs.w = node[3].as<float>();
-			return true;
-		}
-	};
-
-	constexpr const char* TYPE_KINEMATIC = "kinematic";
-	constexpr const char* TYPE_STATIC = "static";
-	constexpr const char* TYPE_DYNAMIC = "dynamic";
-
-	template<>
-	struct convert<fggl::phys::BodyType> {
-		static Node encode(const fggl::phys::BodyType& rhs) {
-			switch (rhs) {
-				case fggl::phys::BodyType::KINEMATIC:
-					return Node(TYPE_KINEMATIC);
-				case fggl::phys::BodyType::STATIC:
-					return Node(TYPE_STATIC);
-				default:
-				case fggl::phys::BodyType::DYNAMIC:
-					return Node(TYPE_DYNAMIC);
-			}
-		}
-
-		static bool decode(const Node& node, fggl::phys::BodyType& rhs) {
-			const auto value = node.as<std::string>();
-			if ( value == TYPE_KINEMATIC ) {
-				rhs = fggl::phys::BodyType::KINEMATIC;
-			} else if ( value == TYPE_STATIC ) {
-				rhs = fggl::phys::BodyType::STATIC;
-			} else {
-				rhs = fggl::phys::BodyType::DYNAMIC;
-			}
-			return true;
-		}
-	};
-
-
-}
-
-namespace fggl::ecs {
-
-	constexpr int DEFAULT_STACKS = 16;
-	constexpr int DEFAULT_SLICES = 16;
-	constexpr const char* SHAPE_SPHERE_VALUE{"sphere"};
-	constexpr const char* SHAPE_BOX_VALUE{"box"};
-
-	// scene template specialisations
-}
-
-#endif //FGGL_ECS3_PROTOTYPE_LOADER_HPP
diff --git a/include/fggl/ecs3/prototype/world.hpp b/include/fggl/ecs3/prototype/world.hpp
deleted file mode 100644
index 9e0a62a9fa44ab8de5e0f6cf38cc0665908af84c..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/prototype/world.hpp
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-//
-// Created by webpigeon on 23/10/2021.
-//
-
-#ifndef FGGL_ECS3_PROTOTYPE_WORLD_HPP
-#define FGGL_ECS3_PROTOTYPE_WORLD_HPP
-
-#include <map>
-#include <functional>
-#include <unordered_set>
-
-#include "fggl/ecs3/types.hpp"
-#include "fggl/debug/logging.hpp"
-#include <yaml-cpp/yaml.h>
-
-/**
- * A component based implementation of a game world.
- *
- * This is not a true ECS but exposes a similar API to it for testing (with a lot less headaches).
- */
-namespace fggl::ecs3::prototype {
-
-	using EntityCallback = std::function<void(const entity_t)>;
-
-	class Entity {
-		public:
-			bool m_abstract;
-
-			explicit Entity(entity_t id) : m_abstract(false), m_id(id) {};
-
-			Entity(const Entity &entity) : m_id(entity.m_id), m_components(entity.m_components) {
-				//spdlog::info("entity created fro copy: {}", m_id);
-			}
-
-			~Entity() = default;
-
-			template<typename C>
-			C *add() {
-				C *ptr = new C();
-				m_components[Component<C>::typeID()] = ptr;
-				return ptr;
-			}
-
-			void *add(std::shared_ptr<ComponentBase> t) {
-				void *ptr = t->construct();
-				m_components[t->id()] = ptr;
-				return ptr;
-			}
-
-			void* add(const std::shared_ptr<ComponentBase>& compMeta, const YAML::Node& config) {
-				void* ptr = compMeta->restore(config);
-				m_components[ compMeta->id() ] = ptr;
-				return ptr;
-			}
-
-			template<typename C>
-			C *set(const C *ptr) {
-				C *newPtr = new C(*ptr);
-				m_components[Component<C>::typeID()] = newPtr;
-				return newPtr;
-			}
-
-			void *set(const std::shared_ptr<ComponentBase> &t, const void *ptr) {
-				void *newPtr = t->copyConstruct(ptr);
-				m_components[t->id()] = newPtr;
-				return newPtr;
-			}
-
-			template<typename C>
-			C *get() const {
-				void *ptr = m_components.at(Component<C>::typeID());
-				return (C *) ptr;
-			}
-
-			template<typename C>
-			void remove(){
-				m_components.erase(Component<C>::typeID());
-			}
-
-			inline void *get(component_type_t t) {
-				return m_components.at(t);
-			}
-
-			std::vector<component_type_t> getComponentIDs() {
-				std::vector<component_type_t> comps{};
-				for (auto &[k, _] : m_components) {
-					comps.push_back(k);
-				}
-				return comps;
-			}
-
-			bool hasComponents(std::vector<component_type_t> &Cs) const {
-				for (auto c : Cs) {
-					if (m_components.find(c) == m_components.end()) {
-						return false;
-					}
-				}
-				return true;
-			}
-
-		private:
-			entity_t m_id;
-			std::map<component_type_t, void *> m_components;
-	};
-
-	class World {
-		public:
-			explicit World(TypeRegistry &reg) : m_types(reg), m_next(1), m_entities() {};
-			~World() = default;
-
-			entity_t create(bool abstract) {
-				auto nextID = m_next++;
-				m_entities.emplace(nextID, nextID);
-
-				auto &entity = m_entities.at(nextID);
-				entity.m_abstract = abstract;
-
-				// meta data
-				auto *meta = entity.add<ecs3::EntityMeta>();
-				meta->id = nextID;
-				meta->abstract = abstract;
-				meta->typeName = "";
-
-				return nextID;
-			}
-
-			inline entity_t createFromPrototype(const std::string& name) {
-				auto prototype = findPrototype(name);
-				if ( prototype == NULL_ENTITY) {
-					debug::log(debug::Level::warning, "attempted to create from non-existant prototype: {}", name);
-					return NULL_ENTITY;
-				}
-				return copy( prototype );
-			}
-
-			entity_t copy(entity_t prototype) {
-				auto clone = create(false);
-
-				auto components = getComponents(prototype);
-				for (auto component : components) {
-					auto protoComp = get(prototype, component);
-					set(clone, component, protoComp);
-				}
-
-				return clone;
-			}
-
-			void addFromConfig(entity_t entity, component_type_t type, const YAML::Node& node) {
-				auto meta = m_types.meta(type);
-
-				auto& entityObj = m_entities.at(entity);
-				entityObj.add(meta, node);
-
-				m_types.fireAdd(this, entity, meta->id());
-			}
-
-			void createFromSpec(entity_t entity, const YAML::Node& compConfig) {
-				if ( compConfig ) {
-					for (const auto& itr : compConfig ) {
-						const auto name = itr.first.as<std::string>();
-						const auto& config = itr.second;
-
-						auto compType = m_types.find( name.c_str() );
-						addFromConfig(entity, compType, config);
-					}
-				}
-			}
-
-			inline auto alive(entity_t entity) const -> bool {
-				return entity != NULL_ENTITY
-					&& m_killList.find( entity ) == m_killList.end()
-					&& m_entities.find( entity ) != m_entities.end();
-			}
-
-			inline auto exists(entity_t entity) const -> bool {
-				return entity != NULL_ENTITY
-					&& m_entities.find( entity ) != m_entities.end();
-			}
-
-			void destroy(entity_t entity) {
-				assert( alive(entity) && "attempted to kill null entity" );
-				// TOOD resolve and clean components
-				//m_entities.erase(entity);
-				m_killList.insert(entity);
-			}
-
-			void reapEntities() {
-				for (const auto& entity : m_killList) {
-					//auto& entityObj = m_entities.at(entity);
-					//entityObj.clear();
-					for (auto& listener : m_deathListeners) {
-						listener( entity );
-					}
-
-					m_entities.erase(entity);
-				}
-				m_killList.clear();
-			}
-
-			inline TypeRegistry &types() {
-				return m_types;
-			}
-
-			std::vector<entity_t> all() {
-				std::vector<entity_t> entities{};
-				for (auto &[eid, entity] : m_entities) {
-					entities.push_back(eid);
-				}
-				return entities;
-			}
-
-			std::vector<component_type_t> getComponents(entity_t entityID) {
-				assert(alive(entityID) && "attempted to get components on dead entity");
-
-				std::vector<component_type_t> components{};
-				auto &entity = m_entities.at(entityID);
-				auto comps = entity.getComponentIDs();
-				for (auto id : comps) {
-					components.push_back(id);
-				}
-				return components;
-			}
-
-			template<typename... Cs>
-			std::vector<entity_t> findMatching() const {
-				// construct the key
-				std::vector<ecs::component_type_t> key;
-				(key.push_back(Component<Cs>::typeID()), ...);
-
-				// entities
-				std::vector<entity_t> entities{};
-				for (auto &[eid, entity] : m_entities) {
-					if (entity.hasComponents(key) && !entity.m_abstract) {
-						entities.push_back(eid);
-					}
-				}
-
-				return entities;
-			}
-
-			template<typename ...Cs>
-			bool has(entity_t entityIdx) const {
-				if ( !alive(entityIdx)) {
-					return false;
-				}
-
-				std::vector<ecs::component_type_t> key;
-				(key.push_back(Component<Cs>::typeID()), ...);
-				return m_entities.at(entityIdx).hasComponents(key);
-			}
-
-			template<typename C>
-			C *add(entity_t entity_id) {
-				assert( alive(entity_id) && "attempted to add component on null entity" );
-
-				//spdlog::info("component '{}' added to '{}'", C::name, entity_id);
-				auto &entity = m_entities.at(entity_id);
-				auto comp = entity.template add<C>();
-
-				m_types.fireAdd(this, entity_id, Component<C>::typeID());
-				return comp;
-			}
-
-			void *add(entity_t entity_id, component_type_t component_id) {
-				assert( alive(entity_id) && "attempted to add component on null entity" );
-
-				auto meta = m_types.meta(component_id);
-				auto &entity = m_entities.at(entity_id);
-
-				void *ptr = entity.add(meta);
-				m_types.fireAdd(this, entity_id, meta->id());
-				return ptr;
-			}
-
-			template<typename C>
-			C *set(entity_t entity_id, const C *ptr) {
-				assert( alive( entity_id ) && "attempted to set component on null entity" );
-
-				//spdlog::info("component '{}' set on '{}'", C::name, entity_id);
-				auto &entity = m_entities.at(entity_id);
-				auto comp = entity.set<C>(ptr);
-
-				m_types.fireAdd(this, entity_id, Component<C>::typeID());
-				return comp;
-			}
-
-			void *set(entity_t entity_id, component_type_t cid, const void *ptr) {
-				assert( alive( entity_id ) && "attempted to set component on null entity" );
-
-				auto &entity = m_entities.at(entity_id);
-				auto cMeta = m_types.meta(cid);
-
-				auto comp = entity.set(cMeta, ptr);
-				m_types.fireAdd(this, entity_id, cid);
-				return comp;
-			}
-
-			template<typename C>
-			C* tryGet(entity_t entity_id) const {
-				if ( entity_id == NULL_ENTITY) {
-					return nullptr;
-				}
-
-				try {
-					return get<C>(entity_id);
-				} catch ( std::out_of_range& e) {
-					fggl::debug::info("component {} on {} did not exist", C::name, entity_id);
-					return nullptr;
-				}
-			}
-
-			template<typename C>
-			C *get(entity_t entity_id) const {
-				assert( exists(entity_id) && "attempted to get component on null entity" );
-
-				const auto& entity = m_entities.at(entity_id);
-				return entity.get<C>();
-			}
-
-			template<typename C>
-			void remove(entity_t entity_id) {
-				assert( alive(entity_id) && "attempted to remove component on null entity" );
-
-				try {
-					auto &entity = m_entities.at(entity_id);
-					try {
-						return entity.remove<C>();
-					} catch ( std::out_of_range& e ) {
-						std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl;
-					}
-				} catch ( std::out_of_range& e) {
-					std::cerr << "tried to delete component on entity that didn't exist, entity was: " << entity_id << std::endl;
-				}
-			}
-
-			void *get(entity_t entity_id, component_type_t componentType) {
-				assert( exists(entity_id) && "attempted to get component on null entity" );
-				auto &entity = m_entities.at(entity_id);
-				return entity.get(componentType);
-			}
-
-			void addDeathListener(const EntityCallback& callback) {
-				m_deathListeners.emplace_back(callback);
-			}
-
-			entity_t findPrototype(const std::string& name) const {
-				for ( const auto& [entity, obj] : m_entities ) {
-					if ( !obj.m_abstract ){
-						continue;
-					}
-
-					auto* metaData = obj.get<EntityMeta>();
-					if ( metaData->typeName == name){
-						return entity;
-					}
-				}
-
-				return NULL_ENTITY;
-			}
-
-		private:
-			std::vector<EntityCallback > m_deathListeners;
-			TypeRegistry &m_types;
-			entity_t m_next;
-			std::map<entity_t, Entity> m_entities;
-			std::unordered_set<entity_t> m_killList;
-
-	};
-
-}
-
-#endif //FGGL_ECS3_PROTOTYPE_WORLD_HPP
diff --git a/include/fggl/ecs3/types.hpp b/include/fggl/ecs3/types.hpp
deleted file mode 100644
index 5947f599fb3bfbcad0409e00b43e08c86658ab7a..0000000000000000000000000000000000000000
--- a/include/fggl/ecs3/types.hpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * This file is part of FGGL.
- *
- * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with FGGL.
- * If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef FGGL_ECS3_TYPES_HPP
-#define FGGL_ECS3_TYPES_HPP
-
-#include <cstdarg>
-#include <utility>
-#include <functional>
-
-#include <fggl/ecs/component.hpp>
-#include <fggl/ecs3/utils.hpp>
-
-#include <iostream>
-#include <memory>
-#include <algorithm>
-#include <map>
-#include <unordered_map>
-
-namespace fggl::ecs3 {
-
-	namespace {
-		using namespace fggl::ecs;
-	};
-
-	namespace prototype {
-		class World;
-	}
-
-	using fggl::ecs::component_type_t;
-
-	class ModuleManager;
-
-	using callback_t = std::function<void(prototype::World *, ecs3::entity_t)>;
-	struct TypeCallbacks {
-		std::vector<callback_t> add;
-	};
-
-	// core component types
-	struct EntityMeta {
-		constexpr static const char name[] = "meta";
-		entity_t id;
-		bool abstract;
-		std::string typeName;
-	};
-
-	struct RecordIdentifier {
-		constexpr static std::size_t MAX_COMPS = 32;
-		component_type_t types[MAX_COMPS];
-		std::size_t count;
-
-		[[nodiscard]]
-		inline std::size_t idx(component_type_t t) const {
-			return utils::search(types, count, t);
-		}
-
-		template<typename T>
-		[[nodiscard]]
-		RecordIdentifier with() const {
-			// check the caller wasn't a muppet
-			const auto typeID = ecs::Component<T>::typeID();
-			if (idx(typeID) != count) {
-				return *this;
-			}
-
-			RecordIdentifier re{};
-			re.count = count + 1;
-			re.types[count] = ecs::Component<T>::typeID();
-
-			// add old types
-			for (std::size_t i = 0; i < count; ++i) {
-				re.types[i] = types[i];
-			}
-			std::sort(re.types, re.types + re.count);
-			return re;
-		}
-
-		template<typename T>
-		[[nodiscard]]
-		RecordIdentifier without() const {
-			// check the caller wasn't a muppet
-			const auto typeID = ecs::Component<T>::typeID();
-			const auto typeIdx = idx(typeID);
-			if (typeIdx == count) {
-				return *this;
-			}
-
-			RecordIdentifier re{};
-			re.count = count - 1;
-
-			// add old types
-			for (std::size_t i = 0, j = 0; i < count; ++i) {
-				if (typeIdx != i) {
-					re.types[j] = types[i];
-					j++;
-				}
-			}
-			std::sort(re.types, re.types + re.count);
-			return re;
-		}
-
-		bool operator<(const RecordIdentifier &other) const {
-			if (count < other.count) {
-				return true;
-			} else if (count > other.count) {
-				return false;
-			} else {
-				for (std::size_t i = 0; i < count; i++) {
-					if (types[i] != other.types[i]) {
-						return types[i] < other.types[i];
-					}
-				}
-				return false;
-			}
-		}
-
-		bool operator==(const RecordIdentifier &arg) const {
-			if (arg.count != count) {
-				return false;
-			}
-
-			for (std::size_t i = 0; i < count; i++) {
-				if (types[i] != arg.types[i]) {
-					return false;
-				}
-			}
-			return true;
-		}
-
-		bool operator!=(const RecordIdentifier &arg) const {
-			return !(*this == arg);
-		}
-	};
-
-	std::ostream &operator<<(std::ostream &out, RecordIdentifier const &curr);
-
-	inline RecordIdentifier make_id(std::size_t count, ...) {
-		assert(count < RecordIdentifier::MAX_COMPS);
-
-		RecordIdentifier re{};
-
-		std::va_list args;
-		va_start(args, count);
-		for (std::size_t i = 0; i < count; ++i) {
-			re.types[i] = va_arg(args, component_type_t);
-		}
-		va_end(args);
-		re.count = count;
-		std::sort(re.types, re.types + count);
-
-		return re;
-	}
-
-	class TypeRegistry {
-		public:
-
-			TypeRegistry() : m_last_virtual(9000), m_callbacks() {
-				// core types always exist
-				make<EntityMeta>();
-			}
-
-			template<typename T>
-			void make() {
-				auto type_id = Component<T>::typeID();
-				if (m_types.find(type_id) != m_types.end())
-					return;
-				m_types[type_id] = std::make_shared<Component<T>>();
-			}
-
-			template<typename T>
-			bool exists() {
-				auto type_id = Component<T>::typeID();
-				return m_types.find(type_id) != m_types.end();
-			}
-
-			template<typename T>
-			std::shared_ptr<fggl::ecs::ComponentBase> meta() const {
-				auto type_id = Component<T>::typeID();
-				return m_types.at(type_id);
-			}
-
-			inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const {
-				try {
-					return m_types.at(type_id);
-				} catch (std::out_of_range& err) {
-					std::cerr << "asked for metadata on type " << type_id << " but no such type is in the type system" << std::endl;
-					return nullptr;
-				}
-			}
-
-			inline component_type_t find(const char *name) const {
-				for (const auto &[type, meta] : m_types) {
-					if (std::strcmp(name, meta->name()) == 0) {
-						return type;
-					}
-				}
-
-				debug::warning("asked for unknown/unregistered component type: {}", name);
-				assert(false && "unknown component type, are you sure it was registered?");
-				return 0;
-			}
-
-			inline void make_virtual(std::shared_ptr<ComponentBase> vtype) {
-				auto type_id = m_last_virtual++;
-				m_types[type_id] = std::move(vtype);
-			}
-
-			void callbackAdd(component_type_t component, const callback_t &callback) {
-				m_callbacks[component].add.push_back(callback);
-			}
-
-			void fireAdd(prototype::World *world, entity_t entity, component_type_t type) {
-				try {
-					auto &callbacks = m_callbacks.at(type).add;
-					for (auto &callback : callbacks) {
-						callback(world, entity);
-					}
-				} catch (std::out_of_range &e) {
-					//spdlog::debug("no callbacks for {}", m_types[type]->name());
-				}
-			}
-
-		private:
-			std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types;
-			component_type_t m_last_virtual;
-			std::map<component_type_t, TypeCallbacks> m_callbacks;
-	};
-
-};
-
-#endif
diff --git a/include/fggl/entity/entity.hpp b/include/fggl/entity/entity.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d12ccb985527ff8fa1e1aba29c77e942ec1c9e7
--- /dev/null
+++ b/include/fggl/entity/entity.hpp
@@ -0,0 +1,107 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 24/07/22.
+//
+
+#ifndef FGGL_ENTITY_ENTITY_HPP
+#define FGGL_ENTITY_ENTITY_HPP
+
+#include <cstdint>
+#include "fggl/vendor/entt.hpp"
+
+namespace fggl::entity {
+
+	using EntityID = entt::entity;
+	constexpr EntityID INVALID = entt::null;
+
+	class EntityManager {
+		public:
+			inline EntityID create() {
+				return m_registry.create();
+			}
+			inline void destroy(EntityID entity) {
+				m_registry.destroy(entity);
+			}
+
+			template<typename Component, typename... Args>
+			inline Component& add(EntityID entity, Args&&... args) {
+				return m_registry.emplace<Component>(entity, std::forward<Args>(args)...);
+			}
+
+			template<typename Component>
+			Component& get(EntityID entity) {
+				return m_registry.get<Component>(entity);
+			}
+
+			template<typename Component>
+			const Component& get(EntityID entity) const {
+				return m_registry.get<Component>(entity);
+			}
+			template<typename Component>
+			Component* tryGet(EntityID entity) {
+				return m_registry.try_get<Component>(entity);
+			}
+
+			template<typename Component>
+			const Component* tryGet(EntityID entity) const {
+				return m_registry.try_get<Component>(entity);
+			}
+
+			template<typename ...Components>
+			auto find() const {
+				return m_registry.view<Components...>();
+			}
+
+			template<typename ...Components>
+			bool has(EntityID idx) const {
+				return m_registry.template all_of<Components...>(idx);
+			}
+
+			inline bool exists(EntityID idx) {
+				return m_registry.valid(idx);
+			}
+
+			inline bool alive(EntityID idx) {
+				return m_registry.valid(idx);
+			}
+
+
+		private:
+			entt::registry m_registry;
+	};
+
+	struct Entity {
+		static Entity make(EntityManager& manager, EntityID idx) {
+			return Entity{idx, manager};
+		}
+
+		EntityID id;
+		EntityManager& manager;
+
+		template<typename Component>
+		Component& get() {
+			return manager.get<Component>(id);
+		}
+
+		template<typename Component>
+		const Component& get() const {
+			return manager.get<Component>(id);
+		}
+	};
+
+} // namespace fggl::entity
+
+#endif //FGGL_ENTITY_ENTITY_HPP
diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8da4709a94c70f2a1e052e5059311db935b2174f
--- /dev/null
+++ b/include/fggl/entity/loader/loader.hpp
@@ -0,0 +1,92 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 23/07/22.
+//
+
+#ifndef FGGL_ENTITY_LOADER_LOADER_HPP
+#define FGGL_ENTITY_LOADER_LOADER_HPP
+
+#include <functional>
+#include <map>
+#include <utility>
+
+#include "fggl/util/guid.hpp"
+#include "fggl/modules/module.hpp"
+#include "fggl/entity/entity.hpp"
+#include "fggl/entity/loader/spec.hpp"
+#include "fggl/assets/loader.hpp"
+
+namespace fggl::entity {
+
+	constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype");
+	using FactoryFunc = std::function<void(const ComponentSpec& config, EntityManager&, const EntityID&)>;
+
+	using ComponentID = util::GUID;
+	using EntityType = util::GUID;
+
+	class EntityFactory {
+		public:
+			constexpr static const modules::ModuleService service = modules::make_service("fggl::entity:Factory");
+
+			EntityID create(const EntityType& spec, EntityManager& manager) {
+				try {
+					auto &prototype = m_prototypes.at(spec);
+
+					// invoke each component factory as required
+					auto entity = manager.create();
+					for (auto &[name, data] : prototype.components) {
+						try {
+							m_factories.at(name)(data, manager, entity);
+						} catch (std::out_of_range& ex) {
+							#ifndef NDEBUG
+								debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(name));
+							#endif
+							manager.destroy(entity);
+							return fggl::entity::INVALID;
+						}
+					}
+					return entity;
+				} catch (std::out_of_range& ex) {
+					#ifndef NDEBUG
+						debug::log(debug::Level::error, "EntityFactory: Unknown entity type '{}'", fggl::util::guidToString(spec));
+					#endif
+					return fggl::entity::INVALID;
+				}
+			}
+
+			void define(EntityType type, const EntitySpec& spec) {
+				m_prototypes[type] = spec;
+			}
+
+			// ability to set and unset factory functions
+			inline void bind(const ComponentID& configNode, FactoryFunc factory) {
+				m_factories[configNode] = std::move(factory);
+			}
+
+			inline void unbind(const ComponentID& configNode) {
+				m_factories.erase(configNode);
+			}
+
+		private:
+			std::map<ComponentID, FactoryFunc> m_factories;
+			std::map<EntityType, EntitySpec> m_prototypes;
+	};
+
+	assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data);
+
+} // namespace fggl::entity
+
+#endif //FGGL_ENTITY_LOADER_LOADER_HPP
diff --git a/include/fggl/entity/loader/serialise.hpp b/include/fggl/entity/loader/serialise.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6650351fcef816b2a8478c9cce6babd2616740ff
--- /dev/null
+++ b/include/fggl/entity/loader/serialise.hpp
@@ -0,0 +1,121 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 24/07/22.
+//
+
+#ifndef FGGL_ENTITY_LOADER_SERIALISE_HPP
+#define FGGL_ENTITY_LOADER_SERIALISE_HPP
+
+#include "yaml-cpp/yaml.h"
+
+#include "fggl/math/types.hpp"
+#include "fggl/data/model.hpp"
+
+namespace YAML {
+
+	template<>
+	struct convert<fggl::math::vec3> {
+		static Node encode(const fggl::math::vec3& rhs) {
+			Node node;
+			node.push_back(rhs.x);
+			node.push_back(rhs.y);
+			node.push_back(rhs.z);
+			return node;
+		}
+
+		static bool decode(const Node& node, fggl::math::vec3& rhs) {
+			if (!node.IsSequence() || node.size() != 3) {
+				return false;
+			}
+
+			rhs.x = node[0].as<float>();
+			rhs.y = node[0].as<float>();
+			rhs.z = node[0].as<float>();
+			return true;
+		}
+	};
+
+	template<>
+	struct convert<fggl::math::vec2> {
+		static Node encode(const fggl::math::vec2& rhs) {
+			Node node;
+			node.push_back(rhs.x);
+			node.push_back(rhs.y);
+			return node;
+		}
+
+		static bool decode(const Node& node, fggl::math::vec2& rhs) {
+			if (!node.IsSequence() || node.size() != 2) {
+				return false;
+			}
+
+			rhs.x = node[0].as<float>();
+			rhs.y = node[0].as<float>();
+			return true;
+		}
+	};
+
+	template<>
+	struct convert<fggl::data::Vertex> {
+		static Node encode(const fggl::data::Vertex& rhs) {
+			Node node;
+			node["position"] = rhs.posititon;
+			node["normal"] = rhs.normal;
+			node["colour"] = rhs.colour;
+			node["texPos"] = rhs.texPos;
+			return node;
+		}
+
+		static bool decode(const Node& node, fggl::data::Vertex& rhs) {
+			if (!node.IsSequence() || node.size() != 2) {
+				return false;
+			}
+
+			rhs.posititon = node["position"].as<fggl::math::vec3>();
+			rhs.normal = node["normal"].as<fggl::math::vec3>();
+			rhs.colour = node["colour"].as<fggl::math::vec3>();
+			rhs.texPos = node["texPos"].as<fggl::math::vec2>();
+			return true;
+		}
+	};
+
+	template<>
+	struct convert<fggl::util::GUID> {
+		static Node encode(const fggl::util::GUID& rhs) {
+			Node node;
+			node = rhs.get();
+			return node;
+		}
+
+		static bool decode(const Node& node, fggl::util::GUID& rhs) {
+			auto longVal = node.as<uint64_t>(0);
+
+			if ( longVal == 0 ) {
+				// probably meant to hash it...
+				auto stringVal = node.as<std::string>();
+				rhs = fggl::util::make_guid_rt(stringVal);
+				return true;
+			}
+
+			// it's probably pre-hashed...
+			rhs = fggl::util::GUID::make(longVal);
+			return true;
+		}
+	};
+
+}
+
+#endif //FGGL_ENTITY_LOADER_SERIALISE_HPP
diff --git a/include/fggl/entity/loader/spec.hpp b/include/fggl/entity/loader/spec.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4dd926c4dd73a3827788c75eb46fb9b9b90b24da
--- /dev/null
+++ b/include/fggl/entity/loader/spec.hpp
@@ -0,0 +1,54 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 23/07/22.
+//
+
+#ifndef FGGL_ENTITY_LOADER_SPEC_HPP
+#define FGGL_ENTITY_LOADER_SPEC_HPP
+
+#include "fggl/util/guid.hpp"
+#include "fggl/entity/loader/serialise.hpp"
+
+#include <map>
+
+namespace fggl::entity {
+
+	struct ComponentSpec {
+
+		template<typename T>
+		T get(const std::string& key, const T& fallback) const {
+			return config[key].template as<T>(fallback);
+		}
+
+		template<typename T>
+		void set(const std::string& key, const T& value) {
+			config[key] = value;
+		}
+
+		inline bool has(const std::string& key) const {
+			return (bool)(config[key]);
+		}
+
+		YAML::Node config;
+	};
+
+	struct EntitySpec {
+		std::map<util::GUID, ComponentSpec> components;
+	};
+
+} // namespace fggl::entity
+
+#endif //FGGL_ENTITY_LOADER_SPEC_HPP
diff --git a/include/fggl/ecs/types.hpp b/include/fggl/entity/module.hpp
similarity index 50%
rename from include/fggl/ecs/types.hpp
rename to include/fggl/entity/module.hpp
index 92f727dd24f0864829f7e1bb2bded988daac3eb2..0b70faaf3b59c3725f7136404f0604cb382891a0 100644
--- a/include/fggl/ecs/types.hpp
+++ b/include/fggl/entity/module.hpp
@@ -13,27 +13,33 @@
  */
 
 //
-// Created by webpigeon on 22/06/22.
+// Created by webpigeon on 24/07/22.
 //
 
-#ifndef FGGL_ECS_TYPES_HPP
-#define FGGL_ECS_TYPES_HPP
+#ifndef FGGL_ENTITY_MODULE_HPP
+#define FGGL_ENTITY_MODULE_HPP
 
-#include <cstdint>
+#include "fggl/modules/module.hpp"
+#include "fggl/assets/loader.hpp"
 
-namespace fggl::ecs {
+#include "fggl/entity/loader/loader.hpp"
 
-	struct entity {
-		const std::uint32_t id;
-	};
-	constexpr const entity NULL_ENTITY = {0};
-	constexpr const entity MAX_ENTITY = {0xFFFFFFFF};
+namespace fggl::entity {
 
-	struct componentID {
-		const std::uint32_t id;
+	struct ECS {
+		constexpr static const char* name = "fggl::entity::ECS";
+		constexpr static const std::array<modules::ModuleService, 1> provides = {
+			EntityFactory::service
+		};
+		constexpr static const std::array<modules::ModuleService, 1> depends = {
+			assets::Loader::service
+		};
+		static const modules::ServiceFactory factory;
 	};
-	constexpr const entity NULL_COMPONENT = {0};
 
-}
+	void install_component_factories(EntityFactory* factory);
+
+
+} // namespace fggl::entity
 
-#endif //FGGL_ECS_TYPES_HPP
+#endif //FGGL_ENTITY_MODULE_HPP
diff --git a/include/fggl/gfx/camera.hpp b/include/fggl/gfx/camera.hpp
index c1fcf989dcd5a2ff2c651cf5e97d75c26557fb9d..0968899ecbe1dce006040c713d9224f9d7290d50 100644
--- a/include/fggl/gfx/camera.hpp
+++ b/include/fggl/gfx/camera.hpp
@@ -15,7 +15,8 @@
 #ifndef FGGL_GFX_CAMERA_HPP
 #define FGGL_GFX_CAMERA_HPP
 
-#include <fggl/math/types.hpp>
+#include "fggl/math/types.hpp"
+#include "fggl/entity/entity.hpp"
 
 namespace fggl::gfx {
 
@@ -28,13 +29,13 @@ namespace fggl::gfx {
 		float farPlane = 100.0f;
 	};
 
-	inline math::mat4 calc_proj_matrix(const Camera* camera) {
-		return glm::perspective(camera->fov, camera->aspectRatio, camera->nearPlane, camera->farPlane);
+	inline math::mat4 calc_proj_matrix(const Camera& camera) {
+		return glm::perspective(camera.fov, camera.aspectRatio, camera.nearPlane, camera.farPlane);
 	}
 
-	inline math::Ray get_camera_ray(const ecs3::World& world, const ecs3::entity_t camera, math::vec2 position) {
-		auto* const camTransform = world.get<fggl::math::Transform>(camera);
-		auto* const camComp = world.get<fggl::gfx::Camera>(camera);
+	inline math::Ray get_camera_ray(const entity::EntityManager& world, const entity::EntityID camera, math::vec2 position) {
+		auto& camTransform = world.get<fggl::math::Transform>(camera);
+		auto& camComp = world.get<fggl::gfx::Camera>(camera);
 
 		const auto projMatrix = fggl::gfx::calc_proj_matrix(camComp);
 		const auto viewMatrix = fggl::math::calc_view_matrix(camTransform);
diff --git a/include/fggl/gfx/interfaces.hpp b/include/fggl/gfx/interfaces.hpp
index f546ab2cd714d59c570d7941a5b080148531baae..2e22ed15adfdc23a4e9cb37bf48f5efb4f1a28a4 100644
--- a/include/fggl/gfx/interfaces.hpp
+++ b/include/fggl/gfx/interfaces.hpp
@@ -20,7 +20,7 @@
 #define FGGL_GFX_INTERFACES_HPP
 
 #include "fggl/gfx/paint.hpp"
-#include "fggl/ecs3/ecs.hpp"
+#include "fggl/entity/entity.hpp"
 #include "fggl/modules/module.hpp"
 
 namespace fggl::gfx {
@@ -43,7 +43,7 @@ namespace fggl::gfx {
 			virtual Bounds canvasBounds() = 0;
 			virtual void draw2D(const Paint &paint) = 0;
 
-			virtual void drawScene(ecs3::World&) = 0;
+			virtual void drawScene(entity::EntityManager&) = 0;
 	};
 
 
diff --git a/include/fggl/gfx/ogl/compat.hpp b/include/fggl/gfx/ogl/compat.hpp
index 3b9e3a280eebdb45a336bae7440668aeceda299a..71594771b19f7390b2ff6fe4d727f7d20850717e 100644
--- a/include/fggl/gfx/ogl/compat.hpp
+++ b/include/fggl/gfx/ogl/compat.hpp
@@ -31,7 +31,7 @@
 
 #include <fggl/gfx/common.hpp>
 #include <fggl/gfx/camera.hpp>
-#include <fggl/ecs/ecs.hpp>
+
 #include <utility>
 #include <fggl/input/camera_input.hpp>
 #include <fggl/data/heightmap.hpp>
@@ -43,7 +43,7 @@ namespace fggl::gfx {
 	//
 	// fake module support - allows us to still RAII
 	//
-	struct SceneUtils : ecs3::Module {
+	/*struct SceneUtils : ecs3::Module {
 
 		SceneUtils() = default;
 
@@ -65,7 +65,7 @@ namespace fggl::gfx {
 			types.make<fggl::input::FreeCamKeys>();
 		}
 
-	};
+	};*/
 
 }
 
diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp
index acda7312f443debd7e776882ac841dc1b8ff7951..4b5311e30281d651c41adb261f240b2d544f3af8 100644
--- a/include/fggl/gfx/ogl/renderer.hpp
+++ b/include/fggl/gfx/ogl/renderer.hpp
@@ -16,10 +16,9 @@
 #define FGGL_GFX_OGL_RENDERER_HPP
 
 #include <fggl/data/model.hpp>
-#include <fggl/ecs3/ecs.hpp>
+
 #include <fggl/gfx/ogl/backend.hpp>
 #include <fggl/gfx/ogl/shader.hpp>
-
 #include "fggl/gfx/ogl4/models.hpp"
 #include "fggl/gfx/ogl4/canvas.hpp"
 #include "fggl/gfx/ogl4/debug.hpp"
@@ -47,17 +46,6 @@ namespace fggl::gfx {
 		GlRenderType renderType = triangles;
 	};
 
-	class GlMeshRenderer {
-		public:
-			using token_t = GlRenderToken;
-
-			token_t upload(fggl::data::Mesh &mesh);
-
-			void render(ecs3::World &ecs, ecs3::entity_t camera, float dt);
-
-			float total;
-	};
-
 	/**
 	 * Class responsible for managing the OpenGL context.
 	 *
@@ -103,7 +91,7 @@ namespace fggl::gfx {
 			 *
 			 * @param world the world to render
 			 */
-			void drawScene(ecs3::World& world) override;
+			void drawScene(entity::EntityManager& world) override;
 
 			/**
 			 * Get the 2D canvas bounds.
diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp
index 73b1b447c5cee86ce616d285108de40df61e4006..aefb536be486e0f239ae69f8e8d072405720c1b8 100644
--- a/include/fggl/gfx/ogl/types.hpp
+++ b/include/fggl/gfx/ogl/types.hpp
@@ -263,7 +263,7 @@ namespace fggl::gfx::ogl {
 			 * @param textureUnit the texture unit to bind to
 			 */
 			inline void bind(unsigned int textureUnit) {
-				assert( 0 <= textureUnit && textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS );
+				assert( textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS );
 				glActiveTexture( GL_TEXTURE0 + textureUnit );
 				glBindTexture( (GLenum) m_type,  m_obj );
 			}
diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp
index fab456ebf757e291bec8ab9154cd708b7a54c353..1a722430ce5d49f5c6436cd1090950d61589b363 100644
--- a/include/fggl/gfx/ogl4/models.hpp
+++ b/include/fggl/gfx/ogl4/models.hpp
@@ -25,7 +25,6 @@
 #include "fggl/gfx/ogl/shader.hpp"
 #include "fggl/gfx/ogl/backend.hpp"
 #include "fggl/gfx/ogl/types.hpp"
-#include "fggl/ecs3/ecs.hpp"
 
 namespace fggl::gfx::ogl4 {
 
@@ -59,7 +58,7 @@ namespace fggl::gfx::ogl4 {
 			StaticModelRenderer& operator=(const StaticModelRenderer& other) = delete;
 			StaticModelRenderer& operator=(StaticModelRenderer&& other) = delete;
 
-			void render(ecs3::World& world) {
+			void render(entity::EntityManager& world) {
 				resolveModels(world);
 				renderModelsForward(world);
 			}
@@ -68,12 +67,12 @@ namespace fggl::gfx::ogl4 {
 			/**
 			 * Attach any missing rendering components to models.
 			 */
-			void resolveModels(ecs3::World& world);
+			void resolveModels(entity::EntityManager& world);
 
 			/**
 			 * Render all visible objects according to their render tokens.
 			 */
-			void renderModelsForward(const ecs3::World& world);
+			void renderModelsForward(const entity::EntityManager& world);
 
 			gfx::ShaderCache* m_shaders;
 			std::shared_ptr< ogl::Shader > m_phong;
diff --git a/include/fggl/gfx/ogl4/module.hpp b/include/fggl/gfx/ogl4/module.hpp
index 6dc9be3226fdbe14bacb6a383513e2256c8026f7..c9501b5fc6f705924dedcde027733fb16bfa2631 100644
--- a/include/fggl/gfx/ogl4/module.hpp
+++ b/include/fggl/gfx/ogl4/module.hpp
@@ -21,6 +21,8 @@
 
 #include <array>
 #include "fggl/modules/module.hpp"
+#include "fggl/entity/loader/loader.hpp"
+
 #include "fggl/gfx/interfaces.hpp"
 #include "fggl/gfx/setup.hpp"
 
@@ -34,24 +36,14 @@ namespace fggl::gfx {
 		constexpr static const std::array<modules::ModuleService, 1> provides = {
 			WindowGraphics::service
 		};
-		constexpr static const std::array<modules::ModuleService, 2> depends = {
+		constexpr static const std::array<modules::ModuleService, 3> depends = {
 			data::Storage::service,
-			gui::FontLibrary::service
+			gui::FontLibrary::service,
+			entity::EntityFactory::service
 		};
 		static const modules::ServiceFactory factory;
 	};
 
-	bool ogl4_factory(modules::ModuleService service, modules::Services& services) {
-		if (service == WindowGraphics::service) {
-			auto storage = services.get<data::Storage>();
-			auto font_library = services.get<gui::FontLibrary>();
-			services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, font_library);
-			return true;
-		}
-		return false;
-	}
-	const modules::ServiceFactory OpenGL4::factory = ogl4_factory;
-
 } //namespace fggl::gfx
 
 #endif //FGGL_GFX_OGL4_MODULE_HPP
diff --git a/include/fggl/gfx/ogl4/setup.hpp b/include/fggl/gfx/ogl4/setup.hpp
index 2da109c19afb012ecaca35dc742f471e16a0d396..9c0f64d766383dac31da418ffdad1c57fc10a113 100644
--- a/include/fggl/gfx/ogl4/setup.hpp
+++ b/include/fggl/gfx/ogl4/setup.hpp
@@ -47,9 +47,6 @@ namespace fggl::gfx::ogl4 {
 			gui::FontLibrary* m_fonts;
 	};
 
-	fggl::gfx::Graphics *WindowGraphics::create(display::Window &window) {
-		return new OpenGL4Backend(m_storage, m_fonts);
-	}
 
 } // namespace fggl::gfx::ogl4
 
diff --git a/include/fggl/gfx/phong.hpp b/include/fggl/gfx/phong.hpp
index e62773e9ae0b0be94194f14a305be95a34841bdb..0dc5f822d9f32cfabe5c4d54344e7194758a403e 100644
--- a/include/fggl/gfx/phong.hpp
+++ b/include/fggl/gfx/phong.hpp
@@ -20,11 +20,13 @@
 #define FGGL_GFX_PHONG_HPP
 
 #include "fggl/math/types.hpp"
+#include "fggl/util/guid.hpp"
 
 namespace fggl::gfx {
 
 	struct PhongMaterial {
 		constexpr static const char* name = "gfx::material";
+		constexpr static const util::GUID guid = util::make_guid("gfx::material");
 		math::vec3 emission;
 		math::vec3 ambient;
 		math::vec3 diffuse;
@@ -49,6 +51,7 @@ namespace fggl::gfx {
 
 	struct Light {
 		constexpr static const char* name = "gfx::light";
+		constexpr static const util::GUID guid = util::make_guid("gfx::light");
 		bool enabled;
 		bool local;
 		bool spot;
diff --git a/include/fggl/gui/widget.hpp b/include/fggl/gui/widget.hpp
index f40967bab2e0863138164e1729e4b6f0532241bf..217b31b072d683ae0f48dfb3d725a9c41c9f20ac 100644
--- a/include/fggl/gui/widget.hpp
+++ b/include/fggl/gui/widget.hpp
@@ -22,8 +22,17 @@
 
 namespace fggl::gui {
 
-	constexpr float RGB_MAX_VAL = 255.0F;
-	constexpr math::vec3 rgbToNormal(char red, char green, char blue) {
+	constexpr uint8_t RGB_MAX_VAL = 255;
+
+	/**
+	 * Convert an RGB value using 0-255 to a 0-1 value.
+	 *
+	 * @param red red component
+	 * @param green green component
+	 * @param blue blue component
+	 * @return the normalized RGB value
+	 */
+	constexpr math::vec3 rgbToNormal(uint8_t red, uint8_t green, uint8_t blue) {
 		return {red / RGB_MAX_VAL, green / RGB_MAX_VAL, blue / RGB_MAX_VAL};
 	}
 
@@ -41,7 +50,7 @@ namespace fggl::gui {
 		assert( 0 < value && value <= 1);
 
 		const float chroma = value * saturation;
-		const float x = chroma * (1 - fabs( fmod(hue / 60.0F, 2) - 1) );
+		const float x = chroma * (1 - std::fabs( std::fmod(hue / 60.0F, 2.0F) - 1.0F) );
 		math::vec3 tmp{0,0,0};
 		if ( 0 <= hue && hue < 60 ) {
 			tmp = {chroma,x,0};
diff --git a/include/fggl/input/camera_input.hpp b/include/fggl/input/camera_input.hpp
index db85605464e4d9021c2cbe19eb373f546a940e2f..6b549761e26a1f7abd897e79ed08c013e44a63cf 100644
--- a/include/fggl/input/camera_input.hpp
+++ b/include/fggl/input/camera_input.hpp
@@ -19,8 +19,8 @@
 #ifndef FGGL_INPUT_CAMERA_INPUT_HPP
 #define FGGL_INPUT_CAMERA_INPUT_HPP
 
-#include <fggl/ecs3/ecs.hpp>
-#include <fggl/math/types.hpp>
+#include "fggl/entity/entity.hpp"
+#include "fggl/math/types.hpp"
 
 namespace fggl::input {
 
@@ -38,7 +38,7 @@ namespace fggl::input {
 		scancode_t rotate_ccw;
 	};
 
-	void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom = 10.0F, float maxZoom = 50.0F);
+	void process_scroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam, float minZoom = 10.0F, float maxZoom = 50.0F);
 
 	/**
 	 * Process the camera based on rotation around a fixed point.
@@ -47,7 +47,7 @@ namespace fggl::input {
 	 * @param input the input module to read the mouse location from
 	 * @param cam the ID of the camera entity
 	 */
-	void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
+	void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
 
 	/**
 	 * Process free (floating) camera movement.
@@ -56,7 +56,7 @@ namespace fggl::input {
 	 * @param input the input module to read the mouse location from
 	 * @param cam the ID of the camera entity
 	 */
-	void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
+	void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
 
 	/**
 	 * Input processing for moving the camera when the mouse is close to the edge of the screen.
@@ -69,7 +69,7 @@ namespace fggl::input {
 	 * @param input the input module to read the mouse location from
 	 * @param cam the ID of the camera entity
 	 */
-	void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam);
+	void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam);
 
 }
 
diff --git a/include/fggl/input/gamepad.hpp b/include/fggl/input/gamepad.hpp
index 0242d7f77512aea486346b6e0f8e6b2b2e48f1aa..fc583361512301b66d9d1b4cf59fd20aa65243ba 100644
--- a/include/fggl/input/gamepad.hpp
+++ b/include/fggl/input/gamepad.hpp
@@ -192,7 +192,7 @@ namespace fggl::input {
 				return m_current[id].buttons[(int) btn] != m_previous[id].buttons[(int) btn];
 			}
 
-			inline void frame(float dt) {
+			inline void frame(float /*dt*/) {
 				m_previous = m_current;
 			}
 
diff --git a/include/fggl/input/keyboard.hpp b/include/fggl/input/keyboard.hpp
index 735cf66626f684f85bd4851199657a1ad9b1fcd7..aba2cee8ba29b17aa36579277c163004713046d0 100644
--- a/include/fggl/input/keyboard.hpp
+++ b/include/fggl/input/keyboard.hpp
@@ -52,7 +52,7 @@ namespace fggl::input {
 
 	class KeyboardInput {
 		public:
-			inline void frame(float dt) {
+			inline void frame(float /*dt*/) {
 				m_prev = m_curr;
 			}
 
diff --git a/include/fggl/input/mouse.hpp b/include/fggl/input/mouse.hpp
index 327d95c7b7111b6f858c8e2a41c765379a191713..f2900e1c1e8b240a91f1a6a477d8fa1b502387b9 100644
--- a/include/fggl/input/mouse.hpp
+++ b/include/fggl/input/mouse.hpp
@@ -80,7 +80,7 @@ namespace fggl::input {
 			MouseInput(const MouseInput &rhs) = delete;
 			void operator=(const MouseInput &rhs) = delete;
 
-			inline void frame(float dt) {
+			inline void frame(float /*dt*/) {
 				m_prev = m_curr;
 			}
 
diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp
index ff88aaed52d8318d43a63d398c1b3e9a31816535..14cc4782f477c618751e12607eba05ca434dac83 100644
--- a/include/fggl/math/types.hpp
+++ b/include/fggl/math/types.hpp
@@ -25,6 +25,8 @@
 #include <glm/gtx/euler_angles.hpp>
 #include <glm/gtx/quaternion.hpp>
 
+#include "fggl/util/guid.hpp"
+
 #ifndef M_PI
     #define M_PI 3.14159265358979323846
 #endif
@@ -119,7 +121,7 @@ namespace fggl::math {
 	};
 
 	struct Transform {
-			constexpr static const char name[] = "Transform";
+			constexpr static const util::GUID guid = util::make_guid("Transform");
 
 			Transform() :
 				m_model(IDENTITY_M4),
@@ -223,12 +225,12 @@ namespace fggl::math {
 	};
 
 
-	inline math::mat4 calc_view_matrix(const Transform* transform) {
-		return glm::lookAt(transform->origin(), transform->origin() + transform->forward(), transform->up());
+	inline math::mat4 calc_view_matrix(const Transform& transform) {
+		return glm::lookAt(transform.origin(), transform.origin() + transform.forward(), transform.up());
 	}
 
-	inline math::mat4 calc_view_matrix(const Transform* transform, vec3 target) {
-		return glm::lookAt(transform->origin(), target, transform->up());
+	inline math::mat4 calc_view_matrix(const Transform& transform, vec3 target) {
+		return glm::lookAt(transform.origin(), target, transform.up());
 	}
 
 }
diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp
index 1348fdd162496fded8fb269e9fce7c8cab56bf67..d804da6f368400025c2b8a2a1c13eaac1d94eb1d 100644
--- a/include/fggl/modules/manager.hpp
+++ b/include/fggl/modules/manager.hpp
@@ -120,7 +120,7 @@ namespace fggl::modules {
 			void use() {
 				assert( !m_locked );
 
-				Config config { .name = T::name };
+				Config config { .name = T::name, .provides = {}, .depends = {} };
 				for ( auto service : T::provides ) {
 					config.provides.push_back(service);
 				}
diff --git a/include/fggl/modules/module.hpp b/include/fggl/modules/module.hpp
index 176b3de87687a2945e1bb9b345dc1e0f2204d5f8..5ff03174b1f71d0250c08917eee17e5b71890525 100644
--- a/include/fggl/modules/module.hpp
+++ b/include/fggl/modules/module.hpp
@@ -45,8 +45,10 @@ namespace fggl::modules {
 			}
 
 			template<typename Svc, typename ...Args>
-			void create(Args... args){
-				m_services[Svc::service] = std::make_shared<Svc>(args...);
+			Svc* create(Args... args){
+				auto svc = std::make_shared<Svc>(args...);
+				m_services[Svc::service] = svc;
+				return svc.get();
 			}
 
 			template<typename Svc>
diff --git a/include/fggl/phys/callbacks.hpp b/include/fggl/phys/callbacks.hpp
index 9ff0eaf1b90dcf45099d212a066eb198cbdf4769..856be21bd24e42943694cf0ea69140f24a5a82cb 100644
--- a/include/fggl/phys/callbacks.hpp
+++ b/include/fggl/phys/callbacks.hpp
@@ -21,11 +21,11 @@
 
 #include <functional>
 #include <unordered_set>
-#include "fggl/ecs3/types.hpp"
+#include "fggl/entity/entity.hpp"
 
 namespace fggl::phys {
 
-	using CollisionCB = std::function<void(ecs3::entity_t, ecs3::entity_t)>;
+	using CollisionCB = std::function<void(entity::EntityID , entity::EntityID)>;
 
 	struct CollisionCallbacks {
 		constexpr static const char* name = "phys::Callbacks";
@@ -36,8 +36,8 @@ namespace fggl::phys {
 
 	struct CollisionCache {
 		constexpr static const char* name = "phys::Cache";
-		std::unordered_set<ecs3::entity_t> collisions;
-		std::unordered_set<ecs3::entity_t> lastFrame;
+		std::unordered_set<entity::EntityID> collisions;
+		std::unordered_set<entity::EntityID> lastFrame;
 	};
 
 } // namespace fggl::phys
diff --git a/include/fggl/phys/types.hpp b/include/fggl/phys/types.hpp
index 8d6bdee5989c67aa08eb0983c7646457e1612e10..e69c45aa5c3497a08ef163c50b4b782e9508e1d2 100644
--- a/include/fggl/phys/types.hpp
+++ b/include/fggl/phys/types.hpp
@@ -72,8 +72,8 @@ namespace fggl::phys {
 	};
 
 	struct ContactPoint {
-		ecs3::entity_t entityA;
-		ecs3::entity_t entityB;
+		entity::EntityID entityA;
+		entity::EntityID entityB;
 		math::vec3 localA;
 		math::vec3 localB;
 		math::vec3 normal;
@@ -104,16 +104,15 @@ namespace fggl::phys {
 			PhysicsEngine& operator=(PhysicsEngine&&) = delete;
 
 			// query methods (first cut - unstable APIs)
-			virtual std::vector<ContactPoint> scanCollisions(ecs3::entity_t entity) = 0;
-			virtual ecs3::entity_t raycast(math::vec3 from, math::vec3 to) = 0;
+			virtual std::vector<ContactPoint> scanCollisions(entity::EntityID entity) = 0;
 
-			inline ecs3::entity_t raycast(math::Ray ray, float maxDist = 1000.0F) {
+			virtual entity::EntityID raycast(math::vec3 from, math::vec3 to) = 0;
+			inline entity::EntityID raycast(math::Ray ray, float maxDist = 1000.0F) {
 				return raycast(ray.origin, ray.origin + ray.direction * maxDist);
 			}
 
-
-			virtual std::vector<ecs3::entity_t> raycastAll(math::vec3 from, math::vec3 to) = 0;
-			virtual std::vector<ecs3::entity_t> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) = 0;
+			virtual std::vector<entity::EntityID> raycastAll(math::vec3 from, math::vec3 to) = 0;
+			virtual std::vector<entity::EntityID> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) = 0;
 
 			// update
 			virtual void step() = 0;
diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp
index 59970604fc2a67dd6245462cac6b1a349403b069..2b1ad8145e206f7c7c2c2abfe5c7961ec41fde91 100644
--- a/include/fggl/scenes/game.hpp
+++ b/include/fggl/scenes/game.hpp
@@ -20,6 +20,7 @@
 #define FGGL_SCENES_GAME_HPP
 
 #include "fggl/app.hpp"
+#include "fggl/entity/entity.hpp"
 #include "fggl/phys/types.hpp"
 
 namespace fggl::scenes {
@@ -36,7 +37,7 @@ namespace fggl::scenes {
 			void render(fggl::gfx::Graphics& gfx) override;
 
 		protected:
-			inline auto world() -> ecs3::World& {
+			inline auto world() -> entity::EntityManager& {
 				return *m_world;
 			}
 
@@ -50,7 +51,7 @@ namespace fggl::scenes {
 
 		private:
 			input::Input* m_input;
-			std::unique_ptr<ecs3::World> m_world;
+			std::unique_ptr<entity::EntityManager> m_world;
 			std::unique_ptr<phys::PhysicsEngine> m_phys;
 			std::string m_previous = "menu";
 	};
diff --git a/include/fggl/util/guid.hpp b/include/fggl/util/guid.hpp
index c9c85b2db090d69700223911214b6ad3b982c683..5feca83726357effedc4d2417fe2cd7847c186a6 100644
--- a/include/fggl/util/guid.hpp
+++ b/include/fggl/util/guid.hpp
@@ -21,6 +21,8 @@
 #define FGGL_UTIL_GUID_HPP
 
 #include <cstdint>
+#include <cassert>
+
 #include "fggl/util/safety.hpp"
 
 namespace fggl::util {
@@ -64,13 +66,23 @@ namespace fggl::util {
 		return hash;
 	}
 
+	// debug-only functions
+	#ifndef NDEBUG
+		GUID internString(const char* str);
+		std::string guidToString(GUID guid);
+	#endif
+
 	constexpr GUID make_guid(const char* str) {
 		return GUID::make(hash_fnv1a_64(str));
 	}
 
-	// debug-only functions
-	GUID internString(const char* str);
-	std::string guidToString(GUID guid);
+	inline GUID make_guid_rt(const std::string& str) {
+		#ifndef NDEBUG
+			return internString(str.c_str());
+		#else
+			return make_guid(str.c_str());
+		#endif
+	}
 
 } // namespace fggl::util
 
diff --git a/include/fggl/util/safety.hpp b/include/fggl/util/safety.hpp
index 9b9c0d281644130ce83d7f8a8e30d20e3d056ffc..e11a5723555cc17669fa8b1c354d7032dee0c35e 100644
--- a/include/fggl/util/safety.hpp
+++ b/include/fggl/util/safety.hpp
@@ -26,7 +26,7 @@ namespace fggl::util {
 	/**
 	 * A type-safe opaque handle.
 	 *
-	 * Lots of low-level libaries we use pass around handles as some primative type. It's fairly easy to accidentally
+	 * Lots of low-level libraries we use pass around handles as some primative type. It's fairly easy to accidentally
 	 * mix these up. This wrapper's job is to make sure that mixing up handle types is impossible (and results in
 	 * compiler errors).
 	 *
@@ -39,7 +39,12 @@ namespace fggl::util {
 			T m_value;
 
 		public:
-			constexpr OpaqueName(T value) : m_value(value) {}
+			explicit constexpr OpaqueName(T value) : m_value(value) {}
+			constexpr OpaqueName() : m_value() {}
+
+			constexpr T get() const {
+				return m_value;
+			}
 
 			constexpr explicit operator std::string_view() const { return m_value; }
 
@@ -47,12 +52,20 @@ namespace fggl::util {
 				return m_value == other.m_value;
 			}
 
+			bool operator!=(const OpaqueName<T, Tag> &other) const {
+				return !(*this == other);
+			}
+
+			bool operator<(const OpaqueName<T, Tag> &other) const {
+				return m_value < other.m_value;
+			}
+
 			std::strong_ordering operator<=>(const OpaqueName<T, Tag> &other) const noexcept {
 				return m_value <=> other.m_value;
 			}
 
 			constexpr static OpaqueName<T, Tag> make(T value) {
-				return OpaqueName(value);
+				return OpaqueName<T, Tag>(value);
 			}
 	};
 
diff --git a/include/fggl/util/states.hpp b/include/fggl/util/states.hpp
index fa2c713047e4b38e53ff43107691bceb042a8846..e5b81ccf2f2778f7c2788e9b13b4c917c48c48c7 100644
--- a/include/fggl/util/states.hpp
+++ b/include/fggl/util/states.hpp
@@ -20,6 +20,8 @@
 #include <memory>
 #include <unordered_map>
 
+#include "fggl/debug/logging.hpp"
+
 namespace fggl::util {
 
 	template<typename S, typename I>
diff --git a/integrations/bullet/include/fggl/phys/bullet/bullet.hpp b/integrations/bullet/include/fggl/phys/bullet/bullet.hpp
index fb28ce6110466b8ad56781d6289649d91cdf5777..a65250a19b480c6083880e66d727aa369655baac 100644
--- a/integrations/bullet/include/fggl/phys/bullet/bullet.hpp
+++ b/integrations/bullet/include/fggl/phys/bullet/bullet.hpp
@@ -35,8 +35,6 @@
 
 #define FGGL_MODULE_BULLET fggl::phys::Bullet3
 
-#include "fggl/ecs3/module/module.hpp"
-
 #include "fggl/phys/types.hpp"
 #include "fggl/phys/bullet/types.hpp"
 
@@ -49,7 +47,7 @@ namespace fggl::phys::bullet {
 	 * engine which makes it suitable for most use cases. For use with FGGL there is a reasonable amount of copying
 	 * back and forth to keep the two states in sync.
 	 */
-	struct BulletModule : ecs3::Module {
+	/*struct BulletModule : ecs3::Module {
 		public:
 			BulletModule() = default;
 
@@ -58,7 +56,7 @@ namespace fggl::phys::bullet {
 				return "phys::Bullet3";
 			}
 
-			void onLoad(ecs3::ModuleManager & /*manager*/, ecs3::TypeRegistry &types) override {
+			void onLoad(ecs3::ModuleManager&, ecs3::TypeRegistry &types) override {
 				// dependencies
 				types.make<phys::CollisionCallbacks>();
 				types.make<phys::CollisionCache>();
@@ -70,14 +68,14 @@ namespace fggl::phys::bullet {
 				types.make<phys::bullet::BulletBody>();
 			}
 
-	};
+	};*/
 
 } // namespace fggl::phys::bullet
 
 namespace fggl::phys {
 
 	// allows using fggl::phys::Bullet3 as the module name
-	using Bullet3 = bullet::BulletModule;
+	//using Bullet3 = bullet::BulletModule;
 
 } // namespace fggl::phys
 
diff --git a/integrations/bullet/include/fggl/phys/bullet/motion.hpp b/integrations/bullet/include/fggl/phys/bullet/motion.hpp
index 5b0540a62bb29fe96827c8771855a71bc638c9a6..7a38a2209b45a573df623ed20a3ccd2c7e67d38e 100644
--- a/integrations/bullet/include/fggl/phys/bullet/motion.hpp
+++ b/integrations/bullet/include/fggl/phys/bullet/motion.hpp
@@ -39,31 +39,31 @@ namespace fggl::phys::bullet {
 
 	class FgglMotionState : public btMotionState {
 		public:
-			FgglMotionState(fggl::ecs3::World* world, fggl::ecs3::entity_t entity) : m_world(world), m_entity(entity) {
+			FgglMotionState(entity::EntityManager* world, entity::EntityID entity) : m_world(world), m_entity(entity) {
 			}
 			virtual ~FgglMotionState() = default;
 
 			void getWorldTransform(btTransform& worldTrans) const override {
-				const auto* transform = m_world->get<fggl::math::Transform>(m_entity);
-				worldTrans.setFromOpenGLMatrix( glm::value_ptr(transform->model()) );
+				const auto& transform = m_world->get<fggl::math::Transform>(m_entity);
+				worldTrans.setFromOpenGLMatrix( glm::value_ptr(transform.model()) );
 			}
 
 			void setWorldTransform(const btTransform& worldTrans) override {
-				auto* transform = m_world->get<fggl::math::Transform>(m_entity);
+				auto& transform = m_world->get<fggl::math::Transform>(m_entity);
 
 				// set position
 				auto btOrigin = worldTrans.getOrigin();
-				transform->origin( {btOrigin.x(), btOrigin.y(), btOrigin.z()} );
+				transform.origin( {btOrigin.x(), btOrigin.y(), btOrigin.z()} );
 
 				// set rotation
 				math::vec3 angles;
 				worldTrans.getRotation().getEulerZYX(angles.x, angles.y, angles.z);
-				transform->euler(angles);
+				transform.euler(angles);
 			}
 
 		private:
-			fggl::ecs3::World* m_world;
-			fggl::ecs3::entity_t m_entity;
+			entity::EntityManager* m_world;
+			entity::EntityID m_entity;
 	};
 
 } // namespace fggl::phys::bullet
diff --git a/integrations/bullet/include/fggl/phys/bullet/types.hpp b/integrations/bullet/include/fggl/phys/bullet/types.hpp
index 0809053ccd78d85f7bedf965bac26e8307aaef2d..4d6857d3c98c693992187a14d126c2ddcf1983b1 100644
--- a/integrations/bullet/include/fggl/phys/bullet/types.hpp
+++ b/integrations/bullet/include/fggl/phys/bullet/types.hpp
@@ -33,7 +33,6 @@
 #ifndef FGGL_PHYS_BULLET_TYPES_HPP
 #define FGGL_PHYS_BULLET_TYPES_HPP
 
-#include "fggl/ecs3/ecs.hpp"
 #include "fggl/phys/types.hpp"
 #include "phys_draw.hpp"
 
@@ -73,20 +72,20 @@ namespace fggl::phys::bullet {
 	 */
 	class BulletPhysicsEngine : public PhysicsEngine {
 		public:
-			explicit BulletPhysicsEngine(ecs3::World* world);
+			explicit BulletPhysicsEngine(entity::EntityManager* world);
 			~BulletPhysicsEngine() override;
 
 			void step() override;
-			void onEntityDeath(ecs::entity_t entity);
+			void onEntityDeath(entity::EntityID entity);
 
-			std::vector<ContactPoint> scanCollisions(ecs3::entity_t entity) override;
-			ecs3::entity_t raycast(math::vec3 from, math::vec3 to) override;
-			std::vector<ecs3::entity_t> raycastAll(math::vec3 from, math::vec3 to) override;
-			std::vector<ecs3::entity_t> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) override;
+			std::vector<ContactPoint> scanCollisions(entity::EntityID entity) override;
+			entity::EntityID raycast(math::vec3 from, math::vec3 to) override;
+			std::vector<entity::EntityID> raycastAll(math::vec3 from, math::vec3 to) override;
+			std::vector<entity::EntityID> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) override;
 
 			bool debugDraw = false;
 		private:
-			ecs3::World* m_ecs;
+			entity::EntityManager* m_ecs;
 			BulletConfiguration m_config;
 			btDiscreteDynamicsWorld* m_world;
 			std::unique_ptr<debug::BulletDebugDrawList> m_debug;
diff --git a/integrations/bullet/src/simulation.cpp b/integrations/bullet/src/simulation.cpp
index e4fbcbef75833e21509fcaa12647709ea902f33f..4ee06d6d197a63e595041cc5e6e177db6d065886 100644
--- a/integrations/bullet/src/simulation.cpp
+++ b/integrations/bullet/src/simulation.cpp
@@ -35,7 +35,7 @@ namespace fggl::phys::bullet {
 		return {fgglVec.x, fgglVec.y, fgglVec.z};
 	}
 
-	BulletPhysicsEngine::BulletPhysicsEngine(ecs3::World* world) : m_ecs(world), m_config(), m_world(nullptr) {
+	BulletPhysicsEngine::BulletPhysicsEngine(entity::EntityManager* world) : m_ecs(world), m_config(), m_world(nullptr) {
 		m_config.broadphase = new btDbvtBroadphase();
 		m_config.collisionConfiguration = new btDefaultCollisionConfiguration();
 		m_config.dispatcher = new btCollisionDispatcher(m_config.collisionConfiguration);
@@ -54,30 +54,26 @@ namespace fggl::phys::bullet {
 		// callbacks (for handling bullet -> ecs)
 
 		// ensure we deal with ecs -> bullet changes
-		m_ecs->addDeathListener( [this](auto entity) { this->onEntityDeath(entity);} );
+		//m_ecs->addDeathListener( [this](auto entity) { this->onEntityDeath(entity);} );
 	}
 
 	void BulletPhysicsEngine::step() {
 		checkForPhys();
 
-		auto dynamicEnts = m_ecs->findMatching<phys::Dynamics>();
+		auto dynamicEnts = m_ecs->find<phys::Dynamics, phys::bullet::BulletBody>();
 		for (auto& ent : dynamicEnts) {
-			auto* dynamicComp = m_ecs->get<phys::Dynamics>(ent);
-			auto* bulletProxy = m_ecs->get<phys::bullet::BulletBody>(ent);
-			if ( bulletProxy == nullptr ) {
-				std::cerr << "entity has dynamics but isn't a physics object!" << std::endl;
-				continue;
-			}
+			auto& dynamicComp = m_ecs->get<phys::Dynamics>(ent);
+			auto& bulletProxy = m_ecs->get<phys::bullet::BulletBody>(ent);
 
-			const auto forceVec = dynamicComp->force;
+			const auto forceVec = dynamicComp.force;
 			if ( glm::length(forceVec) != 0.F ) {
-				bulletProxy->body->applyCentralForce({forceVec.x, forceVec.y, forceVec.z});
-				bulletProxy->body->activate();
-				dynamicComp->force = math::VEC3_ZERO;
+				bulletProxy.body->applyCentralForce({forceVec.x, forceVec.y, forceVec.z});
+				bulletProxy.body->activate();
+				dynamicComp.force = math::VEC3_ZERO;
 			}
 		}
 
-		m_world->stepSimulation(60.0f);
+		m_world->stepSimulation(60.0F);
 		//syncToECS();
 		pollCollisions();
 
@@ -86,19 +82,19 @@ namespace fggl::phys::bullet {
 		}
 	}
 
-	inline btCollisionShape* shape_to_bullet(const phys::RigidBody* fgglBody) {
-		if ( fgglBody->shape == nullptr ) {
+	inline btCollisionShape* shape_to_bullet(const phys::RigidBody& fgglBody) {
+		if ( fgglBody.shape == nullptr ) {
 			// they forgot to put a shape, we'll assume a unit sphere to avoid crashes...
 			return new btSphereShape(0.5f);
 		}
 
-		switch (fgglBody->shape->type) {
+		switch (fgglBody.shape->type) {
 			case phys::ShapeType::BOX: {
-				auto extents = ((phys::Box*)fgglBody->shape)->extents;
+				auto extents = ((phys::Box*)fgglBody.shape)->extents;
 				return new btBoxShape({extents.x, extents.y, extents.z});
 			}
 			case phys::ShapeType::SPHERE: {
-				float radius = ((phys::Sphere*)fgglBody->shape)->radius;
+				float radius = ((phys::Sphere*)fgglBody.shape)->radius;
 				return new btSphereShape(radius);
 			}
 			default:
@@ -108,7 +104,7 @@ namespace fggl::phys::bullet {
 
 	void BulletPhysicsEngine::checkForPhys() {
 		// FIXME without reactive-based approaches this is very slow
-		auto entsWantPhys = m_ecs->findMatching<phys::RigidBody>();
+		auto entsWantPhys = m_ecs->find<phys::RigidBody>();
 		for (auto ent : entsWantPhys) {
 			auto* btComp = m_ecs->tryGet<BulletBody>(ent);
 			if ( btComp != nullptr ) {
@@ -117,20 +113,20 @@ namespace fggl::phys::bullet {
 			}
 
 			// set up the bullet proxy for our object
-			btComp = m_ecs->add<BulletBody>(ent);
-			const auto* fgBody = m_ecs->get<phys::RigidBody>(ent);
+			btComp = &m_ecs->add<BulletBody>(ent);
+			const auto& fgBody = m_ecs->get<phys::RigidBody>(ent);
 			btComp->motion = new FgglMotionState(m_ecs, ent);
 
 			// collisions
 			btCollisionShape* colShape = shape_to_bullet(fgBody);
 			btVector3 localInt(0, 0, 0);
-			if ( !fgBody->isStatic() ) {
-				colShape->calculateLocalInertia(fgBody->mass, localInt);
+			if ( !fgBody.isStatic() ) {
+				colShape->calculateLocalInertia(fgBody.mass, localInt);
 			}
 
 			// setup the construction information
 			btRigidBody::btRigidBodyConstructionInfo bodyCI(
-				fgBody->mass,
+				fgBody.mass,
 				btComp->motion,
 				colShape,
 				localInt
@@ -139,12 +135,12 @@ namespace fggl::phys::bullet {
 			btComp->body->setUserIndex( static_cast<int>(ent) );
 
 
-			if (!fgBody->isStatic()) {
+			if (!fgBody.isStatic()) {
 				btComp->body->setRestitution(0.5f);
 				btComp->body->setRollingFriction(0.0f);
 			}
 
-			if ( fgBody->type == BodyType::KINEMATIC ) {
+			if ( fgBody.type == BodyType::KINEMATIC ) {
 				auto flags = btComp->body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT;
 				btComp->body->setCollisionFlags( flags );
 
@@ -158,42 +154,42 @@ namespace fggl::phys::bullet {
 	}
 
 	void BulletPhysicsEngine::forceSyncToECS() {
-		const auto physEnts = m_ecs->findMatching<BulletBody>();
+		const auto physEnts = m_ecs->find<math::Transform, BulletBody>();
 		for (const auto& ent : physEnts) {
-			auto* transform = m_ecs->get<math::Transform>(ent);
-			auto* physBody = m_ecs->get<BulletBody>(ent);
+			auto& transform = m_ecs->get<math::Transform>(ent);
+			auto& physBody = m_ecs->get<BulletBody>(ent);
 
-			if ( physBody->body->isKinematicObject() ) {
+			if ( physBody.body->isKinematicObject() ) {
 				continue;
 			}
 
 			btTransform bTransform;
-			physBody->motion->getWorldTransform(bTransform);
+			physBody.motion->getWorldTransform(bTransform);
 
 			// set calculate and set ecs position
 			auto& btPos = bTransform.getOrigin();
 			math::vec3 pos{btPos.x(), btPos.y(), btPos.z()};
-			transform->origin( pos );
+			transform.origin( pos );
 
 			// set the rotation
 			const auto& btRot = bTransform.getRotation();
 
 			math::vec3 rot;
 			btRot.getEulerZYX(rot.x, rot.y, rot.z);
-			transform->euler(rot);
+			transform.euler(rot);
 		}
 	}
 
 	BulletPhysicsEngine::~BulletPhysicsEngine() {
 		// clean up the rigid bodies
-		auto entRB = m_ecs->findMatching<BulletBody>();
-		for (auto& ent : entRB) {
-			auto* bulletBody = m_ecs->get<BulletBody>(ent);
-			m_world->removeCollisionObject(bulletBody->body);
+		auto entRB = m_ecs->find<BulletBody>();
+		for (const auto& ent : entRB) {
+			auto& bulletBody = m_ecs->get<BulletBody>(ent);
+			m_world->removeCollisionObject(bulletBody.body);
 
 			// release resources and delete
-			bulletBody->release();
-			m_ecs->remove<BulletBody>(ent);
+			bulletBody.release();
+			//m_ecs->remove<BulletBody>(ent);
 		}
 
 		// delete the world
@@ -206,7 +202,7 @@ namespace fggl::phys::bullet {
 		delete m_config.collisionConfiguration;
 	}
 
-	static void handleCollisionCallbacks(ecs3::World* world, ecs3::entity_t owner, ecs3::entity_t other) {
+	static void handleCollisionCallbacks(entity::EntityManager* world, entity::EntityID owner, entity::EntityID other) {
 		if ( !world->has<CollisionCallbacks>(owner) ) {
 			return;
 		}
@@ -230,11 +226,11 @@ namespace fggl::phys::bullet {
 
 	void BulletPhysicsEngine::pollCollisions() {
 		// flush collision caches
-		auto caches = m_ecs->findMatching<CollisionCache>();
-		for( auto& ent : caches) {
-			auto* cache = m_ecs->get<CollisionCache>(ent);
-			std::swap(cache->collisions, cache->lastFrame);
-			cache->collisions.clear();
+		auto caches = m_ecs->find<CollisionCache>();
+		for( const auto& ent : caches) {
+			auto& cache = m_ecs->get<CollisionCache>(ent);
+			std::swap(cache.collisions, cache.lastFrame);
+			cache.collisions.clear();
 		}
 
 		// deal with the number of manifolds
@@ -245,15 +241,6 @@ namespace fggl::phys::bullet {
 			const auto* body0 = contactManifold->getBody0();
 			const auto* body1 = contactManifold->getBody1();
 
-			// FIXME rigid body didn't die according to plan!
-			if ( body0->getUserIndex() == ecs::NULL_ENTITY || body1->getUserIndex() == ecs::NULL_ENTITY) {
-				continue;
-			}
-
-			if ( !m_ecs->alive( body0->getUserIndex()) || !m_ecs->alive(body1->getUserIndex()) ) {
-				continue;
-			}
-
 			int numContacts = contactManifold->getNumContacts();
 			for ( auto contactIdx = 0; contactIdx < numContacts; ++contactIdx ) {
 				auto& point = contactManifold->getContactPoint(contactIdx);
@@ -264,14 +251,14 @@ namespace fggl::phys::bullet {
 				}
 			}
 
-			handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex());
-			handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex());
+			//handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex());
+			//handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex());
 		}
 
 		// note contacts that have ended
-		caches = m_ecs->findMatching<CollisionCache>(); // re-fetch, entities can (and probably do) die in handlers.
-		for( auto& ent : caches) {
-			auto* cache = m_ecs->get<CollisionCache>(ent);
+		caches = m_ecs->find<CollisionCache>(); // re-fetch, entities can (and probably do) die in handlers.
+		for( const auto& ent : caches) {
+			auto& cache = m_ecs->get<CollisionCache>(ent);
 
 			// if we don't have an exit callback, it's pointless checking the cache
 			auto* callbacks = m_ecs->tryGet<CollisionCallbacks>(ent);
@@ -280,20 +267,20 @@ namespace fggl::phys::bullet {
 			}
 
 			// check if a collision has ended
-			for (const auto& other : cache->lastFrame) {
-				auto itr = cache->collisions.find(other);
-				if (itr == cache->collisions.end()) {
+			for (const auto& other : cache.lastFrame) {
+				auto itr = cache.collisions.find(other);
+				if (itr == cache.collisions.end()) {
 					callbacks->onExit( ent,other);
 				}
 			}
 		}
 	}
 
-	void BulletPhysicsEngine::onEntityDeath(ecs::entity_t entity)  {
+	void BulletPhysicsEngine::onEntityDeath(entity::EntityID entity)  {
 			auto* btPhysics = m_ecs->tryGet<BulletBody>(entity);
 			if ( btPhysics != nullptr) {
 				// decouple physics from entity
-				btPhysics->body->setUserIndex( ecs::NULL_ENTITY );
+				//btPhysics->body->setUserIndex( entity::INVALID );
 				m_world->removeRigidBody( btPhysics->body );
 				btPhysics->release();
 			}
@@ -311,7 +298,7 @@ namespace fggl::phys::bullet {
 		m_contactCache.removed.push_back(point);
 	}
 
-	ecs3::entity_t BulletPhysicsEngine::raycast(math::vec3 from, math::vec3 to) {
+	entity::EntityID BulletPhysicsEngine::raycast(math::vec3 from, math::vec3 to) {
 		const auto btFrom = to_bullet(from);
 		const auto btTo = to_bullet(to);
 
@@ -319,18 +306,19 @@ namespace fggl::phys::bullet {
 		m_world->rayTest(btFrom, btTo, rayTestResult);
 
 		if ( !rayTestResult.hasHit() ) {
-			return ecs3::NULL_ENTITY;
+			return entity::INVALID;
 		}
 
 		// tell the user what it hit
-		return rayTestResult.m_collisionObject->getUserIndex();
+		return entity::INVALID;
+//		return rayTestResult.m_collisionObject->getUserIndex();
 	}
 
-	std::vector<ContactPoint> BulletPhysicsEngine::scanCollisions(ecs3::entity_t entity) {
+	std::vector<ContactPoint> BulletPhysicsEngine::scanCollisions(entity::EntityID entity) {
 		return std::vector<ContactPoint>();
 	}
 
-	std::vector<ecs3::entity_t> BulletPhysicsEngine::raycastAll(math::vec3 fromPos, math::vec3 toPos) {
+	std::vector<entity::EntityID> BulletPhysicsEngine::raycastAll(math::vec3 fromPos, math::vec3 toPos) {
 		const auto btFrom = to_bullet(fromPos);
 		const auto btTo = to_bullet(toPos);
 
@@ -345,21 +333,21 @@ namespace fggl::phys::bullet {
 		// hit processing
 		const auto hits = rayTestResult.m_collisionObjects.size();
 
-		std::vector<ecs3::entity_t> hitVec;
+		std::vector<entity::EntityID> hitVec;
 		hitVec.reserve(hits);
 
 		for ( auto i = 0; i < hits; ++i) {
-			ecs3::entity_t entity = rayTestResult.m_collisionObjects[i]->getUserIndex();
-			hitVec.push_back(entity);
+			auto entity = rayTestResult.m_collisionObjects[i]->getUserIndex();
+			//hitVec.push_back(entity);
 		}
 
 		return hitVec;
 	}
 
-	std::vector<ecs3::entity_t> BulletPhysicsEngine::sweep(PhyShape &shape,
+	std::vector<entity::EntityID> BulletPhysicsEngine::sweep(PhyShape &shape,
 														   math::Transform &from,
 														   math::Transform &to) {
-		return std::vector<ecs3::entity_t>();
+		return std::vector<entity::EntityID>();
 	}
 
 } // namespace fggl::phys::bullet
\ No newline at end of file
diff --git a/integrations/entt/CMakeLists.txt b/integrations/entt/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ed4cc62be5ae900a326bfc8f9c2690bd37894756
--- /dev/null
+++ b/integrations/entt/CMakeLists.txt
@@ -0,0 +1,5 @@
+target_include_directories( fggl
+        PUBLIC
+            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
+            $<INSTALL_INTERFACE:include>
+)
diff --git a/integrations/entt/include/fggl/vendor/entt.hpp b/integrations/entt/include/fggl/vendor/entt.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1523ae01d26c5380182977515d00162d8dce92a
--- /dev/null
+++ b/integrations/entt/include/fggl/vendor/entt.hpp
@@ -0,0 +1,66129 @@
+// #include "config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "config/macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+// #include "config/version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+// #include "container/dense_map.hpp"
+#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+
+namespace entt {
+
+template<
+typename Key,
+typename Type,
+typename = std::hash<Key>,
+typename = std::equal_to<Key>,
+typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+typename Type,
+typename = std::hash<Type>,
+typename = std::equal_to<Type>,
+typename = std::allocator<Type>>
+class dense_set;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+using value_type = std::pair<Key, Type>;
+
+template<typename... Args>
+dense_map_node(const std::size_t pos, Args &&...args)
+: next{pos},
+element{std::forward<Args>(args)...} {}
+
+template<typename Allocator, typename... Args>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+: next{pos},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+std::size_t next;
+value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+template<typename>
+friend class dense_map_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_map_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_map_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_map_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_map_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_map_iterator operator--(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_map_iterator copy = *this;
+return (copy += value);
+}
+
+dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].element.first, it[value].element.second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->element.first, it->element.second};
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+template<typename>
+friend class dense_map_local_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].next, *this;
+}
+
+dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it[offset].element.first, it[offset].element.second};
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = internal::dense_map_node<Key, Type>;
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(key), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other, typename... Args>
+[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+template<typename Other, typename Arg>
+[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+it->second = std::forward<Arg>(value);
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
+for(; *curr != last; curr = &packed.first()[*curr].next) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Key;
+/*! @brief Mapped type of the container. */
+using mapped_type = Type;
+/*! @brief Key-value type of the container. */
+using value_type = std::pair<const Key, Type>;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the keys. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the keys for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Input iterator type. */
+using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_map()
+: dense_map(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const allocator_type &allocator)
+: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const allocator_type &allocator)
+: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_map{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_map(const dense_map &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_map(const dense_map &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_map(dense_map &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_map(dense_map &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(const dense_map &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(dense_map &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if the key does not exist.
+     * @param value A key-value pair eventually convertible to the value type.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value.first, value.second);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+}
+
+/**
+     * @copydoc insert
+     * @tparam Arg Type of the key-value pair to insert into the container.
+     */
+template<typename Arg>
+std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+insert(Arg &&value) {
+return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+}
+
+/**
+     * @brief Inserts elements into the container, if their keys do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Inserts an element into the container or assigns to the current
+     * element if the key already exists.
+     * @tparam Arg Type of the value to insert or assign.
+     * @param key A key used both to look up and to insert if not found.
+     * @param value A value to insert or assign.
+     * @return A pair consisting of an iterator to the element and a bool
+     * denoting whether the insertion took place.
+     */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+return insert_or_overwrite(key, std::forward<Arg>(value));
+}
+
+/*! @copydoc insert_or_assign */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+}
+
+/**
+     * @brief Constructs an element in-place, if the key does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+if constexpr(sizeof...(Args) == 0u) {
+return insert_or_do_nothing(key_type{});
+} else if constexpr(sizeof...(Args) == 1u) {
+return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+} else if constexpr(sizeof...(Args) == 2u) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+const auto index = key_to_bucket(node.element.first);
+
+if(auto it = constrained_find(node.element.first, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.next, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Inserts in-place if the key does not exist, does nothing if the
+     * key exists.
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param key A key used both to look up and to insert if not found.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+return insert_or_do_nothing(key, std::forward<Args>(args)...);
+}
+
+/*! @copydoc try_emplace */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(pos->first);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].element.first);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given key.
+     * @param key A key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const key_type &key) {
+for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+if(packed.second()(packed.first()[*curr].element.first, key)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].next;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_map &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Accesses a given element with bounds checking.
+     * @param key A key of an element to find.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &at(const key_type &key) {
+auto it = find(key);
+ENTT_ASSERT(it != end(), "Invalid key");
+return it->second;
+}
+
+/*! @copydoc at */
+[[nodiscard]] const mapped_type &at(const key_type &key) const {
+auto it = find(key);
+ENTT_ASSERT(it != cend(), "Invalid key");
+return it->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](const key_type &key) {
+return insert_or_do_nothing(key).first->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](key_type &&key) {
+return insert_or_do_nothing(std::move(key)).first->second;
+}
+
+/**
+     * @brief Finds an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const key_type &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const key_type &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Finds an element with a key that compares _equivalent_ to a given
+     * value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const key_type &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element with a key that
+     * compares _equivalent_ to a given value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given key.
+     * @param key The value of the key to examine.
+     * @return The bucket for the given key.
+     */
+[[nodiscard]] size_type bucket(const key_type &key) const {
+return key_to_bucket(key);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = key_to_bucket(packed.first()[pos].element.first);
+packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the keys.
+     * @return The function used to hash the keys.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare keys for equality.
+     * @return The function used to compare keys for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+: std::true_type {};
+
+} // namespace std
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "container/dense_set.hpp"
+#ifndef ENTT_CONTAINER_DENSE_SET_HPP
+#define ENTT_CONTAINER_DENSE_SET_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/compressed_pair.hpp"
+
+// #include "../core/memory.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class dense_set_iterator final {
+template<typename>
+friend class dense_set_iterator;
+
+public:
+using value_type = typename It::value_type::second_type;
+using pointer = const value_type *;
+using reference = const value_type &;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::random_access_iterator_tag;
+
+dense_set_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_set_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_set_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_set_iterator operator++(int) ENTT_NOEXCEPT {
+dense_set_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_set_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_set_iterator operator--(int) ENTT_NOEXCEPT {
+dense_set_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_set_iterator copy = *this;
+return (copy += value);
+}
+
+dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return it[value].second;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return std::addressof(it->second);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_set_local_iterator final {
+template<typename>
+friend class dense_set_local_iterator;
+
+public:
+using value_type = typename It::value_type::second_type;
+using pointer = const value_type *;
+using reference = const value_type &;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::forward_iterator_tag;
+
+dense_set_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_set_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].first, *this;
+}
+
+dense_set_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_set_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return std::addressof(it[offset].second);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for unique objects of a given type.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on its hash. Elements with the same hash code
+ * appear in the same bucket.
+ *
+ * @tparam Type Value type of the associative container.
+ * @tparam Hash Type of function to use to hash the values.
+ * @tparam KeyEqual Type of function to use to compare the values for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_set {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = std::pair<std::size_t, Type>;
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(value), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(*it, value)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(*it, value)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other>
+[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
+const auto index = value_to_bucket(value);
+
+if(auto it = constrained_find(value, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
+for(; *curr != last; curr = &packed.first()[*curr].first) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Type;
+/*! @brief Value type of the container. */
+using value_type = Type;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the elements. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the elements for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Random access iterator type. */
+using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant random access iterator type. */
+using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Forward iterator type. */
+using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant forward iterator type. */
+using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_set()
+: dense_set(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_set(const allocator_type &allocator)
+: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_set(const size_type bucket_count, const allocator_type &allocator)
+: dense_set{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_set{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_set(const dense_set &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_set(const dense_set &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_set(dense_set &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_set(dense_set &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_set &operator=(const dense_set &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_set &operator=(dense_set &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if it does not exist.
+     * @param value An element to insert into the container.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value));
+}
+
+/**
+     * @brief Inserts elements into the container, if they do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Constructs an element in-place, if it does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace(Args &&...args) {
+if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, value_type>)) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
+const auto index = value_to_bucket(node.second);
+
+if(auto it = constrained_find(node.second, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.first, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(*pos);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].second);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given value.
+     * @param value Value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const value_type &value) {
+for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
+if(packed.second()(packed.first()[*curr].second, value)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].first;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_set &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Finds an element with a given value.
+     * @param value Value of an element to search for.
+     * @return An iterator to an element with the given value. If no such
+     * element is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const value_type &value) {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const value_type &value) const {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/**
+     * @brief Finds an element that compares _equivalent_ to a given value.
+     * @tparam Other Type of an element to search for.
+     * @param value Value of an element to search for.
+     * @return An iterator to an element with the given value. If no such
+     * element is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &value) {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &value) const {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given value.
+     * @param value Value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const value_type &value) const {
+return (find(value) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element that compares
+     * _equivalent_ to a given value.
+     * @tparam Other Type of an element to search for.
+     * @param value Value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &value) const {
+return (find(value) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given element.
+     * @param value The value of the element to examine.
+     * @return The bucket for the given element.
+     */
+[[nodiscard]] size_type bucket(const value_type &value) const {
+return value_to_bucket(value);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = value_to_bucket(packed.first()[pos].second);
+packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the elements.
+     * @return The function used to hash the elements.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare elements for equality.
+     * @return The function used to compare elements for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "core/algorithm.hpp"
+#ifndef ENTT_CORE_ALGORITHM_HPP
+#define ENTT_CORE_ALGORITHM_HPP
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <utility>
+#include <vector>
+// #include "utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Function object to wrap `std::sort` in a class type.
+ *
+ * Unfortunately, `std::sort` cannot be passed as template argument to a class
+ * template or a function template.<br/>
+ * This class fills the gap by wrapping some flavors of `std::sort` in a
+ * function object.
+ */
+struct std_sort {
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given binary comparison function.
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Args Types of arguments to forward to the sort function.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param compare A valid comparison function object.
+     * @param args Arguments to forward to the sort function, if any.
+     */
+template<typename It, typename Compare = std::less<>, typename... Args>
+void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const {
+std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
+}
+};
+
+/*! @brief Function object for performing insertion sort. */
+struct insertion_sort {
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given binary comparison function.
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Compare Type of comparison function object.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param compare A valid comparison function object.
+     */
+template<typename It, typename Compare = std::less<>>
+void operator()(It first, It last, Compare compare = Compare{}) const {
+if(first < last) {
+for(auto it = first + 1; it < last; ++it) {
+auto value = std::move(*it);
+auto pre = it;
+
+for(; pre > first && compare(value, *(pre - 1)); --pre) {
+*pre = std::move(*(pre - 1));
+}
+
+*pre = std::move(value);
+}
+}
+}
+};
+
+/**
+ * @brief Function object for performing LSD radix sort.
+ * @tparam Bit Number of bits processed per pass.
+ * @tparam N Maximum number of bits to sort.
+ */
+template<std::size_t Bit, std::size_t N>
+struct radix_sort {
+static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
+
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given _getter_ to access the
+     * actual data to be sorted.
+     *
+     * This implementation is inspired by the online book
+     * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort).
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Getter Type of _getter_ function object.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param getter A valid _getter_ function object.
+     */
+template<typename It, typename Getter = identity>
+void operator()(It first, It last, Getter getter = Getter{}) const {
+if(first < last) {
+static constexpr auto mask = (1 << Bit) - 1;
+static constexpr auto buckets = 1 << Bit;
+static constexpr auto passes = N / Bit;
+
+using value_type = typename std::iterator_traits<It>::value_type;
+std::vector<value_type> aux(std::distance(first, last));
+
+auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
+std::size_t index[buckets]{};
+std::size_t count[buckets]{};
+
+for(auto it = from; it != to; ++it) {
+++count[(getter(*it) >> start) & mask];
+}
+
+for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
+index[pos + 1u] = index[pos] + count[pos];
+}
+
+for(auto it = from; it != to; ++it) {
+out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
+}
+};
+
+for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
+part(first, last, aux.begin(), pass * Bit);
+part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
+}
+
+if constexpr(passes & 1) {
+part(first, last, aux.begin(), (passes - 1) * Bit);
+std::move(aux.begin(), aux.end(), first);
+}
+}
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "core/any.hpp"
+#ifndef ENTT_CORE_ANY_HPP
+#define ENTT_CORE_ANY_HPP
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief A SBO friendly, type-safe container for single values of any type.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ */
+template<std::size_t Len, std::size_t Align>
+class basic_any {
+enum class operation : std::uint8_t {
+copy,
+move,
+transfer,
+assign,
+destroy,
+compare,
+get
+};
+
+enum class policy : std::uint8_t {
+owner,
+ref,
+cref
+};
+
+using storage_type = std::aligned_storage_t<Len + !Len, Align>;
+using vtable_type = const void *(const operation, const basic_any &, const void *);
+
+template<typename Type>
+static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+
+template<typename Type>
+static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
+static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+const Type *element = nullptr;
+
+if constexpr(in_situ<Type>) {
+element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
+} else {
+element = static_cast<const Type *>(value.instance);
+}
+
+switch(op) {
+case operation::copy:
+if constexpr(std::is_copy_constructible_v<Type>) {
+static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
+}
+break;
+case operation::move:
+if constexpr(in_situ<Type>) {
+if(value.owner()) {
+return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
+}
+}
+
+return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
+case operation::transfer:
+if constexpr(std::is_move_assignable_v<Type>) {
+*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
+return other;
+}
+[[fallthrough]];
+case operation::assign:
+if constexpr(std::is_copy_assignable_v<Type>) {
+*const_cast<Type *>(element) = *static_cast<const Type *>(other);
+return other;
+}
+break;
+case operation::destroy:
+if constexpr(in_situ<Type>) {
+element->~Type();
+} else if constexpr(std::is_array_v<Type>) {
+delete[] element;
+} else {
+delete element;
+}
+break;
+case operation::compare:
+if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
+return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
+} else {
+return (element == other) ? other : nullptr;
+}
+case operation::get:
+return element;
+}
+
+return nullptr;
+}
+
+template<typename Type, typename... Args>
+void initialize([[maybe_unused]] Args &&...args) {
+if constexpr(!std::is_void_v<Type>) {
+info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+
+if constexpr(std::is_lvalue_reference_v<Type>) {
+static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
+mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
+instance = (std::addressof(args), ...);
+} else if constexpr(in_situ<Type>) {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+new(&storage) Type{std::forward<Args>(args)...};
+} else {
+new(&storage) Type(std::forward<Args>(args)...);
+}
+} else {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+instance = new Type{std::forward<Args>(args)...};
+} else {
+instance = new Type(std::forward<Args>(args)...);
+}
+}
+}
+}
+
+basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
+: instance{other.data()},
+info{other.info},
+vtable{other.vtable},
+mode{pol} {}
+
+public:
+/*! @brief Size of the internal storage. */
+static constexpr auto length = Len;
+/*! @brief Alignment requirement. */
+static constexpr auto alignment = Align;
+
+/*! @brief Default constructor. */
+constexpr basic_any() ENTT_NOEXCEPT
+: instance{},
+info{&type_id<void>()},
+vtable{},
+mode{policy::owner} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
+: basic_any{} {
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
+basic_any(Type &&value)
+: basic_any{} {
+initialize<std::decay_t<Type>>(std::forward<Type>(value));
+}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+basic_any(const basic_any &other)
+: basic_any{} {
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_any(basic_any &&other) ENTT_NOEXCEPT
+: instance{},
+info{other.info},
+vtable{other.vtable},
+mode{other.mode} {
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+}
+}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~basic_any() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This any object.
+     */
+basic_any &operator=(const basic_any &other) {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This any object.
+     */
+basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+info = other.info;
+vtable = other.vtable;
+mode = other.mode;
+}
+
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/**
+     * @brief Returns the object type if any, `type_id<void>()` otherwise.
+     * @return The object type if any, `type_id<void>()` otherwise.
+     */
+[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Replaces the contained object by creating a new instance directly.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+reset();
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+bool assign(const any &other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+return (vtable(operation::assign, *this, other.data()) != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc assign */
+bool assign(any &&other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+if(auto *val = other.data(); val) {
+return (vtable(operation::transfer, *this, val) != nullptr);
+} else {
+return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
+}
+}
+
+return false;
+}
+
+/*! @brief Destroys contained object */
+void reset() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+
+info = &type_id<void>();
+vtable = nullptr;
+mode = policy::owner;
+}
+
+/**
+     * @brief Returns false if a wrapper is empty, true otherwise.
+     * @return False if the wrapper is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return vtable != nullptr;
+}
+
+/**
+     * @brief Checks if two wrappers differ in their content.
+     * @param other Wrapper with which to compare.
+     * @return False if the two objects differ in their content, true otherwise.
+     */
+bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
+if(vtable && *info == *other.info) {
+return (vtable(operation::compare, *this, other.data()) != nullptr);
+}
+
+return (!vtable && !other.vtable);
+}
+
+/**
+     * @brief Aliasing constructor.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
+return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
+}
+
+/*! @copydoc as_ref */
+[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
+return basic_any{*this, policy::cref};
+}
+
+/**
+     * @brief Returns true if a wrapper owns its object, false otherwise.
+     * @return True if the wrapper owns its object, false otherwise.
+     */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return (mode == policy::owner);
+}
+
+private:
+union {
+const void *instance;
+storage_type storage;
+};
+const type_info *info;
+vtable_type *vtable;
+policy mode;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+template<std::size_t Len, std::size_t Align>
+[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Performs type-safe access to the contained object.
+ * @tparam Type Type to which conversion is required.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param data Target any object.
+ * @return The element converted to the requested type.
+ */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
+if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
+return static_cast<Type>(std::move(*instance));
+} else {
+return any_cast<Type>(data);
+}
+} else {
+auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(std::move(*instance));
+}
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+return static_cast<const Type *>(data->data(info));
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+// last attempt to make wrappers for const references return their values
+return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
+basic_any<Len, Align> make_any(Args &&...args) {
+return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
+basic_any<Len, Align> forward_as_any(Type &&value) {
+return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+} // namespace entt
+
+#endif
+
+// #include "core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "core/enum.hpp"
+#ifndef ENTT_CORE_ENUM_HPP
+#define ENTT_CORE_ENUM_HPP
+
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Enable bitmask support for enum classes.
+ * @tparam Type The enum type for which to enable bitmask support.
+ */
+template<typename Type, typename = void>
+struct enum_as_bitmask: std::false_type {};
+
+/*! @copydoc enum_as_bitmask */
+template<typename Type>
+struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The enum class type for which to enable bitmask support.
+ */
+template<typename Type>
+inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
+
+} // namespace entt
+
+/**
+ * @brief Operator available for enums for which bitmask support is enabled.
+ * @tparam Type Enum class type.
+ * @param lhs The first value to use.
+ * @param rhs The second value to use.
+ * @return The result of invoking the operator on the underlying types of the
+ * two values provided.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/**
+ * @brief Operator available for enums for which bitmask support is enabled.
+ * @tparam Type Enum class type.
+ * @param value The value to use.
+ * @return The result of invoking the operator on the underlying types of the
+ * value provided.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator~(const Type value) ENTT_NOEXCEPT {
+return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
+}
+
+/*! @copydoc operator~ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
+operator!(const Type value) ENTT_NOEXCEPT {
+return !static_cast<std::underlying_type_t<Type>>(value);
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs | rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs & rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs ^ rhs));
+}
+
+#endif
+
+// #include "core/family.hpp"
+#ifndef ENTT_CORE_FAMILY_HPP
+#define ENTT_CORE_FAMILY_HPP
+
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Dynamic identifier generator.
+ *
+ * Utility class template that can be used to assign unique identifiers to types
+ * at runtime. Use different specializations to create separate sets of
+ * identifiers.
+ */
+template<typename...>
+class family {
+inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
+
+public:
+/*! @brief Unsigned integer type. */
+using family_type = id_type;
+
+/*! @brief Statically generated unique identifier for the given type. */
+template<typename... Type>
+// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
+inline static const family_type type = identifier++;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "core/hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+// #include "core/ident.hpp"
+#ifndef ENTT_CORE_IDENT_HPP
+#define ENTT_CORE_IDENT_HPP
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Types identifiers.
+ *
+ * Variable template used to generate identifiers at compile-time for the given
+ * types. Use the `get` member function to know what's the identifier associated
+ * to the specific type.
+ *
+ * @note
+ * Identifiers are constant expression and can be used in any context where such
+ * an expression is required. As an example:
+ * @code{.cpp}
+ * using id = entt::identifier<a_type, another_type>;
+ *
+ * switch(a_type_identifier) {
+ * case id::type<a_type>:
+ *     // ...
+ *     break;
+ * case id::type<another_type>:
+ *     // ...
+ *     break;
+ * default:
+ *     // ...
+ * }
+ * @endcode
+ *
+ * @tparam Types List of types for which to generate identifiers.
+ */
+template<typename... Types>
+class identifier {
+template<typename Type, std::size_t... Index>
+[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+static_assert((std::is_same_v<Type, Types> || ...), "Invalid type");
+return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
+}
+
+public:
+/*! @brief Unsigned integer type. */
+using identifier_type = id_type;
+
+/*! @brief Statically generated unique identifier for the given type. */
+template<typename Type>
+static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
+};
+
+} // namespace entt
+
+#endif
+
+// #include "core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "core/monostate.hpp"
+#ifndef ENTT_CORE_MONOSTATE_HPP
+#define ENTT_CORE_MONOSTATE_HPP
+
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Minimal implementation of the monostate pattern.
+ *
+ * A minimal, yet complete configuration system built on top of the monostate
+ * pattern. Thread safe by design, it works only with basic types like `int`s or
+ * `bool`s.<br/>
+ * Multiple types and therefore more than one value can be associated with a
+ * single key. Because of this, users must pay attention to use the same type
+ * both during an assignment and when they try to read back their data.
+ * Otherwise, they can incur in unexpected results.
+ */
+template<id_type>
+struct monostate {
+/**
+     * @brief Assigns a value of a specific type to a given key.
+     * @tparam Type Type of the value to assign.
+     * @param val User data to assign to the given key.
+     */
+template<typename Type>
+void operator=(Type val) const ENTT_NOEXCEPT {
+value<Type> = val;
+}
+
+/**
+     * @brief Gets a value of a specific type for a given key.
+     * @tparam Type Type of the value to get.
+     * @return Stored value, if any.
+     */
+template<typename Type>
+operator Type() const ENTT_NOEXCEPT {
+return value<Type>;
+}
+
+private:
+template<typename Type>
+inline static ENTT_MAYBE_ATOMIC(Type) value{};
+};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Value Value used to differentiate between different variables.
+ */
+template<id_type Value>
+inline monostate<Value> monostate_v = {};
+
+} // namespace entt
+
+#endif
+
+// #include "core/tuple.hpp"
+#ifndef ENTT_CORE_TUPLE_HPP
+#define ENTT_CORE_TUPLE_HPP
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Utility function to unwrap tuples of a single element.
+ * @tparam Type Tuple type of any sizes.
+ * @param value A tuple object of the given type.
+ * @return The tuple itself if it contains more than one element, the first
+ * element otherwise.
+ */
+template<typename Type>
+constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
+if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
+return std::get<0>(std::forward<Type>(value));
+} else {
+return std::forward<Type>(value);
+}
+}
+
+} // namespace entt
+
+#endif
+
+// #include "core/type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/component.hpp"
+#ifndef ENTT_ENTITY_COMPONENT_HPP
+#define ENTT_ENTITY_COMPONENT_HPP
+
+#include <cstddef>
+#include <type_traits>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct in_place_delete: std::false_type {};
+
+template<typename Type>
+struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
+: std::true_type {};
+
+template<typename Type, typename = void>
+struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {};
+
+template<typename Type>
+struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
+: std::integral_constant<std::size_t, Type::page_size> {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Common way to access various properties of components.
+ * @tparam Type Type of component.
+ */
+template<typename Type, typename = void>
+struct component_traits {
+static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
+
+/*! @brief Pointer stability, default is `false`. */
+static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
+/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
+static constexpr std::size_t page_size = internal::page_size<Type>::value;
+};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type Type of component.
+ */
+template<class Type>
+inline constexpr bool ignore_as_empty_v = (component_traits<Type>::page_size == 0u);
+
+} // namespace entt
+
+#endif
+
+// #include "entity/entity.hpp"
+#ifndef ENTT_ENTITY_ENTITY_HPP
+#define ENTT_ENTITY_ENTITY_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_ENTITY_FWD_HPP
+#define ENTT_ENTITY_FWD_HPP
+
+#include <memory>
+// #include "../core/fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "utility.hpp"
+#ifndef ENTT_ENTITY_UTILITY_HPP
+#define ENTT_ENTITY_UTILITY_HPP
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Alias for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct exclude_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr exclude_t<Type...> exclude{};
+
+/**
+ * @brief Alias for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct get_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr get_t<Type...> get{};
+
+/**
+ * @brief Alias for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct owned_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr owned_t<Type...> owned{};
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+template<typename Entity, typename = std::allocator<Entity>>
+class basic_sparse_set;
+
+template<typename, typename Type, typename = std::allocator<Type>, typename = void>
+class basic_storage;
+
+template<typename>
+class basic_registry;
+
+template<typename, typename, typename, typename = void>
+class basic_view;
+
+template<typename>
+struct basic_runtime_view;
+
+template<typename, typename, typename, typename>
+class basic_group;
+
+template<typename>
+class basic_observer;
+
+template<typename>
+class basic_organizer;
+
+template<typename, typename...>
+struct basic_handle;
+
+template<typename>
+class basic_snapshot;
+
+template<typename>
+class basic_snapshot_loader;
+
+template<typename>
+class basic_continuous_loader;
+
+/*! @brief Default entity identifier. */
+enum class entity : id_type {};
+
+/*! @brief Alias declaration for the most common use case. */
+using sparse_set = basic_sparse_set<entity>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using storage = basic_storage<entity, Args...>;
+
+/*! @brief Alias declaration for the most common use case. */
+using registry = basic_registry<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using observer = basic_observer<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using organizer = basic_organizer<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using handle = basic_handle<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using const_handle = basic_handle<const entity>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using handle_view = basic_handle<entity, Args...>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using const_handle_view = basic_handle<const entity, Args...>;
+
+/*! @brief Alias declaration for the most common use case. */
+using snapshot = basic_snapshot<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using snapshot_loader = basic_snapshot_loader<entity>;
+
+/*! @brief Alias declaration for the most common use case. */
+using continuous_loader = basic_continuous_loader<entity>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Get Types of components iterated by the view.
+ * @tparam Exclude Types of components used to filter the view.
+ */
+template<typename Get, typename Exclude = exclude_t<>>
+using view = basic_view<entity, Get, Exclude>;
+
+/*! @brief Alias declaration for the most common use case. */
+using runtime_view = basic_runtime_view<sparse_set>;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using group = basic_group<entity, Args...>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct entt_traits;
+
+template<typename Type>
+struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
+: entt_traits<std::underlying_type_t<Type>> {};
+
+template<typename Type>
+struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
+: entt_traits<typename Type::entity_type> {};
+
+template<>
+struct entt_traits<std::uint32_t> {
+using entity_type = std::uint32_t;
+using version_type = std::uint16_t;
+
+static constexpr entity_type entity_mask = 0xFFFFF;
+static constexpr entity_type version_mask = 0xFFF;
+static constexpr std::size_t entity_shift = 20u;
+};
+
+template<>
+struct entt_traits<std::uint64_t> {
+using entity_type = std::uint64_t;
+using version_type = std::uint32_t;
+
+static constexpr entity_type entity_mask = 0xFFFFFFFF;
+static constexpr entity_type version_mask = 0xFFFFFFFF;
+static constexpr std::size_t entity_shift = 32u;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Entity traits.
+ * @tparam Type Type of identifier.
+ */
+template<typename Type>
+class entt_traits: internal::entt_traits<Type> {
+using base_type = internal::entt_traits<Type>;
+
+public:
+/*! @brief Value type. */
+using value_type = Type;
+/*! @brief Underlying entity type. */
+using entity_type = typename base_type::entity_type;
+/*! @brief Underlying version type. */
+using version_type = typename base_type::version_type;
+/*! @brief Reserved identifier. */
+static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
+/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
+static constexpr auto page_size = ENTT_SPARSE_PAGE;
+
+/**
+     * @brief Converts an entity to its underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the given value.
+     */
+[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
+return static_cast<entity_type>(value);
+}
+
+/**
+     * @brief Returns the entity part once converted to the underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the entity part.
+     */
+[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
+return (to_integral(value) & base_type::entity_mask);
+}
+
+/**
+     * @brief Returns the version part once converted to the underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the version part.
+     */
+[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
+return (to_integral(value) >> base_type::entity_shift);
+}
+
+/**
+     * @brief Constructs an identifier from its parts.
+     *
+     * If the version part is not provided, a tombstone is returned.<br/>
+     * If the entity part is not provided, a null identifier is returned.
+     *
+     * @param entity The entity part of the identifier.
+     * @param version The version part of the identifier.
+     * @return A properly constructed identifier.
+     */
+[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
+return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
+}
+
+/**
+     * @brief Combines two identifiers in a single one.
+     *
+     * The returned identifier is a copy of the first element except for its
+     * version, which is taken from the second element.
+     *
+     * @param lhs The identifier from which to take the entity part.
+     * @param rhs The identifier from which to take the version part.
+     * @return A properly constructed identifier.
+     */
+[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
+constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
+return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
+}
+};
+
+/**
+ * @copydoc entt_traits<Entity>::to_integral
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_integral(value);
+}
+
+/**
+ * @copydoc entt_traits<Entity>::to_entity
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_entity(value);
+}
+
+/**
+ * @copydoc entt_traits<Entity>::to_version
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_version(value);
+}
+
+/*! @brief Null object for all identifiers.  */
+struct null_t {
+/**
+     * @brief Converts the null object to identifiers of any type.
+     * @tparam Entity Type of identifier.
+     * @return The null representation for the given type.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+}
+
+/**
+     * @brief Compares two null objects.
+     * @param other A null object.
+     * @return True in all cases.
+     */
+[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+return true;
+}
+
+/**
+     * @brief Compares two null objects.
+     * @param other A null object.
+     * @return False in all cases.
+     */
+[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+return false;
+}
+
+/**
+     * @brief Compares a null object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return False if the two elements differ, true otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
+}
+
+/**
+     * @brief Compares a null object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return True if the two elements differ, false otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+return !(entity == *this);
+}
+};
+
+/**
+ * @brief Compares a null object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A null object yet to be converted.
+ * @return False if the two elements differ, true otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+return other.operator==(entity);
+}
+
+/**
+ * @brief Compares a null object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A null object yet to be converted.
+ * @return True if the two elements differ, false otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+return !(other == entity);
+}
+
+/*! @brief Tombstone object for all identifiers.  */
+struct tombstone_t {
+/**
+     * @brief Converts the tombstone object to identifiers of any type.
+     * @tparam Entity Type of identifier.
+     * @return The tombstone representation for the given type.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+}
+
+/**
+     * @brief Compares two tombstone objects.
+     * @param other A tombstone object.
+     * @return True in all cases.
+     */
+[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+return true;
+}
+
+/**
+     * @brief Compares two tombstone objects.
+     * @param other A tombstone object.
+     * @return False in all cases.
+     */
+[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+return false;
+}
+
+/**
+     * @brief Compares a tombstone object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return False if the two elements differ, true otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::to_version(entity) == entity_traits::to_version(*this);
+}
+
+/**
+     * @brief Compares a tombstone object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return True if the two elements differ, false otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+return !(entity == *this);
+}
+};
+
+/**
+ * @brief Compares a tombstone object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A tombstone object yet to be converted.
+ * @return False if the two elements differ, true otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+return other.operator==(entity);
+}
+
+/**
+ * @brief Compares a tombstone object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A tombstone object yet to be converted.
+ * @return True if the two elements differ, false otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+return !(other == entity);
+}
+
+/**
+ * @brief Compile-time constant for null entities.
+ *
+ * There exist implicit conversions from this variable to identifiers of any
+ * allowed type. Similarly, there exist comparison operators between the null
+ * entity and any other identifier.
+ */
+inline constexpr null_t null{};
+
+/**
+ * @brief Compile-time constant for tombstone entities.
+ *
+ * There exist implicit conversions from this variable to identifiers of any
+ * allowed type. Similarly, there exist comparison operators between the
+ * tombstone entity and any other identifier.
+ */
+inline constexpr tombstone_t tombstone{};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/group.hpp"
+#ifndef ENTT_ENTITY_GROUP_HPP
+#define ENTT_ENTITY_GROUP_HPP
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+
+// #include "component.hpp"
+#ifndef ENTT_ENTITY_COMPONENT_HPP
+#define ENTT_ENTITY_COMPONENT_HPP
+
+#include <cstddef>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct in_place_delete: std::false_type {};
+
+template<typename Type>
+struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
+: std::true_type {};
+
+template<typename Type, typename = void>
+struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {};
+
+template<typename Type>
+struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
+: std::integral_constant<std::size_t, Type::page_size> {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Common way to access various properties of components.
+ * @tparam Type Type of component.
+ */
+template<typename Type, typename = void>
+struct component_traits {
+static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
+
+/*! @brief Pointer stability, default is `false`. */
+static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
+/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
+static constexpr std::size_t page_size = internal::page_size<Type>::value;
+};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type Type of component.
+ */
+template<class Type>
+inline constexpr bool ignore_as_empty_v = (component_traits<Type>::page_size == 0u);
+
+} // namespace entt
+
+#endif
+
+// #include "entity.hpp"
+#ifndef ENTT_ENTITY_ENTITY_HPP
+#define ENTT_ENTITY_ENTITY_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct entt_traits;
+
+template<typename Type>
+struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
+: entt_traits<std::underlying_type_t<Type>> {};
+
+template<typename Type>
+struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
+: entt_traits<typename Type::entity_type> {};
+
+template<>
+struct entt_traits<std::uint32_t> {
+using entity_type = std::uint32_t;
+using version_type = std::uint16_t;
+
+static constexpr entity_type entity_mask = 0xFFFFF;
+static constexpr entity_type version_mask = 0xFFF;
+static constexpr std::size_t entity_shift = 20u;
+};
+
+template<>
+struct entt_traits<std::uint64_t> {
+using entity_type = std::uint64_t;
+using version_type = std::uint32_t;
+
+static constexpr entity_type entity_mask = 0xFFFFFFFF;
+static constexpr entity_type version_mask = 0xFFFFFFFF;
+static constexpr std::size_t entity_shift = 32u;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Entity traits.
+ * @tparam Type Type of identifier.
+ */
+template<typename Type>
+class entt_traits: internal::entt_traits<Type> {
+using base_type = internal::entt_traits<Type>;
+
+public:
+/*! @brief Value type. */
+using value_type = Type;
+/*! @brief Underlying entity type. */
+using entity_type = typename base_type::entity_type;
+/*! @brief Underlying version type. */
+using version_type = typename base_type::version_type;
+/*! @brief Reserved identifier. */
+static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
+/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
+static constexpr auto page_size = ENTT_SPARSE_PAGE;
+
+/**
+     * @brief Converts an entity to its underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the given value.
+     */
+[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
+return static_cast<entity_type>(value);
+}
+
+/**
+     * @brief Returns the entity part once converted to the underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the entity part.
+     */
+[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
+return (to_integral(value) & base_type::entity_mask);
+}
+
+/**
+     * @brief Returns the version part once converted to the underlying type.
+     * @param value The value to convert.
+     * @return The integral representation of the version part.
+     */
+[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
+return (to_integral(value) >> base_type::entity_shift);
+}
+
+/**
+     * @brief Constructs an identifier from its parts.
+     *
+     * If the version part is not provided, a tombstone is returned.<br/>
+     * If the entity part is not provided, a null identifier is returned.
+     *
+     * @param entity The entity part of the identifier.
+     * @param version The version part of the identifier.
+     * @return A properly constructed identifier.
+     */
+[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
+return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
+}
+
+/**
+     * @brief Combines two identifiers in a single one.
+     *
+     * The returned identifier is a copy of the first element except for its
+     * version, which is taken from the second element.
+     *
+     * @param lhs The identifier from which to take the entity part.
+     * @param rhs The identifier from which to take the version part.
+     * @return A properly constructed identifier.
+     */
+[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
+constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
+return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
+}
+};
+
+/**
+ * @copydoc entt_traits<Entity>::to_integral
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_integral(value);
+}
+
+/**
+ * @copydoc entt_traits<Entity>::to_entity
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_entity(value);
+}
+
+/**
+ * @copydoc entt_traits<Entity>::to_version
+ * @tparam Entity The value type.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
+return entt_traits<Entity>::to_version(value);
+}
+
+/*! @brief Null object for all identifiers.  */
+struct null_t {
+/**
+     * @brief Converts the null object to identifiers of any type.
+     * @tparam Entity Type of identifier.
+     * @return The null representation for the given type.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+}
+
+/**
+     * @brief Compares two null objects.
+     * @param other A null object.
+     * @return True in all cases.
+     */
+[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+return true;
+}
+
+/**
+     * @brief Compares two null objects.
+     * @param other A null object.
+     * @return False in all cases.
+     */
+[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
+return false;
+}
+
+/**
+     * @brief Compares a null object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return False if the two elements differ, true otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
+}
+
+/**
+     * @brief Compares a null object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return True if the two elements differ, false otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+return !(entity == *this);
+}
+};
+
+/**
+ * @brief Compares a null object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A null object yet to be converted.
+ * @return False if the two elements differ, true otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+return other.operator==(entity);
+}
+
+/**
+ * @brief Compares a null object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A null object yet to be converted.
+ * @return True if the two elements differ, false otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
+return !(other == entity);
+}
+
+/*! @brief Tombstone object for all identifiers.  */
+struct tombstone_t {
+/**
+     * @brief Converts the tombstone object to identifiers of any type.
+     * @tparam Entity Type of identifier.
+     * @return The tombstone representation for the given type.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
+}
+
+/**
+     * @brief Compares two tombstone objects.
+     * @param other A tombstone object.
+     * @return True in all cases.
+     */
+[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+return true;
+}
+
+/**
+     * @brief Compares two tombstone objects.
+     * @param other A tombstone object.
+     * @return False in all cases.
+     */
+[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
+return false;
+}
+
+/**
+     * @brief Compares a tombstone object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return False if the two elements differ, true otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
+using entity_traits = entt_traits<Entity>;
+return entity_traits::to_version(entity) == entity_traits::to_version(*this);
+}
+
+/**
+     * @brief Compares a tombstone object and an identifier of any type.
+     * @tparam Entity Type of identifier.
+     * @param entity Identifier with which to compare.
+     * @return True if the two elements differ, false otherwise.
+     */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+return !(entity == *this);
+}
+};
+
+/**
+ * @brief Compares a tombstone object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A tombstone object yet to be converted.
+ * @return False if the two elements differ, true otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+return other.operator==(entity);
+}
+
+/**
+ * @brief Compares a tombstone object and an identifier of any type.
+ * @tparam Entity Type of identifier.
+ * @param entity Identifier with which to compare.
+ * @param other A tombstone object yet to be converted.
+ * @return True if the two elements differ, false otherwise.
+ */
+template<typename Entity>
+[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
+return !(other == entity);
+}
+
+/**
+ * @brief Compile-time constant for null entities.
+ *
+ * There exist implicit conversions from this variable to identifiers of any
+ * allowed type. Similarly, there exist comparison operators between the null
+ * entity and any other identifier.
+ */
+inline constexpr null_t null{};
+
+/**
+ * @brief Compile-time constant for tombstone entities.
+ *
+ * There exist implicit conversions from this variable to identifiers of any
+ * allowed type. Similarly, there exist comparison operators between the
+ * tombstone entity and any other identifier.
+ */
+inline constexpr tombstone_t tombstone{};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+#ifndef ENTT_ENTITY_SPARSE_SET_HPP
+#define ENTT_ENTITY_SPARSE_SET_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/algorithm.hpp"
+#ifndef ENTT_CORE_ALGORITHM_HPP
+#define ENTT_CORE_ALGORITHM_HPP
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <utility>
+#include <vector>
+// #include "utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Function object to wrap `std::sort` in a class type.
+ *
+ * Unfortunately, `std::sort` cannot be passed as template argument to a class
+ * template or a function template.<br/>
+ * This class fills the gap by wrapping some flavors of `std::sort` in a
+ * function object.
+ */
+struct std_sort {
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given binary comparison function.
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Args Types of arguments to forward to the sort function.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param compare A valid comparison function object.
+     * @param args Arguments to forward to the sort function, if any.
+     */
+template<typename It, typename Compare = std::less<>, typename... Args>
+void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const {
+std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
+}
+};
+
+/*! @brief Function object for performing insertion sort. */
+struct insertion_sort {
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given binary comparison function.
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Compare Type of comparison function object.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param compare A valid comparison function object.
+     */
+template<typename It, typename Compare = std::less<>>
+void operator()(It first, It last, Compare compare = Compare{}) const {
+if(first < last) {
+for(auto it = first + 1; it < last; ++it) {
+auto value = std::move(*it);
+auto pre = it;
+
+for(; pre > first && compare(value, *(pre - 1)); --pre) {
+*pre = std::move(*(pre - 1));
+}
+
+*pre = std::move(value);
+}
+}
+}
+};
+
+/**
+ * @brief Function object for performing LSD radix sort.
+ * @tparam Bit Number of bits processed per pass.
+ * @tparam N Maximum number of bits to sort.
+ */
+template<std::size_t Bit, std::size_t N>
+struct radix_sort {
+static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
+
+/**
+     * @brief Sorts the elements in a range.
+     *
+     * Sorts the elements in a range using the given _getter_ to access the
+     * actual data to be sorted.
+     *
+     * This implementation is inspired by the online book
+     * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort).
+     *
+     * @tparam It Type of random access iterator.
+     * @tparam Getter Type of _getter_ function object.
+     * @param first An iterator to the first element of the range to sort.
+     * @param last An iterator past the last element of the range to sort.
+     * @param getter A valid _getter_ function object.
+     */
+template<typename It, typename Getter = identity>
+void operator()(It first, It last, Getter getter = Getter{}) const {
+if(first < last) {
+static constexpr auto mask = (1 << Bit) - 1;
+static constexpr auto buckets = 1 << Bit;
+static constexpr auto passes = N / Bit;
+
+using value_type = typename std::iterator_traits<It>::value_type;
+std::vector<value_type> aux(std::distance(first, last));
+
+auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
+std::size_t index[buckets]{};
+std::size_t count[buckets]{};
+
+for(auto it = from; it != to; ++it) {
+++count[(getter(*it) >> start) & mask];
+}
+
+for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
+index[pos + 1u] = index[pos] + count[pos];
+}
+
+for(auto it = from; it != to; ++it) {
+out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
+}
+};
+
+for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
+part(first, last, aux.begin(), pass * Bit);
+part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
+}
+
+if constexpr(passes & 1) {
+part(first, last, aux.begin(), (passes - 1) * Bit);
+std::move(aux.begin(), aux.end(), first);
+}
+}
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/any.hpp"
+#ifndef ENTT_CORE_ANY_HPP
+#define ENTT_CORE_ANY_HPP
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief A SBO friendly, type-safe container for single values of any type.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ */
+template<std::size_t Len, std::size_t Align>
+class basic_any {
+enum class operation : std::uint8_t {
+copy,
+move,
+transfer,
+assign,
+destroy,
+compare,
+get
+};
+
+enum class policy : std::uint8_t {
+owner,
+ref,
+cref
+};
+
+using storage_type = std::aligned_storage_t<Len + !Len, Align>;
+using vtable_type = const void *(const operation, const basic_any &, const void *);
+
+template<typename Type>
+static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+
+template<typename Type>
+static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
+static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+const Type *element = nullptr;
+
+if constexpr(in_situ<Type>) {
+element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
+} else {
+element = static_cast<const Type *>(value.instance);
+}
+
+switch(op) {
+case operation::copy:
+if constexpr(std::is_copy_constructible_v<Type>) {
+static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
+}
+break;
+case operation::move:
+if constexpr(in_situ<Type>) {
+if(value.owner()) {
+return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
+}
+}
+
+return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
+case operation::transfer:
+if constexpr(std::is_move_assignable_v<Type>) {
+*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
+return other;
+}
+[[fallthrough]];
+case operation::assign:
+if constexpr(std::is_copy_assignable_v<Type>) {
+*const_cast<Type *>(element) = *static_cast<const Type *>(other);
+return other;
+}
+break;
+case operation::destroy:
+if constexpr(in_situ<Type>) {
+element->~Type();
+} else if constexpr(std::is_array_v<Type>) {
+delete[] element;
+} else {
+delete element;
+}
+break;
+case operation::compare:
+if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
+return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
+} else {
+return (element == other) ? other : nullptr;
+}
+case operation::get:
+return element;
+}
+
+return nullptr;
+}
+
+template<typename Type, typename... Args>
+void initialize([[maybe_unused]] Args &&...args) {
+if constexpr(!std::is_void_v<Type>) {
+info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+
+if constexpr(std::is_lvalue_reference_v<Type>) {
+static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
+mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
+instance = (std::addressof(args), ...);
+} else if constexpr(in_situ<Type>) {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+new(&storage) Type{std::forward<Args>(args)...};
+} else {
+new(&storage) Type(std::forward<Args>(args)...);
+}
+} else {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+instance = new Type{std::forward<Args>(args)...};
+} else {
+instance = new Type(std::forward<Args>(args)...);
+}
+}
+}
+}
+
+basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
+: instance{other.data()},
+info{other.info},
+vtable{other.vtable},
+mode{pol} {}
+
+public:
+/*! @brief Size of the internal storage. */
+static constexpr auto length = Len;
+/*! @brief Alignment requirement. */
+static constexpr auto alignment = Align;
+
+/*! @brief Default constructor. */
+constexpr basic_any() ENTT_NOEXCEPT
+: instance{},
+info{&type_id<void>()},
+vtable{},
+mode{policy::owner} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
+: basic_any{} {
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
+basic_any(Type &&value)
+: basic_any{} {
+initialize<std::decay_t<Type>>(std::forward<Type>(value));
+}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+basic_any(const basic_any &other)
+: basic_any{} {
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_any(basic_any &&other) ENTT_NOEXCEPT
+: instance{},
+info{other.info},
+vtable{other.vtable},
+mode{other.mode} {
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+}
+}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~basic_any() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This any object.
+     */
+basic_any &operator=(const basic_any &other) {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This any object.
+     */
+basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+info = other.info;
+vtable = other.vtable;
+mode = other.mode;
+}
+
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/**
+     * @brief Returns the object type if any, `type_id<void>()` otherwise.
+     * @return The object type if any, `type_id<void>()` otherwise.
+     */
+[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Replaces the contained object by creating a new instance directly.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+reset();
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+bool assign(const any &other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+return (vtable(operation::assign, *this, other.data()) != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc assign */
+bool assign(any &&other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+if(auto *val = other.data(); val) {
+return (vtable(operation::transfer, *this, val) != nullptr);
+} else {
+return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
+}
+}
+
+return false;
+}
+
+/*! @brief Destroys contained object */
+void reset() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+
+info = &type_id<void>();
+vtable = nullptr;
+mode = policy::owner;
+}
+
+/**
+     * @brief Returns false if a wrapper is empty, true otherwise.
+     * @return False if the wrapper is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return vtable != nullptr;
+}
+
+/**
+     * @brief Checks if two wrappers differ in their content.
+     * @param other Wrapper with which to compare.
+     * @return False if the two objects differ in their content, true otherwise.
+     */
+bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
+if(vtable && *info == *other.info) {
+return (vtable(operation::compare, *this, other.data()) != nullptr);
+}
+
+return (!vtable && !other.vtable);
+}
+
+/**
+     * @brief Aliasing constructor.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
+return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
+}
+
+/*! @copydoc as_ref */
+[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
+return basic_any{*this, policy::cref};
+}
+
+/**
+     * @brief Returns true if a wrapper owns its object, false otherwise.
+     * @return True if the wrapper owns its object, false otherwise.
+     */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return (mode == policy::owner);
+}
+
+private:
+union {
+const void *instance;
+storage_type storage;
+};
+const type_info *info;
+vtable_type *vtable;
+policy mode;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+template<std::size_t Len, std::size_t Align>
+[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Performs type-safe access to the contained object.
+ * @tparam Type Type to which conversion is required.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param data Target any object.
+ * @return The element converted to the requested type.
+ */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
+if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
+return static_cast<Type>(std::move(*instance));
+} else {
+return any_cast<Type>(data);
+}
+} else {
+auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(std::move(*instance));
+}
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+return static_cast<const Type *>(data->data(info));
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+// last attempt to make wrappers for const references return their values
+return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
+basic_any<Len, Align> make_any(Args &&...args) {
+return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
+basic_any<Len, Align> forward_as_any(Type &&value) {
+return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Container>
+struct sparse_set_iterator final {
+using value_type = typename Container::value_type;
+using pointer = typename Container::const_pointer;
+using reference = typename Container::const_reference;
+using difference_type = typename Container::difference_type;
+using iterator_category = std::random_access_iterator_tag;
+
+sparse_set_iterator() ENTT_NOEXCEPT
+: packed{},
+offset{} {}
+
+sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT
+: packed{std::addressof(ref)},
+offset{idx} {}
+
+sparse_set_iterator &operator++() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
+sparse_set_iterator orig = *this;
+return ++(*this), orig;
+}
+
+sparse_set_iterator &operator--() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
+sparse_set_iterator orig = *this;
+return operator--(), orig;
+}
+
+sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+offset -= value;
+return *this;
+}
+
+sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+sparse_set_iterator copy = *this;
+return (copy += value);
+}
+
+sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return packed->data()[index() - value];
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return packed->data() + index();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
+return offset - 1;
+}
+
+private:
+const Container *packed;
+difference_type offset;
+};
+
+template<typename Type, typename Other>
+[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return rhs.index() - lhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() > rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Sparse set deletion policy. */
+enum class deletion_policy : std::uint8_t {
+/*! @brief Swap-and-pop deletion policy. */
+swap_and_pop = 0u,
+/*! @brief In-place deletion policy. */
+in_place = 1u
+};
+
+/**
+ * @brief Basic sparse set implementation.
+ *
+ * Sparse set or packed array or whatever is the name users give it.<br/>
+ * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
+ * _packed_ one; one used for direct access through contiguous memory, the other
+ * one used to get the data through an extra level of indirection.<br/>
+ * This is largely used by the registry to offer users the fastest access ever
+ * to the components. Views and groups in general are almost entirely designed
+ * around sparse sets.
+ *
+ * This type of data structure is widely documented in the literature and on the
+ * web. This is nothing more than a customized implementation suitable for the
+ * purpose of the framework.
+ *
+ * @note
+ * Internal data structures arrange elements to maximize performance. There are
+ * no guarantees that entities are returned in the insertion order when iterate
+ * a sparse set. Do not make assumption on the order in any case.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Allocator>
+class basic_sparse_set {
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
+using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
+using packed_container_type = std::vector<Entity, Allocator>;
+using entity_traits = entt_traits<Entity>;
+
+[[nodiscard]] auto sparse_ptr(const Entity entt) const {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+const auto page = pos / entity_traits::page_size;
+return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
+}
+
+[[nodiscard]] auto &sparse_ref(const Entity entt) const {
+ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
+}
+
+[[nodiscard]] auto &assure_at_least(const Entity entt) {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+const auto page = pos / entity_traits::page_size;
+
+if(!(page < sparse.size())) {
+sparse.resize(page + 1u, nullptr);
+}
+
+if(!sparse[page]) {
+auto page_allocator{packed.get_allocator()};
+sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
+std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
+}
+
+auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
+ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available");
+return elem;
+}
+
+void release_sparse_pages() {
+auto page_allocator{packed.get_allocator()};
+
+for(auto &&page: sparse) {
+if(page != nullptr) {
+std::destroy(page, page + entity_traits::page_size);
+alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
+page = nullptr;
+}
+}
+}
+
+private:
+virtual const void *get_at(const std::size_t) const {
+return nullptr;
+}
+
+virtual void swap_at(const std::size_t, const std::size_t) {}
+virtual void move_element(const std::size_t, const std::size_t) {}
+
+protected:
+/*! @brief Random access iterator type. */
+using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
+
+/**
+     * @brief Erases entities from a sparse set.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+virtual void swap_and_pop(basic_iterator first, basic_iterator last) {
+for(; first != last; ++first) {
+sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::to_integral(packed.back()));
+const auto entt = std::exchange(packed[first.index()], packed.back());
+// unnecessary but it helps to detect nasty bugs
+ENTT_ASSERT((packed.back() = tombstone, true), "");
+// lazy self-assignment guard
+sparse_ref(entt) = null;
+packed.pop_back();
+}
+}
+
+/**
+     * @brief Erases entities from a sparse set.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+virtual void in_place_pop(basic_iterator first, basic_iterator last) {
+for(; first != last; ++first) {
+sparse_ref(*first) = null;
+packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::reserved));
+}
+}
+
+/**
+     * @brief Assigns an entity to a sparse set.
+     * @param entt A valid identifier.
+     * @param force_back Force back insertion.
+     * @return Iterator pointing to the emplaced element.
+     */
+virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
+ENTT_ASSERT(!contains(entt), "Set already contains entity");
+
+if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
+packed.push_back(entt);
+elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
+return begin();
+} else {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list));
+elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
+free_list = std::exchange(packed[pos], entt);
+return --(end() - pos);
+}
+}
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Underlying version type. */
+using version_type = typename entity_traits::version_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename packed_container_type::size_type;
+/*! @brief Pointer type to contained entities. */
+using pointer = typename packed_container_type::const_pointer;
+/*! @brief Random access iterator type. */
+using iterator = basic_iterator;
+/*! @brief Constant random access iterator type. */
+using const_iterator = iterator;
+/*! @brief Reverse iterator type. */
+using reverse_iterator = std::reverse_iterator<iterator>;
+/*! @brief Constant reverse iterator type. */
+using const_reverse_iterator = reverse_iterator;
+
+/*! @brief Default constructor. */
+basic_sparse_set()
+: basic_sparse_set{type_id<void>()} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_sparse_set(const allocator_type &allocator)
+: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
+
+/**
+     * @brief Constructs an empty container with the given policy and allocator.
+     * @param pol Type of deletion policy.
+     * @param allocator The allocator to use (possibly default-constructed).
+     */
+explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
+: basic_sparse_set{type_id<void>(), pol, allocator} {}
+
+/**
+     * @brief Constructs an empty container with the given value type, policy
+     * and allocator.
+     * @param value Returned value type, if any.
+     * @param pol Type of deletion policy.
+     * @param allocator The allocator to use (possibly default-constructed).
+     */
+explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
+: sparse{allocator},
+packed{allocator},
+info{&value},
+free_list{tombstone},
+mode{pol} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
+: sparse{std::move(other.sparse)},
+packed{std::move(other.packed)},
+info{other.info},
+free_list{std::exchange(other.free_list, tombstone)},
+mode{other.mode} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: sparse{std::move(other.sparse), allocator},
+packed{std::move(other.packed), allocator},
+info{other.info},
+free_list{std::exchange(other.free_list, tombstone)},
+mode{other.mode} {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
+}
+
+/*! @brief Default destructor. */
+virtual ~basic_sparse_set() {
+release_sparse_pages();
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This sparse set.
+     */
+basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
+
+release_sparse_pages();
+sparse = std::move(other.sparse);
+packed = std::move(other.packed);
+info = other.info;
+free_list = std::exchange(other.free_list, tombstone);
+mode = other.mode;
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given sparse set.
+     * @param other Sparse set to exchange the content with.
+     */
+void swap(basic_sparse_set &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(info, other.info);
+swap(free_list, other.free_list);
+swap(mode, other.mode);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return packed.get_allocator();
+}
+
+/**
+     * @brief Returns the deletion policy of a sparse set.
+     * @return The deletion policy of the sparse set.
+     */
+[[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT {
+return mode;
+}
+
+/**
+     * @brief Increases the capacity of a sparse set.
+     *
+     * If the new capacity is greater than the current capacity, new storage is
+     * allocated, otherwise the method does nothing.
+     *
+     * @param cap Desired capacity.
+     */
+virtual void reserve(const size_type cap) {
+packed.reserve(cap);
+}
+
+/**
+     * @brief Returns the number of elements that a sparse set has currently
+     * allocated space for.
+     * @return Capacity of the sparse set.
+     */
+[[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT {
+return packed.capacity();
+}
+
+/*! @brief Requests the removal of unused capacity. */
+virtual void shrink_to_fit() {
+packed.shrink_to_fit();
+}
+
+/**
+     * @brief Returns the extent of a sparse set.
+     *
+     * The extent of a sparse set is also the size of the internal sparse array.
+     * There is no guarantee that the internal packed array has the same size.
+     * Usually the size of the internal sparse array is equal or greater than
+     * the one of the internal packed array.
+     *
+     * @return Extent of the sparse set.
+     */
+[[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
+return sparse.size() * entity_traits::page_size;
+}
+
+/**
+     * @brief Returns the number of elements in a sparse set.
+     *
+     * The number of elements is also the size of the internal packed array.
+     * There is no guarantee that the internal sparse array has the same size.
+     * Usually the size of the internal sparse array is equal or greater than
+     * the one of the internal packed array.
+     *
+     * @return Number of elements.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.size();
+}
+
+/**
+     * @brief Checks whether a sparse set is empty.
+     * @return True if the sparse set is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.empty();
+}
+
+/**
+     * @brief Direct access to the internal packed array.
+     * @return A pointer to the internal packed array.
+     */
+[[nodiscard]] pointer data() const ENTT_NOEXCEPT {
+return packed.data();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first entity of the internal packed
+     * array. If the sparse set is empty, the returned iterator will be equal to
+     * `end()`.
+     *
+     * @return An iterator to the first entity of the sparse set.
+     */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(packed.size());
+return iterator{packed, pos};
+}
+
+/*! @copydoc begin */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last entity in
+     * a sparse set. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the element following the last entity of a sparse
+     * set.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return iterator{packed, {}};
+}
+
+/*! @copydoc end */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+/**
+     * @brief Returns a reverse iterator to the beginning.
+     *
+     * The returned iterator points to the first entity of the reversed internal
+     * packed array. If the sparse set is empty, the returned iterator will be
+     * equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed internal packed
+     * array.
+     */
+[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(end());
+}
+
+/*! @copydoc rbegin */
+[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
+return rbegin();
+}
+
+/**
+     * @brief Returns a reverse iterator to the end.
+     *
+     * The returned iterator points to the element following the last entity in
+     * the reversed sparse set. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last entity of the
+     * reversed sparse set.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(begin());
+}
+
+/*! @copydoc rend */
+[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
+return rend();
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? --(end() - index(entt)) : end();
+}
+
+/**
+     * @brief Checks if a sparse set contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the sparse set contains the entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+const auto elem = sparse_ptr(entt);
+constexpr auto cap = entity_traits::to_entity(null);
+// testing versions permits to avoid accessing the packed array
+return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
+}
+
+/**
+     * @brief Returns the contained version for an identifier.
+     * @param entt A valid identifier.
+     * @return The version for the given identifier if present, the tombstone
+     * version otherwise.
+     */
+[[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT {
+const auto elem = sparse_ptr(entt);
+constexpr auto fallback = entity_traits::to_version(tombstone);
+return elem ? entity_traits::to_version(*elem) : fallback;
+}
+
+/**
+     * @brief Returns the position of an entity in a sparse set.
+     *
+     * @warning
+     * Attempting to get the position of an entity that doesn't belong to the
+     * sparse set results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return The position of the entity in the sparse set.
+     */
+[[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(contains(entt), "Set does not contain entity");
+return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
+}
+
+/**
+     * @brief Returns the entity at specified location, with bounds checking.
+     * @param pos The position for which to return the entity.
+     * @return The entity at specified location if any, a null entity otherwise.
+     */
+[[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT {
+return pos < packed.size() ? packed[pos] : null;
+}
+
+/**
+     * @brief Returns the entity at specified location, without bounds checking.
+     * @param pos The position for which to return the entity.
+     * @return The entity at specified location.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
+ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
+return packed[pos];
+}
+
+/**
+     * @brief Returns the element assigned to an entity, if any.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the sparse set results
+     * in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return An opaque pointer to the element assigned to the entity, if any.
+     */
+const void *get(const entity_type entt) const ENTT_NOEXCEPT {
+return get_at(index(entt));
+}
+
+/*! @copydoc get */
+void *get(const entity_type entt) ENTT_NOEXCEPT {
+return const_cast<void *>(std::as_const(*this).get(entt));
+}
+
+/**
+     * @brief Assigns an entity to a sparse set.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the sparse set
+     * results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @param value Optional opaque value to forward to mixins, if any.
+     * @return Iterator pointing to the emplaced element in case of success, the
+     * `end()` iterator otherwise.
+     */
+iterator emplace(const entity_type entt, const void *value = nullptr) {
+return try_emplace(entt, false, value);
+}
+
+/**
+     * @brief Bump the version number of an entity.
+     *
+     * @warning
+     * Attempting to bump the version of an entity that doesn't belong to the
+     * sparse set results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void bump(const entity_type entt) {
+auto &entity = sparse_ref(entt);
+entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt));
+packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt;
+}
+
+/**
+     * @brief Assigns one or more entities to a sparse set.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the sparse set
+     * results in undefined behavior.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return Iterator pointing to the first element inserted in case of
+     * success, the `end()` iterator otherwise.
+     */
+template<typename It>
+iterator insert(It first, It last) {
+for(auto it = first; it != last; ++it) {
+try_emplace(*it, true);
+}
+
+return first == last ? end() : find(*first);
+}
+
+/**
+     * @brief Erases an entity from a sparse set.
+     *
+     * @warning
+     * Attempting to erase an entity that doesn't belong to the sparse set
+     * results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void erase(const entity_type entt) {
+const auto it = --(end() - index(entt));
+(mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u);
+}
+
+/**
+     * @brief Erases entities from a set.
+     *
+     * @sa erase
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void erase(It first, It last) {
+if constexpr(std::is_same_v<It, basic_iterator>) {
+(mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last);
+} else {
+for(; first != last; ++first) {
+erase(*first);
+}
+}
+}
+
+/**
+     * @brief Removes an entity from a sparse set if it exists.
+     * @param entt A valid identifier.
+     * @return True if the entity is actually removed, false otherwise.
+     */
+bool remove(const entity_type entt) {
+return contains(entt) && (erase(entt), true);
+}
+
+/**
+     * @brief Removes entities from a sparse set if they exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return The number of entities actually removed.
+     */
+template<typename It>
+size_type remove(It first, It last) {
+size_type count{};
+
+for(; first != last; ++first) {
+count += remove(*first);
+}
+
+return count;
+}
+
+/*! @brief Removes all tombstones from the packed array of a sparse set. */
+void compact() {
+size_type from = packed.size();
+for(; from && packed[from - 1u] == tombstone; --from) {}
+
+for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
+if(const size_type to = entity_traits::to_entity(*it); to < from) {
+--from;
+move_element(from, to);
+
+using std::swap;
+swap(packed[from], packed[to]);
+
+const auto entity = static_cast<typename entity_traits::entity_type>(to);
+sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to]));
+*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved);
+for(; from && packed[from - 1u] == tombstone; --from) {}
+}
+}
+
+free_list = tombstone;
+packed.resize(from);
+}
+
+/**
+     * @brief Swaps two entities in a sparse set.
+     *
+     * For what it's worth, this function affects both the internal sparse array
+     * and the internal packed array. Users should not care of that anyway.
+     *
+     * @warning
+     * Attempting to swap entities that don't belong to the sparse set results
+     * in undefined behavior.
+     *
+     * @param lhs A valid identifier.
+     * @param rhs A valid identifier.
+     */
+void swap_elements(const entity_type lhs, const entity_type rhs) {
+ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
+
+auto &entt = sparse_ref(lhs);
+auto &other = sparse_ref(rhs);
+
+const auto from = entity_traits::to_entity(entt);
+const auto to = entity_traits::to_entity(other);
+
+// basic no-leak guarantee (with invalid state) if swapping throws
+swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
+entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
+other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
+
+using std::swap;
+swap(packed[from], packed[to]);
+}
+
+/**
+     * @brief Sort the first count elements according to the given comparison
+     * function.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param length Number of elements to sort.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Compare, typename Sort = std_sort, typename... Args>
+void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
+ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
+ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
+
+algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
+
+for(size_type pos{}; pos < length; ++pos) {
+auto curr = pos;
+auto next = index(packed[curr]);
+
+while(curr != next) {
+const auto idx = index(packed[next]);
+const auto entt = packed[curr];
+
+swap_at(next, idx);
+const auto entity = static_cast<typename entity_traits::entity_type>(curr);
+sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr]));
+curr = std::exchange(next, idx);
+}
+}
+}
+
+/**
+     * @brief Sort all elements according to the given comparison function.
+     *
+     * @sa sort_n
+     *
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+compact();
+sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Sort entities according to their order in another sparse set.
+     *
+     * Entities that are part of both the sparse sets are ordered internally
+     * according to the order they have in `other`. All the other entities goes
+     * to the end of the list and there are no guarantees on their order.<br/>
+     * In other terms, this function can be used to impose the same order on two
+     * sets by using one of them as a master and the other one as a slave.
+     *
+     * Iterating the sparse set with a couple of iterators returns elements in
+     * the expected order after a call to `respect`. See `begin` and `end` for
+     * more details.
+     *
+     * @param other The sparse sets that imposes the order of the entities.
+     */
+void respect(const basic_sparse_set &other) {
+compact();
+
+const auto to = other.end();
+auto from = other.begin();
+
+for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
+if(contains(*from)) {
+if(*from != packed[pos]) {
+// basic no-leak guarantee (with invalid state) if swapping throws
+swap_elements(packed[pos], *from);
+}
+
+--pos;
+}
+}
+}
+
+/*! @brief Clears a sparse set. */
+void clear() {
+if(const auto last = end(); free_list == null) {
+in_place_pop(begin(), last);
+} else {
+for(auto &&entity: *this) {
+// tombstone filter on itself
+if(const auto it = find(entity); it != last) {
+in_place_pop(it, it + 1u);
+}
+}
+}
+
+free_list = tombstone;
+packed.clear();
+}
+
+/**
+     * @brief Returned value type, if any.
+     * @return Returned value type, if any.
+     */
+const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/*! @brief Forwards variables to mixins, if any. */
+virtual void bind(any) ENTT_NOEXCEPT {}
+
+private:
+sparse_container_type sparse;
+packed_container_type packed;
+const type_info *info;
+entity_type free_list;
+deletion_policy mode;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "storage.hpp"
+#ifndef ENTT_ENTITY_STORAGE_HPP
+#define ENTT_ENTITY_STORAGE_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/memory.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sigh_storage_mixin.hpp"
+#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
+#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/any.hpp"
+
+// #include "../signal/sigh.hpp"
+#ifndef ENTT_SIGNAL_SIGH_HPP
+#define ENTT_SIGNAL_SIGH_HPP
+
+#include <algorithm>
+#include <functional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "delegate.hpp"
+#ifndef ENTT_SIGNAL_DELEGATE_HPP
+#define ENTT_SIGNAL_DELEGATE_HPP
+
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_SIGNAL_FWD_HPP
+#define ENTT_SIGNAL_FWD_HPP
+
+#include <memory>
+
+namespace entt {
+
+template<typename>
+class delegate;
+
+template<typename = std::allocator<char>>
+class basic_dispatcher;
+
+template<typename>
+class emitter;
+
+class connection;
+
+struct scoped_connection;
+
+template<typename>
+class sink;
+
+template<typename Type, typename = std::allocator<Type *>>
+class sigh;
+
+/*! @brief Alias declaration for the most common use case. */
+using dispatcher = basic_dispatcher<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Ret, typename... Args>
+auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
+
+template<typename Ret, typename Type, typename... Args, typename Other>
+auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Type, typename... Other>
+auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
+
+template<typename... Type>
+using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
+
+template<typename... Class, typename Ret, typename... Args>
+[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
+return std::index_sequence_for<Class..., Args...>{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Used to wrap a function or a member of a specified type. */
+template<auto>
+struct connect_arg_t {};
+
+/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
+template<auto Func>
+inline constexpr connect_arg_t<Func> connect_arg{};
+
+/**
+ * @brief Basic delegate implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ */
+template<typename>
+class delegate;
+
+/**
+ * @brief Utility class to use to send around functions and members.
+ *
+ * Unmanaged delegate for function pointers and members. Users of this class are
+ * in charge of disconnecting instances before deleting them.
+ *
+ * A delegate can be used as a general purpose invoker without memory overhead
+ * for free functions possibly with payloads and bound or unbound members.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+class delegate<Ret(Args...)> {
+template<auto Candidate, std::size_t... Index>
+[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+public:
+/*! @brief Function type of the contained target. */
+using function_type = Ret(const void *, Args...);
+/*! @brief Function type of the delegate. */
+using type = Ret(Args...);
+/*! @brief Return type of the delegate. */
+using result_type = Ret;
+
+/*! @brief Default constructor. */
+delegate() ENTT_NOEXCEPT
+: instance{nullptr},
+fn{nullptr} {}
+
+/**
+     * @brief Constructs a delegate and connects a free function or an unbound
+     * member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
+connect<Candidate>();
+}
+
+/**
+     * @brief Constructs a delegate and connects a free function with payload or
+     * a bound member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
+connect<Candidate>(std::forward<Type>(value_or_instance));
+}
+
+/**
+     * @brief Constructs a delegate and connects an user defined function with
+     * optional payload.
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+connect(function, payload);
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a delegate.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+void connect() ENTT_NOEXCEPT {
+instance = nullptr;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+fn = [](const void *, Args... args) -> Ret {
+return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
+};
+} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
+fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
+} else {
+fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the delegate.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the delegate itself.
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid reference that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type &value_or_instance) ENTT_NOEXCEPT {
+instance = &value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * @sa connect(Type &)
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type *value_or_instance) ENTT_NOEXCEPT {
+instance = value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects an user defined function with optional payload to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of an instance overcomes
+     * the one of the delegate.<br/>
+     * The payload is returned as the first argument to the target function in
+     * all cases.
+     *
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+instance = payload;
+fn = function;
+}
+
+/**
+     * @brief Resets a delegate.
+     *
+     * After a reset, a delegate cannot be invoked anymore.
+     */
+void reset() ENTT_NOEXCEPT {
+instance = nullptr;
+fn = nullptr;
+}
+
+/**
+     * @brief Returns the instance or the payload linked to a delegate, if any.
+     * @return An opaque pointer to the underlying data.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return instance;
+}
+
+/**
+     * @brief Triggers a delegate.
+     *
+     * The delegate invokes the underlying function and returns the result.
+     *
+     * @warning
+     * Attempting to trigger an invalid delegate results in undefined
+     * behavior.
+     *
+     * @param args Arguments to use to invoke the underlying function.
+     * @return The value returned by the underlying function.
+     */
+Ret operator()(Args... args) const {
+ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
+return fn(instance, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Checks whether a delegate actually stores a listener.
+     * @return False if the delegate is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+// no need to also test instance
+return !(fn == nullptr);
+}
+
+/**
+     * @brief Compares the contents of two delegates.
+     * @param other Delegate with which to compare.
+     * @return False if the two contents differ, true otherwise.
+     */
+[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
+return fn == other.fn && instance == other.instance;
+}
+
+private:
+const void *instance;
+function_type *fn;
+};
+
+/**
+ * @brief Compares the contents of two delegates.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @param lhs A valid delegate object.
+ * @param rhs A valid delegate object.
+ * @return True if the two contents differ, false otherwise.
+ */
+template<typename Ret, typename... Args>
+[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ * @tparam Type Type of class or type of payload.
+ */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Sink class.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid signal handler type.
+ */
+template<typename Type>
+class sink;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Allocator>
+class sigh;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * It works directly with references to classes and pointers to member functions
+ * as well as pointers to free functions. Users of this class are in charge of
+ * disconnecting instances before deleting them.
+ *
+ * This class serves mainly two purposes:
+ *
+ * * Creating signals to use later to notify a bunch of listeners.
+ * * Collecting results from a set of functions like in a voting system.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sigh<Ret(Args...), Allocator> {
+/*! @brief A sink is allowed to modify a signal. */
+friend class sink<sigh<Ret(Args...), Allocator>>;
+
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type");
+using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Sink type. */
+using sink_type = sink<sigh<Ret(Args...), Allocator>>;
+
+/*! @brief Default constructor. */
+sigh()
+: sigh{allocator_type{}} {}
+
+/**
+     * @brief Constructs a signal handler with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit sigh(const allocator_type &allocator)
+: calls{allocator} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+sigh(const sigh &other)
+: calls{other.calls} {}
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+sigh(const sigh &other, const allocator_type &allocator)
+: calls{other.calls, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+sigh(sigh &&other) ENTT_NOEXCEPT
+: calls{std::move(other.calls)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: calls{std::move(other.calls), allocator} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This signal handler.
+     */
+sigh &operator=(const sigh &other) {
+calls = other.calls;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This signal handler.
+     */
+sigh &operator=(sigh &&other) ENTT_NOEXCEPT {
+calls = std::move(other.calls);
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given signal handler.
+     * @param other Signal handler to exchange the content with.
+     */
+void swap(sigh &other) {
+using std::swap;
+swap(calls, other.calls);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return calls.get_allocator();
+}
+
+/**
+     * @brief Instance type when it comes to connecting member functions.
+     * @tparam Class Type of class to which the member function belongs.
+     */
+template<typename Class>
+using instance_type = Class *;
+
+/**
+     * @brief Number of listeners connected to the signal.
+     * @return Number of listeners currently connected.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return calls.size();
+}
+
+/**
+     * @brief Returns false if at least a listener is connected to the signal.
+     * @return True if the signal has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return calls.empty();
+}
+
+/**
+     * @brief Triggers a signal.
+     *
+     * All the listeners are notified. Order isn't guaranteed.
+     *
+     * @param args Arguments to use to invoke listeners.
+     */
+void publish(Args... args) const {
+for(auto &&call: std::as_const(calls)) {
+call(args...);
+}
+}
+
+/**
+     * @brief Collects return values from the listeners.
+     *
+     * The collector must expose a call operator with the following properties:
+     *
+     * * The return type is either `void` or such that it's convertible to
+     *   `bool`. In the second case, a true value will stop the iteration.
+     * * The list of parameters is empty if `Ret` is `void`, otherwise it
+     *   contains a single element such that `Ret` is convertible to it.
+     *
+     * @tparam Func Type of collector to use, if any.
+     * @param func A valid function object.
+     * @param args Arguments to use to invoke listeners.
+     */
+template<typename Func>
+void collect(Func func, Args... args) const {
+for(auto &&call: calls) {
+if constexpr(std::is_void_v<Ret>) {
+if constexpr(std::is_invocable_r_v<bool, Func>) {
+call(args...);
+if(func()) { break; }
+} else {
+call(args...);
+func();
+}
+} else {
+if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
+if(func(call(args...))) { break; }
+} else {
+func(call(args...));
+}
+}
+}
+}
+
+private:
+container_type calls;
+};
+
+/**
+ * @brief Connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.
+ */
+class connection {
+/*! @brief A sink is allowed to create connection objects. */
+template<typename>
+friend class sink;
+
+connection(delegate<void(void *)> fn, void *ref)
+: disconnect{fn}, signal{ref} {}
+
+public:
+/*! @brief Default constructor. */
+connection() = default;
+
+/**
+     * @brief Checks whether a connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(disconnect);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+if(disconnect) {
+disconnect(signal);
+disconnect.reset();
+}
+}
+
+private:
+delegate<void(void *)> disconnect;
+void *signal{};
+};
+
+/**
+ * @brief Scoped connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.<br/>
+ * A scoped connection automatically breaks the link between the two objects
+ * when it goes out of scope.
+ */
+struct scoped_connection {
+/*! @brief Default constructor. */
+scoped_connection() = default;
+
+/**
+     * @brief Constructs a scoped connection from a basic connection.
+     * @param other A valid connection object.
+     */
+scoped_connection(const connection &other)
+: conn{other} {}
+
+/*! @brief Default copy constructor, deleted on purpose. */
+scoped_connection(const scoped_connection &) = delete;
+
+/**
+     * @brief Move constructor.
+     * @param other The scoped connection to move from.
+     */
+scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT
+: conn{std::exchange(other.conn, {})} {}
+
+/*! @brief Automatically breaks the link on destruction. */
+~scoped_connection() {
+conn.release();
+}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(const scoped_connection &) = delete;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The scoped connection to move from.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT {
+conn = std::exchange(other.conn, {});
+return *this;
+}
+
+/**
+     * @brief Acquires a connection.
+     * @param other The connection object to acquire.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(connection other) {
+conn = std::move(other);
+return *this;
+}
+
+/**
+     * @brief Checks whether a scoped connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(conn);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+conn.release();
+}
+
+private:
+connection conn;
+};
+
+/**
+ * @brief Sink class.
+ *
+ * A sink is used to connect listeners to signals and to disconnect them.<br/>
+ * The function type for a listener is the one of the signal to which it
+ * belongs.
+ *
+ * The clear separation between a signal and a sink permits to store the former
+ * as private data member without exposing the publish functionality to the
+ * users of the class.
+ *
+ * @warning
+ * Lifetime of a sink must not overcome that of the signal to which it refers.
+ * In any other case, attempting to use a sink results in undefined behavior.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sink<sigh<Ret(Args...), Allocator>> {
+using signal_type = sigh<Ret(Args...), Allocator>;
+using difference_type = typename signal_type::container_type::difference_type;
+
+template<auto Candidate, typename Type>
+static void release(Type value_or_instance, void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
+}
+
+template<auto Candidate>
+static void release(void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
+}
+
+public:
+/**
+     * @brief Constructs a sink that is allowed to modify a given signal.
+     * @param ref A valid reference to a signal object.
+     */
+sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT
+: offset{},
+signal{&ref} {}
+
+/**
+     * @brief Returns false if at least a listener is connected to the sink.
+     * @return True if the sink has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return signal->calls.empty();
+}
+
+/**
+     * @brief Returns a sink that connects before a given free function or an
+     * unbound member.
+     * @tparam Function A valid free function pointer.
+     * @return A properly initialized sink object.
+     */
+template<auto Function>
+[[nodiscard]] sink before() {
+delegate<Ret(Args...)> call{};
+call.template connect<Function>();
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a free function with payload
+     * or a bound member.
+     * @tparam Candidate Member or free function to look for.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<auto Candidate, typename Type>
+[[nodiscard]] sink before(Type &&value_or_instance) {
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type &value_or_instance) {
+return before(&value_or_instance);
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type *value_or_instance) {
+sink other{*this};
+
+if(value_or_instance) {
+const auto &calls = signal->calls;
+const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
+return delegate.data() == value_or_instance;
+});
+
+other.offset = calls.cend() - it;
+}
+
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before anything else.
+     * @return A properly initialized sink object.
+     */
+[[nodiscard]] sink before() {
+sink other{*this};
+other.offset = signal->calls.size();
+return other;
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a signal.
+     *
+     * The signal handler performs checks to avoid multiple connections for the
+     * same function.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate>
+connection connect() {
+disconnect<Candidate>();
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate>>();
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * signal.
+     *
+     * The signal isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the signal. On the other side, the signal handler performs
+     * checks to avoid multiple connections for the same function.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the signal itself.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate, typename Type>
+connection connect(Type &&value_or_instance) {
+disconnect<Candidate>(value_or_instance);
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate, Type>>(value_or_instance);
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Disconnects a free function or an unbound member from a signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     */
+template<auto Candidate>
+void disconnect() {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects a free function with payload or a bound member from a
+     * signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void disconnect(Type &&value_or_instance) {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type &value_or_instance) {
+disconnect(&value_or_instance);
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type *value_or_instance) {
+if(value_or_instance) {
+auto &calls = signal->calls;
+auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
+calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
+}
+}
+
+/*! @brief Disconnects all the listeners from a signal. */
+void disconnect() {
+signal->calls.clear();
+}
+
+private:
+difference_type offset;
+signal_type *signal;
+};
+
+/**
+ * @brief Deduction guide.
+ *
+ * It allows to deduce the signal handler type of a sink directly from the
+ * signal it refers to.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Mixin type used to add signal support to storage types.
+ *
+ * The function type of a listener is equivalent to:
+ *
+ * @code{.cpp}
+ * void(basic_registry<entity_type> &, entity_type);
+ * @endcode
+ *
+ * This applies to all signals made available.
+ *
+ * @tparam Type The type of the underlying storage.
+ */
+template<typename Type>
+class sigh_storage_mixin final: public Type {
+using basic_iterator = typename Type::basic_iterator;
+
+template<typename Func>
+void notify_destruction(basic_iterator first, basic_iterator last, Func func) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+
+for(; first != last; ++first) {
+const auto entt = *first;
+destruction.publish(*owner, entt);
+const auto it = Type::find(entt);
+func(it, it + 1u);
+}
+}
+
+void swap_and_pop(basic_iterator first, basic_iterator last) final {
+notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); });
+}
+
+void in_place_pop(basic_iterator first, basic_iterator last) final {
+notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); });
+}
+
+basic_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::try_emplace(entt, force_back, value);
+construction.publish(*owner, entt);
+return Type::find(entt);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = typename Type::entity_type;
+
+/*! @brief Inherited constructors. */
+using Type::Type;
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever a new instance is created and assigned to an entity.<br/>
+     * Listeners are invoked after the object has been assigned to the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
+return sink{construction};
+}
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is explicitly updated.<br/>
+     * Listeners are invoked after the object has been updated.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_update() ENTT_NOEXCEPT {
+return sink{update};
+}
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is removed from an entity and thus destroyed.<br/>
+     * Listeners are invoked before the object has been removed from the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
+return sink{destruction};
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     * @param args Parameters to use to initialize the object.
+     * @return A reference to the newly created object.
+     */
+template<typename... Args>
+decltype(auto) emplace(const entity_type entt, Args &&...args) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::emplace(entt, std::forward<Args>(args)...);
+construction.publish(*owner, entt);
+return this->get(entt);
+}
+
+/**
+     * @brief Patches the given instance for an entity.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched instance.
+     */
+template<typename... Func>
+decltype(auto) patch(const entity_type entt, Func &&...func) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::patch(entt, std::forward<Func>(func)...);
+update.publish(*owner, entt);
+return this->get(entt);
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam It Type of input iterator.
+     * @tparam Args Types of arguments to use to construct the objects assigned
+     * to the entities.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param args Parameters to use to initialize the objects assigned to the
+     * entities.
+     */
+template<typename It, typename... Args>
+void insert(It first, It last, Args &&...args) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::insert(first, last, std::forward<Args>(args)...);
+
+for(auto it = construction.empty() ? last : first; it != last; ++it) {
+construction.publish(*owner, *it);
+}
+}
+
+/**
+     * @brief Forwards variables to mixins, if any.
+     * @param value A variable wrapped in an opaque container.
+     */
+void bind(any value) ENTT_NOEXCEPT final {
+auto *reg = any_cast<basic_registry<entity_type>>(&value);
+owner = reg ? reg : owner;
+Type::bind(std::move(value));
+}
+
+private:
+sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
+sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
+sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
+basic_registry<entity_type> *owner{};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "sparse_set.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Container>
+class storage_iterator final {
+friend storage_iterator<const Container>;
+
+using container_type = std::remove_const_t<Container>;
+using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
+using comp_traits = component_traits<typename container_type::value_type>;
+
+using iterator_traits = std::iterator_traits<std::conditional_t<
+std::is_const_v<Container>,
+typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer,
+typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>;
+
+public:
+using value_type = typename iterator_traits::value_type;
+using pointer = typename iterator_traits::pointer;
+using reference = typename iterator_traits::reference;
+using difference_type = typename iterator_traits::difference_type;
+using iterator_category = std::random_access_iterator_tag;
+
+storage_iterator() ENTT_NOEXCEPT = default;
+
+storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT
+: packed{ref},
+offset{idx} {}
+
+template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
+storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) ENTT_NOEXCEPT
+: packed{other.packed},
+offset{other.offset} {}
+
+storage_iterator &operator++() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+storage_iterator operator++(int) ENTT_NOEXCEPT {
+storage_iterator orig = *this;
+return ++(*this), orig;
+}
+
+storage_iterator &operator--() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+storage_iterator operator--(int) ENTT_NOEXCEPT {
+storage_iterator orig = *this;
+return operator--(), orig;
+}
+
+storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+offset -= value;
+return *this;
+}
+
+storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+storage_iterator copy = *this;
+return (copy += value);
+}
+
+storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+const auto pos = index() - value;
+return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+const auto pos = index();
+return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
+return offset - 1;
+}
+
+private:
+Container *packed;
+difference_type offset;
+};
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return rhs.index() - lhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() > rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It, typename... Other>
+class extended_storage_iterator final {
+template<typename Iter, typename... Args>
+friend class extended_storage_iterator;
+
+public:
+using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+extended_storage_iterator() = default;
+
+extended_storage_iterator(It base, Other... other)
+: it{base, other...} {}
+
+template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
+extended_storage_iterator(const extended_storage_iterator<It, Args...> &other)
+: it{other.it} {}
+
+extended_storage_iterator &operator++() ENTT_NOEXCEPT {
+return ++std::get<It>(it), (++std::get<Other>(it), ...), *this;
+}
+
+extended_storage_iterator operator++(int) ENTT_NOEXCEPT {
+extended_storage_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {*std::get<It>(it), *std::get<Other>(it)...};
+}
+
+template<typename... CLhs, typename... CRhs>
+friend bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) ENTT_NOEXCEPT;
+
+private:
+std::tuple<It, Other...> it;
+};
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+return std::get<0>(lhs.it) == std::get<0>(rhs.it);
+}
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic storage implementation.
+ *
+ * Internal data structures arrange elements to maximize performance. There are
+ * no guarantees that objects are returned in the insertion order when iterate
+ * a storage. Do not make assumption on the order in any case.
+ *
+ * @warning
+ * Empty types aren't explicitly instantiated. Therefore, many of the functions
+ * normally available for non-empty types will not be available for empty ones.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Type of objects assigned to the entities.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Type, typename Allocator, typename>
+class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
+static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The type must be at least move constructible/assignable");
+
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
+using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
+using comp_traits = component_traits<Type>;
+
+[[nodiscard]] auto &element_at(const std::size_t pos) const {
+return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
+}
+
+auto assure_at_least(const std::size_t pos) {
+auto &&container = packed.first();
+const auto idx = pos / comp_traits::page_size;
+
+if(!(idx < container.size())) {
+auto curr = container.size();
+container.resize(idx + 1u, nullptr);
+
+ENTT_TRY {
+for(const auto last = container.size(); curr < last; ++curr) {
+container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size);
+}
+}
+ENTT_CATCH {
+container.resize(curr);
+ENTT_THROW;
+}
+}
+
+return container[idx] + fast_mod(pos, comp_traits::page_size);
+}
+
+template<typename... Args>
+auto emplace_element(const Entity entt, const bool force_back, Args &&...args) {
+const auto it = base_type::try_emplace(entt, force_back);
+
+ENTT_TRY {
+auto elem = assure_at_least(static_cast<size_type>(it.index()));
+entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+if constexpr(comp_traits::in_place_delete) {
+base_type::in_place_pop(it, it + 1u);
+} else {
+base_type::swap_and_pop(it, it + 1u);
+}
+
+ENTT_THROW;
+}
+
+return it;
+}
+
+void shrink_to_size(const std::size_t sz) {
+for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
+if constexpr(comp_traits::in_place_delete) {
+if(base_type::at(pos) != tombstone) {
+std::destroy_at(std::addressof(element_at(pos)));
+}
+} else {
+std::destroy_at(std::addressof(element_at(pos)));
+}
+}
+
+auto &&container = packed.first();
+auto page_allocator{packed.second()};
+const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size;
+
+for(auto pos = from, last = container.size(); pos < last; ++pos) {
+alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size);
+}
+
+container.resize(from);
+}
+
+private:
+const void *get_at(const std::size_t pos) const final {
+return std::addressof(element_at(pos));
+}
+
+void swap_at(const std::size_t lhs, const std::size_t rhs) final {
+using std::swap;
+swap(element_at(lhs), element_at(rhs));
+}
+
+void move_element(const std::size_t from, const std::size_t to) final {
+auto &elem = element_at(from);
+entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem));
+std::destroy_at(std::addressof(elem));
+}
+
+protected:
+/**
+     * @brief Erases elements from a storage.
+     * @param first An iterator to the first element to erase.
+     * @param last An iterator past the last element to erase.
+     */
+void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
+for(; first != last; ++first) {
+auto &elem = element_at(base_type::size() - 1u);
+// destroying on exit allows reentrant destructors
+[[maybe_unused]] auto unused = std::exchange(element_at(static_cast<size_type>(first.index())), std::move(elem));
+std::destroy_at(std::addressof(elem));
+base_type::swap_and_pop(first, first + 1u);
+}
+}
+
+/**
+     * @brief Erases elements from a storage.
+     * @param first An iterator to the first element to erase.
+     * @param last An iterator past the last element to erase.
+     */
+void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
+for(; first != last; ++first) {
+base_type::in_place_pop(first, first + 1u);
+std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index()))));
+}
+}
+
+/**
+     * @brief Assigns an entity to a storage.
+     * @param entt A valid identifier.
+     * @param value Optional opaque value.
+     * @param force_back Force back insertion.
+     * @return Iterator pointing to the emplaced element.
+     */
+typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override {
+if(value) {
+if constexpr(std::is_copy_constructible_v<value_type>) {
+return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
+} else {
+return base_type::end();
+}
+} else {
+if constexpr(std::is_default_constructible_v<value_type>) {
+return emplace_element(entt, force_back);
+} else {
+return base_type::end();
+}
+}
+}
+
+public:
+/*! @brief Base type. */
+using base_type = underlying_type;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Type of the objects assigned to entities. */
+using value_type = Type;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Pointer type to contained elements. */
+using pointer = typename container_type::pointer;
+/*! @brief Constant pointer type to contained elements. */
+using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
+/*! @brief Random access iterator type. */
+using iterator = internal::storage_iterator<container_type>;
+/*! @brief Constant random access iterator type. */
+using const_iterator = internal::storage_iterator<const container_type>;
+/*! @brief Reverse iterator type. */
+using reverse_iterator = std::reverse_iterator<iterator>;
+/*! @brief Constant reverse iterator type. */
+using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+/*! @brief Extended iterable storage proxy. */
+using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
+/*! @brief Constant extended iterable storage proxy. */
+using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
+
+/*! @brief Default constructor. */
+basic_storage()
+: basic_storage{allocator_type{}} {}
+
+/**
+     * @brief Constructs an empty storage with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_storage(const allocator_type &allocator)
+: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator},
+packed{container_type{allocator}, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_storage(basic_storage &&other) ENTT_NOEXCEPT
+: base_type{std::move(other)},
+packed{std::move(other.packed)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: base_type{std::move(other), allocator},
+packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
+}
+
+/*! @brief Default destructor. */
+~basic_storage() override {
+shrink_to_size(0u);
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
+
+shrink_to_size(0u);
+base_type::operator=(std::move(other));
+packed.first() = std::move(other.packed.first());
+propagate_on_container_move_assignment(packed.second(), other.packed.second());
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given storage.
+     * @param other Storage to exchange the content with.
+     */
+void swap(basic_storage &other) {
+using std::swap;
+underlying_type::swap(other);
+propagate_on_container_swap(packed.second(), other.packed.second());
+swap(packed.first(), other.packed.first());
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return allocator_type{packed.second()};
+}
+
+/**
+     * @brief Increases the capacity of a storage.
+     *
+     * If the new capacity is greater than the current capacity, new storage is
+     * allocated, otherwise the method does nothing.
+     *
+     * @param cap Desired capacity.
+     */
+void reserve(const size_type cap) override {
+if(cap != 0u) {
+base_type::reserve(cap);
+assure_at_least(cap - 1u);
+}
+}
+
+/**
+     * @brief Returns the number of elements that a storage has currently
+     * allocated space for.
+     * @return Capacity of the storage.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override {
+return packed.first().size() * comp_traits::page_size;
+}
+
+/*! @brief Requests the removal of unused capacity. */
+void shrink_to_fit() override {
+base_type::shrink_to_fit();
+shrink_to_size(base_type::size());
+}
+
+/**
+     * @brief Direct access to the array of objects.
+     * @return A pointer to the array of objects.
+     */
+[[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT {
+return packed.first().data();
+}
+
+/*! @copydoc raw */
+[[nodiscard]] pointer raw() ENTT_NOEXCEPT {
+return packed.first().data();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the storage is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
+return const_iterator{&packed.first(), pos};
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
+return iterator{&packed.first(), pos};
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return const_iterator{&packed.first(), {}};
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return iterator{&packed.first(), {}};
+}
+
+/**
+     * @brief Returns a reverse iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the reversed
+     * internal array. If the storage is empty, the returned iterator will be
+     * equal to `rend()`.
+     *
+     * @return An iterator to the first instance of the reversed internal array.
+     */
+[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(cend());
+}
+
+/*! @copydoc crbegin */
+[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return crbegin();
+}
+
+/*! @copydoc rbegin */
+[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT {
+return std::make_reverse_iterator(end());
+}
+
+/**
+     * @brief Returns a reverse iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the reversed internal array. Attempting to dereference the returned
+     * iterator results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * reversed internal array.
+     */
+[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(cbegin());
+}
+
+/*! @copydoc crend */
+[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT {
+return crend();
+}
+
+/*! @copydoc rend */
+[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT {
+return std::make_reverse_iterator(begin());
+}
+
+/**
+     * @brief Returns the object assigned to an entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return The object assigned to the entity.
+     */
+[[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT {
+return element_at(base_type::index(entt));
+}
+
+/*! @copydoc get */
+[[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT {
+return const_cast<value_type &>(std::as_const(*this).get(entt));
+}
+
+/**
+     * @brief Returns the object assigned to an entity as a tuple.
+     * @param entt A valid identifier.
+     * @return The object assigned to the entity as a tuple.
+     */
+[[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const ENTT_NOEXCEPT {
+return std::forward_as_tuple(get(entt));
+}
+
+/*! @copydoc get_as_tuple */
+[[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) ENTT_NOEXCEPT {
+return std::forward_as_tuple(get(entt));
+}
+
+/**
+     * @brief Assigns an entity to a storage and constructs its object.
+     *
+     * @warning
+     * Attempting to use an entity that already belongs to the storage results
+     * in undefined behavior.
+     *
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     * @param args Parameters to use to construct an object for the entity.
+     * @return A reference to the newly created object.
+     */
+template<typename... Args>
+value_type &emplace(const entity_type entt, Args &&...args) {
+if constexpr(std::is_aggregate_v<value_type>) {
+const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
+return element_at(static_cast<size_type>(it.index()));
+} else {
+const auto it = emplace_element(entt, false, std::forward<Args>(args)...);
+return element_at(static_cast<size_type>(it.index()));
+}
+}
+
+/**
+     * @brief Updates the instance assigned to a given entity in-place.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the updated instance.
+     */
+template<typename... Func>
+value_type &patch(const entity_type entt, Func &&...func) {
+const auto idx = base_type::index(entt);
+auto &elem = element_at(idx);
+(std::forward<Func>(func)(elem), ...);
+return elem;
+}
+
+/**
+     * @brief Assigns one or more entities to a storage and constructs their
+     * objects from a given instance.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the storage
+     * results in undefined behavior.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param value An instance of the object to construct.
+     */
+template<typename It>
+void insert(It first, It last, const value_type &value = {}) {
+for(; first != last; ++first) {
+emplace_element(*first, true, value);
+}
+}
+
+/**
+     * @brief Assigns one or more entities to a storage and constructs their
+     * objects from a given range.
+     *
+     * @sa construct
+     *
+     * @tparam EIt Type of input iterator.
+     * @tparam CIt Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param from An iterator to the first element of the range of objects.
+     */
+template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
+void insert(EIt first, EIt last, CIt from) {
+for(; first != last; ++first, ++from) {
+emplace_element(*first, true, *from);
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a storage.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a reference to its component.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+[[nodiscard]] iterable each() ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
+}
+
+/*! @copydoc each */
+[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
+}
+
+private:
+compressed_pair<container_type, allocator_type> packed;
+};
+
+/*! @copydoc basic_storage */
+template<typename Entity, typename Type, typename Allocator>
+class basic_storage<Entity, Type, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
+: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
+using comp_traits = component_traits<Type>;
+
+public:
+/*! @brief Base type. */
+using base_type = underlying_type;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Type of the objects assigned to entities. */
+using value_type = Type;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Extended iterable storage proxy. */
+using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
+/*! @brief Constant extended iterable storage proxy. */
+using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
+
+/*! @brief Default constructor. */
+basic_storage()
+: basic_storage{allocator_type{}} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_storage(const allocator_type &allocator)
+: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: base_type{std::move(other), allocator} {}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return allocator_type{base_type::get_allocator()};
+}
+
+/**
+     * @brief Returns the object assigned to an entity, that is `void`.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+}
+
+/**
+     * @brief Returns an empty tuple.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return Returns an empty tuple.
+     */
+[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+return std::tuple{};
+}
+
+/**
+     * @brief Assigns an entity to a storage and constructs its object.
+     *
+     * @warning
+     * Attempting to use an entity that already belongs to the storage results
+     * in undefined behavior.
+     *
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     */
+template<typename... Args>
+void emplace(const entity_type entt, Args &&...) {
+base_type::try_emplace(entt, false);
+}
+
+/**
+     * @brief Updates the instance assigned to a given entity in-place.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     */
+template<typename... Func>
+void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+(std::forward<Func>(func)(), ...);
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam It Type of input iterator.
+     * @tparam Args Types of optional arguments.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It, typename... Args>
+void insert(It first, It last, Args &&...) {
+for(; first != last; ++first) {
+base_type::try_emplace(*first, true);
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a storage.
+     *
+     * The iterable object returns a tuple that contains the current entity.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+[[nodiscard]] iterable each() ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
+}
+
+/*! @copydoc each */
+[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
+}
+};
+
+/**
+ * @brief Provides a common way to access certain properties of storage types.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Type of objects managed by the storage class.
+ */
+template<typename Entity, typename Type, typename = void>
+struct storage_traits {
+/*! @brief Resulting type after component-to-storage conversion. */
+using storage_type = sigh_storage_mixin<basic_storage<Entity, Type>>;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "utility.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Group.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename, typename, typename, typename>
+class basic_group;
+
+/**
+ * @brief Non-owning group.
+ *
+ * A non-owning group returns all entities and only the entities that have at
+ * least the given components. Moreover, it's guaranteed that the entity list
+ * is tightly packed in memory for fast iterations.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the group in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @note
+ * Groups share references to the underlying data structures of the registry
+ * that generated them. Therefore any change to the entities and to the
+ * components made by means of the registry are immediately reflected by all the
+ * groups.<br/>
+ * Moreover, sorting a non-owning group affects all the instances of the same
+ * group (it means that users don't have to call `sort` on each instance to sort
+ * all of them because they _share_ entities and components).
+ *
+ * @warning
+ * Lifetime of a group must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a group results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Get Type of components observed by the group.
+ * @tparam Exclude Types of components used to filter the group.
+ */
+template<typename Entity, typename... Get, typename... Exclude>
+class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
+/*! @brief A registry is allowed to create groups. */
+friend class basic_registry<Entity>;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
+
+struct extended_group_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_group_iterator() = default;
+
+extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args)
+: it{from},
+pools{args} {}
+
+extended_group_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+extended_group_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+const auto entt = *it;
+return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return other.it == it;
+}
+
+[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+typename basic_common_type::iterator it;
+std::tuple<storage_type<Get> *...> pools;
+};
+
+basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
+: handler{&ref},
+pools{&gpool...} {}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable group type. */
+using iterable = iterable_adaptor<extended_group_iterator>;
+
+/*! @brief Default constructor to use to create empty, invalid groups. */
+basic_group() ENTT_NOEXCEPT
+: handler{} {}
+
+/**
+     * @brief Returns a const reference to the underlying handler.
+     * @return A const reference to the underlying handler.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *handler;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given components.
+     * @return Number of entities that have the given components.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return *this ? handler->size() : size_type{};
+}
+
+/**
+     * @brief Returns the number of elements that a group has currently
+     * allocated space for.
+     * @return Capacity of the group.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
+return *this ? handler->capacity() : size_type{};
+}
+
+/*! @brief Requests the removal of unused capacity. */
+void shrink_to_fit() {
+if(*this) {
+handler->shrink_to_fit();
+}
+}
+
+/**
+     * @brief Checks whether a group is empty.
+     * @return True if the group is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return !*this || handler->empty();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the group.
+     *
+     * The returned iterator points to the first entity of the group. If the
+     * group is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the group.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return *this ? handler->begin() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the group. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * group.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return *this ? handler->end() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed group.
+     *
+     * The returned iterator points to the first entity of the reversed group.
+     * If the group is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed group.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return *this ? handler->rbegin() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed group. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed group.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return *this ? handler->rend() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns the first entity of the group, if any.
+     * @return The first entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the group, if any.
+     * @return The last entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+const auto it = rbegin();
+return it != rend() ? *it : null;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+const auto it = *this ? handler->find(entt) : iterator{};
+return it != end() && *it == entt ? it : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Checks if a group is properly initialized.
+     * @return True if the group is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return handler != nullptr;
+}
+
+/**
+     * @brief Checks if a group contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the group contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return *this && handler->contains(entt);
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `registry::get` during iterations. It has
+     * far better performance than its counterpart.
+     *
+     * @warning
+     * Attempting to use an invalid component type results in a compilation
+     * error. Attempting to use an entity that doesn't belong to the group
+     * results in undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "Group does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(const auto entt: *this) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
+std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
+} else {
+std::apply(func, get(entt));
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a group.
+     *
+     * The iterable object returns tuples that contain the current entity and a
+     * set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @return An iterable object to use to _visit_ the group.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
+: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
+}
+
+/**
+     * @brief Sort a group according to the given comparison function.
+     *
+     * Sort the group so that iterating it with a couple of iterators returns
+     * entities and components in the expected order. See `begin` and `end` for
+     * more details.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(std::tuple<Component &...>, std::tuple<Component &...>);
+     * bool(const Component &..., const Component &...);
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Where `Component` are such that they are iterated by the group.<br/>
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Comp Optional types of components to compare.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+if(*this) {
+if constexpr(sizeof...(Comp) == 0) {
+static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
+handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
+} else {
+auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
+if constexpr(sizeof...(Comp) == 1) {
+return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
+} else {
+return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
+}
+};
+
+handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
+}
+}
+}
+
+/**
+     * @brief Sort the shared pool of entities according to the given component.
+     *
+     * Non-owning groups of the same type share with the registry a pool of
+     * entities with its own order that doesn't depend on the order of any pool
+     * of components. Users can order the underlying data structure so that it
+     * respects the order of the pool of the given component.
+     *
+     * @note
+     * The shared pool of entities and thus its order is affected by the changes
+     * to each and every pool that it tracks. Therefore changes to those pools
+     * can quickly ruin the order imposed to the pool of entities shared between
+     * the non-owning groups.
+     *
+     * @tparam Comp Type of component to use to impose the order.
+     */
+template<typename Comp>
+void sort() const {
+if(*this) {
+handler->respect(*std::get<storage_type<Comp> *>(pools));
+}
+}
+
+private:
+base_type *const handler;
+const std::tuple<storage_type<Get> *...> pools;
+};
+
+/**
+ * @brief Owning group.
+ *
+ * Owning groups return all entities and only the entities that have at least
+ * the given components. Moreover:
+ *
+ * * It's guaranteed that the entity list is tightly packed in memory for fast
+ *   iterations.
+ * * It's guaranteed that the lists of owned components are tightly packed in
+ *   memory for even faster iterations and to allow direct access.
+ * * They stay true to the order of the owned components and all instances have
+ *   the same order in memory.
+ *
+ * The more types of components are owned by a group, the faster it is to
+ * iterate them.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the group in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @note
+ * Groups share references to the underlying data structures of the registry
+ * that generated them. Therefore any change to the entities and to the
+ * components made by means of the registry are immediately reflected by all the
+ * groups.
+ * Moreover, sorting an owning group affects all the instance of the same group
+ * (it means that users don't have to call `sort` on each instance to sort all
+ * of them because they share the underlying data structure).
+ *
+ * @warning
+ * Lifetime of a group must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a group results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Owned Types of components owned by the group.
+ * @tparam Get Types of components observed by the group.
+ * @tparam Exclude Types of components used to filter the group.
+ */
+template<typename Entity, typename... Owned, typename... Get, typename... Exclude>
+class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
+/*! @brief A registry is allowed to create groups. */
+friend class basic_registry<Entity>;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
+
+class extended_group_iterator final {
+template<typename Type>
+auto index_to_element(storage_type<Type> &cpool) const {
+if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
+return std::make_tuple();
+} else {
+return std::forward_as_tuple(cpool.rbegin()[it.index()]);
+}
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_group_iterator() = default;
+
+template<typename... Other>
+extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> &cpools)
+: it{from},
+pools{cpools} {}
+
+extended_group_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+extended_group_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return std::tuple_cat(
+std::make_tuple(*it),
+index_to_element<Owned>(*std::get<storage_type<Owned> *>(pools))...,
+std::get<storage_type<Get> *>(pools)->get_as_tuple(*it)...);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return other.it == it;
+}
+
+[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+typename basic_common_type::iterator it;
+std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
+};
+
+basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
+: pools{&opool..., &gpool...},
+length{&extent} {}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable group type. */
+using iterable = iterable_adaptor<extended_group_iterator>;
+
+/*! @brief Default constructor to use to create empty, invalid groups. */
+basic_group() ENTT_NOEXCEPT
+: length{} {}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given components.
+     * @return Number of entities that have the given components.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return *this ? *length : size_type{};
+}
+
+/**
+     * @brief Checks whether a group is empty.
+     * @return True if the group is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return !*this || !*length;
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the group.
+     *
+     * The returned iterator points to the first entity of the group. If the
+     * group is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the group.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the group. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * group.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return *this ? std::get<0>(pools)->base_type::end() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed group.
+     *
+     * The returned iterator points to the first entity of the reversed group.
+     * If the group is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed group.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed group. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed group.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
+}
+
+/**
+     * @brief Returns the first entity of the group, if any.
+     * @return The first entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the group, if any.
+     * @return The last entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+const auto it = rbegin();
+return it != rend() ? *it : null;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
+return it != end() && it >= begin() && *it == entt ? it : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Checks if a group is properly initialized.
+     * @return True if the group is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return length != nullptr;
+}
+
+/**
+     * @brief Checks if a group contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the group contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `registry::get` during iterations. It has
+     * far better performance than its counterpart.
+     *
+     * @warning
+     * Attempting to use an invalid component type results in a compilation
+     * error. Attempting to use an entity that doesn't belong to the group
+     * results in undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "Group does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(auto args: each()) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
+std::apply(func, args);
+} else {
+std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args);
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a group.
+     *
+     * The iterable object returns tuples that contain the current entity and a
+     * set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @return An iterable object to use to _visit_ the group.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
+return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}};
+}
+
+/**
+     * @brief Sort a group according to the given comparison function.
+     *
+     * Sort the group so that iterating it with a couple of iterators returns
+     * entities and components in the expected order. See `begin` and `end` for
+     * more details.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(std::tuple<Component &...>, std::tuple<Component &...>);
+     * bool(const Component &, const Component &);
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Where `Component` are either owned types or not but still such that they
+     * are iterated by the group.<br/>
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Comp Optional types of components to compare.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
+auto *cpool = std::get<0>(pools);
+
+if constexpr(sizeof...(Comp) == 0) {
+static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
+cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
+} else {
+auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
+if constexpr(sizeof...(Comp) == 1) {
+return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
+} else {
+return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
+}
+};
+
+cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
+}
+
+[this](auto *head, auto *...other) {
+for(auto next = *length; next; --next) {
+const auto pos = next - 1;
+[[maybe_unused]] const auto entt = head->data()[pos];
+(other->swap_elements(other->data()[pos], entt), ...);
+}
+}(std::get<storage_type<Owned> *>(pools)...);
+}
+
+private:
+const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
+const size_type *const length;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/handle.hpp"
+#ifndef ENTT_ENTITY_HANDLE_HPP
+#define ENTT_ENTITY_HANDLE_HPP
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "fwd.hpp"
+
+// #include "registry.hpp"
+#ifndef ENTT_ENTITY_REGISTRY_HPP
+#define ENTT_ENTITY_REGISTRY_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../container/dense_map.hpp"
+#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+
+namespace entt {
+
+template<
+typename Key,
+typename Type,
+typename = std::hash<Key>,
+typename = std::equal_to<Key>,
+typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+typename Type,
+typename = std::hash<Type>,
+typename = std::equal_to<Type>,
+typename = std::allocator<Type>>
+class dense_set;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+using value_type = std::pair<Key, Type>;
+
+template<typename... Args>
+dense_map_node(const std::size_t pos, Args &&...args)
+: next{pos},
+element{std::forward<Args>(args)...} {}
+
+template<typename Allocator, typename... Args>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+: next{pos},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+std::size_t next;
+value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+template<typename>
+friend class dense_map_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_map_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_map_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_map_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_map_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_map_iterator operator--(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_map_iterator copy = *this;
+return (copy += value);
+}
+
+dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].element.first, it[value].element.second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->element.first, it->element.second};
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+template<typename>
+friend class dense_map_local_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].next, *this;
+}
+
+dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it[offset].element.first, it[offset].element.second};
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = internal::dense_map_node<Key, Type>;
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(key), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other, typename... Args>
+[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+template<typename Other, typename Arg>
+[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+it->second = std::forward<Arg>(value);
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
+for(; *curr != last; curr = &packed.first()[*curr].next) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Key;
+/*! @brief Mapped type of the container. */
+using mapped_type = Type;
+/*! @brief Key-value type of the container. */
+using value_type = std::pair<const Key, Type>;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the keys. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the keys for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Input iterator type. */
+using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_map()
+: dense_map(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const allocator_type &allocator)
+: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const allocator_type &allocator)
+: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_map{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_map(const dense_map &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_map(const dense_map &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_map(dense_map &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_map(dense_map &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(const dense_map &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(dense_map &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if the key does not exist.
+     * @param value A key-value pair eventually convertible to the value type.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value.first, value.second);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+}
+
+/**
+     * @copydoc insert
+     * @tparam Arg Type of the key-value pair to insert into the container.
+     */
+template<typename Arg>
+std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+insert(Arg &&value) {
+return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+}
+
+/**
+     * @brief Inserts elements into the container, if their keys do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Inserts an element into the container or assigns to the current
+     * element if the key already exists.
+     * @tparam Arg Type of the value to insert or assign.
+     * @param key A key used both to look up and to insert if not found.
+     * @param value A value to insert or assign.
+     * @return A pair consisting of an iterator to the element and a bool
+     * denoting whether the insertion took place.
+     */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+return insert_or_overwrite(key, std::forward<Arg>(value));
+}
+
+/*! @copydoc insert_or_assign */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+}
+
+/**
+     * @brief Constructs an element in-place, if the key does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+if constexpr(sizeof...(Args) == 0u) {
+return insert_or_do_nothing(key_type{});
+} else if constexpr(sizeof...(Args) == 1u) {
+return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+} else if constexpr(sizeof...(Args) == 2u) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+const auto index = key_to_bucket(node.element.first);
+
+if(auto it = constrained_find(node.element.first, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.next, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Inserts in-place if the key does not exist, does nothing if the
+     * key exists.
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param key A key used both to look up and to insert if not found.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+return insert_or_do_nothing(key, std::forward<Args>(args)...);
+}
+
+/*! @copydoc try_emplace */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(pos->first);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].element.first);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given key.
+     * @param key A key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const key_type &key) {
+for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+if(packed.second()(packed.first()[*curr].element.first, key)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].next;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_map &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Accesses a given element with bounds checking.
+     * @param key A key of an element to find.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &at(const key_type &key) {
+auto it = find(key);
+ENTT_ASSERT(it != end(), "Invalid key");
+return it->second;
+}
+
+/*! @copydoc at */
+[[nodiscard]] const mapped_type &at(const key_type &key) const {
+auto it = find(key);
+ENTT_ASSERT(it != cend(), "Invalid key");
+return it->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](const key_type &key) {
+return insert_or_do_nothing(key).first->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](key_type &&key) {
+return insert_or_do_nothing(std::move(key)).first->second;
+}
+
+/**
+     * @brief Finds an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const key_type &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const key_type &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Finds an element with a key that compares _equivalent_ to a given
+     * value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const key_type &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element with a key that
+     * compares _equivalent_ to a given value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given key.
+     * @param key The value of the key to examine.
+     * @return The bucket for the given key.
+     */
+[[nodiscard]] size_type bucket(const key_type &key) const {
+return key_to_bucket(key);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = key_to_bucket(packed.first()[pos].element.first);
+packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the keys.
+     * @return The function used to hash the keys.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare keys for equality.
+     * @return The function used to compare keys for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+: std::true_type {};
+
+} // namespace std
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "../core/algorithm.hpp"
+
+// #include "../core/any.hpp"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "group.hpp"
+#ifndef ENTT_ENTITY_GROUP_HPP
+#define ENTT_ENTITY_GROUP_HPP
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Group.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename, typename, typename, typename>
+class basic_group;
+
+/**
+ * @brief Non-owning group.
+ *
+ * A non-owning group returns all entities and only the entities that have at
+ * least the given components. Moreover, it's guaranteed that the entity list
+ * is tightly packed in memory for fast iterations.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the group in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @note
+ * Groups share references to the underlying data structures of the registry
+ * that generated them. Therefore any change to the entities and to the
+ * components made by means of the registry are immediately reflected by all the
+ * groups.<br/>
+ * Moreover, sorting a non-owning group affects all the instances of the same
+ * group (it means that users don't have to call `sort` on each instance to sort
+ * all of them because they _share_ entities and components).
+ *
+ * @warning
+ * Lifetime of a group must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a group results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Get Type of components observed by the group.
+ * @tparam Exclude Types of components used to filter the group.
+ */
+template<typename Entity, typename... Get, typename... Exclude>
+class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
+/*! @brief A registry is allowed to create groups. */
+friend class basic_registry<Entity>;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
+
+struct extended_group_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_group_iterator() = default;
+
+extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args)
+: it{from},
+pools{args} {}
+
+extended_group_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+extended_group_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+const auto entt = *it;
+return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return other.it == it;
+}
+
+[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+typename basic_common_type::iterator it;
+std::tuple<storage_type<Get> *...> pools;
+};
+
+basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
+: handler{&ref},
+pools{&gpool...} {}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable group type. */
+using iterable = iterable_adaptor<extended_group_iterator>;
+
+/*! @brief Default constructor to use to create empty, invalid groups. */
+basic_group() ENTT_NOEXCEPT
+: handler{} {}
+
+/**
+     * @brief Returns a const reference to the underlying handler.
+     * @return A const reference to the underlying handler.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *handler;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given components.
+     * @return Number of entities that have the given components.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return *this ? handler->size() : size_type{};
+}
+
+/**
+     * @brief Returns the number of elements that a group has currently
+     * allocated space for.
+     * @return Capacity of the group.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
+return *this ? handler->capacity() : size_type{};
+}
+
+/*! @brief Requests the removal of unused capacity. */
+void shrink_to_fit() {
+if(*this) {
+handler->shrink_to_fit();
+}
+}
+
+/**
+     * @brief Checks whether a group is empty.
+     * @return True if the group is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return !*this || handler->empty();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the group.
+     *
+     * The returned iterator points to the first entity of the group. If the
+     * group is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the group.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return *this ? handler->begin() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the group. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * group.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return *this ? handler->end() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed group.
+     *
+     * The returned iterator points to the first entity of the reversed group.
+     * If the group is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed group.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return *this ? handler->rbegin() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed group. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed group.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return *this ? handler->rend() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns the first entity of the group, if any.
+     * @return The first entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the group, if any.
+     * @return The last entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+const auto it = rbegin();
+return it != rend() ? *it : null;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+const auto it = *this ? handler->find(entt) : iterator{};
+return it != end() && *it == entt ? it : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Checks if a group is properly initialized.
+     * @return True if the group is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return handler != nullptr;
+}
+
+/**
+     * @brief Checks if a group contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the group contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return *this && handler->contains(entt);
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `registry::get` during iterations. It has
+     * far better performance than its counterpart.
+     *
+     * @warning
+     * Attempting to use an invalid component type results in a compilation
+     * error. Attempting to use an entity that doesn't belong to the group
+     * results in undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "Group does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(const auto entt: *this) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
+std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
+} else {
+std::apply(func, get(entt));
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a group.
+     *
+     * The iterable object returns tuples that contain the current entity and a
+     * set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @return An iterable object to use to _visit_ the group.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
+: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
+}
+
+/**
+     * @brief Sort a group according to the given comparison function.
+     *
+     * Sort the group so that iterating it with a couple of iterators returns
+     * entities and components in the expected order. See `begin` and `end` for
+     * more details.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(std::tuple<Component &...>, std::tuple<Component &...>);
+     * bool(const Component &..., const Component &...);
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Where `Component` are such that they are iterated by the group.<br/>
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Comp Optional types of components to compare.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+if(*this) {
+if constexpr(sizeof...(Comp) == 0) {
+static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
+handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
+} else {
+auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
+if constexpr(sizeof...(Comp) == 1) {
+return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
+} else {
+return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
+}
+};
+
+handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
+}
+}
+}
+
+/**
+     * @brief Sort the shared pool of entities according to the given component.
+     *
+     * Non-owning groups of the same type share with the registry a pool of
+     * entities with its own order that doesn't depend on the order of any pool
+     * of components. Users can order the underlying data structure so that it
+     * respects the order of the pool of the given component.
+     *
+     * @note
+     * The shared pool of entities and thus its order is affected by the changes
+     * to each and every pool that it tracks. Therefore changes to those pools
+     * can quickly ruin the order imposed to the pool of entities shared between
+     * the non-owning groups.
+     *
+     * @tparam Comp Type of component to use to impose the order.
+     */
+template<typename Comp>
+void sort() const {
+if(*this) {
+handler->respect(*std::get<storage_type<Comp> *>(pools));
+}
+}
+
+private:
+base_type *const handler;
+const std::tuple<storage_type<Get> *...> pools;
+};
+
+/**
+ * @brief Owning group.
+ *
+ * Owning groups return all entities and only the entities that have at least
+ * the given components. Moreover:
+ *
+ * * It's guaranteed that the entity list is tightly packed in memory for fast
+ *   iterations.
+ * * It's guaranteed that the lists of owned components are tightly packed in
+ *   memory for even faster iterations and to allow direct access.
+ * * They stay true to the order of the owned components and all instances have
+ *   the same order in memory.
+ *
+ * The more types of components are owned by a group, the faster it is to
+ * iterate them.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the group in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @note
+ * Groups share references to the underlying data structures of the registry
+ * that generated them. Therefore any change to the entities and to the
+ * components made by means of the registry are immediately reflected by all the
+ * groups.
+ * Moreover, sorting an owning group affects all the instance of the same group
+ * (it means that users don't have to call `sort` on each instance to sort all
+ * of them because they share the underlying data structure).
+ *
+ * @warning
+ * Lifetime of a group must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a group results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Owned Types of components owned by the group.
+ * @tparam Get Types of components observed by the group.
+ * @tparam Exclude Types of components used to filter the group.
+ */
+template<typename Entity, typename... Owned, typename... Get, typename... Exclude>
+class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
+/*! @brief A registry is allowed to create groups. */
+friend class basic_registry<Entity>;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
+
+class extended_group_iterator final {
+template<typename Type>
+auto index_to_element(storage_type<Type> &cpool) const {
+if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) {
+return std::make_tuple();
+} else {
+return std::forward_as_tuple(cpool.rbegin()[it.index()]);
+}
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_group_iterator() = default;
+
+template<typename... Other>
+extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> &cpools)
+: it{from},
+pools{cpools} {}
+
+extended_group_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+extended_group_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return std::tuple_cat(
+std::make_tuple(*it),
+index_to_element<Owned>(*std::get<storage_type<Owned> *>(pools))...,
+std::get<storage_type<Get> *>(pools)->get_as_tuple(*it)...);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return other.it == it;
+}
+
+[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+typename basic_common_type::iterator it;
+std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
+};
+
+basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
+: pools{&opool..., &gpool...},
+length{&extent} {}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable group type. */
+using iterable = iterable_adaptor<extended_group_iterator>;
+
+/*! @brief Default constructor to use to create empty, invalid groups. */
+basic_group() ENTT_NOEXCEPT
+: length{} {}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given components.
+     * @return Number of entities that have the given components.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return *this ? *length : size_type{};
+}
+
+/**
+     * @brief Checks whether a group is empty.
+     * @return True if the group is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return !*this || !*length;
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the group.
+     *
+     * The returned iterator points to the first entity of the group. If the
+     * group is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the group.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the group. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * group.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return *this ? std::get<0>(pools)->base_type::end() : iterator{};
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed group.
+     *
+     * The returned iterator points to the first entity of the reversed group.
+     * If the group is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed group.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * group.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed group. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed group.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
+}
+
+/**
+     * @brief Returns the first entity of the group, if any.
+     * @return The first entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the group, if any.
+     * @return The last entity of the group if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+const auto it = rbegin();
+return it != rend() ? *it : null;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
+return it != end() && it >= begin() && *it == entt ? it : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Checks if a group is properly initialized.
+     * @return True if the group is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return length != nullptr;
+}
+
+/**
+     * @brief Checks if a group contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the group contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `registry::get` during iterations. It has
+     * far better performance than its counterpart.
+     *
+     * @warning
+     * Attempting to use an invalid component type results in a compilation
+     * error. Attempting to use an entity that doesn't belong to the group
+     * results in undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "Group does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(auto args: each()) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
+std::apply(func, args);
+} else {
+std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args);
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a group.
+     *
+     * The iterable object returns tuples that contain the current entity and a
+     * set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @return An iterable object to use to _visit_ the group.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
+return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}};
+}
+
+/**
+     * @brief Sort a group according to the given comparison function.
+     *
+     * Sort the group so that iterating it with a couple of iterators returns
+     * entities and components in the expected order. See `begin` and `end` for
+     * more details.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(std::tuple<Component &...>, std::tuple<Component &...>);
+     * bool(const Component &, const Component &);
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Where `Component` are either owned types or not but still such that they
+     * are iterated by the group.<br/>
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Comp Optional types of components to compare.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
+auto *cpool = std::get<0>(pools);
+
+if constexpr(sizeof...(Comp) == 0) {
+static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
+cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
+} else {
+auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
+if constexpr(sizeof...(Comp) == 1) {
+return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
+} else {
+return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
+}
+};
+
+cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
+}
+
+[this](auto *head, auto *...other) {
+for(auto next = *length; next; --next) {
+const auto pos = next - 1;
+[[maybe_unused]] const auto entt = head->data()[pos];
+(other->swap_elements(other->data()[pos], entt), ...);
+}
+}(std::get<storage_type<Owned> *>(pools)...);
+}
+
+private:
+const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
+const size_type *const length;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "runtime_view.hpp"
+#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
+#define ENTT_ENTITY_RUNTIME_VIEW_HPP
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Set>
+class runtime_view_iterator final {
+using iterator_type = typename Set::iterator;
+
+[[nodiscard]] bool valid() const {
+return (!tombstone_check || *it != tombstone)
+&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
+&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
+}
+
+public:
+using difference_type = typename iterator_type::difference_type;
+using value_type = typename iterator_type::value_type;
+using pointer = typename iterator_type::pointer;
+using reference = typename iterator_type::reference;
+using iterator_category = std::bidirectional_iterator_tag;
+
+runtime_view_iterator() ENTT_NOEXCEPT
+: pools{},
+filter{},
+it{},
+tombstone_check{} {}
+
+runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT
+: pools{&cpools},
+filter{&ignore},
+it{curr},
+tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} {
+if(it != (*pools)[0]->end() && !valid()) {
+++(*this);
+}
+}
+
+runtime_view_iterator &operator++() {
+while(++it != (*pools)[0]->end() && !valid()) {}
+return *this;
+}
+
+runtime_view_iterator operator++(int) {
+runtime_view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+runtime_view_iterator &operator--() {
+while(--it != (*pools)[0]->begin() && !valid()) {}
+return *this;
+}
+
+runtime_view_iterator operator--(int) {
+runtime_view_iterator orig = *this;
+return operator--(), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return it.operator->();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+return it == other.it;
+}
+
+[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+const std::vector<const Set *> *pools;
+const std::vector<const Set *> *filter;
+iterator_type it;
+bool tombstone_check;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Runtime view implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename>
+struct basic_runtime_view;
+
+/**
+ * @brief Generic runtime view.
+ *
+ * Runtime views iterate over those entities that have at least all the given
+ * components in their bags. During initialization, a runtime view looks at the
+ * number of entities available for each component and picks up a reference to
+ * the smallest set of candidate entities in order to get a performance boost
+ * when iterate.<br/>
+ * Order of elements during iterations are highly dependent on the order of the
+ * underlying data structures. See sparse_set and its specializations for more
+ * details.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all the other cases, modifying the pools of the given components in any
+ * way invalidates all the iterators and using them results in undefined
+ * behavior.
+ *
+ * @note
+ * Views share references to the underlying data structures of the registry that
+ * generated them. Therefore any change to the entities and to the components
+ * made by means of the registry are immediately reflected by the views, unless
+ * a pool was missing when the view was built (in this case, the view won't
+ * have a valid reference and won't be updated accordingly).
+ *
+ * @warning
+ * Lifetime of a view must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a view results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Allocator>
+struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> {
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_sparse_set<Entity, Allocator>;
+/*! @brief Bidirectional iterator type. */
+using iterator = internal::runtime_view_iterator<base_type>;
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_runtime_view() ENTT_NOEXCEPT
+: pools{},
+filter{} {}
+
+/**
+     * @brief Appends an opaque storage object to a runtime view.
+     * @param base An opaque reference to a storage object.
+     * @return This runtime view.
+     */
+basic_runtime_view &iterate(const base_type &base) {
+if(pools.empty() || !(base.size() < pools[0u]->size())) {
+pools.push_back(&base);
+} else {
+pools.push_back(std::exchange(pools[0u], &base));
+}
+
+return *this;
+}
+
+/**
+     * @brief Adds an opaque storage object as a filter of a runtime view.
+     * @param base An opaque reference to a storage object.
+     * @return This runtime view.
+     */
+basic_runtime_view &exclude(const base_type &base) {
+filter.push_back(&base);
+return *this;
+}
+
+/**
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
+     */
+[[nodiscard]] size_type size_hint() const {
+return pools.empty() ? size_type{} : pools.front()->size();
+}
+
+/**
+     * @brief Returns an iterator to the first entity that has the given
+     * components.
+     *
+     * The returned iterator points to the first entity that has the given
+     * components. If the view is empty, the returned iterator will be equal to
+     * `end()`.
+     *
+     * @return An iterator to the first entity that has the given components.
+     */
+[[nodiscard]] iterator begin() const {
+return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity that has the
+     * given components.
+     *
+     * The returned iterator points to the entity following the last entity that
+     * has the given components. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity that has the
+     * given components.
+     */
+[[nodiscard]] iterator end() const {
+return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const {
+return !pools.empty()
+&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
+&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
+}
+
+/**
+     * @brief Iterates entities and applies the given function object to them.
+     *
+     * The function object is invoked for each entity. It is provided only with
+     * the entity itself. To get the components, users can use the registry with
+     * which the view was built.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(const entity_type);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(const auto entity: *this) {
+func(entity);
+}
+}
+
+private:
+std::vector<const base_type *> pools;
+std::vector<const base_type *> filter;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "sparse_set.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+// #include "view.hpp"
+#ifndef ENTT_ENTITY_VIEW_HPP
+#define ENTT_ENTITY_VIEW_HPP
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t Component, std::size_t Exclude>
+class view_iterator final {
+using iterator_type = typename Type::const_iterator;
+
+[[nodiscard]] bool valid() const ENTT_NOEXCEPT {
+return ((Component != 0u) || (*it != tombstone))
+&& std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
+&& std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
+}
+
+public:
+using value_type = typename iterator_type::value_type;
+using pointer = typename iterator_type::pointer;
+using reference = typename iterator_type::reference;
+using difference_type = typename iterator_type::difference_type;
+using iterator_category = std::forward_iterator_tag;
+
+view_iterator() ENTT_NOEXCEPT = default;
+
+view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Component> all_of, std::array<const Type *, Exclude> none_of) ENTT_NOEXCEPT
+: it{curr},
+last{to},
+pools{all_of},
+filter{none_of} {
+if(it != last && !valid()) {
+++(*this);
+}
+}
+
+view_iterator &operator++() ENTT_NOEXCEPT {
+while(++it != last && !valid()) {}
+return *this;
+}
+
+view_iterator operator++(int) ENTT_NOEXCEPT {
+view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return &*it;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+friend bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) ENTT_NOEXCEPT;
+
+private:
+iterator_type it;
+iterator_type last;
+std::array<const Type *, Component> pools;
+std::array<const Type *, Exclude> filter;
+};
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename It, typename... Storage>
+struct extended_view_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_view_iterator() = default;
+
+extended_view_iterator(It from, std::tuple<Storage *...> storage)
+: it{from},
+pools{storage} {}
+
+extended_view_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_view_iterator operator++(int) ENTT_NOEXCEPT {
+extended_view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+template<typename... Lhs, typename... Rhs>
+friend bool operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+std::tuple<Storage *...> pools;
+};
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief View implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename, typename, typename, typename>
+class basic_view;
+
+/**
+ * @brief Multi component view.
+ *
+ * Multi component views iterate over those entities that have at least all the
+ * given components in their bags. During initialization, a multi component view
+ * looks at the number of entities available for each component and uses the
+ * smallest set in order to get a performance boost when iterate.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the view in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Types of components iterated by the view.
+ * @tparam Exclude Types of components used to filter the view.
+ */
+template<typename Entity, typename... Component, typename... Exclude>
+class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> {
+template<typename, typename, typename, typename>
+friend class basic_view;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+template<std::size_t... Index>
+[[nodiscard]] auto pools_to_array(std::index_sequence<Index...>) const ENTT_NOEXCEPT {
+std::size_t pos{};
+std::array<const base_type *, sizeof...(Component) - 1u> other{};
+(static_cast<void>(std::get<Index>(pools) == view ? void() : void(other[pos++] = std::get<Index>(pools))), ...);
+return other;
+}
+
+template<std::size_t Comp, std::size_t Other, typename... Args>
+[[nodiscard]] auto dispatch_get(const std::tuple<Entity, Args...> &curr) const {
+if constexpr(Comp == Other) {
+return std::forward_as_tuple(std::get<Args>(curr)...);
+} else {
+return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr));
+}
+}
+
+template<std::size_t Comp, typename Func, std::size_t... Index>
+void each(Func func, std::index_sequence<Index...>) const {
+for(const auto curr: std::get<Comp>(pools)->each()) {
+const auto entt = std::get<0>(curr);
+
+if(((sizeof...(Component) != 1u) || (entt != tombstone))
+&& ((Comp == Index || std::get<Index>(pools)->contains(entt)) && ...)
+&& std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
+std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Comp, Index>(curr)...));
+} else {
+std::apply(func, std::tuple_cat(dispatch_get<Comp, Index>(curr)...));
+}
+}
+}
+}
+
+template<typename Func, std::size_t... Index>
+void pick_and_each(Func func, std::index_sequence<Index...> seq) const {
+((std::get<Index>(pools) == view ? each<Index>(std::move(func), seq) : void()), ...);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = std::common_type_t<typename storage_type<Component>::base_type...>;
+/*! @brief Bidirectional iterator type. */
+using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>;
+/*! @brief Iterable view type. */
+using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>;
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_view() ENTT_NOEXCEPT
+: pools{},
+filter{},
+view{} {}
+
+/**
+     * @brief Constructs a multi-type view from a set of storage classes.
+     * @param component The storage for the types to iterate.
+     * @param epool The storage for the types used to filter the view.
+     */
+basic_view(storage_type<Component> &...component, const storage_type<Exclude> &...epool) ENTT_NOEXCEPT
+: pools{&component...},
+filter{&epool...},
+view{(std::min)({&static_cast<const base_type &>(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {}
+
+/**
+     * @brief Creates a new view driven by a given component in its iterations.
+     * @tparam Comp Type of component used to drive the iteration.
+     * @return A new view driven by the given component in its iterations.
+     */
+template<typename Comp>
+[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
+basic_view other{*this};
+other.view = std::get<storage_type<Comp> *>(pools);
+return other;
+}
+
+/**
+     * @brief Creates a new view driven by a given component in its iterations.
+     * @tparam Comp Index of the component used to drive the iteration.
+     * @return A new view driven by the given component in its iterations.
+     */
+template<std::size_t Comp>
+[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
+basic_view other{*this};
+other.view = std::get<Comp>(pools);
+return other;
+}
+
+/**
+     * @brief Returns the leading storage of a view.
+     * @return The leading storage of the view.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *view;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
+     */
+[[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT {
+return view->size();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the view.
+     *
+     * The returned iterator points to the first entity of the view. If the view
+     * is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the view.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the view. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the view.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
+}
+
+/**
+     * @brief Returns the first entity of the view, if any.
+     * @return The first entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the view, if any.
+     * @return The last entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+auto it = view->rbegin();
+for(const auto last = view->rend(); it != last && !contains(*it); ++it) {}
+return it == view->rend() ? null : *it;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter} : end();
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     * @param entt A valid identifier.
+     * @return The components assigned to the given entity.
+     */
+[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
+return get<Component...>(entt);
+}
+
+/**
+     * @brief Checks if a view is properly initialized.
+     * @return True if the view is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return view != nullptr;
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
+&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam First Index of a component to get.
+     * @tparam Other Indexes of other components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<std::size_t First, std::size_t... Other>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Other) == 0) {
+return std::get<First>(pools)->get(entt);
+} else {
+return std::tuple_cat(std::get<First>(pools)->get_as_tuple(entt), std::get<Other>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+pick_and_each(std::move(func), std::index_sequence_for<Component...>{});
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a view.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @return An iterable object to use to _visit_ the view.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}};
+}
+
+/**
+     * @brief Combines two views in a _more specific_ one (friend function).
+     * @tparam Get Component list of the view to combine with.
+     * @tparam Excl Filter list of the view to combine with.
+     * @param other The view to combine with.
+     * @return A more specific view.
+     */
+template<typename... Get, typename... Excl>
+[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
+using view_type = basic_view<Entity, get_t<Component..., Get...>, exclude_t<Exclude..., Excl...>>;
+return std::make_from_tuple<view_type>(std::tuple_cat(
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools),
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Exclude> &>(*curr)...); }, filter),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Excl> &>(*curr)...); }, other.filter)));
+}
+
+private:
+std::tuple<storage_type<Component> *...> pools;
+std::array<const base_type *, sizeof...(Exclude)> filter;
+const base_type *view;
+};
+
+/**
+ * @brief Single component view specialization.
+ *
+ * Single component views are specialized in order to get a boost in terms of
+ * performance. This kind of views can access the underlying data structure
+ * directly and avoid superfluous checks.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given component are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, the given
+ *   component is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pool iterated by the view in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Type of component iterated by the view.
+ */
+template<typename Entity, typename Component>
+class basic_view<Entity, get_t<Component>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<std::remove_const_t<Component>>::in_place_delete>>> {
+template<typename, typename, typename, typename>
+friend class basic_view;
+
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = typename storage_type::base_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable view type. */
+using iterable = decltype(std::declval<storage_type>().each());
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_view() ENTT_NOEXCEPT
+: pools{},
+filter{},
+view{} {}
+
+/**
+     * @brief Constructs a single-type view from a storage class.
+     * @param ref The storage for the type to iterate.
+     */
+basic_view(storage_type &ref) ENTT_NOEXCEPT
+: pools{&ref},
+filter{},
+view{&ref} {}
+
+/**
+     * @brief Returns the leading storage of a view.
+     * @return The leading storage of the view.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *view;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp = Component>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+static_assert(std::is_same_v<Comp, Component>, "Invalid component type");
+return *std::get<0>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given component.
+     * @return Number of entities that have the given component.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return view->size();
+}
+
+/**
+     * @brief Checks whether a view is empty.
+     * @return True if the view is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return view->empty();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the view.
+     *
+     * The returned iterator points to the first entity of the view. If the view
+     * is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the view.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return view->begin();
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the view. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the view.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return view->end();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed view.
+     *
+     * The returned iterator points to the first entity of the reversed view. If
+     * the view is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed view.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return view->rbegin();
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed view. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed view.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return view->rend();
+}
+
+/**
+     * @brief Returns the first entity of the view, if any.
+     * @return The first entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+return empty() ? null : *begin();
+}
+
+/**
+     * @brief Returns the last entity of the view, if any.
+     * @return The last entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+return empty() ? null : *rbegin();
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? view->find(entt) : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Returns the component assigned to the given entity.
+     * @param entt A valid identifier.
+     * @return The component assigned to the given entity.
+     */
+[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
+return get<Component>(entt);
+}
+
+/**
+     * @brief Checks if a view is properly initialized.
+     * @return True if the view is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return view != nullptr;
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return view->contains(entt);
+}
+
+/**
+     * @brief Returns the component assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam Comp Type or index of the component to get.
+     * @param entt A valid identifier.
+     * @return The component assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::get<0>(pools)->get_as_tuple(entt);
+} else {
+static_assert(std::is_same_v<Comp..., Component>, "Invalid component type");
+return std::get<0>(pools)->get(entt);
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+return std::get<0>(pools)->get(entt);
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a reference to the component if it's a non-empty one.
+     * The _constness_ of the component is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Component &);
+     * void(Component &);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
+for(const auto pack: each()) {
+std::apply(func, pack);
+}
+} else if constexpr(std::is_invocable_v<Func, Component &>) {
+for(auto &&component: *std::get<0>(pools)) {
+func(component);
+}
+} else if constexpr(std::is_invocable_v<Func, Entity>) {
+for(auto entity: *view) {
+func(entity);
+}
+} else {
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+func();
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a view.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a reference to its component if it's a non-empty one. The _constness_ of
+     * the component is as requested.
+     *
+     * @return An iterable object to use to _visit_ the view.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return std::get<0>(pools)->each();
+}
+
+/**
+     * @brief Combines two views in a _more specific_ one (friend function).
+     * @tparam Get Component list of the view to combine with.
+     * @tparam Excl Filter list of the view to combine with.
+     * @param other The view to combine with.
+     * @return A more specific view.
+     */
+template<typename... Get, typename... Excl>
+[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
+using view_type = basic_view<Entity, get_t<Component, Get...>, exclude_t<Excl...>>;
+return std::make_from_tuple<view_type>(std::tuple_cat(
+std::forward_as_tuple(*std::get<0>(pools)),
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const typename view_type::template storage_type<Excl> &>(*curr)...); }, other.filter)));
+}
+
+private:
+std::tuple<storage_type *> pools;
+std::array<const base_type *, 0u> filter;
+const base_type *view;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Storage Type of storage classes used to create the view.
+ * @param storage The storage for the types to iterate.
+ */
+template<typename... Storage>
+basic_view(Storage &...storage) -> basic_view<std::common_type_t<typename Storage::entity_type...>, get_t<constness_as_t<typename Storage::value_type, Storage>...>, exclude_t<>>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class storage_proxy_iterator final {
+template<typename Other>
+friend class storage_proxy_iterator;
+
+using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>;
+
+public:
+using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_type> &>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+storage_proxy_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+storage_proxy_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+storage_proxy_iterator(const storage_proxy_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+storage_proxy_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+storage_proxy_iterator operator++(int) ENTT_NOEXCEPT {
+storage_proxy_iterator orig = *this;
+return ++(*this), orig;
+}
+
+storage_proxy_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+storage_proxy_iterator operator--(int) ENTT_NOEXCEPT {
+storage_proxy_iterator orig = *this;
+return operator--(), orig;
+}
+
+storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+storage_proxy_iterator copy = *this;
+return (copy += value);
+}
+
+storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].first, *it[value].second};
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->first, *it->second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+struct registry_context {
+template<typename Type, typename... Args>
+Type &emplace_hint(const id_type id, Args &&...args) {
+return any_cast<Type &>(data.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
+}
+
+template<typename Type, typename... Args>
+Type &emplace(Args &&...args) {
+return emplace_hint<Type>(type_id<Type>().hash(), std::forward<Args>(args)...);
+}
+
+template<typename Type>
+bool erase(const id_type id = type_id<Type>().hash()) {
+const auto it = data.find(id);
+return it != data.end() && it->second.type() == type_id<Type>() ? (data.erase(it), true) : false;
+}
+
+template<typename Type>
+[[nodiscard]] std::add_const_t<Type> &at(const id_type id = type_id<Type>().hash()) const {
+return any_cast<std::add_const_t<Type> &>(data.at(id));
+}
+
+template<typename Type>
+[[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) {
+return any_cast<Type &>(data.at(id));
+}
+
+template<typename Type>
+[[nodiscard]] std::add_const_t<Type> *find(const id_type id = type_id<Type>().hash()) const {
+const auto it = data.find(id);
+return it != data.cend() ? any_cast<std::add_const_t<Type>>(&it->second) : nullptr;
+}
+
+template<typename Type>
+[[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) {
+const auto it = data.find(id);
+return it != data.end() ? any_cast<Type>(&it->second) : nullptr;
+}
+
+template<typename Type>
+[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
+const auto it = data.find(id);
+return it != data.end() && it->second.type() == type_id<Type>();
+}
+
+private:
+dense_map<id_type, basic_any<0u>, identity> data;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Fast and reliable entity-component system.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_registry {
+using entity_traits = entt_traits<Entity>;
+using basic_common_type = basic_sparse_set<Entity>;
+
+template<typename Component>
+using storage_type = typename storage_traits<Entity, Component>::storage_type;
+
+template<typename...>
+struct group_handler;
+
+template<typename... Exclude, typename... Get, typename... Owned>
+struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
+// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
+static_assert(!std::disjunction_v<std::bool_constant<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete");
+std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{};
+
+template<typename Component>
+void maybe_valid_if(basic_registry &owner, const Entity entt) {
+[[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
+
+const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...)
+&& ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...)
+&& ((std::is_same_v<Component, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...);
+
+if constexpr(sizeof...(Owned) == 0) {
+if(is_valid && !current.contains(entt)) {
+current.emplace(entt);
+}
+} else {
+if(is_valid && !(std::get<0>(cpools).index(entt) < current)) {
+const auto pos = current++;
+(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
+}
+}
+}
+
+void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) {
+if constexpr(sizeof...(Owned) == 0) {
+current.remove(entt);
+} else {
+if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
+const auto pos = --current;
+(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
+}
+}
+}
+};
+
+struct group_data {
+std::size_t size;
+std::unique_ptr<void, void (*)(void *)> group;
+bool (*owned)(const id_type) ENTT_NOEXCEPT;
+bool (*get)(const id_type) ENTT_NOEXCEPT;
+bool (*exclude)(const id_type) ENTT_NOEXCEPT;
+};
+
+template<typename Component>
+[[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) {
+static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
+auto &&cpool = pools[id];
+
+if(!cpool) {
+cpool.reset(new storage_type<Component>{});
+cpool->bind(forward_as_any(*this));
+}
+
+ENTT_ASSERT(cpool->type() == type_id<Component>(), "Unexpected type");
+return static_cast<storage_type<Component> &>(*cpool);
+}
+
+template<typename Component>
+[[nodiscard]] const auto &assure(const id_type id = type_hash<Component>::value()) const {
+static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
+
+if(const auto it = pools.find(id); it != pools.cend()) {
+ENTT_ASSERT(it->second->type() == type_id<Component>(), "Unexpected type");
+return static_cast<const storage_type<Component> &>(*it->second);
+}
+
+static storage_type<Component> placeholder{};
+return placeholder;
+}
+
+auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT {
+ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available");
+return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {});
+}
+
+auto recycle_identifier() ENTT_NOEXCEPT {
+ENTT_ASSERT(free_list != null, "No entities available");
+const auto curr = entity_traits::to_entity(free_list);
+free_list = entity_traits::combine(entity_traits::to_integral(entities[curr]), tombstone);
+return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr])));
+}
+
+auto release_entity(const Entity entity, const typename entity_traits::version_type version) {
+const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone));
+entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers);
+free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone);
+return vers;
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Underlying version type. */
+using version_type = typename entity_traits::version_type;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Context type. */
+using context = internal::registry_context;
+
+/*! @brief Default constructor. */
+basic_registry()
+: pools{},
+groups{},
+entities{},
+free_list{tombstone},
+vars{} {}
+
+/**
+     * @brief Allocates enough memory upon construction to store `count` pools.
+     * @param count The number of pools to allocate memory for.
+     */
+basic_registry(const size_type count)
+: pools{},
+groups{},
+entities{},
+free_list{tombstone},
+vars{} {
+pools.reserve(count);
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_registry(basic_registry &&other)
+: pools{std::move(other.pools)},
+groups{std::move(other.groups)},
+entities{std::move(other.entities)},
+free_list{other.free_list},
+vars{std::move(other.vars)} {
+for(auto &&curr: pools) {
+curr.second->bind(forward_as_any(*this));
+}
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This registry.
+     */
+basic_registry &operator=(basic_registry &&other) {
+pools = std::move(other.pools);
+groups = std::move(other.groups);
+entities = std::move(other.entities);
+free_list = other.free_list;
+vars = std::move(other.vars);
+
+for(auto &&curr: pools) {
+curr.second->bind(forward_as_any(*this));
+}
+
+return *this;
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a registry.
+     *
+     * The iterable object returns a pair that contains the name and a reference
+     * to the current storage.
+     *
+     * @return An iterable object to use to _visit_ the registry.
+     */
+[[nodiscard]] auto storage() ENTT_NOEXCEPT {
+return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
+}
+
+/*! @copydoc storage */
+[[nodiscard]] auto storage() const ENTT_NOEXCEPT {
+return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}};
+}
+
+/**
+     * @brief Finds the storage associated with a given name, if any.
+     * @param id Name used to map the storage within the registry.
+     * @return An iterator to the given storage if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] auto storage(const id_type id) {
+return internal::storage_proxy_iterator{pools.find(id)};
+}
+
+/**
+     * @brief Finds the storage associated with a given name, if any.
+     * @param id Name used to map the storage within the registry.
+     * @return An iterator to the given storage if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] auto storage(const id_type id) const {
+return internal::storage_proxy_iterator{pools.find(id)};
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Component Type of component of which to return the storage.
+     * @param id Optional name used to map the storage within the registry.
+     * @return The storage for the given component type.
+     */
+template<typename Component>
+decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) {
+if constexpr(std::is_const_v<Component>) {
+return std::as_const(*this).template storage<std::remove_const_t<Component>>(id);
+} else {
+return assure<Component>(id);
+}
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     *
+     * @warning
+     * If a storage for the given component doesn't exist yet, a temporary
+     * placeholder is returned instead.
+     *
+     * @tparam Component Type of component of which to return the storage.
+     * @param id Optional name used to map the storage within the registry.
+     * @return The storage for the given component type.
+     */
+template<typename Component>
+decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) const {
+return assure<std::remove_const_t<Component>>(id);
+}
+
+/**
+     * @brief Returns the number of entities created so far.
+     * @return Number of entities created so far.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return entities.size();
+}
+
+/**
+     * @brief Returns the number of entities still in use.
+     * @return Number of entities still in use.
+     */
+[[nodiscard]] size_type alive() const {
+auto sz = entities.size();
+
+for(auto curr = free_list; curr != null; --sz) {
+curr = entities[entity_traits::to_entity(curr)];
+}
+
+return sz;
+}
+
+/**
+     * @brief Increases the capacity (number of entities) of the registry.
+     * @param cap Desired capacity.
+     */
+void reserve(const size_type cap) {
+entities.reserve(cap);
+}
+
+/**
+     * @brief Returns the number of entities that a registry has currently
+     * allocated space for.
+     * @return Capacity of the registry.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
+return entities.capacity();
+}
+
+/**
+     * @brief Checks whether the registry is empty (no entities still in use).
+     * @return True if the registry is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const {
+return !alive();
+}
+
+/**
+     * @brief Direct access to the list of entities of a registry.
+     *
+     * The returned pointer is such that range `[data(), data() + size())` is
+     * always a valid range, even if the registry is empty.
+     *
+     * @warning
+     * This list contains both valid and destroyed entities and isn't suitable
+     * for direct use.
+     *
+     * @return A pointer to the array of entities.
+     */
+[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
+return entities.data();
+}
+
+/**
+     * @brief Returns the head of the list of released entities.
+     *
+     * This function is intended for use in conjunction with `assign`.<br/>
+     * The returned entity has an invalid identifier in all cases.
+     *
+     * @return The head of the list of released entities.
+     */
+[[nodiscard]] entity_type released() const ENTT_NOEXCEPT {
+return free_list;
+}
+
+/**
+     * @brief Checks if an identifier refers to a valid entity.
+     * @param entity An identifier, either valid or not.
+     * @return True if the identifier is valid, false otherwise.
+     */
+[[nodiscard]] bool valid(const entity_type entity) const {
+const auto pos = size_type(entity_traits::to_entity(entity));
+return (pos < entities.size() && entities[pos] == entity);
+}
+
+/**
+     * @brief Returns the actual version for an identifier.
+     * @param entity A valid identifier.
+     * @return The version for the given identifier if valid, the tombstone
+     * version otherwise.
+     */
+[[nodiscard]] version_type current(const entity_type entity) const {
+const auto pos = size_type(entity_traits::to_entity(entity));
+return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone);
+}
+
+/**
+     * @brief Creates a new entity or recycles a destroyed one.
+     * @return A valid identifier.
+     */
+[[nodiscard]] entity_type create() {
+return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier();
+}
+
+/**
+     * @copybrief create
+     *
+     * If the requested entity isn't in use, the suggested identifier is used.
+     * Otherwise, a new identifier is generated.
+     *
+     * @param hint Required identifier.
+     * @return A valid identifier.
+     */
+[[nodiscard]] entity_type create(const entity_type hint) {
+const auto length = entities.size();
+
+if(hint == null || hint == tombstone) {
+return create();
+} else if(const auto req = entity_traits::to_entity(hint); !(req < length)) {
+entities.resize(size_type(req) + 1u, null);
+
+for(auto pos = length; pos < req; ++pos) {
+release_entity(generate_identifier(pos), {});
+}
+
+return (entities[req] = hint);
+} else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) {
+return create();
+} else {
+auto *it = &free_list;
+for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {}
+*it = entity_traits::combine(curr, entity_traits::to_integral(*it));
+return (entities[req] = hint);
+}
+}
+
+/**
+     * @brief Assigns each element in a range an identifier.
+     *
+     * @sa create
+     *
+     * @tparam It Type of forward iterator.
+     * @param first An iterator to the first element of the range to generate.
+     * @param last An iterator past the last element of the range to generate.
+     */
+template<typename It>
+void create(It first, It last) {
+for(; free_list != null && first != last; ++first) {
+*first = recycle_identifier();
+}
+
+const auto length = entities.size();
+entities.resize(length + std::distance(first, last), null);
+
+for(auto pos = length; first != last; ++first, ++pos) {
+*first = entities[pos] = generate_identifier(pos);
+}
+}
+
+/**
+     * @brief Assigns identifiers to an empty registry.
+     *
+     * This function is intended for use in conjunction with `data`, `size` and
+     * `destroyed`.<br/>
+     * Don't try to inject ranges of randomly generated entities nor the _wrong_
+     * head for the list of destroyed entities. There is no guarantee that a
+     * registry will continue to work properly in this case.
+     *
+     * @warning
+     * There must be no entities still alive for this to work properly.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param destroyed The head of the list of destroyed entities.
+     */
+template<typename It>
+void assign(It first, It last, const entity_type destroyed) {
+ENTT_ASSERT(!alive(), "Entities still alive");
+entities.assign(first, last);
+free_list = destroyed;
+}
+
+/**
+     * @brief Releases an identifier.
+     *
+     * The version is updated and the identifier can be recycled at any time.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @param entity A valid identifier.
+     * @return The version of the recycled entity.
+     */
+version_type release(const entity_type entity) {
+return release(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
+}
+
+/**
+     * @brief Releases an identifier.
+     *
+     * The suggested version or the valid version closest to the suggested one
+     * is used instead of the implicitly generated version.
+     *
+     * @sa release
+     *
+     * @param entity A valid identifier.
+     * @param version A desired version upon destruction.
+     * @return The version actually assigned to the entity.
+     */
+version_type release(const entity_type entity, const version_type version) {
+ENTT_ASSERT(orphan(entity), "Non-orphan entity");
+return release_entity(entity, version);
+}
+
+/**
+     * @brief Releases all identifiers in a range.
+     *
+     * @sa release
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void release(It first, It last) {
+for(; first != last; ++first) {
+release(*first);
+}
+}
+
+/**
+     * @brief Destroys an entity and releases its identifier.
+     *
+     * @sa release
+     *
+     * @warning
+     * Adding or removing components to an entity that is being destroyed can
+     * result in undefined behavior. Attempting to use an invalid entity results
+     * in undefined behavior.
+     *
+     * @param entity A valid identifier.
+     * @return The version of the recycled entity.
+     */
+version_type destroy(const entity_type entity) {
+return destroy(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
+}
+
+/**
+     * @brief Destroys an entity and releases its identifier.
+     *
+     * The suggested version or the valid version closest to the suggested one
+     * is used instead of the implicitly generated version.
+     *
+     * @sa destroy
+     *
+     * @param entity A valid identifier.
+     * @param version A desired version upon destruction.
+     * @return The version actually assigned to the entity.
+     */
+version_type destroy(const entity_type entity, const version_type version) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+
+for(size_type pos = pools.size(); pos; --pos) {
+pools.begin()[pos - 1u].second->remove(entity);
+}
+
+return release_entity(entity, version);
+}
+
+/**
+     * @brief Destroys all entities in a range and releases their identifiers.
+     *
+     * @sa destroy
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void destroy(It first, It last) {
+for(; first != last; ++first) {
+destroy(*first);
+}
+}
+
+/**
+     * @brief Assigns the given component to an entity.
+     *
+     * The component must have a proper constructor or be of aggregate type.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to assign a component to an entity
+     * that already owns it results in undefined behavior.
+     *
+     * @tparam Component Type of component to create.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns each entity in a range the given component.
+     *
+     * @sa emplace
+     *
+     * @tparam Component Type of component to create.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param value An instance of the component to assign.
+     */
+template<typename Component, typename It>
+void insert(It first, It last, const Component &value = {}) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().insert(first, last, value);
+}
+
+/**
+     * @brief Assigns each entity in a range the given components.
+     *
+     * @sa emplace
+     *
+     * @tparam Component Type of component to create.
+     * @tparam EIt Type of input iterator.
+     * @tparam CIt Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param from An iterator to the first element of the range of components.
+     */
+template<typename Component, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Component>>>
+void insert(EIt first, EIt last, CIt from) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().insert(first, last, from);
+}
+
+/**
+     * @brief Assigns or replaces the given component for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to assign or replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+auto &cpool = assure<Component>();
+
+return cpool.contains(entity)
+? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); })
+: cpool.emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Patches the given component for an entity.
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(Component &);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned. However, this function can be used to trigger an update signal
+     * for them.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to patch a component of an entity
+     * that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Type of component to patch.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entity A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched component.
+     */
+template<typename Component, typename... Func>
+decltype(auto) patch(const entity_type entity, Func &&...func) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().patch(entity, std::forward<Func>(func)...);
+}
+
+/**
+     * @brief Replaces the given component for an entity.
+     *
+     * The component must have a proper constructor or be of aggregate type.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to replace a component of an
+     * entity that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Type of component to replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the component being replaced.
+     */
+template<typename Component, typename... Args>
+decltype(auto) replace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); });
+}
+
+/**
+     * @brief Removes the given components from an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to remove.
+     * @tparam Other Other types of components to remove.
+     * @param entity A valid identifier.
+     * @return The number of components actually removed.
+     */
+template<typename Component, typename... Other>
+size_type remove(const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<Component>().remove(entity) + ... + assure<Other>().remove(entity));
+}
+
+/**
+     * @brief Removes the given components from all the entities in a range.
+     *
+     * @sa remove
+     *
+     * @tparam Component Type of component to remove.
+     * @tparam Other Other types of components to remove.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return The number of components actually removed.
+     */
+template<typename Component, typename... Other, typename It>
+size_type remove(It first, It last) {
+if constexpr(sizeof...(Other) == 0u) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+return assure<Component>().remove(std::move(first), std::move(last));
+} else {
+size_type count{};
+
+for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
+ENTT_ASSERT(valid(*first), "Invalid entity");
+count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
+}
+
+return count;
+}
+}
+
+/**
+     * @brief Erases the given components from an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to erase a component from an
+     * entity that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Types of components to erase.
+     * @tparam Other Other types of components to erase.
+     * @param entity A valid identifier.
+     */
+template<typename Component, typename... Other>
+void erase(const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+(assure<Component>().erase(entity), (assure<Other>().erase(entity), ...));
+}
+
+/**
+     * @brief Erases the given components from all the entities in a range.
+     *
+     * @sa erase
+     *
+     * @tparam Component Types of components to erase.
+     * @tparam Other Other types of components to erase.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename Component, typename... Other, typename It>
+void erase(It first, It last) {
+if constexpr(sizeof...(Other) == 0u) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().erase(std::move(first), std::move(last));
+} else {
+for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
+ENTT_ASSERT(valid(*first), "Invalid entity");
+std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools);
+}
+}
+}
+
+/**
+     * @brief Removes all tombstones from a registry or only the pools for the
+     * given components.
+     * @tparam Component Types of components for which to clear all tombstones.
+     */
+template<typename... Component>
+void compact() {
+if constexpr(sizeof...(Component) == 0) {
+for(auto &&curr: pools) {
+curr.second->compact();
+}
+} else {
+(assure<Component>().compact(), ...);
+}
+}
+
+/**
+     * @brief Checks if an entity has all the given components.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Components for which to perform the check.
+     * @param entity A valid identifier.
+     * @return True if the entity has all the components, false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool all_of(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<std::remove_const_t<Component>>().contains(entity) && ...);
+}
+
+/**
+     * @brief Checks if an entity has at least one of the given components.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Components for which to perform the check.
+     * @param entity A valid identifier.
+     * @return True if the entity has at least one of the given components,
+     * false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool any_of(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<std::remove_const_t<Component>>().contains(entity) || ...);
+}
+
+/**
+     * @brief Returns references to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to get a component from an entity
+     * that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Types of components to get.
+     * @param entity A valid identifier.
+     * @return References to the components owned by the entity.
+     */
+template<typename... Component>
+[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return view<Component...>().template get<const Component...>(entity);
+}
+
+/*! @copydoc get */
+template<typename... Component>
+[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return view<Component...>().template get<Component...>(entity);
+}
+
+/**
+     * @brief Returns a reference to the given component for an entity.
+     *
+     * In case the entity doesn't own the component, the parameters provided are
+     * used to construct it.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to get.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return Reference to the component owned by the entity.
+     */
+template<typename Component, typename... Args>
+[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+auto &cpool = assure<Component>();
+return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Returns pointers to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @note
+     * The registry retains ownership of the pointed-to components.
+     *
+     * @tparam Component Types of components to get.
+     * @param entity A valid identifier.
+     * @return Pointers to the components owned by the entity.
+     */
+template<typename... Component>
+[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+
+if constexpr(sizeof...(Component) == 1) {
+const auto &cpool = assure<std::remove_const_t<Component>...>();
+return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr;
+} else {
+return std::make_tuple(try_get<Component>(entity)...);
+}
+}
+
+/*! @copydoc try_get */
+template<typename... Component>
+[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) {
+if constexpr(sizeof...(Component) == 1) {
+return (const_cast<Component *>(std::as_const(*this).template try_get<Component>(entity)), ...);
+} else {
+return std::make_tuple(try_get<Component>(entity)...);
+}
+}
+
+/**
+     * @brief Clears a whole registry or the pools for the given components.
+     * @tparam Component Types of components to remove from their entities.
+     */
+template<typename... Component>
+void clear() {
+if constexpr(sizeof...(Component) == 0) {
+for(auto &&curr: pools) {
+curr.second->clear();
+}
+
+each([this](const auto entity) { this->release(entity); });
+} else {
+(assure<Component>().clear(), ...);
+}
+}
+
+/**
+     * @brief Iterates all the entities that are still in use.
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(const Entity);
+     * @endcode
+     *
+     * It's not defined whether entities created during iteration are returned.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+if(free_list == null) {
+for(auto pos = entities.size(); pos; --pos) {
+func(entities[pos - 1]);
+}
+} else {
+for(auto pos = entities.size(); pos; --pos) {
+if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) {
+func(entity);
+}
+}
+}
+}
+
+/**
+     * @brief Checks if an entity has components assigned.
+     * @param entity A valid identifier.
+     * @return True if the entity has no components assigned, false otherwise.
+     */
+[[nodiscard]] bool orphan(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); });
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever a new instance of the
+     * given component is created and assigned to an entity.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **after** assigning the component to the entity.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_construct() {
+return assure<Component>().on_construct();
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever an instance of the
+     * given component is explicitly updated.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **after** updating the component.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_update() {
+return assure<Component>().on_update();
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever an instance of the
+     * given component is removed from an entity and thus destroyed.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **before** removing the component from the entity.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_destroy() {
+return assure<Component>().on_destroy();
+}
+
+/**
+     * @brief Returns a view for the given components.
+     *
+     * Views are created on the fly and share with the registry its internal
+     * data structures. Feel free to discard them after the use.<br/>
+     * Creating and destroying a view is an incredibly cheap operation. As a
+     * rule of thumb, storing a view should never be an option.
+     *
+     * @tparam Component Type of component used to construct the view.
+     * @tparam Other Other types of components used to construct the view.
+     * @tparam Exclude Types of components used to filter the view.
+     * @return A newly created view.
+     */
+template<typename Component, typename... Other, typename... Exclude>
+[[nodiscard]] basic_view<entity_type, get_t<std::add_const_t<Component>, std::add_const_t<Other>...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) const {
+return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
+}
+
+/*! @copydoc view */
+template<typename Component, typename... Other, typename... Exclude>
+[[nodiscard]] basic_view<entity_type, get_t<Component, Other...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) {
+return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
+}
+
+/**
+     * @brief Returns a group for the given components.
+     *
+     * Groups are created on the fly and share with the registry its internal
+     * data structures. Feel free to discard them after the use.<br/>
+     * Creating and destroying a group is an incredibly cheap operation. As a
+     * rule of thumb, storing a group should never be an option.
+     *
+     * Groups support exclusion lists and can own types of components. The more
+     * types are owned by a group, the faster it is to iterate entities and
+     * components.<br/>
+     * However, groups also affect some features of the registry such as the
+     * creation and destruction of components.
+     *
+     * @note
+     * Pools of components that are owned by a group cannot be sorted anymore.
+     * The group takes the ownership of the pools and arrange components so as
+     * to iterate them as fast as possible.
+     *
+     * @tparam Owned Types of components owned by the group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @return A newly created group.
+     */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> group(get_t<Get...>, exclude_t<Exclude...> = {}) {
+static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported");
+static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed");
+
+using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
+
+const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...);
+constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
+handler_type *handler = nullptr;
+
+auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
+return gdata.size == size
+&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
+&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
+&& (gdata.exclude(type_hash<Exclude>::value()) && ...);
+});
+
+if(it != groups.cend()) {
+handler = static_cast<handler_type *>(it->group.get());
+} else {
+group_data candidate = {
+size,
+{new handler_type{}, [](void *instance) { delete static_cast<handler_type *>(instance); }},
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); },
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); },
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<Exclude>::value()) || ...); },
+};
+
+handler = static_cast<handler_type *>(candidate.group.get());
+
+const void *maybe_valid_if = nullptr;
+const void *discard_if = nullptr;
+
+if constexpr(sizeof...(Owned) == 0) {
+groups.push_back(std::move(candidate));
+} else {
+[[maybe_unused]] auto has_conflict = [size](const auto &gdata) {
+const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
+const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<Exclude>::value()));
+return !overlapping || ((sz == size) || (sz == gdata.size));
+};
+
+ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups");
+
+const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
+return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size);
+});
+
+const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) {
+return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
+});
+
+maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
+discard_if = (prev == groups.crend() ? discard_if : prev->group.get());
+groups.insert(next, std::move(candidate));
+}
+
+(on_construct<std::remove_const_t<Owned>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Owned>>>(*handler), ...);
+(on_construct<std::remove_const_t<Get>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Get>>>(*handler), ...);
+(on_destroy<Exclude>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*handler), ...);
+
+(on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+(on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+(on_construct<Exclude>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+
+if constexpr(sizeof...(Owned) == 0) {
+for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) {
+handler->current.emplace(entity);
+}
+} else {
+// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
+for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) {
+handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first);
+}
+}
+}
+
+return {handler->current, std::get<storage_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_type<std::remove_const_t<Get>> &>(cpools)...};
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<std::add_const_t<Get>...>, exclude_t<Exclude...>> group_if_exists(get_t<Get...>, exclude_t<Exclude...> = {}) const {
+auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) {
+return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude))
+&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
+&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
+&& (gdata.exclude(type_hash<Exclude>::value()) && ...);
+});
+
+if(it == groups.cend()) {
+return {};
+} else {
+using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
+return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...};
+}
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<>, exclude_t<Exclude...>> group(exclude_t<Exclude...> = {}) {
+return group<Owned...>(get_t<>{}, exclude<Exclude...>);
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<>, exclude_t<Exclude...>> group_if_exists(exclude_t<Exclude...> = {}) const {
+return group_if_exists<std::add_const_t<Owned>...>(get_t<>{}, exclude<Exclude...>);
+}
+
+/**
+     * @brief Checks whether the given components belong to any group.
+     * @tparam Component Types of components in which one is interested.
+     * @return True if the pools of the given components are _free_, false
+     * otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool owned() const {
+return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Component>>::value()) || ...); });
+}
+
+/**
+     * @brief Checks whether a group can be sorted.
+     * @tparam Owned Types of components owned by the group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @return True if the group can be sorted, false otherwise.
+     */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] bool sortable(const basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> &) ENTT_NOEXCEPT {
+constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
+auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) && (size < gdata.size); };
+return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend();
+}
+
+/**
+     * @brief Sorts the elements of a given component.
+     *
+     * The order remains valid until a component of the given type is assigned
+     * to or removed from an entity.<br/>
+     * The comparison function object returns `true` if the first element is
+     * _less_ than the second one, `false` otherwise. Its signature is also
+     * equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(const Entity, const Entity);
+     * bool(const Component &, const Component &);
+     * @endcode
+     *
+     * Moreover, it shall induce a _strict weak ordering_ on the values.<br/>
+     * The sort function object offers an `operator()` that accepts:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function object to use to compare the elements.
+     *
+     * The comparison function object hasn't necessarily the type of the one
+     * passed along with the other parameters to this member function.
+     *
+     * @warning
+     * Pools of components owned by a group cannot be sorted.
+     *
+     * @tparam Component Type of components to sort.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+ENTT_ASSERT(!owned<Component>(), "Cannot sort owned storage");
+auto &cpool = assure<Component>();
+
+if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) {
+auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); };
+cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
+} else {
+cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
+}
+}
+
+/**
+     * @brief Sorts two pools of components in the same way.
+     *
+     * Being `To` and `From` the two sets, after invoking this function an
+     * iterator for `To` returns elements according to the following rules:
+     *
+     * * All entities in `To` that are also in `From` are returned first
+     *   according to the order they have in `From`.
+     * * All entities in `To` that are not in `From` are returned in no
+     *   particular order after all the other entities.
+     *
+     * Any subsequent change to `From` won't affect the order in `To`.
+     *
+     * @warning
+     * Pools of components owned by a group cannot be sorted.
+     *
+     * @tparam To Type of components to sort.
+     * @tparam From Type of components to use to sort.
+     */
+template<typename To, typename From>
+void sort() {
+ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage");
+assure<To>().respect(assure<From>());
+}
+
+/**
+     * @brief Returns the context object, that is, a general purpose container.
+     * @return The context object, that is, a general purpose container.
+     */
+context &ctx() ENTT_NOEXCEPT {
+return vars;
+}
+
+/*! @copydoc ctx */
+const context &ctx() const ENTT_NOEXCEPT {
+return vars;
+}
+
+private:
+dense_map<id_type, std::unique_ptr<base_type>, identity> pools;
+std::vector<group_data> groups;
+std::vector<entity_type> entities;
+entity_type free_list;
+context vars;
+};
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Non-owning handle to an entity.
+ *
+ * Tiny wrapper around a registry and an entity.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Types to which to restrict the scope of a handle.
+ */
+template<typename Entity, typename... Type>
+struct basic_handle {
+/*! @brief Type of registry accepted by the handle. */
+using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
+/*! @brief Underlying entity identifier. */
+using entity_type = typename registry_type::entity_type;
+/*! @brief Underlying version type. */
+using version_type = typename registry_type::version_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename registry_type::size_type;
+
+/*! @brief Constructs an invalid handle. */
+basic_handle() ENTT_NOEXCEPT
+: reg{},
+entt{null} {}
+
+/**
+     * @brief Constructs a handle from a given registry and entity.
+     * @param ref An instance of the registry class.
+     * @param value A valid identifier.
+     */
+basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
+: reg{&ref},
+entt{value} {}
+
+/**
+     * @brief Constructs a const handle from a non-const one.
+     * @tparam Other A valid entity type (see entt_traits for more details).
+     * @tparam Args Scope of the handle to construct.
+     * @return A const handle referring to the same registry and the same
+     * entity.
+     */
+template<typename Other, typename... Args>
+operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
+static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles");
+static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles");
+
+return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
+}
+
+/**
+     * @brief Converts a handle to its underlying entity.
+     * @return The contained identifier.
+     */
+[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
+return entity();
+}
+
+/**
+     * @brief Checks if a handle refers to non-null registry pointer and entity.
+     * @return True if the handle refers to non-null registry and entity, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return reg && reg->valid(entt);
+}
+
+/**
+     * @brief Checks if a handle refers to a valid entity or not.
+     * @return True if the handle refers to a valid entity, false otherwise.
+     */
+[[nodiscard]] bool valid() const {
+return reg->valid(entt);
+}
+
+/**
+     * @brief Returns a pointer to the underlying registry, if any.
+     * @return A pointer to the underlying registry, if any.
+     */
+[[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT {
+return reg;
+}
+
+/**
+     * @brief Returns the entity associated with a handle.
+     * @return The entity associated with the handle.
+     */
+[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
+return entt;
+}
+
+/**
+     * @brief Destroys the entity associated with a handle.
+     * @sa basic_registry::destroy
+     */
+void destroy() {
+reg->destroy(entt);
+}
+
+/**
+     * @brief Destroys the entity associated with a handle.
+     * @sa basic_registry::destroy
+     * @param version A desired version upon destruction.
+     */
+void destroy(const version_type version) {
+reg->destroy(entt, version);
+}
+
+/**
+     * @brief Assigns the given component to a handle.
+     * @sa basic_registry::emplace
+     * @tparam Component Type of component to create.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace(Args &&...args) const {
+static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns or replaces the given component for a handle.
+     * @sa basic_registry::emplace_or_replace
+     * @tparam Component Type of component to assign or replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace_or_replace(Args &&...args) const {
+static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Patches the given component for a handle.
+     * @sa basic_registry::patch
+     * @tparam Component Type of component to patch.
+     * @tparam Func Types of the function objects to invoke.
+     * @param func Valid function objects.
+     * @return A reference to the patched component.
+     */
+template<typename Component, typename... Func>
+decltype(auto) patch(Func &&...func) const {
+static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+return reg->template patch<Component>(entt, std::forward<Func>(func)...);
+}
+
+/**
+     * @brief Replaces the given component for a handle.
+     * @sa basic_registry::replace
+     * @tparam Component Type of component to replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the component being replaced.
+     */
+template<typename Component, typename... Args>
+decltype(auto) replace(Args &&...args) const {
+static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+return reg->template replace<Component>(entt, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes the given components from a handle.
+     * @sa basic_registry::remove
+     * @tparam Component Types of components to remove.
+     * @return The number of components actually removed.
+     */
+template<typename... Component>
+size_type remove() const {
+static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+return reg->template remove<Component...>(entt);
+}
+
+/**
+     * @brief Erases the given components from a handle.
+     * @sa basic_registry::erase
+     * @tparam Component Types of components to erase.
+     */
+template<typename... Component>
+void erase() const {
+static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+reg->template erase<Component...>(entt);
+}
+
+/**
+     * @brief Checks if a handle has all the given components.
+     * @sa basic_registry::all_of
+     * @tparam Component Components for which to perform the check.
+     * @return True if the handle has all the components, false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] decltype(auto) all_of() const {
+return reg->template all_of<Component...>(entt);
+}
+
+/**
+     * @brief Checks if a handle has at least one of the given components.
+     * @sa basic_registry::any_of
+     * @tparam Component Components for which to perform the check.
+     * @return True if the handle has at least one of the given components,
+     * false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] decltype(auto) any_of() const {
+return reg->template any_of<Component...>(entt);
+}
+
+/**
+     * @brief Returns references to the given components for a handle.
+     * @sa basic_registry::get
+     * @tparam Component Types of components to get.
+     * @return References to the components owned by the handle.
+     */
+template<typename... Component>
+[[nodiscard]] decltype(auto) get() const {
+static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+return reg->template get<Component...>(entt);
+}
+
+/**
+     * @brief Returns a reference to the given component for a handle.
+     * @sa basic_registry::get_or_emplace
+     * @tparam Component Type of component to get.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param args Parameters to use to initialize the component.
+     * @return Reference to the component owned by the handle.
+     */
+template<typename Component, typename... Args>
+[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
+static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
+return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Returns pointers to the given components for a handle.
+     * @sa basic_registry::try_get
+     * @tparam Component Types of components to get.
+     * @return Pointers to the components owned by the handle.
+     */
+template<typename... Component>
+[[nodiscard]] auto try_get() const {
+static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
+return reg->template try_get<Component...>(entt);
+}
+
+/**
+     * @brief Checks if a handle has components assigned.
+     * @return True if the handle has no components assigned, false otherwise.
+     */
+[[nodiscard]] bool orphan() const {
+return reg->orphan(entt);
+}
+
+/**
+     * @brief Visits a handle and returns the pools for its components.
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(id_type, const basic_sparse_set<entity_type> &);
+     * @endcode
+     *
+     * Returned pools are those that contain the entity associated with the
+     * handle.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void visit(Func &&func) const {
+for(auto [id, storage]: reg->storage()) {
+if(storage.contains(entt)) {
+func(id, storage);
+}
+}
+}
+
+private:
+registry_type *reg;
+entity_type entt;
+};
+
+/**
+ * @brief Compares two handles.
+ * @tparam Args Scope of the first handle.
+ * @tparam Other Scope of the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if both handles refer to the same registry and the same
+ * entity, false otherwise.
+ */
+template<typename... Args, typename... Other>
+[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
+return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Args Scope of the first handle.
+ * @tparam Other Scope of the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return False if both handles refer to the same registry and the same
+ * entity, true otherwise.
+ */
+template<typename... Args, typename... Other>
+[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
+
+} // namespace entt
+
+#endif
+
+// #include "entity/helper.hpp"
+#ifndef ENTT_ENTITY_HELPER_HPP
+#define ENTT_ENTITY_HELPER_HPP
+
+#include <type_traits>
+// #include "../config/config.h"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../signal/delegate.hpp"
+#ifndef ENTT_SIGNAL_DELEGATE_HPP
+#define ENTT_SIGNAL_DELEGATE_HPP
+
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Ret, typename... Args>
+auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
+
+template<typename Ret, typename Type, typename... Args, typename Other>
+auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Type, typename... Other>
+auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
+
+template<typename... Type>
+using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
+
+template<typename... Class, typename Ret, typename... Args>
+[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
+return std::index_sequence_for<Class..., Args...>{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Used to wrap a function or a member of a specified type. */
+template<auto>
+struct connect_arg_t {};
+
+/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
+template<auto Func>
+inline constexpr connect_arg_t<Func> connect_arg{};
+
+/**
+ * @brief Basic delegate implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ */
+template<typename>
+class delegate;
+
+/**
+ * @brief Utility class to use to send around functions and members.
+ *
+ * Unmanaged delegate for function pointers and members. Users of this class are
+ * in charge of disconnecting instances before deleting them.
+ *
+ * A delegate can be used as a general purpose invoker without memory overhead
+ * for free functions possibly with payloads and bound or unbound members.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+class delegate<Ret(Args...)> {
+template<auto Candidate, std::size_t... Index>
+[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+public:
+/*! @brief Function type of the contained target. */
+using function_type = Ret(const void *, Args...);
+/*! @brief Function type of the delegate. */
+using type = Ret(Args...);
+/*! @brief Return type of the delegate. */
+using result_type = Ret;
+
+/*! @brief Default constructor. */
+delegate() ENTT_NOEXCEPT
+: instance{nullptr},
+fn{nullptr} {}
+
+/**
+     * @brief Constructs a delegate and connects a free function or an unbound
+     * member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
+connect<Candidate>();
+}
+
+/**
+     * @brief Constructs a delegate and connects a free function with payload or
+     * a bound member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
+connect<Candidate>(std::forward<Type>(value_or_instance));
+}
+
+/**
+     * @brief Constructs a delegate and connects an user defined function with
+     * optional payload.
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+connect(function, payload);
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a delegate.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+void connect() ENTT_NOEXCEPT {
+instance = nullptr;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+fn = [](const void *, Args... args) -> Ret {
+return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
+};
+} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
+fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
+} else {
+fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the delegate.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the delegate itself.
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid reference that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type &value_or_instance) ENTT_NOEXCEPT {
+instance = &value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * @sa connect(Type &)
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type *value_or_instance) ENTT_NOEXCEPT {
+instance = value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects an user defined function with optional payload to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of an instance overcomes
+     * the one of the delegate.<br/>
+     * The payload is returned as the first argument to the target function in
+     * all cases.
+     *
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+instance = payload;
+fn = function;
+}
+
+/**
+     * @brief Resets a delegate.
+     *
+     * After a reset, a delegate cannot be invoked anymore.
+     */
+void reset() ENTT_NOEXCEPT {
+instance = nullptr;
+fn = nullptr;
+}
+
+/**
+     * @brief Returns the instance or the payload linked to a delegate, if any.
+     * @return An opaque pointer to the underlying data.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return instance;
+}
+
+/**
+     * @brief Triggers a delegate.
+     *
+     * The delegate invokes the underlying function and returns the result.
+     *
+     * @warning
+     * Attempting to trigger an invalid delegate results in undefined
+     * behavior.
+     *
+     * @param args Arguments to use to invoke the underlying function.
+     * @return The value returned by the underlying function.
+     */
+Ret operator()(Args... args) const {
+ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
+return fn(instance, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Checks whether a delegate actually stores a listener.
+     * @return False if the delegate is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+// no need to also test instance
+return !(fn == nullptr);
+}
+
+/**
+     * @brief Compares the contents of two delegates.
+     * @param other Delegate with which to compare.
+     * @return False if the two contents differ, true otherwise.
+     */
+[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
+return fn == other.fn && instance == other.instance;
+}
+
+private:
+const void *instance;
+function_type *fn;
+};
+
+/**
+ * @brief Compares the contents of two delegates.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @param lhs A valid delegate object.
+ * @param rhs A valid delegate object.
+ * @return True if the two contents differ, false otherwise.
+ */
+template<typename Ret, typename... Args>
+[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ * @tparam Type Type of class or type of payload.
+ */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "registry.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Converts a registry to a view.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+struct as_view {
+/*! @brief Underlying entity identifier. */
+using entity_type = std::remove_const_t<Entity>;
+/*! @brief Type of registry to convert. */
+using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+
+/**
+     * @brief Constructs a converter for a given registry.
+     * @param source A valid reference to a registry.
+     */
+as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+
+/**
+     * @brief Conversion function from a registry to a view.
+     * @tparam Exclude Types of components used to filter the view.
+     * @tparam Component Type of components used to construct the view.
+     * @return A newly created view.
+     */
+template<typename Exclude, typename... Component>
+operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
+return reg.template view<Component...>(Exclude{});
+}
+
+private:
+registry_type &reg;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_view(basic_registry<Entity> &) -> as_view<Entity>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
+
+/**
+ * @brief Converts a registry to a group.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+struct as_group {
+/*! @brief Underlying entity identifier. */
+using entity_type = std::remove_const_t<Entity>;
+/*! @brief Type of registry to convert. */
+using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+
+/**
+     * @brief Constructs a converter for a given registry.
+     * @param source A valid reference to a registry.
+     */
+as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+
+/**
+     * @brief Conversion function from a registry to a group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @tparam Owned Types of components owned by the group.
+     * @return A newly created group.
+     */
+template<typename Get, typename Exclude, typename... Owned>
+operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
+if constexpr(std::is_const_v<registry_type>) {
+return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
+} else {
+return reg.template group<Owned...>(Get{}, Exclude{});
+}
+}
+
+private:
+registry_type &reg;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_group(basic_registry<Entity> &) -> as_group<Entity>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
+
+/**
+ * @brief Helper to create a listener that directly invokes a member function.
+ * @tparam Member Member function to invoke on a component of the given type.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @param reg A registry that contains the given entity and its components.
+ * @param entt Entity from which to get the component.
+ */
+template<auto Member, typename Entity = entity>
+void invoke(basic_registry<Entity> &reg, const Entity entt) {
+static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
+delegate<void(basic_registry<Entity> &, const Entity)> func;
+func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
+func(reg, entt);
+}
+
+/**
+ * @brief Returns the entity associated with a given component.
+ *
+ * @warning
+ * Currently, this function only works correctly with the default pool as it
+ * makes assumptions about how the components are laid out.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Type of component.
+ * @param reg A registry that contains the given entity and its components.
+ * @param instance A valid component instance.
+ * @return The entity associated with the given component.
+ */
+template<typename Entity, typename Component>
+Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
+const auto &storage = reg.template storage<Component>();
+const typename basic_registry<Entity>::base_type &base = storage;
+const auto *addr = std::addressof(instance);
+
+for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) {
+if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
+return *(it + dist);
+}
+}
+
+return null;
+}
+
+} // namespace entt
+
+#endif
+
+// #include "entity/observer.hpp"
+#ifndef ENTT_ENTITY_OBSERVER_HPP
+#define ENTT_ENTITY_OBSERVER_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../signal/delegate.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "registry.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+
+namespace entt {
+
+/*! @brief Grouping matcher. */
+template<typename...>
+struct matcher {};
+
+/**
+ * @brief Collector.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename...>
+struct basic_collector;
+
+/**
+ * @brief Collector.
+ *
+ * A collector contains a set of rules (literally, matchers) to use to track
+ * entities.<br/>
+ * Its main purpose is to generate a descriptor that allows an observer to know
+ * how to connect to a registry.
+ */
+template<>
+struct basic_collector<> {
+/**
+     * @brief Adds a grouping matcher to the collector.
+     * @tparam AllOf Types of components tracked by the matcher.
+     * @tparam NoneOf Types of components used to filter out entities.
+     * @return The updated collector.
+     */
+template<typename... AllOf, typename... NoneOf>
+static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
+}
+
+/**
+     * @brief Adds an observing matcher to the collector.
+     * @tparam AnyOf Type of component for which changes should be detected.
+     * @return The updated collector.
+     */
+template<typename AnyOf>
+static constexpr auto update() ENTT_NOEXCEPT {
+return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
+}
+};
+
+/**
+ * @brief Collector.
+ * @copydetails basic_collector<>
+ * @tparam Reject Untracked types used to filter out entities.
+ * @tparam Require Untracked types required by the matcher.
+ * @tparam Rule Specific details of the current matcher.
+ * @tparam Other Other matchers.
+ */
+template<typename... Reject, typename... Require, typename... Rule, typename... Other>
+struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
+/*! @brief Current matcher. */
+using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
+
+/**
+     * @brief Adds a grouping matcher to the collector.
+     * @tparam AllOf Types of components tracked by the matcher.
+     * @tparam NoneOf Types of components used to filter out entities.
+     * @return The updated collector.
+     */
+template<typename... AllOf, typename... NoneOf>
+static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
+}
+
+/**
+     * @brief Adds an observing matcher to the collector.
+     * @tparam AnyOf Type of component for which changes should be detected.
+     * @return The updated collector.
+     */
+template<typename AnyOf>
+static constexpr auto update() ENTT_NOEXCEPT {
+return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
+}
+
+/**
+     * @brief Updates the filter of the last added matcher.
+     * @tparam AllOf Types of components required by the matcher.
+     * @tparam NoneOf Types of components used to filter out entities.
+     * @return The updated collector.
+     */
+template<typename... AllOf, typename... NoneOf>
+static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
+using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
+return basic_collector<extended_type, Other...>{};
+}
+};
+
+/*! @brief Variable template used to ease the definition of collectors. */
+inline constexpr basic_collector<> collector{};
+
+/**
+ * @brief Observer.
+ *
+ * An observer returns all the entities and only the entities that fit the
+ * requirements of at least one matcher. Moreover, it's guaranteed that the
+ * entity list is tightly packed in memory for fast iterations.<br/>
+ * In general, observers don't stay true to the order of any set of components.
+ *
+ * Observers work mainly with two types of matchers, provided through a
+ * collector:
+ *
+ * * Observing matcher: an observer will return at least all the living entities
+ *   for which one or more of the given components have been updated and not yet
+ *   destroyed.
+ * * Grouping matcher: an observer will return at least all the living entities
+ *   that would have entered the given group if it existed and that would have
+ *   not yet left it.
+ *
+ * If an entity respects the requirements of multiple matchers, it will be
+ * returned once and only once by the observer in any case.
+ *
+ * Matchers support also filtering by means of a _where_ clause that accepts
+ * both a list of types and an exclusion list.<br/>
+ * Whenever a matcher finds that an entity matches its requirements, the
+ * condition of the filter is verified before to register the entity itself.
+ * Moreover, a registered entity isn't returned by the observer if the condition
+ * set by the filter is broken in the meantime.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all the other cases, modifying the pools of the given components in any
+ * way invalidates all the iterators and using them results in undefined
+ * behavior.
+ *
+ * @warning
+ * Lifetime of an observer doesn't necessarily have to overcome that of the
+ * registry to which it is connected. However, the observer must be disconnected
+ * from the registry before being destroyed to avoid crashes due to dangling
+ * pointers.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_observer {
+using payload_type = std::uint32_t;
+
+template<typename>
+struct matcher_handler;
+
+template<typename... Reject, typename... Require, typename AnyOf>
+struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
+template<std::size_t Index>
+static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
+if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
+if(!obs.storage.contains(entt)) {
+obs.storage.emplace(entt);
+}
+
+obs.storage.get(entt) |= (1 << Index);
+}
+}
+
+template<std::size_t Index>
+static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
+if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
+obs.storage.erase(entt);
+}
+}
+
+template<std::size_t Index>
+static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
+(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
+(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
+reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
+reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
+}
+
+static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
+(reg.template on_destroy<Require>().disconnect(obs), ...);
+(reg.template on_construct<Reject>().disconnect(obs), ...);
+reg.template on_update<AnyOf>().disconnect(obs);
+reg.template on_destroy<AnyOf>().disconnect(obs);
+}
+};
+
+template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
+struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
+template<std::size_t Index, typename... Ignore>
+static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> &reg, const Entity entt) {
+auto condition = [&reg, entt]() {
+if constexpr(sizeof...(Ignore) == 0) {
+return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
+} else {
+return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
+}
+};
+
+if(condition()) {
+if(!obs.storage.contains(entt)) {
+obs.storage.emplace(entt);
+}
+
+obs.storage.get(entt) |= (1 << Index);
+}
+}
+
+template<std::size_t Index>
+static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
+if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
+obs.storage.erase(entt);
+}
+}
+
+template<std::size_t Index>
+static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
+(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
+(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
+(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
+(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
+(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
+(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
+}
+
+static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
+(reg.template on_destroy<Require>().disconnect(obs), ...);
+(reg.template on_construct<Reject>().disconnect(obs), ...);
+(reg.template on_construct<AllOf>().disconnect(obs), ...);
+(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
+(reg.template on_destroy<AllOf>().disconnect(obs), ...);
+(reg.template on_construct<NoneOf>().disconnect(obs), ...);
+}
+};
+
+template<typename... Matcher>
+static void disconnect(basic_registry<Entity> &reg, basic_observer &obs) {
+(matcher_handler<Matcher>::disconnect(obs, reg), ...);
+}
+
+template<typename... Matcher, std::size_t... Index>
+void connect(basic_registry<Entity> &reg, std::index_sequence<Index...>) {
+static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
+(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
+release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Random access iterator type. */
+using iterator = typename basic_sparse_set<Entity>::iterator;
+
+/*! @brief Default constructor. */
+basic_observer()
+: release{},
+storage{} {}
+
+/*! @brief Default copy constructor, deleted on purpose. */
+basic_observer(const basic_observer &) = delete;
+/*! @brief Default move constructor, deleted on purpose. */
+basic_observer(basic_observer &&) = delete;
+
+/**
+     * @brief Creates an observer and connects it to a given registry.
+     * @tparam Matcher Types of matchers to use to initialize the observer.
+     * @param reg A valid reference to a registry.
+     */
+template<typename... Matcher>
+basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>)
+: basic_observer{} {
+connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
+}
+
+/*! @brief Default destructor. */
+~basic_observer() = default;
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This observer.
+     */
+basic_observer &operator=(const basic_observer &) = delete;
+
+/**
+     * @brief Default move assignment operator, deleted on purpose.
+     * @return This observer.
+     */
+basic_observer &operator=(basic_observer &&) = delete;
+
+/**
+     * @brief Connects an observer to a given registry.
+     * @tparam Matcher Types of matchers to use to initialize the observer.
+     * @param reg A valid reference to a registry.
+     */
+template<typename... Matcher>
+void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
+disconnect();
+connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
+storage.clear();
+}
+
+/*! @brief Disconnects an observer from the registry it keeps track of. */
+void disconnect() {
+if(release) {
+release(*this);
+release.reset();
+}
+}
+
+/**
+     * @brief Returns the number of elements in an observer.
+     * @return Number of elements.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return storage.size();
+}
+
+/**
+     * @brief Checks whether an observer is empty.
+     * @return True if the observer is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return storage.empty();
+}
+
+/**
+     * @brief Direct access to the list of entities of the observer.
+     *
+     * The returned pointer is such that range `[data(), data() + size())` is
+     * always a valid range, even if the container is empty.
+     *
+     * @note
+     * Entities are in the reverse order as returned by the `begin`/`end`
+     * iterators.
+     *
+     * @return A pointer to the array of entities.
+     */
+[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the observer.
+     *
+     * The returned iterator points to the first entity of the observer. If the
+     * container is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the observer.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return storage.basic_sparse_set<entity_type>::begin();
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the observer.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the observer. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * observer.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return storage.basic_sparse_set<entity_type>::end();
+}
+
+/*! @brief Clears the underlying container. */
+void clear() ENTT_NOEXCEPT {
+storage.clear();
+}
+
+/**
+     * @brief Iterates entities and applies the given function object to them.
+     *
+     * The function object is invoked for each entity.<br/>
+     * The signature of the function must be equivalent to the following form:
+     *
+     * @code{.cpp}
+     * void(const entity_type);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(const auto entity: *this) {
+func(entity);
+}
+}
+
+/**
+     * @brief Iterates entities and applies the given function object to them,
+     * then clears the observer.
+     *
+     * @sa each
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) {
+std::as_const(*this).each(std::move(func));
+clear();
+}
+
+private:
+delegate<void(basic_observer &)> release;
+basic_storage<entity_type, payload_type> storage;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/organizer.hpp"
+#ifndef ENTT_ENTITY_ORGANIZER_HPP
+#define ENTT_ENTITY_ORGANIZER_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../container/dense_map.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../core/utility.hpp"
+
+// #include "fwd.hpp"
+
+// #include "helper.hpp"
+#ifndef ENTT_ENTITY_HELPER_HPP
+#define ENTT_ENTITY_HELPER_HPP
+
+#include <type_traits>
+// #include "../config/config.h"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../signal/delegate.hpp"
+
+// #include "fwd.hpp"
+
+// #include "registry.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Converts a registry to a view.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+struct as_view {
+/*! @brief Underlying entity identifier. */
+using entity_type = std::remove_const_t<Entity>;
+/*! @brief Type of registry to convert. */
+using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+
+/**
+     * @brief Constructs a converter for a given registry.
+     * @param source A valid reference to a registry.
+     */
+as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+
+/**
+     * @brief Conversion function from a registry to a view.
+     * @tparam Exclude Types of components used to filter the view.
+     * @tparam Component Type of components used to construct the view.
+     * @return A newly created view.
+     */
+template<typename Exclude, typename... Component>
+operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
+return reg.template view<Component...>(Exclude{});
+}
+
+private:
+registry_type &reg;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_view(basic_registry<Entity> &) -> as_view<Entity>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
+
+/**
+ * @brief Converts a registry to a group.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+struct as_group {
+/*! @brief Underlying entity identifier. */
+using entity_type = std::remove_const_t<Entity>;
+/*! @brief Type of registry to convert. */
+using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
+
+/**
+     * @brief Constructs a converter for a given registry.
+     * @param source A valid reference to a registry.
+     */
+as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
+
+/**
+     * @brief Conversion function from a registry to a group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @tparam Owned Types of components owned by the group.
+     * @return A newly created group.
+     */
+template<typename Get, typename Exclude, typename... Owned>
+operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
+if constexpr(std::is_const_v<registry_type>) {
+return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
+} else {
+return reg.template group<Owned...>(Get{}, Exclude{});
+}
+}
+
+private:
+registry_type &reg;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_group(basic_registry<Entity> &) -> as_group<Entity>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
+
+/**
+ * @brief Helper to create a listener that directly invokes a member function.
+ * @tparam Member Member function to invoke on a component of the given type.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @param reg A registry that contains the given entity and its components.
+ * @param entt Entity from which to get the component.
+ */
+template<auto Member, typename Entity = entity>
+void invoke(basic_registry<Entity> &reg, const Entity entt) {
+static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
+delegate<void(basic_registry<Entity> &, const Entity)> func;
+func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
+func(reg, entt);
+}
+
+/**
+ * @brief Returns the entity associated with a given component.
+ *
+ * @warning
+ * Currently, this function only works correctly with the default pool as it
+ * makes assumptions about how the components are laid out.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Type of component.
+ * @param reg A registry that contains the given entity and its components.
+ * @param instance A valid component instance.
+ * @return The entity associated with the given component.
+ */
+template<typename Entity, typename Component>
+Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
+const auto &storage = reg.template storage<Component>();
+const typename basic_registry<Entity>::base_type &base = storage;
+const auto *addr = std::addressof(instance);
+
+for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) {
+if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
+return *(it + dist);
+}
+}
+
+return null;
+}
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct is_view: std::false_type {};
+
+template<typename Entity, typename... Component, typename... Exclude>
+struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
+
+template<typename Type>
+inline constexpr bool is_view_v = is_view<Type>::value;
+
+template<typename Type, typename Override>
+struct unpack_type {
+using ro = std::conditional_t<
+type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
+type_list<std::remove_const_t<Type>>,
+type_list<>>;
+
+using rw = std::conditional_t<
+type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
+type_list<Type>,
+type_list<>>;
+};
+
+template<typename Entity, typename... Override>
+struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
+using ro = type_list<>;
+using rw = type_list<>;
+};
+
+template<typename Entity, typename... Override>
+struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
+: unpack_type<basic_registry<Entity>, type_list<Override...>> {};
+
+template<typename Entity, typename... Component, typename... Exclude, typename... Override>
+struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
+using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
+using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
+};
+
+template<typename Entity, typename... Component, typename... Exclude, typename... Override>
+struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
+: unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
+
+template<typename, typename>
+struct resource_traits;
+
+template<typename... Args, typename... Req>
+struct resource_traits<type_list<Args...>, type_list<Req...>> {
+using args = type_list<std::remove_const_t<Args>...>;
+using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
+using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
+};
+
+template<typename... Req, typename Ret, typename... Args>
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
+
+template<typename... Req, typename Ret, typename Type, typename... Args>
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
+
+template<typename... Req, typename Ret, typename Class, typename... Args>
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
+
+template<typename... Req, typename Ret, typename Class, typename... Args>
+resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Utility class for creating a static task graph.
+ *
+ * This class offers minimal support (but sufficient in many cases) for creating
+ * an execution graph from functions and their requirements on resources.<br/>
+ * Note that the resulting tasks aren't executed in any case. This isn't the
+ * goal of the tool. Instead, they are returned to the user in the form of a
+ * graph that allows for safe execution.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_organizer final {
+using callback_type = void(const void *, basic_registry<Entity> &);
+using prepare_type = void(basic_registry<Entity> &);
+using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
+
+struct vertex_data final {
+std::size_t ro_count{};
+std::size_t rw_count{};
+const char *name{};
+const void *payload{};
+callback_type *callback{};
+dependency_type *dependency;
+prepare_type *prepare{};
+const type_info *info{};
+};
+
+template<typename Type>
+[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> &reg) {
+if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
+return reg;
+} else if constexpr(internal::is_view_v<Type>) {
+return as_view{reg};
+} else {
+return reg.ctx().template emplace<std::remove_reference_t<Type>>();
+}
+}
+
+template<typename... Args>
+[[nodiscard]] static auto to_args(basic_registry<Entity> &reg, type_list<Args...>) {
+return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
+}
+
+template<typename... Type>
+static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
+if constexpr(sizeof...(Type) == 0u) {
+return {};
+} else {
+const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
+const auto length = (std::min)(count, sizeof...(Type));
+std::copy_n(info, length, buffer);
+return length;
+}
+}
+
+template<typename... RO, typename... RW>
+void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
+dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
+(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
+(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
+}
+
+[[nodiscard]] std::vector<bool> adjacency_matrix() {
+const auto length = vertices.size();
+std::vector<bool> edges(length * length, false);
+
+// creates the adjacency matrix
+for(const auto &deps: dependencies) {
+const auto last = deps.second.cend();
+auto it = deps.second.cbegin();
+
+while(it != last) {
+if(it->second) {
+// rw item
+if(auto curr = it++; it != last) {
+if(it->second) {
+edges[curr->first * length + it->first] = true;
+} else {
+if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
+for(; it != next; ++it) {
+edges[curr->first * length + it->first] = true;
+edges[it->first * length + next->first] = true;
+}
+} else {
+for(; it != next; ++it) {
+edges[curr->first * length + it->first] = true;
+}
+}
+}
+}
+} else {
+// ro item, possibly only on first iteration
+if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
+for(; it != next; ++it) {
+edges[it->first * length + next->first] = true;
+}
+} else {
+it = last;
+}
+}
+}
+}
+
+// computes the transitive closure
+for(std::size_t vk{}; vk < length; ++vk) {
+for(std::size_t vi{}; vi < length; ++vi) {
+for(std::size_t vj{}; vj < length; ++vj) {
+edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
+}
+}
+}
+
+// applies the transitive reduction
+for(std::size_t vert{}; vert < length; ++vert) {
+edges[vert * length + vert] = false;
+}
+
+for(std::size_t vj{}; vj < length; ++vj) {
+for(std::size_t vi{}; vi < length; ++vi) {
+if(edges[vi * length + vj]) {
+for(std::size_t vk{}; vk < length; ++vk) {
+if(edges[vj * length + vk]) {
+edges[vi * length + vk] = false;
+}
+}
+}
+}
+}
+
+return edges;
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Raw task function type. */
+using function_type = callback_type;
+
+/*! @brief Vertex type of a task graph defined as an adjacency list. */
+struct vertex {
+/**
+         * @brief Constructs a vertex of the task graph.
+         * @param vtype True if the vertex is a top-level one, false otherwise.
+         * @param data The data associated with the vertex.
+         * @param edges The indices of the children in the adjacency list.
+         */
+vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
+: is_top_level{vtype},
+node{std::move(data)},
+reachable{std::move(edges)} {}
+
+/**
+         * @brief Fills a buffer with the type info objects for the writable
+         * resources of a vertex.
+         * @param buffer A buffer pre-allocated by the user.
+         * @param length The length of the user-supplied buffer.
+         * @return The number of type info objects written to the buffer.
+         */
+size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
+return node.dependency(false, buffer, length);
+}
+
+/**
+         * @brief Fills a buffer with the type info objects for the read-only
+         * resources of a vertex.
+         * @param buffer A buffer pre-allocated by the user.
+         * @param length The length of the user-supplied buffer.
+         * @return The number of type info objects written to the buffer.
+         */
+size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
+return node.dependency(true, buffer, length);
+}
+
+/**
+         * @brief Returns the number of read-only resources of a vertex.
+         * @return The number of read-only resources of the vertex.
+         */
+size_type ro_count() const ENTT_NOEXCEPT {
+return node.ro_count;
+}
+
+/**
+         * @brief Returns the number of writable resources of a vertex.
+         * @return The number of writable resources of the vertex.
+         */
+size_type rw_count() const ENTT_NOEXCEPT {
+return node.rw_count;
+}
+
+/**
+         * @brief Checks if a vertex is also a top-level one.
+         * @return True if the vertex is a top-level one, false otherwise.
+         */
+bool top_level() const ENTT_NOEXCEPT {
+return is_top_level;
+}
+
+/**
+         * @brief Returns a type info object associated with a vertex.
+         * @return A properly initialized type info object.
+         */
+const type_info &info() const ENTT_NOEXCEPT {
+return *node.info;
+}
+
+/**
+         * @brief Returns a user defined name associated with a vertex, if any.
+         * @return The user defined name associated with the vertex, if any.
+         */
+const char *name() const ENTT_NOEXCEPT {
+return node.name;
+}
+
+/**
+         * @brief Returns the function associated with a vertex.
+         * @return The function associated with the vertex.
+         */
+function_type *callback() const ENTT_NOEXCEPT {
+return node.callback;
+}
+
+/**
+         * @brief Returns the payload associated with a vertex, if any.
+         * @return The payload associated with the vertex, if any.
+         */
+const void *data() const ENTT_NOEXCEPT {
+return node.payload;
+}
+
+/**
+         * @brief Returns the list of nodes reachable from a given vertex.
+         * @return The list of nodes reachable from the vertex.
+         */
+const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
+return reachable;
+}
+
+/**
+         * @brief Prepares a registry and assures that all required resources
+         * are properly instantiated before using them.
+         * @param reg A valid registry.
+         */
+void prepare(basic_registry<entity_type> &reg) const {
+node.prepare ? node.prepare(reg) : void();
+}
+
+private:
+bool is_top_level;
+vertex_data node;
+std::vector<std::size_t> reachable;
+};
+
+/**
+     * @brief Adds a free function to the task list.
+     * @tparam Candidate Function to add to the task list.
+     * @tparam Req Additional requirements and/or override resource access mode.
+     * @param name Optional name to associate with the task.
+     */
+template<auto Candidate, typename... Req>
+void emplace(const char *name = nullptr) {
+using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
+constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
+
+callback_type *callback = +[](const void *, basic_registry<entity_type> &reg) {
+std::apply(Candidate, to_args(reg, typename resource_type::args{}));
+};
+
+vertex_data vdata{
+resource_type::ro::size,
+resource_type::rw::size,
+name,
+nullptr,
+callback,
++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
++[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
+&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
+
+track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
+vertices.push_back(std::move(vdata));
+}
+
+/**
+     * @brief Adds a free function with payload or a member function with an
+     * instance to the task list.
+     * @tparam Candidate Function or member to add to the task list.
+     * @tparam Req Additional requirements and/or override resource access mode.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @param name Optional name to associate with the task.
+     */
+template<auto Candidate, typename... Req, typename Type>
+void emplace(Type &value_or_instance, const char *name = nullptr) {
+using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
+constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
+
+callback_type *callback = +[](const void *payload, basic_registry<entity_type> &reg) {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
+};
+
+vertex_data vdata{
+resource_type::ro::size,
+resource_type::rw::size,
+name,
+&value_or_instance,
+callback,
++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
++[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
+&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
+
+track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
+vertices.push_back(std::move(vdata));
+}
+
+/**
+     * @brief Adds an user defined function with optional payload to the task
+     * list.
+     * @tparam Req Additional requirements and/or override resource access mode.
+     * @param func Function to add to the task list.
+     * @param payload User defined arbitrary data.
+     * @param name Optional name to associate with the task.
+     */
+template<typename... Req>
+void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
+using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
+track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
+
+vertex_data vdata{
+resource_type::ro::size,
+resource_type::rw::size,
+name,
+payload,
+func,
++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
+nullptr,
+&type_id<void>()};
+
+vertices.push_back(std::move(vdata));
+}
+
+/**
+     * @brief Generates a task graph for the current content.
+     * @return The adjacency list of the task graph.
+     */
+std::vector<vertex> graph() {
+const auto edges = adjacency_matrix();
+
+// creates the adjacency list
+std::vector<vertex> adjacency_list{};
+adjacency_list.reserve(vertices.size());
+
+for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
+std::vector<std::size_t> reachable{};
+const auto row = col * length;
+bool is_top_level = true;
+
+for(std::size_t next{}; next < length; ++next) {
+if(edges[row + next]) {
+reachable.push_back(next);
+}
+}
+
+for(std::size_t next{}; next < length && is_top_level; ++next) {
+is_top_level = !edges[next * length + col];
+}
+
+adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
+}
+
+return adjacency_list;
+}
+
+/*! @brief Erases all elements from a container. */
+void clear() {
+dependencies.clear();
+vertices.clear();
+}
+
+private:
+dense_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
+std::vector<vertex_data> vertices;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/registry.hpp"
+#ifndef ENTT_ENTITY_REGISTRY_HPP
+#define ENTT_ENTITY_REGISTRY_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../container/dense_map.hpp"
+
+// #include "../core/algorithm.hpp"
+
+// #include "../core/any.hpp"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../core/utility.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "group.hpp"
+
+// #include "runtime_view.hpp"
+
+// #include "sparse_set.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+// #include "view.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class storage_proxy_iterator final {
+template<typename Other>
+friend class storage_proxy_iterator;
+
+using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>;
+
+public:
+using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_type> &>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+storage_proxy_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+storage_proxy_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+storage_proxy_iterator(const storage_proxy_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+storage_proxy_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+storage_proxy_iterator operator++(int) ENTT_NOEXCEPT {
+storage_proxy_iterator orig = *this;
+return ++(*this), orig;
+}
+
+storage_proxy_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+storage_proxy_iterator operator--(int) ENTT_NOEXCEPT {
+storage_proxy_iterator orig = *this;
+return operator--(), orig;
+}
+
+storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+storage_proxy_iterator copy = *this;
+return (copy += value);
+}
+
+storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].first, *it[value].second};
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->first, *it->second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+struct registry_context {
+template<typename Type, typename... Args>
+Type &emplace_hint(const id_type id, Args &&...args) {
+return any_cast<Type &>(data.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
+}
+
+template<typename Type, typename... Args>
+Type &emplace(Args &&...args) {
+return emplace_hint<Type>(type_id<Type>().hash(), std::forward<Args>(args)...);
+}
+
+template<typename Type>
+bool erase(const id_type id = type_id<Type>().hash()) {
+const auto it = data.find(id);
+return it != data.end() && it->second.type() == type_id<Type>() ? (data.erase(it), true) : false;
+}
+
+template<typename Type>
+[[nodiscard]] std::add_const_t<Type> &at(const id_type id = type_id<Type>().hash()) const {
+return any_cast<std::add_const_t<Type> &>(data.at(id));
+}
+
+template<typename Type>
+[[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) {
+return any_cast<Type &>(data.at(id));
+}
+
+template<typename Type>
+[[nodiscard]] std::add_const_t<Type> *find(const id_type id = type_id<Type>().hash()) const {
+const auto it = data.find(id);
+return it != data.cend() ? any_cast<std::add_const_t<Type>>(&it->second) : nullptr;
+}
+
+template<typename Type>
+[[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) {
+const auto it = data.find(id);
+return it != data.end() ? any_cast<Type>(&it->second) : nullptr;
+}
+
+template<typename Type>
+[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
+const auto it = data.find(id);
+return it != data.end() && it->second.type() == type_id<Type>();
+}
+
+private:
+dense_map<id_type, basic_any<0u>, identity> data;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Fast and reliable entity-component system.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_registry {
+using entity_traits = entt_traits<Entity>;
+using basic_common_type = basic_sparse_set<Entity>;
+
+template<typename Component>
+using storage_type = typename storage_traits<Entity, Component>::storage_type;
+
+template<typename...>
+struct group_handler;
+
+template<typename... Exclude, typename... Get, typename... Owned>
+struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
+// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
+static_assert(!std::disjunction_v<std::bool_constant<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete");
+std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{};
+
+template<typename Component>
+void maybe_valid_if(basic_registry &owner, const Entity entt) {
+[[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
+
+const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...)
+&& ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...)
+&& ((std::is_same_v<Component, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...);
+
+if constexpr(sizeof...(Owned) == 0) {
+if(is_valid && !current.contains(entt)) {
+current.emplace(entt);
+}
+} else {
+if(is_valid && !(std::get<0>(cpools).index(entt) < current)) {
+const auto pos = current++;
+(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
+}
+}
+}
+
+void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) {
+if constexpr(sizeof...(Owned) == 0) {
+current.remove(entt);
+} else {
+if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
+const auto pos = --current;
+(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
+}
+}
+}
+};
+
+struct group_data {
+std::size_t size;
+std::unique_ptr<void, void (*)(void *)> group;
+bool (*owned)(const id_type) ENTT_NOEXCEPT;
+bool (*get)(const id_type) ENTT_NOEXCEPT;
+bool (*exclude)(const id_type) ENTT_NOEXCEPT;
+};
+
+template<typename Component>
+[[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) {
+static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
+auto &&cpool = pools[id];
+
+if(!cpool) {
+cpool.reset(new storage_type<Component>{});
+cpool->bind(forward_as_any(*this));
+}
+
+ENTT_ASSERT(cpool->type() == type_id<Component>(), "Unexpected type");
+return static_cast<storage_type<Component> &>(*cpool);
+}
+
+template<typename Component>
+[[nodiscard]] const auto &assure(const id_type id = type_hash<Component>::value()) const {
+static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
+
+if(const auto it = pools.find(id); it != pools.cend()) {
+ENTT_ASSERT(it->second->type() == type_id<Component>(), "Unexpected type");
+return static_cast<const storage_type<Component> &>(*it->second);
+}
+
+static storage_type<Component> placeholder{};
+return placeholder;
+}
+
+auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT {
+ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available");
+return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {});
+}
+
+auto recycle_identifier() ENTT_NOEXCEPT {
+ENTT_ASSERT(free_list != null, "No entities available");
+const auto curr = entity_traits::to_entity(free_list);
+free_list = entity_traits::combine(entity_traits::to_integral(entities[curr]), tombstone);
+return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr])));
+}
+
+auto release_entity(const Entity entity, const typename entity_traits::version_type version) {
+const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone));
+entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers);
+free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone);
+return vers;
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Underlying version type. */
+using version_type = typename entity_traits::version_type;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_common_type;
+/*! @brief Context type. */
+using context = internal::registry_context;
+
+/*! @brief Default constructor. */
+basic_registry()
+: pools{},
+groups{},
+entities{},
+free_list{tombstone},
+vars{} {}
+
+/**
+     * @brief Allocates enough memory upon construction to store `count` pools.
+     * @param count The number of pools to allocate memory for.
+     */
+basic_registry(const size_type count)
+: pools{},
+groups{},
+entities{},
+free_list{tombstone},
+vars{} {
+pools.reserve(count);
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_registry(basic_registry &&other)
+: pools{std::move(other.pools)},
+groups{std::move(other.groups)},
+entities{std::move(other.entities)},
+free_list{other.free_list},
+vars{std::move(other.vars)} {
+for(auto &&curr: pools) {
+curr.second->bind(forward_as_any(*this));
+}
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This registry.
+     */
+basic_registry &operator=(basic_registry &&other) {
+pools = std::move(other.pools);
+groups = std::move(other.groups);
+entities = std::move(other.entities);
+free_list = other.free_list;
+vars = std::move(other.vars);
+
+for(auto &&curr: pools) {
+curr.second->bind(forward_as_any(*this));
+}
+
+return *this;
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a registry.
+     *
+     * The iterable object returns a pair that contains the name and a reference
+     * to the current storage.
+     *
+     * @return An iterable object to use to _visit_ the registry.
+     */
+[[nodiscard]] auto storage() ENTT_NOEXCEPT {
+return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
+}
+
+/*! @copydoc storage */
+[[nodiscard]] auto storage() const ENTT_NOEXCEPT {
+return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}};
+}
+
+/**
+     * @brief Finds the storage associated with a given name, if any.
+     * @param id Name used to map the storage within the registry.
+     * @return An iterator to the given storage if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] auto storage(const id_type id) {
+return internal::storage_proxy_iterator{pools.find(id)};
+}
+
+/**
+     * @brief Finds the storage associated with a given name, if any.
+     * @param id Name used to map the storage within the registry.
+     * @return An iterator to the given storage if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] auto storage(const id_type id) const {
+return internal::storage_proxy_iterator{pools.find(id)};
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Component Type of component of which to return the storage.
+     * @param id Optional name used to map the storage within the registry.
+     * @return The storage for the given component type.
+     */
+template<typename Component>
+decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) {
+if constexpr(std::is_const_v<Component>) {
+return std::as_const(*this).template storage<std::remove_const_t<Component>>(id);
+} else {
+return assure<Component>(id);
+}
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     *
+     * @warning
+     * If a storage for the given component doesn't exist yet, a temporary
+     * placeholder is returned instead.
+     *
+     * @tparam Component Type of component of which to return the storage.
+     * @param id Optional name used to map the storage within the registry.
+     * @return The storage for the given component type.
+     */
+template<typename Component>
+decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) const {
+return assure<std::remove_const_t<Component>>(id);
+}
+
+/**
+     * @brief Returns the number of entities created so far.
+     * @return Number of entities created so far.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return entities.size();
+}
+
+/**
+     * @brief Returns the number of entities still in use.
+     * @return Number of entities still in use.
+     */
+[[nodiscard]] size_type alive() const {
+auto sz = entities.size();
+
+for(auto curr = free_list; curr != null; --sz) {
+curr = entities[entity_traits::to_entity(curr)];
+}
+
+return sz;
+}
+
+/**
+     * @brief Increases the capacity (number of entities) of the registry.
+     * @param cap Desired capacity.
+     */
+void reserve(const size_type cap) {
+entities.reserve(cap);
+}
+
+/**
+     * @brief Returns the number of entities that a registry has currently
+     * allocated space for.
+     * @return Capacity of the registry.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
+return entities.capacity();
+}
+
+/**
+     * @brief Checks whether the registry is empty (no entities still in use).
+     * @return True if the registry is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const {
+return !alive();
+}
+
+/**
+     * @brief Direct access to the list of entities of a registry.
+     *
+     * The returned pointer is such that range `[data(), data() + size())` is
+     * always a valid range, even if the registry is empty.
+     *
+     * @warning
+     * This list contains both valid and destroyed entities and isn't suitable
+     * for direct use.
+     *
+     * @return A pointer to the array of entities.
+     */
+[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
+return entities.data();
+}
+
+/**
+     * @brief Returns the head of the list of released entities.
+     *
+     * This function is intended for use in conjunction with `assign`.<br/>
+     * The returned entity has an invalid identifier in all cases.
+     *
+     * @return The head of the list of released entities.
+     */
+[[nodiscard]] entity_type released() const ENTT_NOEXCEPT {
+return free_list;
+}
+
+/**
+     * @brief Checks if an identifier refers to a valid entity.
+     * @param entity An identifier, either valid or not.
+     * @return True if the identifier is valid, false otherwise.
+     */
+[[nodiscard]] bool valid(const entity_type entity) const {
+const auto pos = size_type(entity_traits::to_entity(entity));
+return (pos < entities.size() && entities[pos] == entity);
+}
+
+/**
+     * @brief Returns the actual version for an identifier.
+     * @param entity A valid identifier.
+     * @return The version for the given identifier if valid, the tombstone
+     * version otherwise.
+     */
+[[nodiscard]] version_type current(const entity_type entity) const {
+const auto pos = size_type(entity_traits::to_entity(entity));
+return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone);
+}
+
+/**
+     * @brief Creates a new entity or recycles a destroyed one.
+     * @return A valid identifier.
+     */
+[[nodiscard]] entity_type create() {
+return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier();
+}
+
+/**
+     * @copybrief create
+     *
+     * If the requested entity isn't in use, the suggested identifier is used.
+     * Otherwise, a new identifier is generated.
+     *
+     * @param hint Required identifier.
+     * @return A valid identifier.
+     */
+[[nodiscard]] entity_type create(const entity_type hint) {
+const auto length = entities.size();
+
+if(hint == null || hint == tombstone) {
+return create();
+} else if(const auto req = entity_traits::to_entity(hint); !(req < length)) {
+entities.resize(size_type(req) + 1u, null);
+
+for(auto pos = length; pos < req; ++pos) {
+release_entity(generate_identifier(pos), {});
+}
+
+return (entities[req] = hint);
+} else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) {
+return create();
+} else {
+auto *it = &free_list;
+for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {}
+*it = entity_traits::combine(curr, entity_traits::to_integral(*it));
+return (entities[req] = hint);
+}
+}
+
+/**
+     * @brief Assigns each element in a range an identifier.
+     *
+     * @sa create
+     *
+     * @tparam It Type of forward iterator.
+     * @param first An iterator to the first element of the range to generate.
+     * @param last An iterator past the last element of the range to generate.
+     */
+template<typename It>
+void create(It first, It last) {
+for(; free_list != null && first != last; ++first) {
+*first = recycle_identifier();
+}
+
+const auto length = entities.size();
+entities.resize(length + std::distance(first, last), null);
+
+for(auto pos = length; first != last; ++first, ++pos) {
+*first = entities[pos] = generate_identifier(pos);
+}
+}
+
+/**
+     * @brief Assigns identifiers to an empty registry.
+     *
+     * This function is intended for use in conjunction with `data`, `size` and
+     * `destroyed`.<br/>
+     * Don't try to inject ranges of randomly generated entities nor the _wrong_
+     * head for the list of destroyed entities. There is no guarantee that a
+     * registry will continue to work properly in this case.
+     *
+     * @warning
+     * There must be no entities still alive for this to work properly.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param destroyed The head of the list of destroyed entities.
+     */
+template<typename It>
+void assign(It first, It last, const entity_type destroyed) {
+ENTT_ASSERT(!alive(), "Entities still alive");
+entities.assign(first, last);
+free_list = destroyed;
+}
+
+/**
+     * @brief Releases an identifier.
+     *
+     * The version is updated and the identifier can be recycled at any time.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @param entity A valid identifier.
+     * @return The version of the recycled entity.
+     */
+version_type release(const entity_type entity) {
+return release(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
+}
+
+/**
+     * @brief Releases an identifier.
+     *
+     * The suggested version or the valid version closest to the suggested one
+     * is used instead of the implicitly generated version.
+     *
+     * @sa release
+     *
+     * @param entity A valid identifier.
+     * @param version A desired version upon destruction.
+     * @return The version actually assigned to the entity.
+     */
+version_type release(const entity_type entity, const version_type version) {
+ENTT_ASSERT(orphan(entity), "Non-orphan entity");
+return release_entity(entity, version);
+}
+
+/**
+     * @brief Releases all identifiers in a range.
+     *
+     * @sa release
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void release(It first, It last) {
+for(; first != last; ++first) {
+release(*first);
+}
+}
+
+/**
+     * @brief Destroys an entity and releases its identifier.
+     *
+     * @sa release
+     *
+     * @warning
+     * Adding or removing components to an entity that is being destroyed can
+     * result in undefined behavior. Attempting to use an invalid entity results
+     * in undefined behavior.
+     *
+     * @param entity A valid identifier.
+     * @return The version of the recycled entity.
+     */
+version_type destroy(const entity_type entity) {
+return destroy(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
+}
+
+/**
+     * @brief Destroys an entity and releases its identifier.
+     *
+     * The suggested version or the valid version closest to the suggested one
+     * is used instead of the implicitly generated version.
+     *
+     * @sa destroy
+     *
+     * @param entity A valid identifier.
+     * @param version A desired version upon destruction.
+     * @return The version actually assigned to the entity.
+     */
+version_type destroy(const entity_type entity, const version_type version) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+
+for(size_type pos = pools.size(); pos; --pos) {
+pools.begin()[pos - 1u].second->remove(entity);
+}
+
+return release_entity(entity, version);
+}
+
+/**
+     * @brief Destroys all entities in a range and releases their identifiers.
+     *
+     * @sa destroy
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void destroy(It first, It last) {
+for(; first != last; ++first) {
+destroy(*first);
+}
+}
+
+/**
+     * @brief Assigns the given component to an entity.
+     *
+     * The component must have a proper constructor or be of aggregate type.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to assign a component to an entity
+     * that already owns it results in undefined behavior.
+     *
+     * @tparam Component Type of component to create.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns each entity in a range the given component.
+     *
+     * @sa emplace
+     *
+     * @tparam Component Type of component to create.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param value An instance of the component to assign.
+     */
+template<typename Component, typename It>
+void insert(It first, It last, const Component &value = {}) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().insert(first, last, value);
+}
+
+/**
+     * @brief Assigns each entity in a range the given components.
+     *
+     * @sa emplace
+     *
+     * @tparam Component Type of component to create.
+     * @tparam EIt Type of input iterator.
+     * @tparam CIt Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param from An iterator to the first element of the range of components.
+     */
+template<typename Component, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Component>>>
+void insert(EIt first, EIt last, CIt from) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().insert(first, last, from);
+}
+
+/**
+     * @brief Assigns or replaces the given component for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to assign or replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the newly created component.
+     */
+template<typename Component, typename... Args>
+decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+auto &cpool = assure<Component>();
+
+return cpool.contains(entity)
+? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); })
+: cpool.emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Patches the given component for an entity.
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(Component &);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned. However, this function can be used to trigger an update signal
+     * for them.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to patch a component of an entity
+     * that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Type of component to patch.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entity A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched component.
+     */
+template<typename Component, typename... Func>
+decltype(auto) patch(const entity_type entity, Func &&...func) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().patch(entity, std::forward<Func>(func)...);
+}
+
+/**
+     * @brief Replaces the given component for an entity.
+     *
+     * The component must have a proper constructor or be of aggregate type.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to replace a component of an
+     * entity that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Type of component to replace.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return A reference to the component being replaced.
+     */
+template<typename Component, typename... Args>
+decltype(auto) replace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return assure<Component>().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); });
+}
+
+/**
+     * @brief Removes the given components from an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to remove.
+     * @tparam Other Other types of components to remove.
+     * @param entity A valid identifier.
+     * @return The number of components actually removed.
+     */
+template<typename Component, typename... Other>
+size_type remove(const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<Component>().remove(entity) + ... + assure<Other>().remove(entity));
+}
+
+/**
+     * @brief Removes the given components from all the entities in a range.
+     *
+     * @sa remove
+     *
+     * @tparam Component Type of component to remove.
+     * @tparam Other Other types of components to remove.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return The number of components actually removed.
+     */
+template<typename Component, typename... Other, typename It>
+size_type remove(It first, It last) {
+if constexpr(sizeof...(Other) == 0u) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+return assure<Component>().remove(std::move(first), std::move(last));
+} else {
+size_type count{};
+
+for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
+ENTT_ASSERT(valid(*first), "Invalid entity");
+count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
+}
+
+return count;
+}
+}
+
+/**
+     * @brief Erases the given components from an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to erase a component from an
+     * entity that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Types of components to erase.
+     * @tparam Other Other types of components to erase.
+     * @param entity A valid identifier.
+     */
+template<typename Component, typename... Other>
+void erase(const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+(assure<Component>().erase(entity), (assure<Other>().erase(entity), ...));
+}
+
+/**
+     * @brief Erases the given components from all the entities in a range.
+     *
+     * @sa erase
+     *
+     * @tparam Component Types of components to erase.
+     * @tparam Other Other types of components to erase.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename Component, typename... Other, typename It>
+void erase(It first, It last) {
+if constexpr(sizeof...(Other) == 0u) {
+ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
+assure<Component>().erase(std::move(first), std::move(last));
+} else {
+for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
+ENTT_ASSERT(valid(*first), "Invalid entity");
+std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools);
+}
+}
+}
+
+/**
+     * @brief Removes all tombstones from a registry or only the pools for the
+     * given components.
+     * @tparam Component Types of components for which to clear all tombstones.
+     */
+template<typename... Component>
+void compact() {
+if constexpr(sizeof...(Component) == 0) {
+for(auto &&curr: pools) {
+curr.second->compact();
+}
+} else {
+(assure<Component>().compact(), ...);
+}
+}
+
+/**
+     * @brief Checks if an entity has all the given components.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Components for which to perform the check.
+     * @param entity A valid identifier.
+     * @return True if the entity has all the components, false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool all_of(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<std::remove_const_t<Component>>().contains(entity) && ...);
+}
+
+/**
+     * @brief Checks if an entity has at least one of the given components.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Components for which to perform the check.
+     * @param entity A valid identifier.
+     * @return True if the entity has at least one of the given components,
+     * false otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool any_of(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return (assure<std::remove_const_t<Component>>().contains(entity) || ...);
+}
+
+/**
+     * @brief Returns references to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to get a component from an entity
+     * that doesn't own it results in undefined behavior.
+     *
+     * @tparam Component Types of components to get.
+     * @param entity A valid identifier.
+     * @return References to the components owned by the entity.
+     */
+template<typename... Component>
+[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return view<Component...>().template get<const Component...>(entity);
+}
+
+/*! @copydoc get */
+template<typename... Component>
+[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return view<Component...>().template get<Component...>(entity);
+}
+
+/**
+     * @brief Returns a reference to the given component for an entity.
+     *
+     * In case the entity doesn't own the component, the parameters provided are
+     * used to construct it.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @tparam Component Type of component to get.
+     * @tparam Args Types of arguments to use to construct the component.
+     * @param entity A valid identifier.
+     * @param args Parameters to use to initialize the component.
+     * @return Reference to the component owned by the entity.
+     */
+template<typename Component, typename... Args>
+[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+auto &cpool = assure<Component>();
+return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Returns pointers to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @note
+     * The registry retains ownership of the pointed-to components.
+     *
+     * @tparam Component Types of components to get.
+     * @param entity A valid identifier.
+     * @return Pointers to the components owned by the entity.
+     */
+template<typename... Component>
+[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+
+if constexpr(sizeof...(Component) == 1) {
+const auto &cpool = assure<std::remove_const_t<Component>...>();
+return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr;
+} else {
+return std::make_tuple(try_get<Component>(entity)...);
+}
+}
+
+/*! @copydoc try_get */
+template<typename... Component>
+[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) {
+if constexpr(sizeof...(Component) == 1) {
+return (const_cast<Component *>(std::as_const(*this).template try_get<Component>(entity)), ...);
+} else {
+return std::make_tuple(try_get<Component>(entity)...);
+}
+}
+
+/**
+     * @brief Clears a whole registry or the pools for the given components.
+     * @tparam Component Types of components to remove from their entities.
+     */
+template<typename... Component>
+void clear() {
+if constexpr(sizeof...(Component) == 0) {
+for(auto &&curr: pools) {
+curr.second->clear();
+}
+
+each([this](const auto entity) { this->release(entity); });
+} else {
+(assure<Component>().clear(), ...);
+}
+}
+
+/**
+     * @brief Iterates all the entities that are still in use.
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(const Entity);
+     * @endcode
+     *
+     * It's not defined whether entities created during iteration are returned.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+if(free_list == null) {
+for(auto pos = entities.size(); pos; --pos) {
+func(entities[pos - 1]);
+}
+} else {
+for(auto pos = entities.size(); pos; --pos) {
+if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) {
+func(entity);
+}
+}
+}
+}
+
+/**
+     * @brief Checks if an entity has components assigned.
+     * @param entity A valid identifier.
+     * @return True if the entity has no components assigned, false otherwise.
+     */
+[[nodiscard]] bool orphan(const entity_type entity) const {
+ENTT_ASSERT(valid(entity), "Invalid entity");
+return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); });
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever a new instance of the
+     * given component is created and assigned to an entity.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **after** assigning the component to the entity.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_construct() {
+return assure<Component>().on_construct();
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever an instance of the
+     * given component is explicitly updated.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **after** updating the component.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_update() {
+return assure<Component>().on_update();
+}
+
+/**
+     * @brief Returns a sink object for the given component.
+     *
+     * Use this function to receive notifications whenever an instance of the
+     * given component is removed from an entity and thus destroyed.<br/>
+     * The function type for a listener is equivalent to:
+     *
+     * @code{.cpp}
+     * void(basic_registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **before** removing the component from the entity.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+template<typename Component>
+[[nodiscard]] auto on_destroy() {
+return assure<Component>().on_destroy();
+}
+
+/**
+     * @brief Returns a view for the given components.
+     *
+     * Views are created on the fly and share with the registry its internal
+     * data structures. Feel free to discard them after the use.<br/>
+     * Creating and destroying a view is an incredibly cheap operation. As a
+     * rule of thumb, storing a view should never be an option.
+     *
+     * @tparam Component Type of component used to construct the view.
+     * @tparam Other Other types of components used to construct the view.
+     * @tparam Exclude Types of components used to filter the view.
+     * @return A newly created view.
+     */
+template<typename Component, typename... Other, typename... Exclude>
+[[nodiscard]] basic_view<entity_type, get_t<std::add_const_t<Component>, std::add_const_t<Other>...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) const {
+return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
+}
+
+/*! @copydoc view */
+template<typename Component, typename... Other, typename... Exclude>
+[[nodiscard]] basic_view<entity_type, get_t<Component, Other...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) {
+return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
+}
+
+/**
+     * @brief Returns a group for the given components.
+     *
+     * Groups are created on the fly and share with the registry its internal
+     * data structures. Feel free to discard them after the use.<br/>
+     * Creating and destroying a group is an incredibly cheap operation. As a
+     * rule of thumb, storing a group should never be an option.
+     *
+     * Groups support exclusion lists and can own types of components. The more
+     * types are owned by a group, the faster it is to iterate entities and
+     * components.<br/>
+     * However, groups also affect some features of the registry such as the
+     * creation and destruction of components.
+     *
+     * @note
+     * Pools of components that are owned by a group cannot be sorted anymore.
+     * The group takes the ownership of the pools and arrange components so as
+     * to iterate them as fast as possible.
+     *
+     * @tparam Owned Types of components owned by the group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @return A newly created group.
+     */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> group(get_t<Get...>, exclude_t<Exclude...> = {}) {
+static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported");
+static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed");
+
+using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
+
+const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...);
+constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
+handler_type *handler = nullptr;
+
+auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
+return gdata.size == size
+&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
+&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
+&& (gdata.exclude(type_hash<Exclude>::value()) && ...);
+});
+
+if(it != groups.cend()) {
+handler = static_cast<handler_type *>(it->group.get());
+} else {
+group_data candidate = {
+size,
+{new handler_type{}, [](void *instance) { delete static_cast<handler_type *>(instance); }},
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); },
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); },
+[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<Exclude>::value()) || ...); },
+};
+
+handler = static_cast<handler_type *>(candidate.group.get());
+
+const void *maybe_valid_if = nullptr;
+const void *discard_if = nullptr;
+
+if constexpr(sizeof...(Owned) == 0) {
+groups.push_back(std::move(candidate));
+} else {
+[[maybe_unused]] auto has_conflict = [size](const auto &gdata) {
+const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
+const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<Exclude>::value()));
+return !overlapping || ((sz == size) || (sz == gdata.size));
+};
+
+ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups");
+
+const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
+return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size);
+});
+
+const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) {
+return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
+});
+
+maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
+discard_if = (prev == groups.crend() ? discard_if : prev->group.get());
+groups.insert(next, std::move(candidate));
+}
+
+(on_construct<std::remove_const_t<Owned>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Owned>>>(*handler), ...);
+(on_construct<std::remove_const_t<Get>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Get>>>(*handler), ...);
+(on_destroy<Exclude>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*handler), ...);
+
+(on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+(on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+(on_construct<Exclude>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
+
+if constexpr(sizeof...(Owned) == 0) {
+for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) {
+handler->current.emplace(entity);
+}
+} else {
+// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
+for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) {
+handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first);
+}
+}
+}
+
+return {handler->current, std::get<storage_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_type<std::remove_const_t<Get>> &>(cpools)...};
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<std::add_const_t<Get>...>, exclude_t<Exclude...>> group_if_exists(get_t<Get...>, exclude_t<Exclude...> = {}) const {
+auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) {
+return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude))
+&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
+&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
+&& (gdata.exclude(type_hash<Exclude>::value()) && ...);
+});
+
+if(it == groups.cend()) {
+return {};
+} else {
+using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
+return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...};
+}
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<>, exclude_t<Exclude...>> group(exclude_t<Exclude...> = {}) {
+return group<Owned...>(get_t<>{}, exclude<Exclude...>);
+}
+
+/*! @copydoc group */
+template<typename... Owned, typename... Exclude>
+[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<>, exclude_t<Exclude...>> group_if_exists(exclude_t<Exclude...> = {}) const {
+return group_if_exists<std::add_const_t<Owned>...>(get_t<>{}, exclude<Exclude...>);
+}
+
+/**
+     * @brief Checks whether the given components belong to any group.
+     * @tparam Component Types of components in which one is interested.
+     * @return True if the pools of the given components are _free_, false
+     * otherwise.
+     */
+template<typename... Component>
+[[nodiscard]] bool owned() const {
+return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Component>>::value()) || ...); });
+}
+
+/**
+     * @brief Checks whether a group can be sorted.
+     * @tparam Owned Types of components owned by the group.
+     * @tparam Get Types of components observed by the group.
+     * @tparam Exclude Types of components used to filter the group.
+     * @return True if the group can be sorted, false otherwise.
+     */
+template<typename... Owned, typename... Get, typename... Exclude>
+[[nodiscard]] bool sortable(const basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> &) ENTT_NOEXCEPT {
+constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
+auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) && (size < gdata.size); };
+return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend();
+}
+
+/**
+     * @brief Sorts the elements of a given component.
+     *
+     * The order remains valid until a component of the given type is assigned
+     * to or removed from an entity.<br/>
+     * The comparison function object returns `true` if the first element is
+     * _less_ than the second one, `false` otherwise. Its signature is also
+     * equivalent to one of the following:
+     *
+     * @code{.cpp}
+     * bool(const Entity, const Entity);
+     * bool(const Component &, const Component &);
+     * @endcode
+     *
+     * Moreover, it shall induce a _strict weak ordering_ on the values.<br/>
+     * The sort function object offers an `operator()` that accepts:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function object to use to compare the elements.
+     *
+     * The comparison function object hasn't necessarily the type of the one
+     * passed along with the other parameters to this member function.
+     *
+     * @warning
+     * Pools of components owned by a group cannot be sorted.
+     *
+     * @tparam Component Type of components to sort.
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+ENTT_ASSERT(!owned<Component>(), "Cannot sort owned storage");
+auto &cpool = assure<Component>();
+
+if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) {
+auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); };
+cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
+} else {
+cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
+}
+}
+
+/**
+     * @brief Sorts two pools of components in the same way.
+     *
+     * Being `To` and `From` the two sets, after invoking this function an
+     * iterator for `To` returns elements according to the following rules:
+     *
+     * * All entities in `To` that are also in `From` are returned first
+     *   according to the order they have in `From`.
+     * * All entities in `To` that are not in `From` are returned in no
+     *   particular order after all the other entities.
+     *
+     * Any subsequent change to `From` won't affect the order in `To`.
+     *
+     * @warning
+     * Pools of components owned by a group cannot be sorted.
+     *
+     * @tparam To Type of components to sort.
+     * @tparam From Type of components to use to sort.
+     */
+template<typename To, typename From>
+void sort() {
+ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage");
+assure<To>().respect(assure<From>());
+}
+
+/**
+     * @brief Returns the context object, that is, a general purpose container.
+     * @return The context object, that is, a general purpose container.
+     */
+context &ctx() ENTT_NOEXCEPT {
+return vars;
+}
+
+/*! @copydoc ctx */
+const context &ctx() const ENTT_NOEXCEPT {
+return vars;
+}
+
+private:
+dense_map<id_type, std::unique_ptr<base_type>, identity> pools;
+std::vector<group_data> groups;
+std::vector<entity_type> entities;
+entity_type free_list;
+context vars;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/runtime_view.hpp"
+#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
+#define ENTT_ENTITY_RUNTIME_VIEW_HPP
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Set>
+class runtime_view_iterator final {
+using iterator_type = typename Set::iterator;
+
+[[nodiscard]] bool valid() const {
+return (!tombstone_check || *it != tombstone)
+&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
+&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
+}
+
+public:
+using difference_type = typename iterator_type::difference_type;
+using value_type = typename iterator_type::value_type;
+using pointer = typename iterator_type::pointer;
+using reference = typename iterator_type::reference;
+using iterator_category = std::bidirectional_iterator_tag;
+
+runtime_view_iterator() ENTT_NOEXCEPT
+: pools{},
+filter{},
+it{},
+tombstone_check{} {}
+
+runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT
+: pools{&cpools},
+filter{&ignore},
+it{curr},
+tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} {
+if(it != (*pools)[0]->end() && !valid()) {
+++(*this);
+}
+}
+
+runtime_view_iterator &operator++() {
+while(++it != (*pools)[0]->end() && !valid()) {}
+return *this;
+}
+
+runtime_view_iterator operator++(int) {
+runtime_view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+runtime_view_iterator &operator--() {
+while(--it != (*pools)[0]->begin() && !valid()) {}
+return *this;
+}
+
+runtime_view_iterator operator--(int) {
+runtime_view_iterator orig = *this;
+return operator--(), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return it.operator->();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+return it == other.it;
+}
+
+[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+const std::vector<const Set *> *pools;
+const std::vector<const Set *> *filter;
+iterator_type it;
+bool tombstone_check;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Runtime view implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename>
+struct basic_runtime_view;
+
+/**
+ * @brief Generic runtime view.
+ *
+ * Runtime views iterate over those entities that have at least all the given
+ * components in their bags. During initialization, a runtime view looks at the
+ * number of entities available for each component and picks up a reference to
+ * the smallest set of candidate entities in order to get a performance boost
+ * when iterate.<br/>
+ * Order of elements during iterations are highly dependent on the order of the
+ * underlying data structures. See sparse_set and its specializations for more
+ * details.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all the other cases, modifying the pools of the given components in any
+ * way invalidates all the iterators and using them results in undefined
+ * behavior.
+ *
+ * @note
+ * Views share references to the underlying data structures of the registry that
+ * generated them. Therefore any change to the entities and to the components
+ * made by means of the registry are immediately reflected by the views, unless
+ * a pool was missing when the view was built (in this case, the view won't
+ * have a valid reference and won't be updated accordingly).
+ *
+ * @warning
+ * Lifetime of a view must not overcome that of the registry that generated it.
+ * In any other case, attempting to use a view results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Allocator>
+struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> {
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = basic_sparse_set<Entity, Allocator>;
+/*! @brief Bidirectional iterator type. */
+using iterator = internal::runtime_view_iterator<base_type>;
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_runtime_view() ENTT_NOEXCEPT
+: pools{},
+filter{} {}
+
+/**
+     * @brief Appends an opaque storage object to a runtime view.
+     * @param base An opaque reference to a storage object.
+     * @return This runtime view.
+     */
+basic_runtime_view &iterate(const base_type &base) {
+if(pools.empty() || !(base.size() < pools[0u]->size())) {
+pools.push_back(&base);
+} else {
+pools.push_back(std::exchange(pools[0u], &base));
+}
+
+return *this;
+}
+
+/**
+     * @brief Adds an opaque storage object as a filter of a runtime view.
+     * @param base An opaque reference to a storage object.
+     * @return This runtime view.
+     */
+basic_runtime_view &exclude(const base_type &base) {
+filter.push_back(&base);
+return *this;
+}
+
+/**
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
+     */
+[[nodiscard]] size_type size_hint() const {
+return pools.empty() ? size_type{} : pools.front()->size();
+}
+
+/**
+     * @brief Returns an iterator to the first entity that has the given
+     * components.
+     *
+     * The returned iterator points to the first entity that has the given
+     * components. If the view is empty, the returned iterator will be equal to
+     * `end()`.
+     *
+     * @return An iterator to the first entity that has the given components.
+     */
+[[nodiscard]] iterator begin() const {
+return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity that has the
+     * given components.
+     *
+     * The returned iterator points to the entity following the last entity that
+     * has the given components. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity that has the
+     * given components.
+     */
+[[nodiscard]] iterator end() const {
+return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const {
+return !pools.empty()
+&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
+&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
+}
+
+/**
+     * @brief Iterates entities and applies the given function object to them.
+     *
+     * The function object is invoked for each entity. It is provided only with
+     * the entity itself. To get the components, users can use the registry with
+     * which the view was built.<br/>
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(const entity_type);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+for(const auto entity: *this) {
+func(entity);
+}
+}
+
+private:
+std::vector<const base_type *> pools;
+std::vector<const base_type *> filter;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/sigh_storage_mixin.hpp"
+#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
+#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/any.hpp"
+
+// #include "../signal/sigh.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Mixin type used to add signal support to storage types.
+ *
+ * The function type of a listener is equivalent to:
+ *
+ * @code{.cpp}
+ * void(basic_registry<entity_type> &, entity_type);
+ * @endcode
+ *
+ * This applies to all signals made available.
+ *
+ * @tparam Type The type of the underlying storage.
+ */
+template<typename Type>
+class sigh_storage_mixin final: public Type {
+using basic_iterator = typename Type::basic_iterator;
+
+template<typename Func>
+void notify_destruction(basic_iterator first, basic_iterator last, Func func) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+
+for(; first != last; ++first) {
+const auto entt = *first;
+destruction.publish(*owner, entt);
+const auto it = Type::find(entt);
+func(it, it + 1u);
+}
+}
+
+void swap_and_pop(basic_iterator first, basic_iterator last) final {
+notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); });
+}
+
+void in_place_pop(basic_iterator first, basic_iterator last) final {
+notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); });
+}
+
+basic_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::try_emplace(entt, force_back, value);
+construction.publish(*owner, entt);
+return Type::find(entt);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = typename Type::entity_type;
+
+/*! @brief Inherited constructors. */
+using Type::Type;
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever a new instance is created and assigned to an entity.<br/>
+     * Listeners are invoked after the object has been assigned to the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
+return sink{construction};
+}
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is explicitly updated.<br/>
+     * Listeners are invoked after the object has been updated.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_update() ENTT_NOEXCEPT {
+return sink{update};
+}
+
+/**
+     * @brief Returns a sink object.
+     *
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance is removed from an entity and thus destroyed.<br/>
+     * Listeners are invoked before the object has been removed from the entity.
+     *
+     * @sa sink
+     *
+     * @return A temporary sink object.
+     */
+[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
+return sink{destruction};
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     * @param args Parameters to use to initialize the object.
+     * @return A reference to the newly created object.
+     */
+template<typename... Args>
+decltype(auto) emplace(const entity_type entt, Args &&...args) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::emplace(entt, std::forward<Args>(args)...);
+construction.publish(*owner, entt);
+return this->get(entt);
+}
+
+/**
+     * @brief Patches the given instance for an entity.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the patched instance.
+     */
+template<typename... Func>
+decltype(auto) patch(const entity_type entt, Func &&...func) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::patch(entt, std::forward<Func>(func)...);
+update.publish(*owner, entt);
+return this->get(entt);
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam It Type of input iterator.
+     * @tparam Args Types of arguments to use to construct the objects assigned
+     * to the entities.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param args Parameters to use to initialize the objects assigned to the
+     * entities.
+     */
+template<typename It, typename... Args>
+void insert(It first, It last, Args &&...args) {
+ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+Type::insert(first, last, std::forward<Args>(args)...);
+
+for(auto it = construction.empty() ? last : first; it != last; ++it) {
+construction.publish(*owner, *it);
+}
+}
+
+/**
+     * @brief Forwards variables to mixins, if any.
+     * @param value A variable wrapped in an opaque container.
+     */
+void bind(any value) ENTT_NOEXCEPT final {
+auto *reg = any_cast<basic_registry<entity_type>>(&value);
+owner = reg ? reg : owner;
+Type::bind(std::move(value));
+}
+
+private:
+sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
+sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
+sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
+basic_registry<entity_type> *owner{};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/snapshot.hpp"
+#ifndef ENTT_ENTITY_SNAPSHOT_HPP
+#define ENTT_ENTITY_SNAPSHOT_HPP
+
+#include <array>
+#include <cstddef>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../container/dense_map.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "registry.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to create snapshots from a registry.
+ *
+ * A _snapshot_ can be either a dump of the entire registry or a narrower
+ * selection of components of interest.<br/>
+ * This type can be used in both cases if provided with a correctly configured
+ * output archive.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_snapshot {
+using entity_traits = entt_traits<Entity>;
+
+template<typename Component, typename Archive, typename It>
+void get(Archive &archive, std::size_t sz, It first, It last) const {
+const auto view = reg->template view<std::add_const_t<Component>>();
+archive(typename entity_traits::entity_type(sz));
+
+while(first != last) {
+const auto entt = *(first++);
+
+if(reg->template all_of<Component>(entt)) {
+std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
+}
+}
+}
+
+template<typename... Component, typename Archive, typename It, std::size_t... Index>
+void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
+std::array<std::size_t, sizeof...(Index)> size{};
+auto begin = first;
+
+while(begin != last) {
+const auto entt = *(begin++);
+((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
+}
+
+(get<Component>(archive, size[Index], first, last), ...);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+
+/**
+     * @brief Constructs an instance that is bound to a given registry.
+     * @param source A valid reference to a registry.
+     */
+basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
+: reg{&source} {}
+
+/*! @brief Default move constructor. */
+basic_snapshot(basic_snapshot &&) ENTT_NOEXCEPT = default;
+
+/*! @brief Default move assignment operator. @return This snapshot. */
+basic_snapshot &operator=(basic_snapshot &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Puts aside all the entities from the underlying registry.
+     *
+     * Entities are serialized along with their versions. Destroyed entities are
+     * taken in consideration as well by this function.
+     *
+     * @tparam Archive Type of output archive.
+     * @param archive A valid reference to an output archive.
+     * @return An object of this type to continue creating the snapshot.
+     */
+template<typename Archive>
+const basic_snapshot &entities(Archive &archive) const {
+const auto sz = reg->size();
+
+archive(typename entity_traits::entity_type(sz + 1u));
+archive(reg->released());
+
+for(auto first = reg->data(), last = first + sz; first != last; ++first) {
+archive(*first);
+}
+
+return *this;
+}
+
+/**
+     * @brief Puts aside the given components.
+     *
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
+     *
+     * @tparam Component Types of components to serialize.
+     * @tparam Archive Type of output archive.
+     * @param archive A valid reference to an output archive.
+     * @return An object of this type to continue creating the snapshot.
+     */
+template<typename... Component, typename Archive>
+const basic_snapshot &component(Archive &archive) const {
+if constexpr(sizeof...(Component) == 1u) {
+const auto view = reg->template view<const Component...>();
+(component<Component>(archive, view.rbegin(), view.rend()), ...);
+return *this;
+} else {
+(component<Component>(archive), ...);
+return *this;
+}
+}
+
+/**
+     * @brief Puts aside the given components for the entities in a range.
+     *
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
+     *
+     * @tparam Component Types of components to serialize.
+     * @tparam Archive Type of output archive.
+     * @tparam It Type of input iterator.
+     * @param archive A valid reference to an output archive.
+     * @param first An iterator to the first element of the range to serialize.
+     * @param last An iterator past the last element of the range to serialize.
+     * @return An object of this type to continue creating the snapshot.
+     */
+template<typename... Component, typename Archive, typename It>
+const basic_snapshot &component(Archive &archive, It first, It last) const {
+component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
+return *this;
+}
+
+private:
+const basic_registry<entity_type> *reg;
+};
+
+/**
+ * @brief Utility class to restore a snapshot as a whole.
+ *
+ * A snapshot loader requires that the destination registry be empty and loads
+ * all the data at once while keeping intact the identifiers that the entities
+ * originally had.<br/>
+ * An example of use is the implementation of a save/restore utility.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_snapshot_loader {
+using entity_traits = entt_traits<Entity>;
+
+template<typename Type, typename Archive>
+void assign(Archive &archive) const {
+typename entity_traits::entity_type length{};
+entity_type entt;
+
+archive(length);
+
+if constexpr(ignore_as_empty_v<Type>) {
+while(length--) {
+archive(entt);
+const auto entity = reg->valid(entt) ? entt : reg->create(entt);
+ENTT_ASSERT(entity == entt, "Entity not available for use");
+reg->template emplace<Type>(entt);
+}
+} else {
+Type instance;
+
+while(length--) {
+archive(entt, instance);
+const auto entity = reg->valid(entt) ? entt : reg->create(entt);
+ENTT_ASSERT(entity == entt, "Entity not available for use");
+reg->template emplace<Type>(entt, std::move(instance));
+}
+}
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+
+/**
+     * @brief Constructs an instance that is bound to a given registry.
+     * @param source A valid reference to a registry.
+     */
+basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
+: reg{&source} {
+// restoring a snapshot as a whole requires a clean registry
+ENTT_ASSERT(reg->empty(), "Registry must be empty");
+}
+
+/*! @brief Default move constructor. */
+basic_snapshot_loader(basic_snapshot_loader &&) ENTT_NOEXCEPT = default;
+
+/*! @brief Default move assignment operator. @return This loader. */
+basic_snapshot_loader &operator=(basic_snapshot_loader &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Restores entities that were in use during serialization.
+     *
+     * This function restores the entities that were in use during serialization
+     * and gives them the versions they originally had.
+     *
+     * @tparam Archive Type of input archive.
+     * @param archive A valid reference to an input archive.
+     * @return A valid loader to continue restoring data.
+     */
+template<typename Archive>
+const basic_snapshot_loader &entities(Archive &archive) const {
+typename entity_traits::entity_type length{};
+
+archive(length);
+std::vector<entity_type> all(length);
+
+for(std::size_t pos{}; pos < length; ++pos) {
+archive(all[pos]);
+}
+
+reg->assign(++all.cbegin(), all.cend(), all[0u]);
+
+return *this;
+}
+
+/**
+     * @brief Restores components and assigns them to the right entities.
+     *
+     * The template parameter list must be exactly the same used during
+     * serialization. In the event that the entity to which the component is
+     * assigned doesn't exist yet, the loader will take care to create it with
+     * the version it originally had.
+     *
+     * @tparam Component Types of components to restore.
+     * @tparam Archive Type of input archive.
+     * @param archive A valid reference to an input archive.
+     * @return A valid loader to continue restoring data.
+     */
+template<typename... Component, typename Archive>
+const basic_snapshot_loader &component(Archive &archive) const {
+(assign<Component>(archive), ...);
+return *this;
+}
+
+/**
+     * @brief Destroys those entities that have no components.
+     *
+     * In case all the entities were serialized but only part of the components
+     * was saved, it could happen that some of the entities have no components
+     * once restored.<br/>
+     * This functions helps to identify and destroy those entities.
+     *
+     * @return A valid loader to continue restoring data.
+     */
+const basic_snapshot_loader &orphans() const {
+reg->each([this](const auto entt) {
+if(reg->orphan(entt)) {
+reg->release(entt);
+}
+});
+
+return *this;
+}
+
+private:
+basic_registry<entity_type> *reg;
+};
+
+/**
+ * @brief Utility class for _continuous loading_.
+ *
+ * A _continuous loader_ is designed to load data from a source registry to a
+ * (possibly) non-empty destination. The loader can accommodate in a registry
+ * more than one snapshot in a sort of _continuous loading_ that updates the
+ * destination one step at a time.<br/>
+ * Identifiers that entities originally had are not transferred to the target.
+ * Instead, the loader maps remote identifiers to local ones while restoring a
+ * snapshot.<br/>
+ * An example of use is the implementation of a client-server applications with
+ * the requirement of transferring somehow parts of the representation side to
+ * side.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+class basic_continuous_loader {
+using entity_traits = entt_traits<Entity>;
+
+void destroy(Entity entt) {
+if(const auto it = remloc.find(entt); it == remloc.cend()) {
+const auto local = reg->create();
+remloc.emplace(entt, std::make_pair(local, true));
+reg->destroy(local);
+}
+}
+
+void restore(Entity entt) {
+const auto it = remloc.find(entt);
+
+if(it == remloc.cend()) {
+const auto local = reg->create();
+remloc.emplace(entt, std::make_pair(local, true));
+} else {
+if(!reg->valid(remloc[entt].first)) {
+remloc[entt].first = reg->create();
+}
+
+// set the dirty flag
+remloc[entt].second = true;
+}
+}
+
+template<typename Container>
+auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
+// map like container
+Container other;
+
+for(auto &&pair: container) {
+using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
+using second_type = typename std::decay_t<decltype(pair)>::second_type;
+
+if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
+other.emplace(map(pair.first), map(pair.second));
+} else if constexpr(std::is_same_v<first_type, entity_type>) {
+other.emplace(map(pair.first), std::move(pair.second));
+} else {
+static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type");
+other.emplace(std::move(pair.first), map(pair.second));
+}
+}
+
+using std::swap;
+swap(container, other);
+}
+
+template<typename Container>
+auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
+// vector like container
+static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
+
+for(auto &&entt: container) {
+entt = map(entt);
+}
+}
+
+template<typename Other, typename Type, typename Member>
+void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) {
+if constexpr(!std::is_same_v<Other, Type>) {
+return;
+} else if constexpr(std::is_same_v<Member, entity_type>) {
+instance.*member = map(instance.*member);
+} else {
+// maybe a container? let's try...
+update(0, instance.*member);
+}
+}
+
+template<typename Component>
+void remove_if_exists() {
+for(auto &&ref: remloc) {
+const auto local = ref.second.first;
+
+if(reg->valid(local)) {
+reg->template remove<Component>(local);
+}
+}
+}
+
+template<typename Other, typename Archive, typename... Type, typename... Member>
+void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) {
+typename entity_traits::entity_type length{};
+entity_type entt;
+
+archive(length);
+
+if constexpr(ignore_as_empty_v<Other>) {
+while(length--) {
+archive(entt);
+restore(entt);
+reg->template emplace_or_replace<Other>(map(entt));
+}
+} else {
+Other instance;
+
+while(length--) {
+archive(entt, instance);
+(update(instance, member), ...);
+restore(entt);
+reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
+}
+}
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+
+/**
+     * @brief Constructs an instance that is bound to a given registry.
+     * @param source A valid reference to a registry.
+     */
+basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
+: reg{&source} {}
+
+/*! @brief Default move constructor. */
+basic_continuous_loader(basic_continuous_loader &&) = default;
+
+/*! @brief Default move assignment operator. @return This loader. */
+basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
+
+/**
+     * @brief Restores entities that were in use during serialization.
+     *
+     * This function restores the entities that were in use during serialization
+     * and creates local counterparts for them if required.
+     *
+     * @tparam Archive Type of input archive.
+     * @param archive A valid reference to an input archive.
+     * @return A non-const reference to this loader.
+     */
+template<typename Archive>
+basic_continuous_loader &entities(Archive &archive) {
+typename entity_traits::entity_type length{};
+entity_type entt{};
+
+archive(length);
+// discards the head of the list of destroyed entities
+archive(entt);
+
+for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
+archive(entt);
+
+if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
+restore(entt);
+} else {
+destroy(entt);
+}
+}
+
+return *this;
+}
+
+/**
+     * @brief Restores components and assigns them to the right entities.
+     *
+     * The template parameter list must be exactly the same used during
+     * serialization. In the event that the entity to which the component is
+     * assigned doesn't exist yet, the loader will take care to create a local
+     * counterpart for it.<br/>
+     * Members can be either data members of type entity_type or containers of
+     * entities. In both cases, the loader will visit them and update the
+     * entities by replacing each one with its local counterpart.
+     *
+     * @tparam Component Type of component to restore.
+     * @tparam Archive Type of input archive.
+     * @tparam Type Types of components to update with local counterparts.
+     * @tparam Member Types of members to update with their local counterparts.
+     * @param archive A valid reference to an input archive.
+     * @param member Members to update with their local counterparts.
+     * @return A non-const reference to this loader.
+     */
+template<typename... Component, typename Archive, typename... Type, typename... Member>
+basic_continuous_loader &component(Archive &archive, Member Type::*...member) {
+(remove_if_exists<Component>(), ...);
+(assign<Component>(archive, member...), ...);
+return *this;
+}
+
+/**
+     * @brief Helps to purge entities that no longer have a conterpart.
+     *
+     * Users should invoke this member function after restoring each snapshot,
+     * unless they know exactly what they are doing.
+     *
+     * @return A non-const reference to this loader.
+     */
+basic_continuous_loader &shrink() {
+auto it = remloc.begin();
+
+while(it != remloc.cend()) {
+const auto local = it->second.first;
+bool &dirty = it->second.second;
+
+if(dirty) {
+dirty = false;
+++it;
+} else {
+if(reg->valid(local)) {
+reg->destroy(local);
+}
+
+it = remloc.erase(it);
+}
+}
+
+return *this;
+}
+
+/**
+     * @brief Destroys those entities that have no components.
+     *
+     * In case all the entities were serialized but only part of the components
+     * was saved, it could happen that some of the entities have no components
+     * once restored.<br/>
+     * This functions helps to identify and destroy those entities.
+     *
+     * @return A non-const reference to this loader.
+     */
+basic_continuous_loader &orphans() {
+reg->each([this](const auto entt) {
+if(reg->orphan(entt)) {
+reg->release(entt);
+}
+});
+
+return *this;
+}
+
+/**
+     * @brief Tests if a loader knows about a given entity.
+     * @param entt A valid identifier.
+     * @return True if `entity` is managed by the loader, false otherwise.
+     */
+[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
+return (remloc.find(entt) != remloc.cend());
+}
+
+/**
+     * @brief Returns the identifier to which an entity refers.
+     * @param entt A valid identifier.
+     * @return The local identifier if any, the null entity otherwise.
+     */
+[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
+const auto it = remloc.find(entt);
+entity_type other = null;
+
+if(it != remloc.cend()) {
+other = it->second.first;
+}
+
+return other;
+}
+
+private:
+dense_map<entity_type, std::pair<entity_type, bool>> remloc;
+basic_registry<entity_type> *reg;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/sparse_set.hpp"
+#ifndef ENTT_ENTITY_SPARSE_SET_HPP
+#define ENTT_ENTITY_SPARSE_SET_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/algorithm.hpp"
+
+// #include "../core/any.hpp"
+
+// #include "../core/memory.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Container>
+struct sparse_set_iterator final {
+using value_type = typename Container::value_type;
+using pointer = typename Container::const_pointer;
+using reference = typename Container::const_reference;
+using difference_type = typename Container::difference_type;
+using iterator_category = std::random_access_iterator_tag;
+
+sparse_set_iterator() ENTT_NOEXCEPT
+: packed{},
+offset{} {}
+
+sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT
+: packed{std::addressof(ref)},
+offset{idx} {}
+
+sparse_set_iterator &operator++() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
+sparse_set_iterator orig = *this;
+return ++(*this), orig;
+}
+
+sparse_set_iterator &operator--() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
+sparse_set_iterator orig = *this;
+return operator--(), orig;
+}
+
+sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+offset -= value;
+return *this;
+}
+
+sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+sparse_set_iterator copy = *this;
+return (copy += value);
+}
+
+sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return packed->data()[index() - value];
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return packed->data() + index();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
+return offset - 1;
+}
+
+private:
+const Container *packed;
+difference_type offset;
+};
+
+template<typename Type, typename Other>
+[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return rhs.index() - lhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() > rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename Type, typename Other>
+[[nodiscard]] bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Sparse set deletion policy. */
+enum class deletion_policy : std::uint8_t {
+/*! @brief Swap-and-pop deletion policy. */
+swap_and_pop = 0u,
+/*! @brief In-place deletion policy. */
+in_place = 1u
+};
+
+/**
+ * @brief Basic sparse set implementation.
+ *
+ * Sparse set or packed array or whatever is the name users give it.<br/>
+ * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
+ * _packed_ one; one used for direct access through contiguous memory, the other
+ * one used to get the data through an extra level of indirection.<br/>
+ * This is largely used by the registry to offer users the fastest access ever
+ * to the components. Views and groups in general are almost entirely designed
+ * around sparse sets.
+ *
+ * This type of data structure is widely documented in the literature and on the
+ * web. This is nothing more than a customized implementation suitable for the
+ * purpose of the framework.
+ *
+ * @note
+ * Internal data structures arrange elements to maximize performance. There are
+ * no guarantees that entities are returned in the insertion order when iterate
+ * a sparse set. Do not make assumption on the order in any case.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Allocator>
+class basic_sparse_set {
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
+using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
+using packed_container_type = std::vector<Entity, Allocator>;
+using entity_traits = entt_traits<Entity>;
+
+[[nodiscard]] auto sparse_ptr(const Entity entt) const {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+const auto page = pos / entity_traits::page_size;
+return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
+}
+
+[[nodiscard]] auto &sparse_ref(const Entity entt) const {
+ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
+}
+
+[[nodiscard]] auto &assure_at_least(const Entity entt) {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+const auto page = pos / entity_traits::page_size;
+
+if(!(page < sparse.size())) {
+sparse.resize(page + 1u, nullptr);
+}
+
+if(!sparse[page]) {
+auto page_allocator{packed.get_allocator()};
+sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
+std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
+}
+
+auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
+ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available");
+return elem;
+}
+
+void release_sparse_pages() {
+auto page_allocator{packed.get_allocator()};
+
+for(auto &&page: sparse) {
+if(page != nullptr) {
+std::destroy(page, page + entity_traits::page_size);
+alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
+page = nullptr;
+}
+}
+}
+
+private:
+virtual const void *get_at(const std::size_t) const {
+return nullptr;
+}
+
+virtual void swap_at(const std::size_t, const std::size_t) {}
+virtual void move_element(const std::size_t, const std::size_t) {}
+
+protected:
+/*! @brief Random access iterator type. */
+using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
+
+/**
+     * @brief Erases entities from a sparse set.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+virtual void swap_and_pop(basic_iterator first, basic_iterator last) {
+for(; first != last; ++first) {
+sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::to_integral(packed.back()));
+const auto entt = std::exchange(packed[first.index()], packed.back());
+// unnecessary but it helps to detect nasty bugs
+ENTT_ASSERT((packed.back() = tombstone, true), "");
+// lazy self-assignment guard
+sparse_ref(entt) = null;
+packed.pop_back();
+}
+}
+
+/**
+     * @brief Erases entities from a sparse set.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+virtual void in_place_pop(basic_iterator first, basic_iterator last) {
+for(; first != last; ++first) {
+sparse_ref(*first) = null;
+packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::reserved));
+}
+}
+
+/**
+     * @brief Assigns an entity to a sparse set.
+     * @param entt A valid identifier.
+     * @param force_back Force back insertion.
+     * @return Iterator pointing to the emplaced element.
+     */
+virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
+ENTT_ASSERT(!contains(entt), "Set already contains entity");
+
+if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
+packed.push_back(entt);
+elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
+return begin();
+} else {
+const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list));
+elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
+free_list = std::exchange(packed[pos], entt);
+return --(end() - pos);
+}
+}
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Underlying version type. */
+using version_type = typename entity_traits::version_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename packed_container_type::size_type;
+/*! @brief Pointer type to contained entities. */
+using pointer = typename packed_container_type::const_pointer;
+/*! @brief Random access iterator type. */
+using iterator = basic_iterator;
+/*! @brief Constant random access iterator type. */
+using const_iterator = iterator;
+/*! @brief Reverse iterator type. */
+using reverse_iterator = std::reverse_iterator<iterator>;
+/*! @brief Constant reverse iterator type. */
+using const_reverse_iterator = reverse_iterator;
+
+/*! @brief Default constructor. */
+basic_sparse_set()
+: basic_sparse_set{type_id<void>()} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_sparse_set(const allocator_type &allocator)
+: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
+
+/**
+     * @brief Constructs an empty container with the given policy and allocator.
+     * @param pol Type of deletion policy.
+     * @param allocator The allocator to use (possibly default-constructed).
+     */
+explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
+: basic_sparse_set{type_id<void>(), pol, allocator} {}
+
+/**
+     * @brief Constructs an empty container with the given value type, policy
+     * and allocator.
+     * @param value Returned value type, if any.
+     * @param pol Type of deletion policy.
+     * @param allocator The allocator to use (possibly default-constructed).
+     */
+explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
+: sparse{allocator},
+packed{allocator},
+info{&value},
+free_list{tombstone},
+mode{pol} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
+: sparse{std::move(other.sparse)},
+packed{std::move(other.packed)},
+info{other.info},
+free_list{std::exchange(other.free_list, tombstone)},
+mode{other.mode} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: sparse{std::move(other.sparse), allocator},
+packed{std::move(other.packed), allocator},
+info{other.info},
+free_list{std::exchange(other.free_list, tombstone)},
+mode{other.mode} {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
+}
+
+/*! @brief Default destructor. */
+virtual ~basic_sparse_set() {
+release_sparse_pages();
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This sparse set.
+     */
+basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
+
+release_sparse_pages();
+sparse = std::move(other.sparse);
+packed = std::move(other.packed);
+info = other.info;
+free_list = std::exchange(other.free_list, tombstone);
+mode = other.mode;
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given sparse set.
+     * @param other Sparse set to exchange the content with.
+     */
+void swap(basic_sparse_set &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(info, other.info);
+swap(free_list, other.free_list);
+swap(mode, other.mode);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return packed.get_allocator();
+}
+
+/**
+     * @brief Returns the deletion policy of a sparse set.
+     * @return The deletion policy of the sparse set.
+     */
+[[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT {
+return mode;
+}
+
+/**
+     * @brief Increases the capacity of a sparse set.
+     *
+     * If the new capacity is greater than the current capacity, new storage is
+     * allocated, otherwise the method does nothing.
+     *
+     * @param cap Desired capacity.
+     */
+virtual void reserve(const size_type cap) {
+packed.reserve(cap);
+}
+
+/**
+     * @brief Returns the number of elements that a sparse set has currently
+     * allocated space for.
+     * @return Capacity of the sparse set.
+     */
+[[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT {
+return packed.capacity();
+}
+
+/*! @brief Requests the removal of unused capacity. */
+virtual void shrink_to_fit() {
+packed.shrink_to_fit();
+}
+
+/**
+     * @brief Returns the extent of a sparse set.
+     *
+     * The extent of a sparse set is also the size of the internal sparse array.
+     * There is no guarantee that the internal packed array has the same size.
+     * Usually the size of the internal sparse array is equal or greater than
+     * the one of the internal packed array.
+     *
+     * @return Extent of the sparse set.
+     */
+[[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
+return sparse.size() * entity_traits::page_size;
+}
+
+/**
+     * @brief Returns the number of elements in a sparse set.
+     *
+     * The number of elements is also the size of the internal packed array.
+     * There is no guarantee that the internal sparse array has the same size.
+     * Usually the size of the internal sparse array is equal or greater than
+     * the one of the internal packed array.
+     *
+     * @return Number of elements.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.size();
+}
+
+/**
+     * @brief Checks whether a sparse set is empty.
+     * @return True if the sparse set is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.empty();
+}
+
+/**
+     * @brief Direct access to the internal packed array.
+     * @return A pointer to the internal packed array.
+     */
+[[nodiscard]] pointer data() const ENTT_NOEXCEPT {
+return packed.data();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first entity of the internal packed
+     * array. If the sparse set is empty, the returned iterator will be equal to
+     * `end()`.
+     *
+     * @return An iterator to the first entity of the sparse set.
+     */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(packed.size());
+return iterator{packed, pos};
+}
+
+/*! @copydoc begin */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last entity in
+     * a sparse set. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the element following the last entity of a sparse
+     * set.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return iterator{packed, {}};
+}
+
+/*! @copydoc end */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+/**
+     * @brief Returns a reverse iterator to the beginning.
+     *
+     * The returned iterator points to the first entity of the reversed internal
+     * packed array. If the sparse set is empty, the returned iterator will be
+     * equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed internal packed
+     * array.
+     */
+[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(end());
+}
+
+/*! @copydoc rbegin */
+[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
+return rbegin();
+}
+
+/**
+     * @brief Returns a reverse iterator to the end.
+     *
+     * The returned iterator points to the element following the last entity in
+     * the reversed sparse set. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last entity of the
+     * reversed sparse set.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(begin());
+}
+
+/*! @copydoc rend */
+[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
+return rend();
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? --(end() - index(entt)) : end();
+}
+
+/**
+     * @brief Checks if a sparse set contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the sparse set contains the entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+const auto elem = sparse_ptr(entt);
+constexpr auto cap = entity_traits::to_entity(null);
+// testing versions permits to avoid accessing the packed array
+return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
+}
+
+/**
+     * @brief Returns the contained version for an identifier.
+     * @param entt A valid identifier.
+     * @return The version for the given identifier if present, the tombstone
+     * version otherwise.
+     */
+[[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT {
+const auto elem = sparse_ptr(entt);
+constexpr auto fallback = entity_traits::to_version(tombstone);
+return elem ? entity_traits::to_version(*elem) : fallback;
+}
+
+/**
+     * @brief Returns the position of an entity in a sparse set.
+     *
+     * @warning
+     * Attempting to get the position of an entity that doesn't belong to the
+     * sparse set results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return The position of the entity in the sparse set.
+     */
+[[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(contains(entt), "Set does not contain entity");
+return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
+}
+
+/**
+     * @brief Returns the entity at specified location, with bounds checking.
+     * @param pos The position for which to return the entity.
+     * @return The entity at specified location if any, a null entity otherwise.
+     */
+[[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT {
+return pos < packed.size() ? packed[pos] : null;
+}
+
+/**
+     * @brief Returns the entity at specified location, without bounds checking.
+     * @param pos The position for which to return the entity.
+     * @return The entity at specified location.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
+ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
+return packed[pos];
+}
+
+/**
+     * @brief Returns the element assigned to an entity, if any.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the sparse set results
+     * in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return An opaque pointer to the element assigned to the entity, if any.
+     */
+const void *get(const entity_type entt) const ENTT_NOEXCEPT {
+return get_at(index(entt));
+}
+
+/*! @copydoc get */
+void *get(const entity_type entt) ENTT_NOEXCEPT {
+return const_cast<void *>(std::as_const(*this).get(entt));
+}
+
+/**
+     * @brief Assigns an entity to a sparse set.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the sparse set
+     * results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @param value Optional opaque value to forward to mixins, if any.
+     * @return Iterator pointing to the emplaced element in case of success, the
+     * `end()` iterator otherwise.
+     */
+iterator emplace(const entity_type entt, const void *value = nullptr) {
+return try_emplace(entt, false, value);
+}
+
+/**
+     * @brief Bump the version number of an entity.
+     *
+     * @warning
+     * Attempting to bump the version of an entity that doesn't belong to the
+     * sparse set results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void bump(const entity_type entt) {
+auto &entity = sparse_ref(entt);
+entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt));
+packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt;
+}
+
+/**
+     * @brief Assigns one or more entities to a sparse set.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the sparse set
+     * results in undefined behavior.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return Iterator pointing to the first element inserted in case of
+     * success, the `end()` iterator otherwise.
+     */
+template<typename It>
+iterator insert(It first, It last) {
+for(auto it = first; it != last; ++it) {
+try_emplace(*it, true);
+}
+
+return first == last ? end() : find(*first);
+}
+
+/**
+     * @brief Erases an entity from a sparse set.
+     *
+     * @warning
+     * Attempting to erase an entity that doesn't belong to the sparse set
+     * results in undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void erase(const entity_type entt) {
+const auto it = --(end() - index(entt));
+(mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u);
+}
+
+/**
+     * @brief Erases entities from a set.
+     *
+     * @sa erase
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It>
+void erase(It first, It last) {
+if constexpr(std::is_same_v<It, basic_iterator>) {
+(mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last);
+} else {
+for(; first != last; ++first) {
+erase(*first);
+}
+}
+}
+
+/**
+     * @brief Removes an entity from a sparse set if it exists.
+     * @param entt A valid identifier.
+     * @return True if the entity is actually removed, false otherwise.
+     */
+bool remove(const entity_type entt) {
+return contains(entt) && (erase(entt), true);
+}
+
+/**
+     * @brief Removes entities from a sparse set if they exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @return The number of entities actually removed.
+     */
+template<typename It>
+size_type remove(It first, It last) {
+size_type count{};
+
+for(; first != last; ++first) {
+count += remove(*first);
+}
+
+return count;
+}
+
+/*! @brief Removes all tombstones from the packed array of a sparse set. */
+void compact() {
+size_type from = packed.size();
+for(; from && packed[from - 1u] == tombstone; --from) {}
+
+for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
+if(const size_type to = entity_traits::to_entity(*it); to < from) {
+--from;
+move_element(from, to);
+
+using std::swap;
+swap(packed[from], packed[to]);
+
+const auto entity = static_cast<typename entity_traits::entity_type>(to);
+sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to]));
+*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved);
+for(; from && packed[from - 1u] == tombstone; --from) {}
+}
+}
+
+free_list = tombstone;
+packed.resize(from);
+}
+
+/**
+     * @brief Swaps two entities in a sparse set.
+     *
+     * For what it's worth, this function affects both the internal sparse array
+     * and the internal packed array. Users should not care of that anyway.
+     *
+     * @warning
+     * Attempting to swap entities that don't belong to the sparse set results
+     * in undefined behavior.
+     *
+     * @param lhs A valid identifier.
+     * @param rhs A valid identifier.
+     */
+void swap_elements(const entity_type lhs, const entity_type rhs) {
+ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
+
+auto &entt = sparse_ref(lhs);
+auto &other = sparse_ref(rhs);
+
+const auto from = entity_traits::to_entity(entt);
+const auto to = entity_traits::to_entity(other);
+
+// basic no-leak guarantee (with invalid state) if swapping throws
+swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
+entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
+other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
+
+using std::swap;
+swap(packed[from], packed[to]);
+}
+
+/**
+     * @brief Sort the first count elements according to the given comparison
+     * function.
+     *
+     * The comparison function object must return `true` if the first element
+     * is _less_ than the second one, `false` otherwise. The signature of the
+     * comparison function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * bool(const Entity, const Entity);
+     * @endcode
+     *
+     * Moreover, the comparison function object shall induce a
+     * _strict weak ordering_ on the values.
+     *
+     * The sort function object must offer a member function template
+     * `operator()` that accepts three arguments:
+     *
+     * * An iterator to the first element of the range to sort.
+     * * An iterator past the last element of the range to sort.
+     * * A comparison function to use to compare the elements.
+     *
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param length Number of elements to sort.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Compare, typename Sort = std_sort, typename... Args>
+void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
+ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
+ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
+
+algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
+
+for(size_type pos{}; pos < length; ++pos) {
+auto curr = pos;
+auto next = index(packed[curr]);
+
+while(curr != next) {
+const auto idx = index(packed[next]);
+const auto entt = packed[curr];
+
+swap_at(next, idx);
+const auto entity = static_cast<typename entity_traits::entity_type>(curr);
+sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr]));
+curr = std::exchange(next, idx);
+}
+}
+}
+
+/**
+     * @brief Sort all elements according to the given comparison function.
+     *
+     * @sa sort_n
+     *
+     * @tparam Compare Type of comparison function object.
+     * @tparam Sort Type of sort function object.
+     * @tparam Args Types of arguments to forward to the sort function object.
+     * @param compare A valid comparison function object.
+     * @param algo A valid sort function object.
+     * @param args Arguments to forward to the sort function object, if any.
+     */
+template<typename Compare, typename Sort = std_sort, typename... Args>
+void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
+compact();
+sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Sort entities according to their order in another sparse set.
+     *
+     * Entities that are part of both the sparse sets are ordered internally
+     * according to the order they have in `other`. All the other entities goes
+     * to the end of the list and there are no guarantees on their order.<br/>
+     * In other terms, this function can be used to impose the same order on two
+     * sets by using one of them as a master and the other one as a slave.
+     *
+     * Iterating the sparse set with a couple of iterators returns elements in
+     * the expected order after a call to `respect`. See `begin` and `end` for
+     * more details.
+     *
+     * @param other The sparse sets that imposes the order of the entities.
+     */
+void respect(const basic_sparse_set &other) {
+compact();
+
+const auto to = other.end();
+auto from = other.begin();
+
+for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
+if(contains(*from)) {
+if(*from != packed[pos]) {
+// basic no-leak guarantee (with invalid state) if swapping throws
+swap_elements(packed[pos], *from);
+}
+
+--pos;
+}
+}
+}
+
+/*! @brief Clears a sparse set. */
+void clear() {
+if(const auto last = end(); free_list == null) {
+in_place_pop(begin(), last);
+} else {
+for(auto &&entity: *this) {
+// tombstone filter on itself
+if(const auto it = find(entity); it != last) {
+in_place_pop(it, it + 1u);
+}
+}
+}
+
+free_list = tombstone;
+packed.clear();
+}
+
+/**
+     * @brief Returned value type, if any.
+     * @return Returned value type, if any.
+     */
+const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/*! @brief Forwards variables to mixins, if any. */
+virtual void bind(any) ENTT_NOEXCEPT {}
+
+private:
+sparse_container_type sparse;
+packed_container_type packed;
+const type_info *info;
+entity_type free_list;
+deletion_policy mode;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/storage.hpp"
+#ifndef ENTT_ENTITY_STORAGE_HPP
+#define ENTT_ENTITY_STORAGE_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/compressed_pair.hpp"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/memory.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sigh_storage_mixin.hpp"
+
+// #include "sparse_set.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Container>
+class storage_iterator final {
+friend storage_iterator<const Container>;
+
+using container_type = std::remove_const_t<Container>;
+using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
+using comp_traits = component_traits<typename container_type::value_type>;
+
+using iterator_traits = std::iterator_traits<std::conditional_t<
+std::is_const_v<Container>,
+typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer,
+typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>;
+
+public:
+using value_type = typename iterator_traits::value_type;
+using pointer = typename iterator_traits::pointer;
+using reference = typename iterator_traits::reference;
+using difference_type = typename iterator_traits::difference_type;
+using iterator_category = std::random_access_iterator_tag;
+
+storage_iterator() ENTT_NOEXCEPT = default;
+
+storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT
+: packed{ref},
+offset{idx} {}
+
+template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
+storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) ENTT_NOEXCEPT
+: packed{other.packed},
+offset{other.offset} {}
+
+storage_iterator &operator++() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+storage_iterator operator++(int) ENTT_NOEXCEPT {
+storage_iterator orig = *this;
+return ++(*this), orig;
+}
+
+storage_iterator &operator--() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+storage_iterator operator--(int) ENTT_NOEXCEPT {
+storage_iterator orig = *this;
+return operator--(), orig;
+}
+
+storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+offset -= value;
+return *this;
+}
+
+storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+storage_iterator copy = *this;
+return (copy += value);
+}
+
+storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+const auto pos = index() - value;
+return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+const auto pos = index();
+return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
+return offset - 1;
+}
+
+private:
+Container *packed;
+difference_type offset;
+};
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return rhs.index() - lhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() > rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename CLhs, typename CRhs>
+[[nodiscard]] bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It, typename... Other>
+class extended_storage_iterator final {
+template<typename Iter, typename... Args>
+friend class extended_storage_iterator;
+
+public:
+using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+extended_storage_iterator() = default;
+
+extended_storage_iterator(It base, Other... other)
+: it{base, other...} {}
+
+template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
+extended_storage_iterator(const extended_storage_iterator<It, Args...> &other)
+: it{other.it} {}
+
+extended_storage_iterator &operator++() ENTT_NOEXCEPT {
+return ++std::get<It>(it), (++std::get<Other>(it), ...), *this;
+}
+
+extended_storage_iterator operator++(int) ENTT_NOEXCEPT {
+extended_storage_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {*std::get<It>(it), *std::get<Other>(it)...};
+}
+
+template<typename... CLhs, typename... CRhs>
+friend bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) ENTT_NOEXCEPT;
+
+private:
+std::tuple<It, Other...> it;
+};
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+return std::get<0>(lhs.it) == std::get<0>(rhs.it);
+}
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic storage implementation.
+ *
+ * Internal data structures arrange elements to maximize performance. There are
+ * no guarantees that objects are returned in the insertion order when iterate
+ * a storage. Do not make assumption on the order in any case.
+ *
+ * @warning
+ * Empty types aren't explicitly instantiated. Therefore, many of the functions
+ * normally available for non-empty types will not be available for empty ones.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Type of objects assigned to the entities.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Entity, typename Type, typename Allocator, typename>
+class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
+static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The type must be at least move constructible/assignable");
+
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
+using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
+using comp_traits = component_traits<Type>;
+
+[[nodiscard]] auto &element_at(const std::size_t pos) const {
+return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
+}
+
+auto assure_at_least(const std::size_t pos) {
+auto &&container = packed.first();
+const auto idx = pos / comp_traits::page_size;
+
+if(!(idx < container.size())) {
+auto curr = container.size();
+container.resize(idx + 1u, nullptr);
+
+ENTT_TRY {
+for(const auto last = container.size(); curr < last; ++curr) {
+container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size);
+}
+}
+ENTT_CATCH {
+container.resize(curr);
+ENTT_THROW;
+}
+}
+
+return container[idx] + fast_mod(pos, comp_traits::page_size);
+}
+
+template<typename... Args>
+auto emplace_element(const Entity entt, const bool force_back, Args &&...args) {
+const auto it = base_type::try_emplace(entt, force_back);
+
+ENTT_TRY {
+auto elem = assure_at_least(static_cast<size_type>(it.index()));
+entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+if constexpr(comp_traits::in_place_delete) {
+base_type::in_place_pop(it, it + 1u);
+} else {
+base_type::swap_and_pop(it, it + 1u);
+}
+
+ENTT_THROW;
+}
+
+return it;
+}
+
+void shrink_to_size(const std::size_t sz) {
+for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
+if constexpr(comp_traits::in_place_delete) {
+if(base_type::at(pos) != tombstone) {
+std::destroy_at(std::addressof(element_at(pos)));
+}
+} else {
+std::destroy_at(std::addressof(element_at(pos)));
+}
+}
+
+auto &&container = packed.first();
+auto page_allocator{packed.second()};
+const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size;
+
+for(auto pos = from, last = container.size(); pos < last; ++pos) {
+alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size);
+}
+
+container.resize(from);
+}
+
+private:
+const void *get_at(const std::size_t pos) const final {
+return std::addressof(element_at(pos));
+}
+
+void swap_at(const std::size_t lhs, const std::size_t rhs) final {
+using std::swap;
+swap(element_at(lhs), element_at(rhs));
+}
+
+void move_element(const std::size_t from, const std::size_t to) final {
+auto &elem = element_at(from);
+entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem));
+std::destroy_at(std::addressof(elem));
+}
+
+protected:
+/**
+     * @brief Erases elements from a storage.
+     * @param first An iterator to the first element to erase.
+     * @param last An iterator past the last element to erase.
+     */
+void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
+for(; first != last; ++first) {
+auto &elem = element_at(base_type::size() - 1u);
+// destroying on exit allows reentrant destructors
+[[maybe_unused]] auto unused = std::exchange(element_at(static_cast<size_type>(first.index())), std::move(elem));
+std::destroy_at(std::addressof(elem));
+base_type::swap_and_pop(first, first + 1u);
+}
+}
+
+/**
+     * @brief Erases elements from a storage.
+     * @param first An iterator to the first element to erase.
+     * @param last An iterator past the last element to erase.
+     */
+void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
+for(; first != last; ++first) {
+base_type::in_place_pop(first, first + 1u);
+std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index()))));
+}
+}
+
+/**
+     * @brief Assigns an entity to a storage.
+     * @param entt A valid identifier.
+     * @param value Optional opaque value.
+     * @param force_back Force back insertion.
+     * @return Iterator pointing to the emplaced element.
+     */
+typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override {
+if(value) {
+if constexpr(std::is_copy_constructible_v<value_type>) {
+return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
+} else {
+return base_type::end();
+}
+} else {
+if constexpr(std::is_default_constructible_v<value_type>) {
+return emplace_element(entt, force_back);
+} else {
+return base_type::end();
+}
+}
+}
+
+public:
+/*! @brief Base type. */
+using base_type = underlying_type;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Type of the objects assigned to entities. */
+using value_type = Type;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Pointer type to contained elements. */
+using pointer = typename container_type::pointer;
+/*! @brief Constant pointer type to contained elements. */
+using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
+/*! @brief Random access iterator type. */
+using iterator = internal::storage_iterator<container_type>;
+/*! @brief Constant random access iterator type. */
+using const_iterator = internal::storage_iterator<const container_type>;
+/*! @brief Reverse iterator type. */
+using reverse_iterator = std::reverse_iterator<iterator>;
+/*! @brief Constant reverse iterator type. */
+using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+/*! @brief Extended iterable storage proxy. */
+using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
+/*! @brief Constant extended iterable storage proxy. */
+using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
+
+/*! @brief Default constructor. */
+basic_storage()
+: basic_storage{allocator_type{}} {}
+
+/**
+     * @brief Constructs an empty storage with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_storage(const allocator_type &allocator)
+: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator},
+packed{container_type{allocator}, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_storage(basic_storage &&other) ENTT_NOEXCEPT
+: base_type{std::move(other)},
+packed{std::move(other.packed)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: base_type{std::move(other), allocator},
+packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
+}
+
+/*! @brief Default destructor. */
+~basic_storage() override {
+shrink_to_size(0u);
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT {
+ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
+
+shrink_to_size(0u);
+base_type::operator=(std::move(other));
+packed.first() = std::move(other.packed.first());
+propagate_on_container_move_assignment(packed.second(), other.packed.second());
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given storage.
+     * @param other Storage to exchange the content with.
+     */
+void swap(basic_storage &other) {
+using std::swap;
+underlying_type::swap(other);
+propagate_on_container_swap(packed.second(), other.packed.second());
+swap(packed.first(), other.packed.first());
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return allocator_type{packed.second()};
+}
+
+/**
+     * @brief Increases the capacity of a storage.
+     *
+     * If the new capacity is greater than the current capacity, new storage is
+     * allocated, otherwise the method does nothing.
+     *
+     * @param cap Desired capacity.
+     */
+void reserve(const size_type cap) override {
+if(cap != 0u) {
+base_type::reserve(cap);
+assure_at_least(cap - 1u);
+}
+}
+
+/**
+     * @brief Returns the number of elements that a storage has currently
+     * allocated space for.
+     * @return Capacity of the storage.
+     */
+[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override {
+return packed.first().size() * comp_traits::page_size;
+}
+
+/*! @brief Requests the removal of unused capacity. */
+void shrink_to_fit() override {
+base_type::shrink_to_fit();
+shrink_to_size(base_type::size());
+}
+
+/**
+     * @brief Direct access to the array of objects.
+     * @return A pointer to the array of objects.
+     */
+[[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT {
+return packed.first().data();
+}
+
+/*! @copydoc raw */
+[[nodiscard]] pointer raw() ENTT_NOEXCEPT {
+return packed.first().data();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the storage is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
+return const_iterator{&packed.first(), pos};
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
+return iterator{&packed.first(), pos};
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return const_iterator{&packed.first(), {}};
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return iterator{&packed.first(), {}};
+}
+
+/**
+     * @brief Returns a reverse iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the reversed
+     * internal array. If the storage is empty, the returned iterator will be
+     * equal to `rend()`.
+     *
+     * @return An iterator to the first instance of the reversed internal array.
+     */
+[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(cend());
+}
+
+/*! @copydoc crbegin */
+[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return crbegin();
+}
+
+/*! @copydoc rbegin */
+[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT {
+return std::make_reverse_iterator(end());
+}
+
+/**
+     * @brief Returns a reverse iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the reversed internal array. Attempting to dereference the returned
+     * iterator results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * reversed internal array.
+     */
+[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
+return std::make_reverse_iterator(cbegin());
+}
+
+/*! @copydoc crend */
+[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT {
+return crend();
+}
+
+/*! @copydoc rend */
+[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT {
+return std::make_reverse_iterator(begin());
+}
+
+/**
+     * @brief Returns the object assigned to an entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return The object assigned to the entity.
+     */
+[[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT {
+return element_at(base_type::index(entt));
+}
+
+/*! @copydoc get */
+[[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT {
+return const_cast<value_type &>(std::as_const(*this).get(entt));
+}
+
+/**
+     * @brief Returns the object assigned to an entity as a tuple.
+     * @param entt A valid identifier.
+     * @return The object assigned to the entity as a tuple.
+     */
+[[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const ENTT_NOEXCEPT {
+return std::forward_as_tuple(get(entt));
+}
+
+/*! @copydoc get_as_tuple */
+[[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) ENTT_NOEXCEPT {
+return std::forward_as_tuple(get(entt));
+}
+
+/**
+     * @brief Assigns an entity to a storage and constructs its object.
+     *
+     * @warning
+     * Attempting to use an entity that already belongs to the storage results
+     * in undefined behavior.
+     *
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     * @param args Parameters to use to construct an object for the entity.
+     * @return A reference to the newly created object.
+     */
+template<typename... Args>
+value_type &emplace(const entity_type entt, Args &&...args) {
+if constexpr(std::is_aggregate_v<value_type>) {
+const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
+return element_at(static_cast<size_type>(it.index()));
+} else {
+const auto it = emplace_element(entt, false, std::forward<Args>(args)...);
+return element_at(static_cast<size_type>(it.index()));
+}
+}
+
+/**
+     * @brief Updates the instance assigned to a given entity in-place.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     * @return A reference to the updated instance.
+     */
+template<typename... Func>
+value_type &patch(const entity_type entt, Func &&...func) {
+const auto idx = base_type::index(entt);
+auto &elem = element_at(idx);
+(std::forward<Func>(func)(elem), ...);
+return elem;
+}
+
+/**
+     * @brief Assigns one or more entities to a storage and constructs their
+     * objects from a given instance.
+     *
+     * @warning
+     * Attempting to assign an entity that already belongs to the storage
+     * results in undefined behavior.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param value An instance of the object to construct.
+     */
+template<typename It>
+void insert(It first, It last, const value_type &value = {}) {
+for(; first != last; ++first) {
+emplace_element(*first, true, value);
+}
+}
+
+/**
+     * @brief Assigns one or more entities to a storage and constructs their
+     * objects from a given range.
+     *
+     * @sa construct
+     *
+     * @tparam EIt Type of input iterator.
+     * @tparam CIt Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     * @param from An iterator to the first element of the range of objects.
+     */
+template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
+void insert(EIt first, EIt last, CIt from) {
+for(; first != last; ++first, ++from) {
+emplace_element(*first, true, *from);
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a storage.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a reference to its component.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+[[nodiscard]] iterable each() ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
+}
+
+/*! @copydoc each */
+[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
+}
+
+private:
+compressed_pair<container_type, allocator_type> packed;
+};
+
+/*! @copydoc basic_storage */
+template<typename Entity, typename Type, typename Allocator>
+class basic_storage<Entity, Type, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
+: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
+using comp_traits = component_traits<Type>;
+
+public:
+/*! @brief Base type. */
+using base_type = underlying_type;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Type of the objects assigned to entities. */
+using value_type = Type;
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Extended iterable storage proxy. */
+using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
+/*! @brief Constant extended iterable storage proxy. */
+using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
+
+/*! @brief Default constructor. */
+basic_storage()
+: basic_storage{allocator_type{}} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_storage(const allocator_type &allocator)
+: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: base_type{std::move(other), allocator} {}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This storage.
+     */
+basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return allocator_type{base_type::get_allocator()};
+}
+
+/**
+     * @brief Returns the object assigned to an entity, that is `void`.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     */
+void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+}
+
+/**
+     * @brief Returns an empty tuple.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the storage results in
+     * undefined behavior.
+     *
+     * @param entt A valid identifier.
+     * @return Returns an empty tuple.
+     */
+[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+return std::tuple{};
+}
+
+/**
+     * @brief Assigns an entity to a storage and constructs its object.
+     *
+     * @warning
+     * Attempting to use an entity that already belongs to the storage results
+     * in undefined behavior.
+     *
+     * @tparam Args Types of arguments to use to construct the object.
+     * @param entt A valid identifier.
+     */
+template<typename... Args>
+void emplace(const entity_type entt, Args &&...) {
+base_type::try_emplace(entt, false);
+}
+
+/**
+     * @brief Updates the instance assigned to a given entity in-place.
+     * @tparam Func Types of the function objects to invoke.
+     * @param entt A valid identifier.
+     * @param func Valid function objects.
+     */
+template<typename... Func>
+void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
+ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
+(std::forward<Func>(func)(), ...);
+}
+
+/**
+     * @brief Assigns entities to a storage.
+     * @tparam It Type of input iterator.
+     * @tparam Args Types of optional arguments.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+template<typename It, typename... Args>
+void insert(It first, It last, Args &&...) {
+for(; first != last; ++first) {
+base_type::try_emplace(*first, true);
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a storage.
+     *
+     * The iterable object returns a tuple that contains the current entity.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+[[nodiscard]] iterable each() ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
+}
+
+/*! @copydoc each */
+[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
+}
+};
+
+/**
+ * @brief Provides a common way to access certain properties of storage types.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Type of objects managed by the storage class.
+ */
+template<typename Entity, typename Type, typename = void>
+struct storage_traits {
+/*! @brief Resulting type after component-to-storage conversion. */
+using storage_type = sigh_storage_mixin<basic_storage<Entity, Type>>;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/utility.hpp"
+#ifndef ENTT_ENTITY_UTILITY_HPP
+#define ENTT_ENTITY_UTILITY_HPP
+
+// #include "../core/type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Alias for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct exclude_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for exclusion lists.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr exclude_t<Type...> exclude{};
+
+/**
+ * @brief Alias for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct get_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for lists of observed components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr get_t<Type...> get{};
+
+/**
+ * @brief Alias for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+struct owned_t: type_list<Type...> {};
+
+/**
+ * @brief Variable template for lists of owned components.
+ * @tparam Type List of types.
+ */
+template<typename... Type>
+inline constexpr owned_t<Type...> owned{};
+
+} // namespace entt
+
+#endif
+
+// #include "entity/view.hpp"
+#ifndef ENTT_ENTITY_VIEW_HPP
+#define ENTT_ENTITY_VIEW_HPP
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "component.hpp"
+
+// #include "entity.hpp"
+
+// #include "fwd.hpp"
+
+// #include "sparse_set.hpp"
+
+// #include "storage.hpp"
+
+// #include "utility.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t Component, std::size_t Exclude>
+class view_iterator final {
+using iterator_type = typename Type::const_iterator;
+
+[[nodiscard]] bool valid() const ENTT_NOEXCEPT {
+return ((Component != 0u) || (*it != tombstone))
+&& std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
+&& std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
+}
+
+public:
+using value_type = typename iterator_type::value_type;
+using pointer = typename iterator_type::pointer;
+using reference = typename iterator_type::reference;
+using difference_type = typename iterator_type::difference_type;
+using iterator_category = std::forward_iterator_tag;
+
+view_iterator() ENTT_NOEXCEPT = default;
+
+view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Component> all_of, std::array<const Type *, Exclude> none_of) ENTT_NOEXCEPT
+: it{curr},
+last{to},
+pools{all_of},
+filter{none_of} {
+if(it != last && !valid()) {
+++(*this);
+}
+}
+
+view_iterator &operator++() ENTT_NOEXCEPT {
+while(++it != last && !valid()) {}
+return *this;
+}
+
+view_iterator operator++(int) ENTT_NOEXCEPT {
+view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return &*it;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+friend bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) ENTT_NOEXCEPT;
+
+private:
+iterator_type it;
+iterator_type last;
+std::array<const Type *, Component> pools;
+std::array<const Type *, Exclude> filter;
+};
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename It, typename... Storage>
+struct extended_view_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...));
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+extended_view_iterator() = default;
+
+extended_view_iterator(It from, std::tuple<Storage *...> storage)
+: it{from},
+pools{storage} {}
+
+extended_view_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+extended_view_iterator operator++(int) ENTT_NOEXCEPT {
+extended_view_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools);
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+template<typename... Lhs, typename... Rhs>
+friend bool operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+std::tuple<Storage *...> pools;
+};
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief View implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error, but for a few reasonable cases.
+ */
+template<typename, typename, typename, typename>
+class basic_view;
+
+/**
+ * @brief Multi component view.
+ *
+ * Multi component views iterate over those entities that have at least all the
+ * given components in their bags. During initialization, a multi component view
+ * looks at the number of entities available for each component and uses the
+ * smallest set in order to get a performance boost when iterate.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given components are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, if one of the
+ *   given components is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pools iterated by the view in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Types of components iterated by the view.
+ * @tparam Exclude Types of components used to filter the view.
+ */
+template<typename Entity, typename... Component, typename... Exclude>
+class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> {
+template<typename, typename, typename, typename>
+friend class basic_view;
+
+template<typename Comp>
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
+
+template<std::size_t... Index>
+[[nodiscard]] auto pools_to_array(std::index_sequence<Index...>) const ENTT_NOEXCEPT {
+std::size_t pos{};
+std::array<const base_type *, sizeof...(Component) - 1u> other{};
+(static_cast<void>(std::get<Index>(pools) == view ? void() : void(other[pos++] = std::get<Index>(pools))), ...);
+return other;
+}
+
+template<std::size_t Comp, std::size_t Other, typename... Args>
+[[nodiscard]] auto dispatch_get(const std::tuple<Entity, Args...> &curr) const {
+if constexpr(Comp == Other) {
+return std::forward_as_tuple(std::get<Args>(curr)...);
+} else {
+return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr));
+}
+}
+
+template<std::size_t Comp, typename Func, std::size_t... Index>
+void each(Func func, std::index_sequence<Index...>) const {
+for(const auto curr: std::get<Comp>(pools)->each()) {
+const auto entt = std::get<0>(curr);
+
+if(((sizeof...(Component) != 1u) || (entt != tombstone))
+&& ((Comp == Index || std::get<Index>(pools)->contains(entt)) && ...)
+&& std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
+if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
+std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Comp, Index>(curr)...));
+} else {
+std::apply(func, std::tuple_cat(dispatch_get<Comp, Index>(curr)...));
+}
+}
+}
+}
+
+template<typename Func, std::size_t... Index>
+void pick_and_each(Func func, std::index_sequence<Index...> seq) const {
+((std::get<Index>(pools) == view ? each<Index>(std::move(func), seq) : void()), ...);
+}
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = std::common_type_t<typename storage_type<Component>::base_type...>;
+/*! @brief Bidirectional iterator type. */
+using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>;
+/*! @brief Iterable view type. */
+using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>;
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_view() ENTT_NOEXCEPT
+: pools{},
+filter{},
+view{} {}
+
+/**
+     * @brief Constructs a multi-type view from a set of storage classes.
+     * @param component The storage for the types to iterate.
+     * @param epool The storage for the types used to filter the view.
+     */
+basic_view(storage_type<Component> &...component, const storage_type<Exclude> &...epool) ENTT_NOEXCEPT
+: pools{&component...},
+filter{&epool...},
+view{(std::min)({&static_cast<const base_type &>(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {}
+
+/**
+     * @brief Creates a new view driven by a given component in its iterations.
+     * @tparam Comp Type of component used to drive the iteration.
+     * @return A new view driven by the given component in its iterations.
+     */
+template<typename Comp>
+[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
+basic_view other{*this};
+other.view = std::get<storage_type<Comp> *>(pools);
+return other;
+}
+
+/**
+     * @brief Creates a new view driven by a given component in its iterations.
+     * @tparam Comp Index of the component used to drive the iteration.
+     * @return A new view driven by the given component in its iterations.
+     */
+template<std::size_t Comp>
+[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
+basic_view other{*this};
+other.view = std::get<Comp>(pools);
+return other;
+}
+
+/**
+     * @brief Returns the leading storage of a view.
+     * @return The leading storage of the view.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *view;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<storage_type<Comp> *>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
+     */
+[[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT {
+return view->size();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the view.
+     *
+     * The returned iterator points to the first entity of the view. If the view
+     * is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the view.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the view. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the view.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
+}
+
+/**
+     * @brief Returns the first entity of the view, if any.
+     * @return The first entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+const auto it = begin();
+return it != end() ? *it : null;
+}
+
+/**
+     * @brief Returns the last entity of the view, if any.
+     * @return The last entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+auto it = view->rbegin();
+for(const auto last = view->rend(); it != last && !contains(*it); ++it) {}
+return it == view->rend() ? null : *it;
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter} : end();
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     * @param entt A valid identifier.
+     * @return The components assigned to the given entity.
+     */
+[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
+return get<Component...>(entt);
+}
+
+/**
+     * @brief Checks if a view is properly initialized.
+     * @return True if the view is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return view != nullptr;
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
+&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam Comp Types of components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
+} else if constexpr(sizeof...(Comp) == 1) {
+return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
+} else {
+return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam First Index of a component to get.
+     * @tparam Other Indexes of other components to get.
+     * @param entt A valid identifier.
+     * @return The components assigned to the entity.
+     */
+template<std::size_t First, std::size_t... Other>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Other) == 0) {
+return std::get<First>(pools)->get(entt);
+} else {
+return std::tuple_cat(std::get<First>(pools)->get_as_tuple(entt), std::get<Other>(pools)->get_as_tuple(entt)...);
+}
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+pick_and_each(std::move(func), std::index_sequence_for<Component...>{});
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a view.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @return An iterable object to use to _visit_ the view.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}};
+}
+
+/**
+     * @brief Combines two views in a _more specific_ one (friend function).
+     * @tparam Get Component list of the view to combine with.
+     * @tparam Excl Filter list of the view to combine with.
+     * @param other The view to combine with.
+     * @return A more specific view.
+     */
+template<typename... Get, typename... Excl>
+[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
+using view_type = basic_view<Entity, get_t<Component..., Get...>, exclude_t<Exclude..., Excl...>>;
+return std::make_from_tuple<view_type>(std::tuple_cat(
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools),
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Exclude> &>(*curr)...); }, filter),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Excl> &>(*curr)...); }, other.filter)));
+}
+
+private:
+std::tuple<storage_type<Component> *...> pools;
+std::array<const base_type *, sizeof...(Exclude)> filter;
+const base_type *view;
+};
+
+/**
+ * @brief Single component view specialization.
+ *
+ * Single component views are specialized in order to get a boost in terms of
+ * performance. This kind of views can access the underlying data structure
+ * directly and avoid superfluous checks.
+ *
+ * @b Important
+ *
+ * Iterators aren't invalidated if:
+ *
+ * * New instances of the given component are created and assigned to entities.
+ * * The entity currently pointed is modified (as an example, the given
+ *   component is removed from the entity to which the iterator points).
+ * * The entity currently pointed is destroyed.
+ *
+ * In all other cases, modifying the pool iterated by the view in any way
+ * invalidates all the iterators and using them results in undefined behavior.
+ *
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Component Type of component iterated by the view.
+ */
+template<typename Entity, typename Component>
+class basic_view<Entity, get_t<Component>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<std::remove_const_t<Component>>::in_place_delete>>> {
+template<typename, typename, typename, typename>
+friend class basic_view;
+
+using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
+
+public:
+/*! @brief Underlying entity identifier. */
+using entity_type = Entity;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Common type among all storage types. */
+using base_type = typename storage_type::base_type;
+/*! @brief Random access iterator type. */
+using iterator = typename base_type::iterator;
+/*! @brief Reversed iterator type. */
+using reverse_iterator = typename base_type::reverse_iterator;
+/*! @brief Iterable view type. */
+using iterable = decltype(std::declval<storage_type>().each());
+
+/*! @brief Default constructor to use to create empty, invalid views. */
+basic_view() ENTT_NOEXCEPT
+: pools{},
+filter{},
+view{} {}
+
+/**
+     * @brief Constructs a single-type view from a storage class.
+     * @param ref The storage for the type to iterate.
+     */
+basic_view(storage_type &ref) ENTT_NOEXCEPT
+: pools{&ref},
+filter{},
+view{&ref} {}
+
+/**
+     * @brief Returns the leading storage of a view.
+     * @return The leading storage of the view.
+     */
+const base_type &handle() const ENTT_NOEXCEPT {
+return *view;
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Type of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<typename Comp = Component>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+static_assert(std::is_same_v<Comp, Component>, "Invalid component type");
+return *std::get<0>(pools);
+}
+
+/**
+     * @brief Returns the storage for a given component type.
+     * @tparam Comp Index of component of which to return the storage.
+     * @return The storage for the given component type.
+     */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
+return *std::get<Comp>(pools);
+}
+
+/**
+     * @brief Returns the number of entities that have the given component.
+     * @return Number of entities that have the given component.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return view->size();
+}
+
+/**
+     * @brief Checks whether a view is empty.
+     * @return True if the view is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return view->empty();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the view.
+     *
+     * The returned iterator points to the first entity of the view. If the view
+     * is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the view.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return view->begin();
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the view. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the view.
+     */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return view->end();
+}
+
+/**
+     * @brief Returns an iterator to the first entity of the reversed view.
+     *
+     * The returned iterator points to the first entity of the reversed view. If
+     * the view is empty, the returned iterator will be equal to `rend()`.
+     *
+     * @return An iterator to the first entity of the reversed view.
+     */
+[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+return view->rbegin();
+}
+
+/**
+     * @brief Returns an iterator that is past the last entity of the reversed
+     * view.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the reversed view. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the entity following the last entity of the
+     * reversed view.
+     */
+[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+return view->rend();
+}
+
+/**
+     * @brief Returns the first entity of the view, if any.
+     * @return The first entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type front() const ENTT_NOEXCEPT {
+return empty() ? null : *begin();
+}
+
+/**
+     * @brief Returns the last entity of the view, if any.
+     * @return The last entity of the view if one exists, the null entity
+     * otherwise.
+     */
+[[nodiscard]] entity_type back() const ENTT_NOEXCEPT {
+return empty() ? null : *rbegin();
+}
+
+/**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
+return contains(entt) ? view->find(entt) : end();
+}
+
+/**
+     * @brief Returns the identifier that occupies the given position.
+     * @param pos Position of the element to return.
+     * @return The identifier that occupies the given position.
+     */
+[[nodiscard]] entity_type operator[](const size_type pos) const {
+return begin()[pos];
+}
+
+/**
+     * @brief Returns the component assigned to the given entity.
+     * @param entt A valid identifier.
+     * @return The component assigned to the given entity.
+     */
+[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
+return get<Component>(entt);
+}
+
+/**
+     * @brief Checks if a view is properly initialized.
+     * @return True if the view is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return view != nullptr;
+}
+
+/**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
+return view->contains(entt);
+}
+
+/**
+     * @brief Returns the component assigned to the given entity.
+     *
+     * @warning
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.
+     *
+     * @tparam Comp Type or index of the component to get.
+     * @param entt A valid identifier.
+     * @return The component assigned to the entity.
+     */
+template<typename... Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+
+if constexpr(sizeof...(Comp) == 0) {
+return std::get<0>(pools)->get_as_tuple(entt);
+} else {
+static_assert(std::is_same_v<Comp..., Component>, "Invalid component type");
+return std::get<0>(pools)->get(entt);
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Comp>
+[[nodiscard]] decltype(auto) get(const entity_type entt) const {
+ENTT_ASSERT(contains(entt), "View does not contain entity");
+return std::get<0>(pools)->get(entt);
+}
+
+/**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a reference to the component if it's a non-empty one.
+     * The _constness_ of the component is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Component &);
+     * void(Component &);
+     * @endcode
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+template<typename Func>
+void each(Func func) const {
+if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
+for(const auto pack: each()) {
+std::apply(func, pack);
+}
+} else if constexpr(std::is_invocable_v<Func, Component &>) {
+for(auto &&component: *std::get<0>(pools)) {
+func(component);
+}
+} else if constexpr(std::is_invocable_v<Func, Entity>) {
+for(auto entity: *view) {
+func(entity);
+}
+} else {
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+func();
+}
+}
+}
+
+/**
+     * @brief Returns an iterable object to use to _visit_ a view.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a reference to its component if it's a non-empty one. The _constness_ of
+     * the component is as requested.
+     *
+     * @return An iterable object to use to _visit_ the view.
+     */
+[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+return std::get<0>(pools)->each();
+}
+
+/**
+     * @brief Combines two views in a _more specific_ one (friend function).
+     * @tparam Get Component list of the view to combine with.
+     * @tparam Excl Filter list of the view to combine with.
+     * @param other The view to combine with.
+     * @return A more specific view.
+     */
+template<typename... Get, typename... Excl>
+[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
+using view_type = basic_view<Entity, get_t<Component, Get...>, exclude_t<Excl...>>;
+return std::make_from_tuple<view_type>(std::tuple_cat(
+std::forward_as_tuple(*std::get<0>(pools)),
+std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
+std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const typename view_type::template storage_type<Excl> &>(*curr)...); }, other.filter)));
+}
+
+private:
+std::tuple<storage_type *> pools;
+std::array<const base_type *, 0u> filter;
+const base_type *view;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Storage Type of storage classes used to create the view.
+ * @param storage The storage for the types to iterate.
+ */
+template<typename... Storage>
+basic_view(Storage &...storage) -> basic_view<std::common_type_t<typename Storage::entity_type...>, get_t<constness_as_t<typename Storage::value_type, Storage>...>, exclude_t<>>;
+
+} // namespace entt
+
+#endif
+
+// #include "locator/locator.hpp"
+#ifndef ENTT_LOCATOR_LOCATOR_HPP
+#define ENTT_LOCATOR_LOCATOR_HPP
+
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Service locator, nothing more.
+ *
+ * A service locator is used to do what it promises: locate services.<br/>
+ * Usually service locators are tightly bound to the services they expose and
+ * thus it's hard to define a general purpose class to do that. This tiny class
+ * tries to fill the gap and to get rid of the burden of defining a different
+ * specific locator for each application.
+ *
+ * @note
+ * Users shouldn't retain references to a service. The recommended way is to
+ * retrieve the service implementation currently set each and every time the
+ * need for it arises. The risk is to incur in unexpected behaviors otherwise.
+ *
+ * @tparam Service Service type.
+ */
+template<typename Service>
+struct locator final {
+/*! @brief Service type. */
+using type = Service;
+
+/*! @brief Default constructor, deleted on purpose. */
+locator() = delete;
+/*! @brief Default destructor, deleted on purpose. */
+~locator() = delete;
+
+/**
+     * @brief Checks whether a service locator contains a value.
+     * @return True if the service locator contains a value, false otherwise.
+     */
+[[nodiscard]] static bool has_value() ENTT_NOEXCEPT {
+return (service != nullptr);
+}
+
+/**
+     * @brief Returns a reference to a valid service, if any.
+     *
+     * @warning
+     * Invoking this function can result in undefined behavior if the service
+     * hasn't been set yet.
+     *
+     * @return A reference to the service currently set, if any.
+     */
+[[nodiscard]] static Service &value() ENTT_NOEXCEPT {
+ENTT_ASSERT(has_value(), "Service not available");
+return *service;
+}
+
+/**
+     * @brief Returns a service if available or sets it from a fallback type.
+     *
+     * Arguments are used only if a service doesn't already exist. In all other
+     * cases, they are discarded.
+     *
+     * @tparam Args Types of arguments to use to construct the fallback service.
+     * @tparam Impl Fallback service type.
+     * @param args Parameters to use to construct the fallback service.
+     * @return A reference to a valid service.
+     */
+template<typename Impl = Service, typename... Args>
+[[nodiscard]] static Service &value_or(Args &&...args) {
+return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Sets or replaces a service.
+     * @tparam Impl Service type.
+     * @tparam Args Types of arguments to use to construct the service.
+     * @param args Parameters to use to construct the service.
+     * @return A reference to a valid service.
+     */
+template<typename Impl = Service, typename... Args>
+static Service &emplace(Args &&...args) {
+service = std::make_shared<Impl>(std::forward<Args>(args)...);
+return *service;
+}
+
+/**
+     * @brief Sets or replaces a service using a given allocator.
+     * @tparam Impl Service type.
+     * @tparam Allocator Type of allocator used to manage memory and elements.
+     * @tparam Args Types of arguments to use to construct the service.
+     * @param alloc The allocator to use.
+     * @param args Parameters to use to construct the service.
+     * @return A reference to a valid service.
+     */
+template<typename Impl = Service, typename Allocator, typename... Args>
+static Service &allocate_emplace(Allocator alloc, Args &&...args) {
+service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
+return *service;
+}
+
+/*! @brief Resets a service. */
+static void reset() ENTT_NOEXCEPT {
+service.reset();
+}
+
+private:
+// std::shared_ptr because of its type erased allocator which is pretty useful here
+inline static std::shared_ptr<Service> service = nullptr;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/adl_pointer.hpp"
+#ifndef ENTT_META_ADL_POINTER_HPP
+#define ENTT_META_ADL_POINTER_HPP
+
+namespace entt {
+
+/**
+ * @brief ADL based lookup function for dereferencing meta pointer-like types.
+ * @tparam Type Element type.
+ * @param value A pointer-like object.
+ * @return The value returned from the dereferenced pointer.
+ */
+template<typename Type>
+decltype(auto) dereference_meta_pointer_like(const Type &value) {
+return *value;
+}
+
+/**
+ * @brief Fake ADL based lookup function for meta pointer-like types.
+ * @tparam Type Element type.
+ */
+template<typename Type>
+struct adl_meta_pointer_like {
+/**
+     * @brief Uses the default ADL based lookup method to resolve the call.
+     * @param value A pointer-like object.
+     * @return The value returned from the dereferenced pointer.
+     */
+static decltype(auto) dereference(const Type &value) {
+return dereference_meta_pointer_like(value);
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/container.hpp"
+#ifndef ENTT_META_CONTAINER_HPP
+#define ENTT_META_CONTAINER_HPP
+
+#include <array>
+#include <map>
+#include <set>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+// #include "../container/dense_map.hpp"
+#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+
+namespace entt {
+
+template<
+typename Key,
+typename Type,
+typename = std::hash<Key>,
+typename = std::equal_to<Key>,
+typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+typename Type,
+typename = std::hash<Type>,
+typename = std::equal_to<Type>,
+typename = std::allocator<Type>>
+class dense_set;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+using value_type = std::pair<Key, Type>;
+
+template<typename... Args>
+dense_map_node(const std::size_t pos, Args &&...args)
+: next{pos},
+element{std::forward<Args>(args)...} {}
+
+template<typename Allocator, typename... Args>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+: next{pos},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+std::size_t next;
+value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+template<typename>
+friend class dense_map_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_map_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_map_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_map_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_map_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_map_iterator operator--(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_map_iterator copy = *this;
+return (copy += value);
+}
+
+dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].element.first, it[value].element.second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->element.first, it->element.second};
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+template<typename>
+friend class dense_map_local_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].next, *this;
+}
+
+dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it[offset].element.first, it[offset].element.second};
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = internal::dense_map_node<Key, Type>;
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(key), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other, typename... Args>
+[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+template<typename Other, typename Arg>
+[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+it->second = std::forward<Arg>(value);
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
+for(; *curr != last; curr = &packed.first()[*curr].next) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Key;
+/*! @brief Mapped type of the container. */
+using mapped_type = Type;
+/*! @brief Key-value type of the container. */
+using value_type = std::pair<const Key, Type>;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the keys. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the keys for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Input iterator type. */
+using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_map()
+: dense_map(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const allocator_type &allocator)
+: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const allocator_type &allocator)
+: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_map{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_map(const dense_map &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_map(const dense_map &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_map(dense_map &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_map(dense_map &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(const dense_map &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(dense_map &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if the key does not exist.
+     * @param value A key-value pair eventually convertible to the value type.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value.first, value.second);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+}
+
+/**
+     * @copydoc insert
+     * @tparam Arg Type of the key-value pair to insert into the container.
+     */
+template<typename Arg>
+std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+insert(Arg &&value) {
+return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+}
+
+/**
+     * @brief Inserts elements into the container, if their keys do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Inserts an element into the container or assigns to the current
+     * element if the key already exists.
+     * @tparam Arg Type of the value to insert or assign.
+     * @param key A key used both to look up and to insert if not found.
+     * @param value A value to insert or assign.
+     * @return A pair consisting of an iterator to the element and a bool
+     * denoting whether the insertion took place.
+     */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+return insert_or_overwrite(key, std::forward<Arg>(value));
+}
+
+/*! @copydoc insert_or_assign */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+}
+
+/**
+     * @brief Constructs an element in-place, if the key does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+if constexpr(sizeof...(Args) == 0u) {
+return insert_or_do_nothing(key_type{});
+} else if constexpr(sizeof...(Args) == 1u) {
+return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+} else if constexpr(sizeof...(Args) == 2u) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+const auto index = key_to_bucket(node.element.first);
+
+if(auto it = constrained_find(node.element.first, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.next, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Inserts in-place if the key does not exist, does nothing if the
+     * key exists.
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param key A key used both to look up and to insert if not found.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+return insert_or_do_nothing(key, std::forward<Args>(args)...);
+}
+
+/*! @copydoc try_emplace */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(pos->first);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].element.first);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given key.
+     * @param key A key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const key_type &key) {
+for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+if(packed.second()(packed.first()[*curr].element.first, key)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].next;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_map &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Accesses a given element with bounds checking.
+     * @param key A key of an element to find.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &at(const key_type &key) {
+auto it = find(key);
+ENTT_ASSERT(it != end(), "Invalid key");
+return it->second;
+}
+
+/*! @copydoc at */
+[[nodiscard]] const mapped_type &at(const key_type &key) const {
+auto it = find(key);
+ENTT_ASSERT(it != cend(), "Invalid key");
+return it->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](const key_type &key) {
+return insert_or_do_nothing(key).first->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](key_type &&key) {
+return insert_or_do_nothing(std::move(key)).first->second;
+}
+
+/**
+     * @brief Finds an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const key_type &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const key_type &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Finds an element with a key that compares _equivalent_ to a given
+     * value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const key_type &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element with a key that
+     * compares _equivalent_ to a given value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given key.
+     * @param key The value of the key to examine.
+     * @return The bucket for the given key.
+     */
+[[nodiscard]] size_type bucket(const key_type &key) const {
+return key_to_bucket(key);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = key_to_bucket(packed.first()[pos].element.first);
+packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the keys.
+     * @return The function used to hash the keys.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare keys for equality.
+     * @return The function used to compare keys for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+: std::true_type {};
+
+} // namespace std
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "../container/dense_set.hpp"
+#ifndef ENTT_CONTAINER_DENSE_SET_HPP
+#define ENTT_CONTAINER_DENSE_SET_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../core/compressed_pair.hpp"
+
+// #include "../core/memory.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class dense_set_iterator final {
+template<typename>
+friend class dense_set_iterator;
+
+public:
+using value_type = typename It::value_type::second_type;
+using pointer = const value_type *;
+using reference = const value_type &;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::random_access_iterator_tag;
+
+dense_set_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_set_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_set_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_set_iterator operator++(int) ENTT_NOEXCEPT {
+dense_set_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_set_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_set_iterator operator--(int) ENTT_NOEXCEPT {
+dense_set_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_set_iterator copy = *this;
+return (copy += value);
+}
+
+dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return it[value].second;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return std::addressof(it->second);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_set_local_iterator final {
+template<typename>
+friend class dense_set_local_iterator;
+
+public:
+using value_type = typename It::value_type::second_type;
+using pointer = const value_type *;
+using reference = const value_type &;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::forward_iterator_tag;
+
+dense_set_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_set_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].first, *this;
+}
+
+dense_set_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_set_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return std::addressof(it[offset].second);
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return *operator->();
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for unique objects of a given type.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on its hash. Elements with the same hash code
+ * appear in the same bucket.
+ *
+ * @tparam Type Value type of the associative container.
+ * @tparam Hash Type of function to use to hash the values.
+ * @tparam KeyEqual Type of function to use to compare the values for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_set {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = std::pair<std::size_t, Type>;
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(value), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(*it, value)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(*it, value)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other>
+[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
+const auto index = value_to_bucket(value);
+
+if(auto it = constrained_find(value, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
+for(; *curr != last; curr = &packed.first()[*curr].first) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Type;
+/*! @brief Value type of the container. */
+using value_type = Type;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the elements. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the elements for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Random access iterator type. */
+using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant random access iterator type. */
+using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Forward iterator type. */
+using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant forward iterator type. */
+using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_set()
+: dense_set(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_set(const allocator_type &allocator)
+: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_set(const size_type bucket_count, const allocator_type &allocator)
+: dense_set{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_set{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_set(const dense_set &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_set(const dense_set &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_set(dense_set &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_set(dense_set &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_set &operator=(const dense_set &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_set &operator=(dense_set &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if it does not exist.
+     * @param value An element to insert into the container.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value));
+}
+
+/**
+     * @brief Inserts elements into the container, if they do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Constructs an element in-place, if it does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace(Args &&...args) {
+if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, value_type>)) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
+const auto index = value_to_bucket(node.second);
+
+if(auto it = constrained_find(node.second, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.first, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(*pos);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].second);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given value.
+     * @param value Value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const value_type &value) {
+for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
+if(packed.second()(packed.first()[*curr].second, value)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].first;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_set &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Finds an element with a given value.
+     * @param value Value of an element to search for.
+     * @return An iterator to an element with the given value. If no such
+     * element is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const value_type &value) {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const value_type &value) const {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/**
+     * @brief Finds an element that compares _equivalent_ to a given value.
+     * @tparam Other Type of an element to search for.
+     * @param value Value of an element to search for.
+     * @return An iterator to an element with the given value. If no such
+     * element is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &value) {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &value) const {
+return constrained_find(value, value_to_bucket(value));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given value.
+     * @param value Value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const value_type &value) const {
+return (find(value) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element that compares
+     * _equivalent_ to a given value.
+     * @tparam Other Type of an element to search for.
+     * @param value Value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &value) const {
+return (find(value) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given element.
+     * @param value The value of the element to examine.
+     * @return The bucket for the given element.
+     */
+[[nodiscard]] size_type bucket(const value_type &value) const {
+return value_to_bucket(value);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = value_to_bucket(packed.first()[pos].second);
+packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the elements.
+     * @return The function used to hash the elements.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare elements for equality.
+     * @return The function used to compare elements for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta.hpp"
+#ifndef ENTT_META_META_HPP
+#define ENTT_META_META_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/any.hpp"
+#ifndef ENTT_CORE_ANY_HPP
+#define ENTT_CORE_ANY_HPP
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief A SBO friendly, type-safe container for single values of any type.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ */
+template<std::size_t Len, std::size_t Align>
+class basic_any {
+enum class operation : std::uint8_t {
+copy,
+move,
+transfer,
+assign,
+destroy,
+compare,
+get
+};
+
+enum class policy : std::uint8_t {
+owner,
+ref,
+cref
+};
+
+using storage_type = std::aligned_storage_t<Len + !Len, Align>;
+using vtable_type = const void *(const operation, const basic_any &, const void *);
+
+template<typename Type>
+static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+
+template<typename Type>
+static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
+static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+const Type *element = nullptr;
+
+if constexpr(in_situ<Type>) {
+element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
+} else {
+element = static_cast<const Type *>(value.instance);
+}
+
+switch(op) {
+case operation::copy:
+if constexpr(std::is_copy_constructible_v<Type>) {
+static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
+}
+break;
+case operation::move:
+if constexpr(in_situ<Type>) {
+if(value.owner()) {
+return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
+}
+}
+
+return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
+case operation::transfer:
+if constexpr(std::is_move_assignable_v<Type>) {
+*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
+return other;
+}
+[[fallthrough]];
+case operation::assign:
+if constexpr(std::is_copy_assignable_v<Type>) {
+*const_cast<Type *>(element) = *static_cast<const Type *>(other);
+return other;
+}
+break;
+case operation::destroy:
+if constexpr(in_situ<Type>) {
+element->~Type();
+} else if constexpr(std::is_array_v<Type>) {
+delete[] element;
+} else {
+delete element;
+}
+break;
+case operation::compare:
+if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
+return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
+} else {
+return (element == other) ? other : nullptr;
+}
+case operation::get:
+return element;
+}
+
+return nullptr;
+}
+
+template<typename Type, typename... Args>
+void initialize([[maybe_unused]] Args &&...args) {
+if constexpr(!std::is_void_v<Type>) {
+info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+
+if constexpr(std::is_lvalue_reference_v<Type>) {
+static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
+mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
+instance = (std::addressof(args), ...);
+} else if constexpr(in_situ<Type>) {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+new(&storage) Type{std::forward<Args>(args)...};
+} else {
+new(&storage) Type(std::forward<Args>(args)...);
+}
+} else {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+instance = new Type{std::forward<Args>(args)...};
+} else {
+instance = new Type(std::forward<Args>(args)...);
+}
+}
+}
+}
+
+basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
+: instance{other.data()},
+info{other.info},
+vtable{other.vtable},
+mode{pol} {}
+
+public:
+/*! @brief Size of the internal storage. */
+static constexpr auto length = Len;
+/*! @brief Alignment requirement. */
+static constexpr auto alignment = Align;
+
+/*! @brief Default constructor. */
+constexpr basic_any() ENTT_NOEXCEPT
+: instance{},
+info{&type_id<void>()},
+vtable{},
+mode{policy::owner} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
+: basic_any{} {
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
+basic_any(Type &&value)
+: basic_any{} {
+initialize<std::decay_t<Type>>(std::forward<Type>(value));
+}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+basic_any(const basic_any &other)
+: basic_any{} {
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_any(basic_any &&other) ENTT_NOEXCEPT
+: instance{},
+info{other.info},
+vtable{other.vtable},
+mode{other.mode} {
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+}
+}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~basic_any() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This any object.
+     */
+basic_any &operator=(const basic_any &other) {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This any object.
+     */
+basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+info = other.info;
+vtable = other.vtable;
+mode = other.mode;
+}
+
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/**
+     * @brief Returns the object type if any, `type_id<void>()` otherwise.
+     * @return The object type if any, `type_id<void>()` otherwise.
+     */
+[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Replaces the contained object by creating a new instance directly.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+reset();
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+bool assign(const any &other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+return (vtable(operation::assign, *this, other.data()) != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc assign */
+bool assign(any &&other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+if(auto *val = other.data(); val) {
+return (vtable(operation::transfer, *this, val) != nullptr);
+} else {
+return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
+}
+}
+
+return false;
+}
+
+/*! @brief Destroys contained object */
+void reset() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+
+info = &type_id<void>();
+vtable = nullptr;
+mode = policy::owner;
+}
+
+/**
+     * @brief Returns false if a wrapper is empty, true otherwise.
+     * @return False if the wrapper is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return vtable != nullptr;
+}
+
+/**
+     * @brief Checks if two wrappers differ in their content.
+     * @param other Wrapper with which to compare.
+     * @return False if the two objects differ in their content, true otherwise.
+     */
+bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
+if(vtable && *info == *other.info) {
+return (vtable(operation::compare, *this, other.data()) != nullptr);
+}
+
+return (!vtable && !other.vtable);
+}
+
+/**
+     * @brief Aliasing constructor.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
+return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
+}
+
+/*! @copydoc as_ref */
+[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
+return basic_any{*this, policy::cref};
+}
+
+/**
+     * @brief Returns true if a wrapper owns its object, false otherwise.
+     * @return True if the wrapper owns its object, false otherwise.
+     */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return (mode == policy::owner);
+}
+
+private:
+union {
+const void *instance;
+storage_type storage;
+};
+const type_info *info;
+vtable_type *vtable;
+policy mode;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+template<std::size_t Len, std::size_t Align>
+[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Performs type-safe access to the contained object.
+ * @tparam Type Type to which conversion is required.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param data Target any object.
+ * @return The element converted to the requested type.
+ */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
+if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
+return static_cast<Type>(std::move(*instance));
+} else {
+return any_cast<Type>(data);
+}
+} else {
+auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(std::move(*instance));
+}
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+return static_cast<const Type *>(data->data(info));
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+// last attempt to make wrappers for const references return their values
+return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
+basic_any<Len, Align> make_any(Args &&...args) {
+return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
+basic_any<Len, Align> forward_as_any(Type &&value) {
+return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "adl_pointer.hpp"
+#ifndef ENTT_META_ADL_POINTER_HPP
+#define ENTT_META_ADL_POINTER_HPP
+
+namespace entt {
+
+/**
+ * @brief ADL based lookup function for dereferencing meta pointer-like types.
+ * @tparam Type Element type.
+ * @param value A pointer-like object.
+ * @return The value returned from the dereferenced pointer.
+ */
+template<typename Type>
+decltype(auto) dereference_meta_pointer_like(const Type &value) {
+return *value;
+}
+
+/**
+ * @brief Fake ADL based lookup function for meta pointer-like types.
+ * @tparam Type Element type.
+ */
+template<typename Type>
+struct adl_meta_pointer_like {
+/**
+     * @brief Uses the default ADL based lookup method to resolve the call.
+     * @param value A pointer-like object.
+     * @return The value returned from the dereferenced pointer.
+     */
+static decltype(auto) dereference(const Type &value) {
+return dereference_meta_pointer_like(value);
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "ctx.hpp"
+#ifndef ENTT_META_CTX_HPP
+#define ENTT_META_CTX_HPP
+
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct meta_type_node;
+
+struct ENTT_API meta_context {
+// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++
+//     inline static meta_type_node *local = nullptr;
+//     inline static meta_type_node **global = &local;
+
+[[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT {
+static meta_type_node *chain = nullptr;
+return chain;
+}
+
+[[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT {
+static meta_type_node **chain = &local();
+return chain;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Opaque container for a meta context. */
+struct meta_ctx {
+/**
+     * @brief Binds the meta system to a given context.
+     * @param other A valid context to which to bind.
+     */
+static void bind(meta_ctx other) ENTT_NOEXCEPT {
+internal::meta_context::global() = other.ctx;
+}
+
+private:
+internal::meta_type_node **ctx{&internal::meta_context::local()};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_META_FWD_HPP
+#define ENTT_META_FWD_HPP
+
+namespace entt {
+
+class meta_sequence_container;
+
+class meta_associative_container;
+
+class meta_any;
+
+struct meta_handle;
+
+struct meta_prop;
+
+struct meta_data;
+
+struct meta_func;
+
+class meta_type;
+
+} // namespace entt
+
+#endif
+
+// #include "node.hpp"
+#ifndef ENTT_META_NODE_HPP
+#define ENTT_META_NODE_HPP
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "../core/enum.hpp"
+#ifndef ENTT_CORE_ENUM_HPP
+#define ENTT_CORE_ENUM_HPP
+
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Enable bitmask support for enum classes.
+ * @tparam Type The enum type for which to enable bitmask support.
+ */
+template<typename Type, typename = void>
+struct enum_as_bitmask: std::false_type {};
+
+/*! @copydoc enum_as_bitmask */
+template<typename Type>
+struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The enum class type for which to enable bitmask support.
+ */
+template<typename Type>
+inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
+
+} // namespace entt
+
+/**
+ * @brief Operator available for enums for which bitmask support is enabled.
+ * @tparam Type Enum class type.
+ * @param lhs The first value to use.
+ * @param rhs The second value to use.
+ * @return The result of invoking the operator on the underlying types of the
+ * two values provided.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
+return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
+}
+
+/**
+ * @brief Operator available for enums for which bitmask support is enabled.
+ * @tparam Type Enum class type.
+ * @param value The value to use.
+ * @return The result of invoking the operator on the underlying types of the
+ * value provided.
+ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
+operator~(const Type value) ENTT_NOEXCEPT {
+return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
+}
+
+/*! @copydoc operator~ */
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
+operator!(const Type value) ENTT_NOEXCEPT {
+return !static_cast<std::underlying_type_t<Type>>(value);
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs | rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs & rhs));
+}
+
+/*! @copydoc operator| */
+template<typename Type>
+constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
+operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
+return (lhs = (lhs ^ rhs));
+}
+
+#endif
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "type_traits.hpp"
+#ifndef ENTT_META_TYPE_TRAITS_HPP
+#define ENTT_META_TYPE_TRAITS_HPP
+
+#include <type_traits>
+#include <utility>
+
+namespace entt {
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * template information.
+ */
+template<typename>
+struct meta_template_traits;
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * sequence containers.
+ */
+template<typename>
+struct meta_sequence_container_traits;
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * associative containers.
+ */
+template<typename>
+struct meta_associative_container_traits;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is a
+ * pointer-like type from the point of view of the meta system, false otherwise.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename>
+struct is_meta_pointer_like: std::false_type {};
+
+/**
+ * @brief Partial specialization to ensure that const pointer-like types are
+ * also accepted.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename Type>
+struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename Type>
+inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+class meta_any;
+class meta_type;
+struct meta_handle;
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+enum class meta_traits : std::uint32_t {
+is_none = 0x0000,
+is_const = 0x0001,
+is_static = 0x0002,
+is_arithmetic = 0x0004,
+is_array = 0x0008,
+is_enum = 0x0010,
+is_class = 0x0020,
+is_pointer = 0x0040,
+is_meta_pointer_like = 0x0080,
+is_meta_sequence_container = 0x0100,
+is_meta_associative_container = 0x0200,
+_entt_enum_as_bitmask
+};
+
+struct meta_type_node;
+
+struct meta_prop_node {
+meta_prop_node *next;
+const meta_any &id;
+meta_any &value;
+};
+
+struct meta_base_node {
+meta_base_node *next;
+meta_type_node *const type;
+meta_any (*const cast)(meta_any) ENTT_NOEXCEPT;
+};
+
+struct meta_conv_node {
+meta_conv_node *next;
+meta_type_node *const type;
+meta_any (*const conv)(const meta_any &);
+};
+
+struct meta_ctor_node {
+using size_type = std::size_t;
+meta_ctor_node *next;
+const size_type arity;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+meta_any (*const invoke)(meta_any *const);
+};
+
+struct meta_data_node {
+using size_type = std::size_t;
+id_type id;
+const meta_traits traits;
+meta_data_node *next;
+meta_prop_node *prop;
+const size_type arity;
+meta_type_node *const type;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+bool (*const set)(meta_handle, meta_any);
+meta_any (*const get)(meta_handle);
+};
+
+struct meta_func_node {
+using size_type = std::size_t;
+id_type id;
+const meta_traits traits;
+meta_func_node *next;
+meta_prop_node *prop;
+const size_type arity;
+meta_type_node *const ret;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+meta_any (*const invoke)(meta_handle, meta_any *const);
+};
+
+struct meta_template_node {
+using size_type = std::size_t;
+const size_type arity;
+meta_type_node *const type;
+meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT;
+};
+
+struct meta_type_node {
+using size_type = std::size_t;
+const type_info *info;
+id_type id;
+const meta_traits traits;
+meta_type_node *next;
+meta_prop_node *prop;
+const size_type size_of;
+meta_any (*const default_constructor)();
+double (*const conversion_helper)(void *, const void *);
+const meta_template_node *const templ;
+meta_ctor_node *ctor{nullptr};
+meta_base_node *base{nullptr};
+meta_conv_node *conv{nullptr};
+meta_data_node *data{nullptr};
+meta_func_node *func{nullptr};
+void (*dtor)(void *){nullptr};
+};
+
+template<typename... Args>
+meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
+
+template<typename Type>
+class ENTT_API meta_node {
+static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
+
+[[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT {
+if constexpr(std::is_default_constructible_v<Type>) {
+return +[]() { return meta_any{std::in_place_type<Type>}; };
+} else {
+return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr);
+}
+}
+
+[[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT {
+if constexpr(std::is_arithmetic_v<Type>) {
+return +[](void *bin, const void *value) {
+return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
+};
+} else if constexpr(std::is_enum_v<Type>) {
+return +[](void *bin, const void *value) {
+return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
+};
+} else {
+return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr);
+}
+}
+
+[[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT {
+if constexpr(is_complete_v<meta_template_traits<Type>>) {
+static meta_template_node node{
+meta_template_traits<Type>::args_type::size,
+meta_node<typename meta_template_traits<Type>::class_type>::resolve(),
+[](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); }
+// tricks clang-format
+};
+
+return &node;
+} else {
+return nullptr;
+}
+}
+
+public:
+[[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT {
+static meta_type_node node{
+&type_id<Type>(),
+{},
+internal::meta_traits::is_none
+| (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none)
+| (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none)
+| (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none)
+| (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none)
+| (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none)
+| (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none)
+| (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none)
+| (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none),
+nullptr,
+nullptr,
+size_of_v<Type>,
+meta_default_constructor(),
+meta_conversion_helper(),
+meta_template_info()
+// tricks clang-format
+};
+
+return &node;
+}
+};
+
+template<typename... Args>
+[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
+meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...};
+return args[index + 1u];
+}
+
+template<auto Member, typename Type>
+[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT {
+for(auto *curr = node->*Member; curr; curr = curr->next) {
+if constexpr(std::is_same_v<Type, type_info>) {
+if(*curr->type->info == info_or_id) {
+return curr;
+}
+} else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) {
+if(curr->type->id == info_or_id) {
+return curr;
+}
+} else {
+if(curr->id == info_or_id) {
+return curr;
+}
+}
+}
+
+for(auto *curr = node->base; curr; curr = curr->next) {
+if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) {
+return ret;
+}
+}
+
+return nullptr;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+} // namespace entt
+
+#endif
+
+// #include "range.hpp"
+#ifndef ENTT_META_RANGE_HPP
+#define ENTT_META_RANGE_HPP
+
+#include <cstddef>
+#include <iterator>
+// #include "../core/iterator.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename Node>
+struct meta_range_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = Type;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+using node_type = Node;
+
+meta_range_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+meta_range_iterator(node_type *head) ENTT_NOEXCEPT
+: it{head} {}
+
+meta_range_iterator &operator++() ENTT_NOEXCEPT {
+return (it = it->next), *this;
+}
+
+meta_range_iterator operator++(int) ENTT_NOEXCEPT {
+meta_range_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return it;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT {
+return it == other.it;
+}
+
+[[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+node_type *it;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Iterable range to use to iterate all types of meta objects.
+ * @tparam Type Type of meta objects returned.
+ * @tparam Node Type of meta nodes iterated.
+ */
+template<typename Type, typename Node = typename Type::node_type>
+struct meta_range final {
+/*! @brief Node type. */
+using node_type = Node;
+/*! @brief Input iterator type. */
+using iterator = internal::meta_range_iterator<Type, Node>;
+/*! @brief Constant input iterator type. */
+using const_iterator = iterator;
+
+/*! @brief Default constructor. */
+meta_range() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Constructs a meta range from a given node.
+     * @param head The underlying node with which to construct the range.
+     */
+meta_range(node_type *head) ENTT_NOEXCEPT
+: node{head} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first meta object of the range.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return iterator{node};
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last meta object of the
+     * range.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return iterator{};
+}
+
+/*! @copydoc cend */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+private:
+node_type *node{nullptr};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+class meta_any;
+class meta_type;
+
+/*! @brief Proxy object for sequence containers. */
+class meta_sequence_container {
+class meta_iterator;
+
+public:
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Meta iterator type. */
+using iterator = meta_iterator;
+
+/*! @brief Default constructor. */
+meta_sequence_container() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Construct a proxy object for sequence containers.
+     * @tparam Type Type of container to wrap.
+     * @param instance The container to wrap.
+     */
+template<typename Type>
+meta_sequence_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
+: value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
+size_fn{&meta_sequence_container_traits<Type>::size},
+resize_fn{&meta_sequence_container_traits<Type>::resize},
+iter_fn{&meta_sequence_container_traits<Type>::iter},
+insert_fn{&meta_sequence_container_traits<Type>::insert},
+erase_fn{&meta_sequence_container_traits<Type>::erase},
+storage{std::move(instance)} {}
+
+[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
+inline bool resize(const size_type);
+inline bool clear();
+[[nodiscard]] inline iterator begin();
+[[nodiscard]] inline iterator end();
+inline iterator insert(iterator, meta_any);
+inline iterator erase(iterator);
+[[nodiscard]] inline meta_any operator[](const size_type);
+[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
+
+private:
+internal::meta_type_node *value_type_node = nullptr;
+size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr;
+bool (*resize_fn)(any &, size_type) = nullptr;
+iterator (*iter_fn)(any &, const bool) = nullptr;
+iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr;
+iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr;
+any storage{};
+};
+
+/*! @brief Proxy object for associative containers. */
+class meta_associative_container {
+class meta_iterator;
+
+public:
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Meta iterator type. */
+using iterator = meta_iterator;
+
+/*! @brief Default constructor. */
+meta_associative_container() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Construct a proxy object for associative containers.
+     * @tparam Type Type of container to wrap.
+     * @param instance The container to wrap.
+     */
+template<typename Type>
+meta_associative_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
+: key_only_container{meta_associative_container_traits<Type>::key_only},
+key_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::key_type>>>::resolve()},
+mapped_type_node{nullptr},
+value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
+size_fn{&meta_associative_container_traits<Type>::size},
+clear_fn{&meta_associative_container_traits<Type>::clear},
+iter_fn{&meta_associative_container_traits<Type>::iter},
+insert_fn{&meta_associative_container_traits<Type>::insert},
+erase_fn{&meta_associative_container_traits<Type>::erase},
+find_fn{&meta_associative_container_traits<Type>::find},
+storage{std::move(instance)} {
+if constexpr(!meta_associative_container_traits<Type>::key_only) {
+mapped_type_node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::mapped_type>>>::resolve();
+}
+}
+
+[[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
+inline bool clear();
+[[nodiscard]] inline iterator begin();
+[[nodiscard]] inline iterator end();
+inline bool insert(meta_any, meta_any);
+inline bool erase(meta_any);
+[[nodiscard]] inline iterator find(meta_any);
+[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
+
+private:
+bool key_only_container{};
+internal::meta_type_node *key_type_node = nullptr;
+internal::meta_type_node *mapped_type_node = nullptr;
+internal::meta_type_node *value_type_node = nullptr;
+size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr;
+bool (*clear_fn)(any &) = nullptr;
+iterator (*iter_fn)(any &, const bool) = nullptr;
+bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr;
+bool (*erase_fn)(any &, meta_any &) = nullptr;
+iterator (*find_fn)(any &, meta_any &) = nullptr;
+any storage{};
+};
+
+/*! @brief Opaque wrapper for values of any type. */
+class meta_any {
+enum class operation : std::uint8_t {
+deref,
+seq,
+assoc
+};
+
+using vtable_type = void(const operation, const any &, void *);
+
+template<typename Type>
+static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) {
+static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+
+if constexpr(!std::is_void_v<Type>) {
+switch(op) {
+case operation::deref:
+if constexpr(is_meta_pointer_like_v<Type>) {
+if constexpr(std::is_function_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) {
+*static_cast<meta_any *>(other) = any_cast<Type>(value);
+} else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) {
+using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
+
+if constexpr(std::is_constructible_v<bool, Type>) {
+if(const auto &pointer_like = any_cast<const Type &>(value); pointer_like) {
+static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like));
+}
+} else {
+static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
+}
+}
+}
+break;
+case operation::seq:
+if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) {
+*static_cast<meta_sequence_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))};
+}
+break;
+case operation::assoc:
+if constexpr(is_complete_v<meta_associative_container_traits<Type>>) {
+*static_cast<meta_associative_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))};
+}
+break;
+}
+}
+}
+
+void release() {
+if(node && node->dtor && storage.owner()) {
+node->dtor(storage.data());
+}
+}
+
+meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT
+: storage{std::move(ref)},
+node{storage ? other.node : nullptr},
+vtable{storage ? other.vtable : &basic_vtable<void>} {}
+
+public:
+/*! @brief Default constructor. */
+meta_any() ENTT_NOEXCEPT
+: storage{},
+node{},
+vtable{&basic_vtable<void>} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit meta_any(std::in_place_type_t<Type>, Args &&...args)
+: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
+node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()},
+vtable{&basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>} {}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
+meta_any(Type &&value)
+: meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+meta_any(const meta_any &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+meta_any(meta_any &&other) ENTT_NOEXCEPT
+: storage{std::move(other.storage)},
+node{std::exchange(other.node, nullptr)},
+vtable{std::exchange(other.vtable, &basic_vtable<void>)} {}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~meta_any() {
+release();
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This meta any object.
+     */
+meta_any &operator=(const meta_any &other) {
+release();
+vtable = other.vtable;
+storage = other.storage;
+node = other.node;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This meta any object.
+     */
+meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT {
+release();
+vtable = std::exchange(other.vtable, &basic_vtable<void>);
+storage = std::move(other.storage);
+node = std::exchange(other.node, nullptr);
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This meta any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/*! @copydoc any::type */
+[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT;
+
+/*! @copydoc any::data */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/*! @copydoc any::data */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/**
+     * @brief Invokes the underlying function, if possible.
+     *
+     * @sa meta_func::invoke
+     *
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param id Unique identifier.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+template<typename... Args>
+meta_any invoke(const id_type id, Args &&...args) const;
+
+/*! @copydoc invoke */
+template<typename... Args>
+meta_any invoke(const id_type id, Args &&...args);
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param id Unique identifier.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(const id_type id, Type &&value);
+
+/**
+     * @brief Gets the value of a given variable.
+     * @param id Unique identifier.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(const id_type id) const;
+
+/*! @copydoc get */
+[[nodiscard]] meta_any get(const id_type id);
+
+/**
+     * @brief Tries to cast an instance to a given type.
+     * @tparam Type Type to which to cast the instance.
+     * @return A (possibly null) pointer to the contained instance.
+     */
+template<typename Type>
+[[nodiscard]] const Type *try_cast() const {
+if(const auto &info = type_id<Type>(); node && *node->info == info) {
+return any_cast<Type>(&storage);
+} else if(node) {
+for(auto *it = node->base; it; it = it->next) {
+const auto as_const = it->cast(as_ref());
+
+if(const Type *base = as_const.template try_cast<Type>(); base) {
+return base;
+}
+}
+}
+
+return nullptr;
+}
+
+/*! @copydoc try_cast */
+template<typename Type>
+[[nodiscard]] Type *try_cast() {
+if(const auto &info = type_id<Type>(); node && *node->info == info) {
+return any_cast<Type>(&storage);
+} else if(node) {
+for(auto *it = node->base; it; it = it->next) {
+if(Type *base = it->cast(as_ref()).template try_cast<Type>(); base) {
+return base;
+}
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Tries to cast an instance to a given type.
+     *
+     * The type of the instance must be such that the cast is possible.
+     *
+     * @warning
+     * Attempting to perform an invalid cast results is undefined behavior.
+     *
+     * @tparam Type Type to which to cast the instance.
+     * @return A reference to the contained instance.
+     */
+template<typename Type>
+[[nodiscard]] Type cast() const {
+auto *const instance = try_cast<std::remove_reference_t<Type>>();
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc cast */
+template<typename Type>
+[[nodiscard]] Type cast() {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = try_cast<std::remove_reference_t<const Type>>();
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @param type Meta type to which the cast is requested.
+     * @return A valid meta any object if there exists a viable conversion, an
+     * invalid one otherwise.
+     */
+[[nodiscard]] meta_any allow_cast(const meta_type &type) const;
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @param type Meta type to which the cast is requested.
+     * @return True if there exists a viable conversion, false otherwise.
+     */
+[[nodiscard]] bool allow_cast(const meta_type &type) {
+if(auto other = std::as_const(*this).allow_cast(type); other) {
+if(other.storage.owner()) {
+std::swap(*this, other);
+}
+
+return true;
+}
+
+return false;
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @tparam Type Type to which the cast is requested.
+     * @return A valid meta any object if there exists a viable conversion, an
+     * invalid one otherwise.
+     */
+template<typename Type>
+[[nodiscard]] meta_any allow_cast() const {
+const auto other = allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve());
+
+if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
+return other.storage.owner() ? other : meta_any{};
+} else {
+return other;
+}
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @tparam Type Type to which the cast is requested.
+     * @return True if there exists a viable conversion, false otherwise.
+     */
+template<typename Type>
+bool allow_cast() {
+if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); other) {
+if(other.storage.owner()) {
+std::swap(*this, other);
+return true;
+}
+
+return (static_cast<constness_as_t<any, std::remove_reference_t<const Type>> &>(storage).data() != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc any::emplace */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+release();
+vtable = &basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+storage.emplace<Type>(std::forward<Args>(args)...);
+node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve();
+}
+
+/*! @copydoc any::assign */
+bool assign(const meta_any &other);
+
+/*! @copydoc any::assign */
+bool assign(meta_any &&other);
+
+/*! @copydoc any::reset */
+void reset() {
+release();
+vtable = &basic_vtable<void>;
+storage.reset();
+node = nullptr;
+}
+
+/**
+     * @brief Returns a sequence container proxy.
+     * @return A sequence container proxy for the underlying object.
+     */
+[[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_sequence_container proxy;
+vtable(operation::seq, detached, &proxy);
+return proxy;
+}
+
+/*! @copydoc as_sequence_container */
+[[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_sequence_container proxy;
+vtable(operation::seq, detached, &proxy);
+return proxy;
+}
+
+/**
+     * @brief Returns an associative container proxy.
+     * @return An associative container proxy for the underlying object.
+     */
+[[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_associative_container proxy;
+vtable(operation::assoc, detached, &proxy);
+return proxy;
+}
+
+/*! @copydoc as_associative_container */
+[[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_associative_container proxy;
+vtable(operation::assoc, detached, &proxy);
+return proxy;
+}
+
+/**
+     * @brief Indirection operator for dereferencing opaque objects.
+     * @return A wrapper that shares a reference to an unmanaged object if the
+     * wrapped element is dereferenceable, an invalid meta any otherwise.
+     */
+[[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT {
+meta_any ret{};
+vtable(operation::deref, storage, &ret);
+return ret;
+}
+
+/**
+     * @brief Returns false if a wrapper is invalid, true otherwise.
+     * @return False if the wrapper is invalid, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+/*! @copydoc any::operator== */
+[[nodiscard]] bool operator==(const meta_any &other) const {
+return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage);
+}
+
+/*! @copydoc any::as_ref */
+[[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT {
+return meta_any{*this, storage.as_ref()};
+}
+
+/*! @copydoc any::as_ref */
+[[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT {
+return meta_any{*this, storage.as_ref()};
+}
+
+/*! @copydoc any::owner */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return storage.owner();
+}
+
+private:
+any storage;
+internal::meta_type_node *node;
+vtable_type *vtable;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, typename... Args>
+meta_any make_meta(Args &&...args) {
+return meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<typename Type>
+meta_any forward_as_meta(Type &&value) {
+return meta_any{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+/**
+ * @brief Opaque pointers to instances of any type.
+ *
+ * A handle doesn't perform copies and isn't responsible for the contained
+ * object. It doesn't prolong the lifetime of the pointed instance.<br/>
+ * Handles are used to generate references to actual objects when needed.
+ */
+struct meta_handle {
+/*! @brief Default constructor. */
+meta_handle() = default;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+meta_handle(const meta_handle &) = delete;
+
+/*! @brief Default move constructor. */
+meta_handle(meta_handle &&) = default;
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This meta handle.
+     */
+meta_handle &operator=(const meta_handle &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This meta handle.
+     */
+meta_handle &operator=(meta_handle &&) = default;
+
+/**
+     * @brief Creates a handle that points to an unmanaged object.
+     * @tparam Type Type of object to use to initialize the handle.
+     * @param value An instance of an object to use to initialize the handle.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
+meta_handle(Type &value) ENTT_NOEXCEPT
+: meta_handle{} {
+if constexpr(std::is_same_v<std::decay_t<Type>, meta_any>) {
+any = value.as_ref();
+} else {
+any.emplace<Type &>(value);
+}
+}
+
+/**
+     * @brief Returns false if a handle is invalid, true otherwise.
+     * @return False if the handle is invalid, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(any);
+}
+
+/**
+     * @brief Access operator for accessing the contained opaque object.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] meta_any *operator->() {
+return &any;
+}
+
+/*! @copydoc operator-> */
+[[nodiscard]] const meta_any *operator->() const {
+return &any;
+}
+
+private:
+meta_any any;
+};
+
+/*! @brief Opaque wrapper for properties of any type. */
+struct meta_prop {
+/*! @brief Node type. */
+using node_type = internal::meta_prop_node;
+
+/**
+     * @brief Constructs an instance from a given node.
+     * @param curr The underlying node with which to construct the instance.
+     */
+meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/**
+     * @brief Returns the stored key as a const reference.
+     * @return A wrapper containing the key stored with the property.
+     */
+[[nodiscard]] meta_any key() const {
+return node->id.as_ref();
+}
+
+/**
+     * @brief Returns the stored value by copy.
+     * @return A wrapper containing the value stored with the property.
+     */
+[[nodiscard]] meta_any value() const {
+return node->value;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for data members. */
+struct meta_data {
+/*! @brief Node type. */
+using node_type = internal::meta_data_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/*! @copydoc meta_type::id */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the number of setters available.
+     * @return The number of setters available.
+     */
+[[nodiscard]] size_type arity() const ENTT_NOEXCEPT {
+return node->arity;
+}
+
+/**
+     * @brief Indicates whether a data member is constant or not.
+     * @return True if the data member is constant, false otherwise.
+     */
+[[nodiscard]] bool is_const() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_const);
+}
+
+/**
+     * @brief Indicates whether a data member is static or not.
+     * @return True if the data member is static, false otherwise.
+     */
+[[nodiscard]] bool is_static() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_static);
+}
+
+/*! @copydoc meta_any::type */
+[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT;
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.<br/>
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param instance An opaque instance of the underlying type.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(meta_handle instance, Type &&value) const {
+return node->set && node->set(std::move(instance), std::forward<Type>(value));
+}
+
+/**
+     * @brief Gets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.
+     *
+     * @param instance An opaque instance of the underlying type.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(meta_handle instance) const {
+return node->get(std::move(instance));
+}
+
+/**
+     * @brief Returns the type accepted by the i-th setter.
+     * @param index Index of the setter of which to return the accepted type.
+     * @return The type accepted by the i-th setter.
+     */
+[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT;
+
+/**
+     * @brief Returns a range to visit registered meta properties.
+     * @return An iterable range to visit registered meta properties.
+     */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for registered meta properties.
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+for(auto curr: prop()) {
+if(curr.key() == key) {
+return curr;
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for member functions. */
+struct meta_func {
+/*! @brief Node type. */
+using node_type = internal::meta_func_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/*! @copydoc meta_type::id */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the number of arguments accepted by a member function.
+     * @return The number of arguments accepted by the member function.
+     */
+[[nodiscard]] size_type arity() const ENTT_NOEXCEPT {
+return node->arity;
+}
+
+/**
+     * @brief Indicates whether a member function is constant or not.
+     * @return True if the member function is constant, false otherwise.
+     */
+[[nodiscard]] bool is_const() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_const);
+}
+
+/**
+     * @brief Indicates whether a member function is static or not.
+     * @return True if the member function is static, false otherwise.
+     */
+[[nodiscard]] bool is_static() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_static);
+}
+
+/**
+     * @brief Returns the return type of a member function.
+     * @return The return type of the member function.
+     */
+[[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT;
+
+/**
+     * @brief Returns the type of the i-th argument of a member function.
+     * @param index Index of the argument of which to return the type.
+     * @return The type of the i-th argument of a member function.
+     */
+[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT;
+
+/**
+     * @brief Invokes the underlying function, if possible.
+     *
+     * To invoke a member function, the parameters must be such that a cast or
+     * conversion to the required types is possible. Otherwise, an empty and
+     * thus invalid wrapper is returned.<br/>
+     * It must be possible to cast the instance to the parent type of the member
+     * function.
+     *
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @param sz Number of parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const {
+return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{};
+}
+
+/**
+     * @copybrief invoke
+     *
+     * @sa invoke
+     *
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+meta_any invoke(meta_handle instance, Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return invoke(std::move(instance), arguments, sizeof...(Args));
+}
+
+/*! @copydoc meta_data::prop */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for registered meta properties.
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+for(auto curr: prop()) {
+if(curr.key() == key) {
+return curr;
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for types. */
+class meta_type {
+template<auto Member, typename Pred>
+[[nodiscard]] std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const {
+std::decay_t<decltype(node->*Member)> candidate{};
+size_type extent{sz + 1u};
+bool ambiguous{};
+
+for(auto *curr = (node->*Member); curr; curr = curr->next) {
+if(pred(curr) && curr->arity == sz) {
+size_type direct{};
+size_type ext{};
+
+for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) {
+const auto type = args[next].type();
+const auto other = curr->arg(next);
+
+if(const auto &info = other.info(); info == type.info()) {
+++direct;
+} else {
+ext += internal::find_by<&node_type::base>(info, type.node)
+|| internal::find_by<&node_type::conv>(info, type.node)
+|| (type.node->conversion_helper && other.node->conversion_helper);
+}
+}
+
+if((direct + ext) == sz) {
+if(ext < extent) {
+candidate = curr;
+extent = ext;
+ambiguous = false;
+} else if(ext == extent) {
+ambiguous = true;
+}
+}
+}
+}
+
+return (candidate && !ambiguous) ? candidate : decltype(candidate){};
+}
+
+public:
+/*! @brief Node type. */
+using node_type = internal::meta_type_node;
+/*! @brief Node type. */
+using base_node_type = internal::meta_base_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/**
+     * @brief Constructs an instance from a given base node.
+     * @param curr The base node with which to construct the instance.
+     */
+meta_type(const base_node_type *curr) ENTT_NOEXCEPT
+: node{curr ? curr->type : nullptr} {}
+
+/**
+     * @brief Returns the type info object of the underlying type.
+     * @return The type info object of the underlying type.
+     */
+[[nodiscard]] const type_info &info() const ENTT_NOEXCEPT {
+return *node->info;
+}
+
+/**
+     * @brief Returns the identifier assigned to a type.
+     * @return The identifier assigned to the type.
+     */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the size of the underlying type if known.
+     * @return The size of the underlying type if known, 0 otherwise.
+     */
+[[nodiscard]] size_type size_of() const ENTT_NOEXCEPT {
+return node->size_of;
+}
+
+/**
+     * @brief Checks whether a type refers to an arithmetic type or not.
+     * @return True if the underlying type is an arithmetic type, false
+     * otherwise.
+     */
+[[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_arithmetic);
+}
+
+/**
+     * @brief Checks whether a type refers to an array type or not.
+     * @return True if the underlying type is an array type, false otherwise.
+     */
+[[nodiscard]] bool is_array() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_array);
+}
+
+/**
+     * @brief Checks whether a type refers to an enum or not.
+     * @return True if the underlying type is an enum, false otherwise.
+     */
+[[nodiscard]] bool is_enum() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_enum);
+}
+
+/**
+     * @brief Checks whether a type refers to a class or not.
+     * @return True if the underlying type is a class, false otherwise.
+     */
+[[nodiscard]] bool is_class() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_class);
+}
+
+/**
+     * @brief Checks whether a type refers to a pointer or not.
+     * @return True if the underlying type is a pointer, false otherwise.
+     */
+[[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_pointer);
+}
+
+/**
+     * @brief Checks whether a type is a pointer-like type or not.
+     * @return True if the underlying type is a pointer-like one, false
+     * otherwise.
+     */
+[[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_pointer_like);
+}
+
+/**
+     * @brief Checks whether a type refers to a sequence container or not.
+     * @return True if the type is a sequence container, false otherwise.
+     */
+[[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_sequence_container);
+}
+
+/**
+     * @brief Checks whether a type refers to an associative container or not.
+     * @return True if the type is an associative container, false otherwise.
+     */
+[[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_associative_container);
+}
+
+/**
+     * @brief Checks whether a type refers to a recognized class template
+     * specialization or not.
+     * @return True if the type is a recognized class template specialization,
+     * false otherwise.
+     */
+[[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT {
+return (node->templ != nullptr);
+}
+
+/**
+     * @brief Returns the number of template arguments.
+     * @return The number of template arguments.
+     */
+[[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT {
+return node->templ ? node->templ->arity : size_type{};
+}
+
+/**
+     * @brief Returns a tag for the class template of the underlying type.
+     *
+     * @sa meta_class_template_tag
+     *
+     * @return The tag for the class template of the underlying type.
+     */
+[[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT {
+return node->templ ? node->templ->type : meta_type{};
+}
+
+/**
+     * @brief Returns the type of the i-th template argument of a type.
+     * @param index Index of the template argument of which to return the type.
+     * @return The type of the i-th template argument of a type.
+     */
+[[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT {
+return index < template_arity() ? node->templ->arg(index) : meta_type{};
+}
+
+/**
+     * @brief Returns a range to visit registered top-level base meta types.
+     * @return An iterable range to visit registered top-level base meta types.
+     */
+[[nodiscard]] meta_range<meta_type, internal::meta_base_node> base() const ENTT_NOEXCEPT {
+return node->base;
+}
+
+/**
+     * @brief Lookup function for registered base meta types.
+     * @param id Unique identifier.
+     * @return The registered base meta type for the given identifier, if any.
+     */
+[[nodiscard]] meta_type base(const id_type id) const {
+return internal::find_by<&node_type::base>(id, node);
+}
+
+/**
+     * @brief Returns a range to visit registered top-level meta data.
+     * @return An iterable range to visit registered top-level meta data.
+     */
+[[nodiscard]] meta_range<meta_data> data() const ENTT_NOEXCEPT {
+return node->data;
+}
+
+/**
+     * @brief Lookup function for registered meta data.
+     *
+     * Registered meta data of base classes will also be visited.
+     *
+     * @param id Unique identifier.
+     * @return The registered meta data for the given identifier, if any.
+     */
+[[nodiscard]] meta_data data(const id_type id) const {
+return internal::find_by<&node_type::data>(id, node);
+}
+
+/**
+     * @brief Returns a range to visit registered top-level functions.
+     * @return An iterable range to visit registered top-level functions.
+     */
+[[nodiscard]] meta_range<meta_func> func() const ENTT_NOEXCEPT {
+return node->func;
+}
+
+/**
+     * @brief Lookup function for registered meta functions.
+     *
+     * Registered meta functions of base classes will also be visited.<br/>
+     * In case of overloaded functions, the first one with the required
+     * identifier will be returned.
+     *
+     * @param id Unique identifier.
+     * @return The registered meta function for the given identifier, if any.
+     */
+[[nodiscard]] meta_func func(const id_type id) const {
+return internal::find_by<&node_type::func>(id, node);
+}
+
+/**
+     * @brief Creates an instance of the underlying type, if possible.
+     *
+     * Parameters are such that a cast or conversion to the required types is
+     * possible. Otherwise, an empty and thus invalid wrapper is returned.<br/>
+     * If suitable, the implicitly generated default constructor is used.
+     *
+     * @param args Parameters to use to construct the instance.
+     * @param sz Number of parameters to use to construct the instance.
+     * @return A wrapper containing the new instance, if any.
+     */
+[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
+const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; });
+return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{});
+}
+
+/**
+     * @copybrief construct
+     *
+     * @sa construct
+     *
+     * @tparam Args Types of arguments to use to construct the instance.
+     * @param args Parameters to use to construct the instance.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+[[nodiscard]] meta_any construct(Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return construct(arguments, sizeof...(Args));
+}
+
+/**
+     * @brief Invokes a function given an identifier, if possible.
+     *
+     * It must be possible to cast the instance to the parent type of the member
+     * function.
+     *
+     * @sa meta_func::invoke
+     *
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @param sz Number of parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
+const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; });
+
+for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) {
+candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; });
+}
+
+return candidate ? candidate->invoke(std::move(instance), args) : meta_any{};
+}
+
+/**
+     * @copybrief invoke
+     *
+     * @sa invoke
+     *
+     * @param id Unique identifier.
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return invoke(id, std::move(instance), arguments, sizeof...(Args));
+}
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.<br/>
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(const id_type id, meta_handle instance, Type &&value) const {
+const auto candidate = data(id);
+return candidate && candidate.set(std::move(instance), std::forward<Type>(value));
+}
+
+/**
+     * @brief Gets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.
+     *
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(const id_type id, meta_handle instance) const {
+const auto candidate = data(id);
+return candidate ? candidate.get(std::move(instance)) : meta_any{};
+}
+
+/**
+     * @brief Returns a range to visit registered top-level meta properties.
+     * @return An iterable range to visit registered top-level meta properties.
+     */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for meta properties.
+     *
+     * Properties of base classes are also visited.
+     *
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+return internal::find_by<&internal::meta_type_node::prop>(key, node);
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+/**
+     * @brief Checks if two objects refer to the same type.
+     * @param other The object with which to compare.
+     * @return True if the objects refer to the same type, false otherwise.
+     */
+[[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT {
+return (!node && !other.node) || (node && other.node && *node->info == *other.node->info);
+}
+
+private:
+const node_type *node;
+};
+
+/**
+ * @brief Checks if two objects refer to the same type.
+ * @param lhs An object, either valid or not.
+ * @param rhs An object, either valid or not.
+ * @return False if the objects refer to the same node, true otherwise.
+ */
+[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT {
+return node;
+}
+
+template<typename... Args>
+meta_any meta_any::invoke(const id_type id, Args &&...args) const {
+return type().invoke(id, *this, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+meta_any meta_any::invoke(const id_type id, Args &&...args) {
+return type().invoke(id, *this, std::forward<Args>(args)...);
+}
+
+template<typename Type>
+bool meta_any::set(const id_type id, Type &&value) {
+return type().set(id, *this, std::forward<Type>(value));
+}
+
+[[nodiscard]] inline meta_any meta_any::get(const id_type id) const {
+return type().get(id, *this);
+}
+
+[[nodiscard]] inline meta_any meta_any::get(const id_type id) {
+return type().get(id, *this);
+}
+
+[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const {
+if(const auto &info = type.info(); node && *node->info == info) {
+return as_ref();
+} else if(node) {
+for(auto *it = node->conv; it; it = it->next) {
+if(*it->type->info == info) {
+return it->conv(*this);
+}
+}
+
+if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) {
+// exploits the fact that arithmetic types and enums are also default constructible
+auto other = type.construct();
+ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found");
+const auto value = node->conversion_helper(nullptr, storage.data());
+other.node->conversion_helper(other.storage.data(), &value);
+return other;
+}
+
+for(auto *it = node->base; it; it = it->next) {
+const auto as_const = it->cast(as_ref());
+
+if(auto other = as_const.allow_cast(type); other) {
+return other;
+}
+}
+}
+
+return {};
+}
+
+inline bool meta_any::assign(const meta_any &other) {
+auto value = other.allow_cast(node);
+return value && storage.assign(std::move(value.storage));
+}
+
+inline bool meta_any::assign(meta_any &&other) {
+if(*node->info == *other.node->info) {
+return storage.assign(std::move(other.storage));
+}
+
+return assign(std::as_const(other));
+}
+
+[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT {
+return node->type;
+}
+
+[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT {
+return node->ret;
+}
+
+[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT {
+return index < arity() ? node->arg(index) : meta_type{};
+}
+
+[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT {
+return index < arity() ? node->arg(index) : meta_type{};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+class meta_sequence_container::meta_iterator final {
+friend class meta_sequence_container;
+
+using deref_fn_type = meta_any(const any &, const std::ptrdiff_t);
+
+template<typename It>
+static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) {
+return meta_any{std::in_place_type<typename std::iterator_traits<It>::reference>, any_cast<const It &>(value)[pos]};
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = meta_any;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+meta_iterator() ENTT_NOEXCEPT
+: deref{},
+offset{},
+handle{} {}
+
+template<typename It>
+explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT
+: deref{&deref_fn<It>},
+offset{init},
+handle{std::move(iter)} {}
+
+meta_iterator &operator++() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+meta_iterator operator++(int value) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+offset += ++value;
+return orig;
+}
+
+meta_iterator &operator--() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+meta_iterator operator--(int value) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+offset -= ++value;
+return orig;
+}
+
+[[nodiscard]] reference operator*() const {
+return deref(handle, offset);
+}
+
+[[nodiscard]] pointer operator->() const {
+return operator*();
+}
+
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(handle);
+}
+
+[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
+return offset == other.offset;
+}
+
+[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+deref_fn_type *deref;
+difference_type offset;
+any handle;
+};
+
+class meta_associative_container::meta_iterator final {
+enum class operation : std::uint8_t {
+incr,
+deref
+};
+
+using vtable_type = void(const operation, const any &, std::pair<meta_any, meta_any> *);
+
+template<bool KeyOnly, typename It>
+static void basic_vtable(const operation op, const any &value, std::pair<meta_any, meta_any> *other) {
+switch(op) {
+case operation::incr:
+++any_cast<It &>(const_cast<any &>(value));
+break;
+case operation::deref:
+const auto &it = any_cast<const It &>(value);
+if constexpr(KeyOnly) {
+other->first.emplace<decltype(*it)>(*it);
+} else {
+other->first.emplace<decltype((it->first))>(it->first);
+other->second.emplace<decltype((it->second))>(it->second);
+}
+break;
+}
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = std::pair<meta_any, meta_any>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+meta_iterator() ENTT_NOEXCEPT
+: vtable{},
+handle{} {}
+
+template<bool KeyOnly, typename It>
+meta_iterator(std::integral_constant<bool, KeyOnly>, It iter) ENTT_NOEXCEPT
+: vtable{&basic_vtable<KeyOnly, It>},
+handle{std::move(iter)} {}
+
+meta_iterator &operator++() ENTT_NOEXCEPT {
+vtable(operation::incr, handle, nullptr);
+return *this;
+}
+
+meta_iterator operator++(int) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const {
+reference other;
+vtable(operation::deref, handle, &other);
+return other;
+}
+
+[[nodiscard]] pointer operator->() const {
+return operator*();
+}
+
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(handle);
+}
+
+[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
+return handle == other.handle;
+}
+
+[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+vtable_type *vtable;
+any handle;
+};
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Returns the meta value type of a container.
+ * @return The meta value type of the container.
+ */
+[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT {
+return value_type_node;
+}
+
+/**
+ * @brief Returns the size of a container.
+ * @return The size of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT {
+return size_fn(storage);
+}
+
+/**
+ * @brief Resizes a container to contain a given number of elements.
+ * @param sz The new size of the container.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_sequence_container::resize(const size_type sz) {
+return resize_fn(storage, sz);
+}
+
+/**
+ * @brief Clears the content of a container.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_sequence_container::clear() {
+return resize_fn(storage, 0u);
+}
+
+/**
+ * @brief Returns an iterator to the first element of a container.
+ * @return An iterator to the first element of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() {
+return iter_fn(storage, false);
+}
+
+/**
+ * @brief Returns an iterator that is past the last element of a container.
+ * @return An iterator that is past the last element of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() {
+return iter_fn(storage, true);
+}
+
+/**
+ * @brief Inserts an element at a specified location of a container.
+ * @param it Iterator before which the element will be inserted.
+ * @param value Element value to insert.
+ * @return A possibly invalid iterator to the inserted element.
+ */
+inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) {
+return insert_fn(storage, it.offset, value);
+}
+
+/**
+ * @brief Removes a given element from a container.
+ * @param it Iterator to the element to remove.
+ * @return A possibly invalid iterator following the last removed element.
+ */
+inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
+return erase_fn(storage, it.offset);
+}
+
+/**
+ * @brief Returns a reference to the element at a given location of a container
+ * (no bounds checking is performed).
+ * @param pos The position of the element to return.
+ * @return A reference to the requested element properly wrapped.
+ */
+[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) {
+auto it = begin();
+it.operator++(static_cast<int>(pos) - 1);
+return *it;
+}
+
+/**
+ * @brief Returns false if a proxy is invalid, true otherwise.
+ * @return False if the proxy is invalid, true otherwise.
+ */
+[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(storage);
+}
+
+/**
+ * @brief Returns true if a container is also key-only, false otherwise.
+ * @return True if the associative container is also key-only, false otherwise.
+ */
+[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT {
+return key_only_container;
+}
+
+/**
+ * @brief Returns the meta key type of a container.
+ * @return The meta key type of the a container.
+ */
+[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT {
+return key_type_node;
+}
+
+/**
+ * @brief Returns the meta mapped type of a container.
+ * @return The meta mapped type of the a container.
+ */
+[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT {
+return mapped_type_node;
+}
+
+/*! @copydoc meta_sequence_container::value_type */
+[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT {
+return value_type_node;
+}
+
+/*! @copydoc meta_sequence_container::size */
+[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT {
+return size_fn(storage);
+}
+
+/*! @copydoc meta_sequence_container::clear */
+inline bool meta_associative_container::clear() {
+return clear_fn(storage);
+}
+
+/*! @copydoc meta_sequence_container::begin */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() {
+return iter_fn(storage, false);
+}
+
+/*! @copydoc meta_sequence_container::end */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() {
+return iter_fn(storage, true);
+}
+
+/**
+ * @brief Inserts an element (a key/value pair) into a container.
+ * @param key The key of the element to insert.
+ * @param value The value of the element to insert.
+ * @return A bool denoting whether the insertion took place.
+ */
+inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
+return insert_fn(storage, key, value);
+}
+
+/**
+ * @brief Removes the specified element from a container.
+ * @param key The key of the element to remove.
+ * @return A bool denoting whether the removal took place.
+ */
+inline bool meta_associative_container::erase(meta_any key) {
+return erase_fn(storage, key);
+}
+
+/**
+ * @brief Returns an iterator to the element with a given key, if any.
+ * @param key The key of the element to search.
+ * @return An iterator to the element with the given key, if any.
+ */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
+return find_fn(storage, key);
+}
+
+/**
+ * @brief Returns false if a proxy is invalid, true otherwise.
+ * @return False if the proxy is invalid, true otherwise.
+ */
+[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(storage);
+}
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct is_dynamic_sequence_container: std::false_type {};
+
+template<typename Type>
+struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
+
+template<typename, typename = void>
+struct is_key_only_meta_associative_container: std::true_type {};
+
+template<typename Type>
+struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
+
+template<typename Type>
+struct basic_meta_sequence_container_traits {
+using iterator = meta_sequence_container::iterator;
+using size_type = std::size_t;
+
+[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
+return any_cast<const Type &>(container).size();
+}
+
+[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
+if constexpr(is_dynamic_sequence_container<Type>::value) {
+if(auto *const cont = any_cast<Type>(&container); cont) {
+cont->resize(sz);
+return true;
+}
+}
+
+return false;
+}
+
+[[nodiscard]] static iterator iter(any &container, const bool as_end) {
+using std::begin;
+
+if(auto *const cont = any_cast<Type>(&container); cont) {
+return iterator{begin(*cont), static_cast<typename iterator::difference_type>(as_end * cont->size())};
+}
+
+const Type &as_const = any_cast<const Type &>(container);
+return iterator{begin(as_const), static_cast<typename iterator::difference_type>(as_end * as_const.size())};
+}
+
+[[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) {
+if constexpr(is_dynamic_sequence_container<Type>::value) {
+if(auto *const cont = any_cast<Type>(&container); cont) {
+// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
+if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
+using std::begin;
+const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
+const auto curr = cont->insert(begin(*cont) + offset, element ? *element : value.cast<typename Type::value_type>());
+return iterator{curr, curr - begin(*cont)};
+}
+}
+}
+
+return {};
+}
+
+[[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset) {
+if constexpr(is_dynamic_sequence_container<Type>::value) {
+if(auto *const cont = any_cast<Type>(&container); cont) {
+using std::begin;
+const auto curr = cont->erase(begin(*cont) + offset);
+return iterator{curr, curr - begin(*cont)};
+}
+}
+
+return {};
+}
+};
+
+template<typename Type>
+struct basic_meta_associative_container_traits {
+using iterator = meta_associative_container::iterator;
+using size_type = std::size_t;
+
+static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
+
+[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
+return any_cast<const Type &>(container).size();
+}
+
+[[nodiscard]] static bool clear(any &container) {
+if(auto *const cont = any_cast<Type>(&container); cont) {
+cont->clear();
+return true;
+}
+
+return false;
+}
+
+[[nodiscard]] static iterator iter(any &container, const bool as_end) {
+using std::begin;
+using std::end;
+
+if(auto *const cont = any_cast<Type>(&container); cont) {
+return iterator{std::integral_constant<bool, key_only>{}, as_end ? end(*cont) : begin(*cont)};
+}
+
+const auto &as_const = any_cast<const Type &>(container);
+return iterator{std::integral_constant<bool, key_only>{}, as_end ? end(as_const) : begin(as_const)};
+}
+
+[[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) {
+auto *const cont = any_cast<Type>(&container);
+
+if constexpr(is_key_only_meta_associative_container<Type>::value) {
+return cont && key.allow_cast<const typename Type::key_type &>()
+&& cont->insert(key.cast<const typename Type::key_type &>()).second;
+} else {
+return cont && key.allow_cast<const typename Type::key_type &>() && value.allow_cast<const typename Type::mapped_type &>()
+&& cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
+}
+}
+
+[[nodiscard]] static bool erase(any &container, meta_any &key) {
+auto *const cont = any_cast<Type>(&container);
+return cont && key.allow_cast<const typename Type::key_type &>()
+&& (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size());
+}
+
+[[nodiscard]] static iterator find(any &container, meta_any &key) {
+if(key.allow_cast<const typename Type::key_type &>()) {
+if(auto *const cont = any_cast<Type>(&container); cont) {
+return iterator{std::integral_constant<bool, key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
+}
+
+return iterator{std::integral_constant<bool, key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
+}
+
+return {};
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Meta sequence container traits for `std::vector`s of any type.
+ * @tparam Type The type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Type, typename... Args>
+struct meta_sequence_container_traits<std::vector<Type, Args...>>
+: internal::basic_meta_sequence_container_traits<std::vector<Type, Args...>> {};
+
+/**
+ * @brief Meta sequence container traits for `std::array`s of any type.
+ * @tparam Type The type of elements.
+ * @tparam N The number of elements.
+ */
+template<typename Type, auto N>
+struct meta_sequence_container_traits<std::array<Type, N>>
+: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
+
+/**
+ * @brief Meta associative container traits for `std::map`s of any type.
+ * @tparam Key The key type of elements.
+ * @tparam Value The value type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Key, typename Value, typename... Args>
+struct meta_associative_container_traits<std::map<Key, Value, Args...>>
+: internal::basic_meta_associative_container_traits<std::map<Key, Value, Args...>> {};
+
+/**
+ * @brief Meta associative container traits for `std::unordered_map`s of any
+ * type.
+ * @tparam Key The key type of elements.
+ * @tparam Value The value type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Key, typename Value, typename... Args>
+struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
+: internal::basic_meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> {};
+
+/**
+ * @brief Meta associative container traits for `std::set`s of any type.
+ * @tparam Key The type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Key, typename... Args>
+struct meta_associative_container_traits<std::set<Key, Args...>>
+: internal::basic_meta_associative_container_traits<std::set<Key, Args...>> {};
+
+/**
+ * @brief Meta associative container traits for `std::unordered_set`s of any
+ * type.
+ * @tparam Key The type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Key, typename... Args>
+struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
+: internal::basic_meta_associative_container_traits<std::unordered_set<Key, Args...>> {};
+
+/**
+ * @brief Meta associative container traits for `dense_map`s of any type.
+ * @tparam Key The key type of the elements.
+ * @tparam Type The value type of the elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Key, typename Type, typename... Args>
+struct meta_associative_container_traits<dense_map<Key, Type, Args...>>
+: internal::basic_meta_associative_container_traits<dense_map<Key, Type, Args...>> {};
+
+/**
+ * @brief Meta associative container traits for `dense_set`s of any type.
+ * @tparam Type The value type of the elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Type, typename... Args>
+struct meta_associative_container_traits<dense_set<Type, Args...>>
+: internal::basic_meta_associative_container_traits<dense_set<Type, Args...>> {};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/ctx.hpp"
+#ifndef ENTT_META_CTX_HPP
+#define ENTT_META_CTX_HPP
+
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct meta_type_node;
+
+struct ENTT_API meta_context {
+// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++
+//     inline static meta_type_node *local = nullptr;
+//     inline static meta_type_node **global = &local;
+
+[[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT {
+static meta_type_node *chain = nullptr;
+return chain;
+}
+
+[[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT {
+static meta_type_node **chain = &local();
+return chain;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Opaque container for a meta context. */
+struct meta_ctx {
+/**
+     * @brief Binds the meta system to a given context.
+     * @param other A valid context to which to bind.
+     */
+static void bind(meta_ctx other) ENTT_NOEXCEPT {
+internal::meta_context::global() = other.ctx;
+}
+
+private:
+internal::meta_type_node **ctx{&internal::meta_context::local()};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/factory.hpp"
+#ifndef ENTT_META_FACTORY_HPP
+#define ENTT_META_FACTORY_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "meta.hpp"
+
+// #include "node.hpp"
+
+// #include "policy.hpp"
+#ifndef ENTT_META_POLICY_HPP
+#define ENTT_META_POLICY_HPP
+
+#include <type_traits>
+
+namespace entt {
+
+/*! @brief Empty class type used to request the _as ref_ policy. */
+struct as_ref_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename Type>
+static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as cref_ policy. */
+struct as_cref_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename Type>
+static constexpr bool value = std::is_reference_v<Type>;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as-is_ policy. */
+struct as_is_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename>
+static constexpr bool value = true;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as void_ policy. */
+struct as_void_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename>
+static constexpr bool value = true;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+} // namespace entt
+
+#endif
+
+// #include "range.hpp"
+
+// #include "utility.hpp"
+#ifndef ENTT_META_UTILITY_HPP
+#define ENTT_META_UTILITY_HPP
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "meta.hpp"
+
+// #include "node.hpp"
+
+// #include "policy.hpp"
+
+
+namespace entt {
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename, typename>
+struct meta_function_descriptor;
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam Class Actual owner of the member function.
+ * @tparam Args Function arguments.
+ */
+template<typename Type, typename Ret, typename Class, typename... Args>
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = true;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam Class Actual owner of the member function.
+ * @tparam Args Function arguments.
+ */
+template<typename Type, typename Ret, typename Class, typename... Args>
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta data is associated.
+ * @tparam Class Actual owner of the data member.
+ * @tparam Ret Data member type.
+ */
+template<typename Type, typename Ret, typename Class>
+struct meta_function_descriptor<Type, Ret Class::*> {
+/*! @brief Meta data return type. */
+using return_type = Ret &;
+/*! @brief Meta data arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>;
+
+/*! @brief True if the meta data is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta data is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam MaybeType First function argument.
+ * @tparam Args Other function arguments.
+ */
+template<typename Type, typename Ret, typename MaybeType, typename... Args>
+struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ */
+template<typename Type, typename Ret>
+struct meta_function_descriptor<Type, Ret (*)()> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = type_list<>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = true;
+};
+
+/**
+ * @brief Meta function helper.
+ *
+ * Converts a function type to be associated with a reflected type into its meta
+ * function descriptor.
+ *
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Candidate The actual function to associate with the reflected type.
+ */
+template<typename Type, typename Candidate>
+class meta_function_helper {
+template<typename Ret, typename... Args, typename Class>
+static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const);
+
+template<typename Ret, typename... Args, typename Class>
+static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
+
+template<typename Ret, typename Class>
+static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*);
+
+template<typename Ret, typename... Args>
+static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...));
+
+template<typename Class>
+static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class);
+
+public:
+/*! @brief The meta function descriptor of the given function. */
+using type = decltype(get_rid_of_noexcept(std::declval<Candidate>()));
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Candidate The actual function to associate with the reflected type.
+ */
+template<typename Type, typename Candidate>
+using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type;
+
+/**
+ * @brief Wraps a value depending on the given policy.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Type Type of value to wrap.
+ * @param value Value to wrap.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Policy = as_is_t, typename Type>
+meta_any meta_dispatch([[maybe_unused]] Type &&value) {
+if constexpr(std::is_same_v<Policy, as_void_t>) {
+return meta_any{std::in_place_type<void>};
+} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
+return meta_any{std::in_place_type<Type>, std::forward<Type>(value)};
+} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
+static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
+return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
+} else {
+static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
+return meta_any{std::forward<Type>(value)};
+}
+}
+
+/**
+ * @brief Returns the meta type of the i-th element of a list of arguments.
+ * @tparam Type Type list of the actual types of arguments.
+ * @return The meta type of the i-th element of the list of arguments.
+ */
+template<typename Type>
+[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT {
+return internal::meta_arg_node(Type{}, index);
+}
+
+/**
+ * @brief Sets the value of a given variable.
+ * @tparam Type Reflected type to which the variable is associated.
+ * @tparam Data The actual variable to set.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param value Parameter to use to set the variable.
+ * @return True in case of success, false otherwise.
+ */
+template<typename Type, auto Data>
+[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
+if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
+if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+using descriptor = meta_function_helper_t<Type, decltype(Data)>;
+using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
+
+if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
+std::invoke(Data, *clazz, value.cast<data_type>());
+return true;
+}
+} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
+using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
+
+if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
+if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
+std::invoke(Data, *clazz) = value.cast<data_type>();
+return true;
+}
+}
+} else {
+using data_type = std::remove_reference_t<decltype(*Data)>;
+
+if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
+if(value.allow_cast<data_type>()) {
+*Data = value.cast<data_type>();
+return true;
+}
+}
+}
+}
+
+return false;
+}
+
+/**
+ * @brief Gets the value of a given variable.
+ * @tparam Type Reflected type to which the variable is associated.
+ * @tparam Data The actual variable to get.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param instance An opaque instance of the underlying type, if required.
+ * @return A meta any containing the value of the underlying variable.
+ */
+template<typename Type, auto Data, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
+if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
+if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
+if(auto *clazz = instance->try_cast<Type>(); clazz) {
+return meta_dispatch<Policy>(std::invoke(Data, *clazz));
+}
+}
+
+if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
+if(auto *fallback = instance->try_cast<const Type>(); fallback) {
+return meta_dispatch<Policy>(std::invoke(Data, *fallback));
+}
+}
+}
+
+return meta_any{};
+} else if constexpr(std::is_pointer_v<decltype(Data)>) {
+if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
+return meta_any{};
+} else {
+return meta_dispatch<Policy>(*Data);
+}
+} else {
+return meta_dispatch<Policy>(Data);
+}
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename Policy, typename Candidate, typename... Args>
+[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) {
+if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) {
+std::invoke(candidate, args...);
+return meta_any{std::in_place_type<void>};
+} else {
+return meta_dispatch<Policy>(std::invoke(candidate, args...));
+}
+}
+
+template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
+[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
+using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
+
+if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
+if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
+if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+} else {
+if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+}
+
+return meta_any{};
+}
+
+template<typename Type, typename... Args, std::size_t... Index>
+[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) {
+if(((args + Index)->allow_cast<Args>() && ...)) {
+return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...};
+}
+
+return meta_any{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Tries to _invoke_ an object given a list of erased parameters.
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param candidate The actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) {
+return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+}
+
+/**
+ * @brief Tries to invoke a function given a list of erased parameters.
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) {
+return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Actual type of the instance to construct.
+ * @tparam Args Types of arguments expected.
+ * @param args Parameters to use to construct the instance.
+ * @return A meta any containing the new instance, if any.
+ */
+template<typename Type, typename... Args>
+[[nodiscard]] meta_any meta_construct(meta_any *const args) {
+return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{});
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @param candidate The actual object to _invoke_.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
+if constexpr(meta_function_helper_t<Type, Candidate>::is_static) {
+return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+} else {
+return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+}
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_construct(meta_any *const args) {
+return meta_construct<Type, Policy>(Candidate, args);
+}
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Meta factory to be used for reflection purposes.
+ *
+ * The meta factory is an utility class used to reflect types, data members and
+ * functions of all sorts. This class ensures that the underlying web of types
+ * is built correctly and performs some checks in debug mode to ensure that
+ * there are no subtle errors at runtime.
+ */
+template<typename...>
+class meta_factory;
+
+/**
+ * @brief Extended meta factory to be used for reflection purposes.
+ * @tparam Type Reflected type for which the factory was created.
+ * @tparam Spec Property specialization pack used to disambiguate overloads.
+ */
+template<typename Type, typename... Spec>
+class meta_factory<Type, Spec...>: public meta_factory<Type> {
+void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT {
+if(meta_range<internal::meta_prop_node *, internal::meta_prop_node> range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier");
+node.next = *ref;
+*ref = &node;
+}
+}
+
+template<std::size_t Step = 0, typename... Property, typename... Other>
+void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) ENTT_NOEXCEPT {
+std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property);
+unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...);
+}
+
+template<std::size_t Step = 0, typename... Property, typename... Other>
+void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) ENTT_NOEXCEPT {
+assign<Step>(std::move(property.first), std::move(property.second));
+unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
+}
+
+template<std::size_t Step = 0, typename Property, typename... Other>
+void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT {
+assign<Step>(std::forward<Property>(property));
+unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
+}
+
+template<std::size_t>
+void unroll(choice_t<0>) ENTT_NOEXCEPT {}
+
+template<std::size_t = 0>
+void assign(meta_any key, meta_any value = {}) {
+static meta_any property[2u]{};
+
+static internal::meta_prop_node node{
+nullptr,
+property[0u],
+property[1u]
+// tricks clang-format
+};
+
+property[0u] = std::move(key);
+property[1u] = std::move(value);
+
+link_prop_if_required(node);
+}
+
+public:
+/**
+     * @brief Constructs an extended factory from a given node.
+     * @param target The underlying node to which to assign the properties.
+     */
+meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
+: ref{target} {}
+
+/**
+     * @brief Assigns a property to the last meta object created.
+     *
+     * Both the key and the value (if any) must be at least copy constructible.
+     *
+     * @tparam PropertyOrKey Type of the property or property key.
+     * @tparam Value Optional type of the property value.
+     * @param property_or_key Property or property key.
+     * @param value Optional property value.
+     * @return A meta factory for the parent type.
+     */
+template<typename PropertyOrKey, typename... Value>
+meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) {
+if constexpr(sizeof...(Value) == 0) {
+unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key));
+} else {
+assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
+}
+
+return {};
+}
+
+/**
+     * @brief Assigns properties to the last meta object created.
+     *
+     * Both key and value (if any) must be at least copy constructible.
+     *
+     * @tparam Property Types of the properties.
+     * @param property Properties to assign to the last meta object created.
+     * @return A meta factory for the parent type.
+     */
+template<typename... Property>
+meta_factory<Type> props(Property... property) {
+unroll(choice<2>, std::forward<Property>(property)...);
+return {};
+}
+
+private:
+internal::meta_prop_node **ref;
+};
+
+/**
+ * @brief Basic meta factory to be used for reflection purposes.
+ * @tparam Type Reflected type for which the factory was created.
+ */
+template<typename Type>
+class meta_factory<Type> {
+void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT {
+if(meta_range<internal::meta_base_node *, internal::meta_base_node> range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+node.next = owner->base;
+owner->base = &node;
+}
+}
+
+void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT {
+if(meta_range<internal::meta_conv_node *, internal::meta_conv_node> range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+node.next = owner->conv;
+owner->conv = &node;
+}
+}
+
+void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT {
+if(meta_range<internal::meta_ctor_node *, internal::meta_ctor_node> range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+node.next = owner->ctor;
+owner->ctor = &node;
+}
+}
+
+void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT {
+meta_range<internal::meta_data_node *, internal::meta_data_node> range{owner->data};
+ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier");
+node.id = id;
+
+if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+node.next = owner->data;
+owner->data = &node;
+}
+}
+
+void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT {
+node.id = id;
+
+if(meta_range<internal::meta_func_node *, internal::meta_func_node> range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
+node.next = owner->func;
+owner->func = &node;
+}
+}
+
+template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
+auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+using data_type = std::invoke_result_t<decltype(Getter), Type &>;
+using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
+static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
+
+static internal::meta_data_node node{
+{},
+/* this is never static */
+(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+nullptr,
+nullptr,
+Setter::size,
+internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
+&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+[](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
+&meta_getter<Type, Getter, Policy>
+// tricks clang-format
+};
+
+link_data_if_required(id, node);
+return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+}
+
+public:
+/*! @brief Default constructor. */
+meta_factory() ENTT_NOEXCEPT
+: owner{internal::meta_node<Type>::resolve()} {}
+
+/**
+     * @brief Makes a meta type _searchable_.
+     * @param id Optional unique identifier.
+     * @return An extended meta factory for the given type.
+     */
+auto type(const id_type id = type_hash<Type>::value()) ENTT_NOEXCEPT {
+meta_range<internal::meta_type_node *, internal::meta_type_node> range{*internal::meta_context::global()};
+ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier");
+owner->id = id;
+
+if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) {
+owner->next = *internal::meta_context::global();
+*internal::meta_context::global() = owner;
+}
+
+return meta_factory<Type, Type>{&owner->prop};
+}
+
+/**
+     * @brief Assigns a meta base to a meta type.
+     *
+     * A reflected base class must be a real base class of the reflected type.
+     *
+     * @tparam Base Type of the base class to assign to the meta type.
+     * @return A meta factory for the parent type.
+     */
+template<typename Base>
+auto base() ENTT_NOEXCEPT {
+static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
+
+static internal::meta_base_node node{
+nullptr,
+internal::meta_node<Base>::resolve(),
+[](meta_any other) ENTT_NOEXCEPT -> meta_any {
+if(auto *ptr = other.data(); ptr) {
+return forward_as_meta(*static_cast<Base *>(static_cast<Type *>(ptr)));
+}
+
+return forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data())));
+}
+// tricks clang-format
+};
+
+link_base_if_required(node);
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta conversion function to a meta type.
+     *
+     * Conversion functions can be either free functions or member
+     * functions.<br/>
+     * In case of free functions, they must accept a const reference to an
+     * instance of the parent type as an argument. In case of member functions,
+     * they should have no arguments at all.
+     *
+     * @tparam Candidate The actual function to use for the conversion.
+     * @return A meta factory for the parent type.
+     */
+template<auto Candidate>
+auto conv() ENTT_NOEXCEPT {
+static internal::meta_conv_node node{
+nullptr,
+internal::meta_node<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>>::resolve(),
+[](const meta_any &instance) -> meta_any {
+return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data())));
+}
+// tricks clang-format
+};
+
+link_conv_if_required(node);
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta conversion function to a meta type.
+     *
+     * The given type must be such that an instance of the reflected type can be
+     * converted to it.
+     *
+     * @tparam To Type of the conversion function to assign to the meta type.
+     * @return A meta factory for the parent type.
+     */
+template<typename To>
+auto conv() ENTT_NOEXCEPT {
+static internal::meta_conv_node node{
+nullptr,
+internal::meta_node<std::remove_cv_t<std::remove_reference_t<To>>>::resolve(),
+[](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance.data()))); }
+// tricks clang-format
+};
+
+link_conv_if_required(node);
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta constructor to a meta type.
+     *
+     * Both member functions and free function can be assigned to meta types in
+     * the role of constructors. All that is required is that they return an
+     * instance of the underlying type.<br/>
+     * From a client's point of view, nothing changes if a constructor of a meta
+     * type is a built-in one or not.
+     *
+     * @tparam Candidate The actual function to use as a constructor.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @return An extended meta factory for the parent type.
+     */
+template<auto Candidate, typename Policy = as_is_t>
+auto ctor() ENTT_NOEXCEPT {
+using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
+static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
+static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
+
+static internal::meta_ctor_node node{
+nullptr,
+descriptor::args_type::size,
+&meta_arg<typename descriptor::args_type>,
+&meta_construct<Type, Candidate, Policy>
+// tricks clang-format
+};
+
+link_ctor_if_required(node);
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta constructor to a meta type.
+     *
+     * A meta constructor is uniquely identified by the types of its arguments
+     * and is such that there exists an actual constructor of the underlying
+     * type that can be invoked with parameters whose types are those given.
+     *
+     * @tparam Args Types of arguments to use to construct an instance.
+     * @return An extended meta factory for the parent type.
+     */
+template<typename... Args>
+auto ctor() ENTT_NOEXCEPT {
+using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
+
+static internal::meta_ctor_node node{
+nullptr,
+descriptor::args_type::size,
+&meta_arg<typename descriptor::args_type>,
+&meta_construct<Type, Args...>
+// tricks clang-format
+};
+
+link_ctor_if_required(node);
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta destructor to a meta type.
+     *
+     * Both free functions and member functions can be assigned to meta types in
+     * the role of destructors.<br/>
+     * The signature of a free function should be identical to the following:
+     *
+     * @code{.cpp}
+     * void(Type &);
+     * @endcode
+     *
+     * Member functions should not take arguments instead.<br/>
+     * The purpose is to give users the ability to free up resources that
+     * require special treatment before an object is actually destroyed.
+     *
+     * @tparam Func The actual function to use as a destructor.
+     * @return A meta factory for the parent type.
+     */
+template<auto Func>
+auto dtor() ENTT_NOEXCEPT {
+static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
+owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
+return meta_factory<Type>{};
+}
+
+/**
+     * @brief Assigns a meta data to a meta type.
+     *
+     * Both data members and static and global variables, as well as constants
+     * of any kind, can be assigned to a meta type.<br/>
+     * From a client's point of view, all the variables associated with the
+     * reflected object will appear as if they were part of the type itself.
+     *
+     * @tparam Data The actual variable to attach to the meta type.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @param id Unique identifier.
+     * @return An extended meta factory for the parent type.
+     */
+template<auto Data, typename Policy = as_is_t>
+auto data(const id_type id) ENTT_NOEXCEPT {
+if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
+using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
+
+static internal::meta_data_node node{
+{},
+/* this is never static */
+std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
+nullptr,
+nullptr,
+1u,
+internal::meta_node<std::remove_const_t<data_type>>::resolve(),
+&meta_arg<type_list<std::remove_const_t<data_type>>>,
+&meta_setter<Type, Data>,
+&meta_getter<Type, Data, Policy>
+// tricks clang-format
+};
+
+link_data_if_required(id, node);
+return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop};
+} else {
+using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
+
+static internal::meta_data_node node{
+{},
+((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
+nullptr,
+nullptr,
+1u,
+internal::meta_node<std::remove_const_t<data_type>>::resolve(),
+&meta_arg<type_list<std::remove_const_t<data_type>>>,
+&meta_setter<Type, Data>,
+&meta_getter<Type, Data, Policy>
+// tricks clang-format
+};
+
+link_data_if_required(id, node);
+return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
+}
+}
+
+/**
+     * @brief Assigns a meta data to a meta type by means of its setter and
+     * getter.
+     *
+     * Setters and getters can be either free functions, member functions or a
+     * mix of them.<br/>
+     * In case of free functions, setters and getters must accept a reference to
+     * an instance of the parent type as their first argument. A setter has then
+     * an extra argument of a type convertible to that of the parameter to
+     * set.<br/>
+     * In case of member functions, getters have no arguments at all, while
+     * setters has an argument of a type convertible to that of the parameter to
+     * set.
+     *
+     * @tparam Setter The actual function to use as a setter.
+     * @tparam Getter The actual function to use as a getter.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @param id Unique identifier.
+     * @return An extended meta factory for the parent type.
+     */
+template<auto Setter, auto Getter, typename Policy = as_is_t>
+auto data(const id_type id) ENTT_NOEXCEPT {
+using data_type = std::invoke_result_t<decltype(Getter), Type &>;
+static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
+
+if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
+static internal::meta_data_node node{
+{},
+/* this is never static */
+internal::meta_traits::is_const,
+nullptr,
+nullptr,
+0u,
+internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
+&meta_arg<type_list<>>,
+&meta_setter<Type, Setter>,
+&meta_getter<Type, Getter, Policy>
+// tricks clang-format
+};
+
+link_data_if_required(id, node);
+return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+} else {
+using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
+
+static internal::meta_data_node node{
+{},
+/* this is never static nor const */
+internal::meta_traits::is_none,
+nullptr,
+nullptr,
+1u,
+internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
+&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
+&meta_setter<Type, Setter>,
+&meta_getter<Type, Getter, Policy>
+// tricks clang-format
+};
+
+link_data_if_required(id, node);
+return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
+}
+}
+
+/**
+     * @brief Assigns a meta data to a meta type by means of its setters and
+     * getter.
+     *
+     * Multi-setter support for meta data members. All setters are tried in the
+     * order of definition before returning to the caller.<br/>
+     * Setters can be either free functions, member functions or a mix of them
+     * and are provided via a `value_list` type.
+     *
+     * @sa data
+     *
+     * @tparam Setter The actual functions to use as setters.
+     * @tparam Getter The actual getter function.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @param id Unique identifier.
+     * @return An extended meta factory for the parent type.
+     */
+template<typename Setter, auto Getter, typename Policy = as_is_t>
+auto data(const id_type id) ENTT_NOEXCEPT {
+return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
+}
+
+/**
+     * @brief Assigns a meta function to a meta type.
+     *
+     * Both member functions and free functions can be assigned to a meta
+     * type.<br/>
+     * From a client's point of view, all the functions associated with the
+     * reflected object will appear as if they were part of the type itself.
+     *
+     * @tparam Candidate The actual function to attach to the meta type.
+     * @tparam Policy Optional policy (no policy set by default).
+     * @param id Unique identifier.
+     * @return An extended meta factory for the parent type.
+     */
+template<auto Candidate, typename Policy = as_is_t>
+auto func(const id_type id) ENTT_NOEXCEPT {
+using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
+static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
+
+static internal::meta_func_node node{
+{},
+(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
+nullptr,
+nullptr,
+descriptor::args_type::size,
+internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
+&meta_arg<typename descriptor::args_type>,
+&meta_invoke<Type, Candidate, Policy>
+// tricks clang-format
+};
+
+link_func_if_required(id, node);
+return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
+}
+
+private:
+internal::meta_type_node *owner;
+};
+
+/**
+ * @brief Utility function to use for reflection.
+ *
+ * This is the point from which everything starts.<br/>
+ * By invoking this function with a type that is not yet reflected, a meta type
+ * is created to which it will be possible to attach meta objects through a
+ * dedicated factory.
+ *
+ * @tparam Type Type to reflect.
+ * @return A meta factory for the given type.
+ */
+template<typename Type>
+[[nodiscard]] auto meta() ENTT_NOEXCEPT {
+auto *const node = internal::meta_node<Type>::resolve();
+// extended meta factory to allow assigning properties to opaque meta types
+return meta_factory<Type, Type>{&node->prop};
+}
+
+/**
+ * @brief Resets a type and all its parts.
+ *
+ * Resets a type and all its data members, member functions and properties, as
+ * well as its constructors, destructors and conversion functions if any.<br/>
+ * Base classes aren't reset but the link between the two types is removed.
+ *
+ * The type is also removed from the list of searchable types.
+ *
+ * @param id Unique identifier.
+ */
+inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
+auto clear_chain = [](auto **curr, auto... member) {
+for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
+if constexpr(sizeof...(member) != 0u) {
+static_assert(sizeof...(member) == 1u, "Assert in defense of the future me");
+for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {}
+}
+}
+};
+
+for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) {
+if(auto *node = *it; node->id == id) {
+clear_chain(&node->prop);
+clear_chain(&node->base);
+clear_chain(&node->conv);
+clear_chain(&node->ctor);
+clear_chain(&node->data, &internal::meta_data_node::prop);
+clear_chain(&node->func, &internal::meta_func_node::prop);
+
+node->id = {};
+node->dtor = nullptr;
+*it = std::exchange(node->next, nullptr);
+
+break;
+}
+}
+}
+
+/**
+ * @brief Resets a type and all its parts.
+ *
+ * @sa meta_reset
+ *
+ * @tparam Type Type to reset.
+ */
+template<typename Type>
+void meta_reset() ENTT_NOEXCEPT {
+meta_reset(internal::meta_node<Type>::resolve()->id);
+}
+
+/**
+ * @brief Resets all searchable types.
+ *
+ * @sa meta_reset
+ */
+inline void meta_reset() ENTT_NOEXCEPT {
+while(*internal::meta_context::global()) {
+meta_reset((*internal::meta_context::global())->id);
+}
+}
+
+} // namespace entt
+
+#endif
+
+// #include "meta/meta.hpp"
+#ifndef ENTT_META_META_HPP
+#define ENTT_META_META_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/any.hpp"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/iterator.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "../core/utility.hpp"
+
+// #include "adl_pointer.hpp"
+
+// #include "ctx.hpp"
+
+// #include "fwd.hpp"
+
+// #include "node.hpp"
+
+// #include "range.hpp"
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+class meta_any;
+class meta_type;
+
+/*! @brief Proxy object for sequence containers. */
+class meta_sequence_container {
+class meta_iterator;
+
+public:
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Meta iterator type. */
+using iterator = meta_iterator;
+
+/*! @brief Default constructor. */
+meta_sequence_container() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Construct a proxy object for sequence containers.
+     * @tparam Type Type of container to wrap.
+     * @param instance The container to wrap.
+     */
+template<typename Type>
+meta_sequence_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
+: value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
+size_fn{&meta_sequence_container_traits<Type>::size},
+resize_fn{&meta_sequence_container_traits<Type>::resize},
+iter_fn{&meta_sequence_container_traits<Type>::iter},
+insert_fn{&meta_sequence_container_traits<Type>::insert},
+erase_fn{&meta_sequence_container_traits<Type>::erase},
+storage{std::move(instance)} {}
+
+[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
+inline bool resize(const size_type);
+inline bool clear();
+[[nodiscard]] inline iterator begin();
+[[nodiscard]] inline iterator end();
+inline iterator insert(iterator, meta_any);
+inline iterator erase(iterator);
+[[nodiscard]] inline meta_any operator[](const size_type);
+[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
+
+private:
+internal::meta_type_node *value_type_node = nullptr;
+size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr;
+bool (*resize_fn)(any &, size_type) = nullptr;
+iterator (*iter_fn)(any &, const bool) = nullptr;
+iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr;
+iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr;
+any storage{};
+};
+
+/*! @brief Proxy object for associative containers. */
+class meta_associative_container {
+class meta_iterator;
+
+public:
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Meta iterator type. */
+using iterator = meta_iterator;
+
+/*! @brief Default constructor. */
+meta_associative_container() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Construct a proxy object for associative containers.
+     * @tparam Type Type of container to wrap.
+     * @param instance The container to wrap.
+     */
+template<typename Type>
+meta_associative_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
+: key_only_container{meta_associative_container_traits<Type>::key_only},
+key_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::key_type>>>::resolve()},
+mapped_type_node{nullptr},
+value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
+size_fn{&meta_associative_container_traits<Type>::size},
+clear_fn{&meta_associative_container_traits<Type>::clear},
+iter_fn{&meta_associative_container_traits<Type>::iter},
+insert_fn{&meta_associative_container_traits<Type>::insert},
+erase_fn{&meta_associative_container_traits<Type>::erase},
+find_fn{&meta_associative_container_traits<Type>::find},
+storage{std::move(instance)} {
+if constexpr(!meta_associative_container_traits<Type>::key_only) {
+mapped_type_node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::mapped_type>>>::resolve();
+}
+}
+
+[[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT;
+[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
+inline bool clear();
+[[nodiscard]] inline iterator begin();
+[[nodiscard]] inline iterator end();
+inline bool insert(meta_any, meta_any);
+inline bool erase(meta_any);
+[[nodiscard]] inline iterator find(meta_any);
+[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
+
+private:
+bool key_only_container{};
+internal::meta_type_node *key_type_node = nullptr;
+internal::meta_type_node *mapped_type_node = nullptr;
+internal::meta_type_node *value_type_node = nullptr;
+size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr;
+bool (*clear_fn)(any &) = nullptr;
+iterator (*iter_fn)(any &, const bool) = nullptr;
+bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr;
+bool (*erase_fn)(any &, meta_any &) = nullptr;
+iterator (*find_fn)(any &, meta_any &) = nullptr;
+any storage{};
+};
+
+/*! @brief Opaque wrapper for values of any type. */
+class meta_any {
+enum class operation : std::uint8_t {
+deref,
+seq,
+assoc
+};
+
+using vtable_type = void(const operation, const any &, void *);
+
+template<typename Type>
+static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) {
+static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+
+if constexpr(!std::is_void_v<Type>) {
+switch(op) {
+case operation::deref:
+if constexpr(is_meta_pointer_like_v<Type>) {
+if constexpr(std::is_function_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) {
+*static_cast<meta_any *>(other) = any_cast<Type>(value);
+} else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) {
+using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
+
+if constexpr(std::is_constructible_v<bool, Type>) {
+if(const auto &pointer_like = any_cast<const Type &>(value); pointer_like) {
+static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like));
+}
+} else {
+static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
+}
+}
+}
+break;
+case operation::seq:
+if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) {
+*static_cast<meta_sequence_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))};
+}
+break;
+case operation::assoc:
+if constexpr(is_complete_v<meta_associative_container_traits<Type>>) {
+*static_cast<meta_associative_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))};
+}
+break;
+}
+}
+}
+
+void release() {
+if(node && node->dtor && storage.owner()) {
+node->dtor(storage.data());
+}
+}
+
+meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT
+: storage{std::move(ref)},
+node{storage ? other.node : nullptr},
+vtable{storage ? other.vtable : &basic_vtable<void>} {}
+
+public:
+/*! @brief Default constructor. */
+meta_any() ENTT_NOEXCEPT
+: storage{},
+node{},
+vtable{&basic_vtable<void>} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit meta_any(std::in_place_type_t<Type>, Args &&...args)
+: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
+node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()},
+vtable{&basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>} {}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
+meta_any(Type &&value)
+: meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+meta_any(const meta_any &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+meta_any(meta_any &&other) ENTT_NOEXCEPT
+: storage{std::move(other.storage)},
+node{std::exchange(other.node, nullptr)},
+vtable{std::exchange(other.vtable, &basic_vtable<void>)} {}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~meta_any() {
+release();
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This meta any object.
+     */
+meta_any &operator=(const meta_any &other) {
+release();
+vtable = other.vtable;
+storage = other.storage;
+node = other.node;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This meta any object.
+     */
+meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT {
+release();
+vtable = std::exchange(other.vtable, &basic_vtable<void>);
+storage = std::move(other.storage);
+node = std::exchange(other.node, nullptr);
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This meta any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/*! @copydoc any::type */
+[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT;
+
+/*! @copydoc any::data */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/*! @copydoc any::data */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/**
+     * @brief Invokes the underlying function, if possible.
+     *
+     * @sa meta_func::invoke
+     *
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param id Unique identifier.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+template<typename... Args>
+meta_any invoke(const id_type id, Args &&...args) const;
+
+/*! @copydoc invoke */
+template<typename... Args>
+meta_any invoke(const id_type id, Args &&...args);
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param id Unique identifier.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(const id_type id, Type &&value);
+
+/**
+     * @brief Gets the value of a given variable.
+     * @param id Unique identifier.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(const id_type id) const;
+
+/*! @copydoc get */
+[[nodiscard]] meta_any get(const id_type id);
+
+/**
+     * @brief Tries to cast an instance to a given type.
+     * @tparam Type Type to which to cast the instance.
+     * @return A (possibly null) pointer to the contained instance.
+     */
+template<typename Type>
+[[nodiscard]] const Type *try_cast() const {
+if(const auto &info = type_id<Type>(); node && *node->info == info) {
+return any_cast<Type>(&storage);
+} else if(node) {
+for(auto *it = node->base; it; it = it->next) {
+const auto as_const = it->cast(as_ref());
+
+if(const Type *base = as_const.template try_cast<Type>(); base) {
+return base;
+}
+}
+}
+
+return nullptr;
+}
+
+/*! @copydoc try_cast */
+template<typename Type>
+[[nodiscard]] Type *try_cast() {
+if(const auto &info = type_id<Type>(); node && *node->info == info) {
+return any_cast<Type>(&storage);
+} else if(node) {
+for(auto *it = node->base; it; it = it->next) {
+if(Type *base = it->cast(as_ref()).template try_cast<Type>(); base) {
+return base;
+}
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Tries to cast an instance to a given type.
+     *
+     * The type of the instance must be such that the cast is possible.
+     *
+     * @warning
+     * Attempting to perform an invalid cast results is undefined behavior.
+     *
+     * @tparam Type Type to which to cast the instance.
+     * @return A reference to the contained instance.
+     */
+template<typename Type>
+[[nodiscard]] Type cast() const {
+auto *const instance = try_cast<std::remove_reference_t<Type>>();
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc cast */
+template<typename Type>
+[[nodiscard]] Type cast() {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = try_cast<std::remove_reference_t<const Type>>();
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @param type Meta type to which the cast is requested.
+     * @return A valid meta any object if there exists a viable conversion, an
+     * invalid one otherwise.
+     */
+[[nodiscard]] meta_any allow_cast(const meta_type &type) const;
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @param type Meta type to which the cast is requested.
+     * @return True if there exists a viable conversion, false otherwise.
+     */
+[[nodiscard]] bool allow_cast(const meta_type &type) {
+if(auto other = std::as_const(*this).allow_cast(type); other) {
+if(other.storage.owner()) {
+std::swap(*this, other);
+}
+
+return true;
+}
+
+return false;
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @tparam Type Type to which the cast is requested.
+     * @return A valid meta any object if there exists a viable conversion, an
+     * invalid one otherwise.
+     */
+template<typename Type>
+[[nodiscard]] meta_any allow_cast() const {
+const auto other = allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve());
+
+if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
+return other.storage.owner() ? other : meta_any{};
+} else {
+return other;
+}
+}
+
+/**
+     * @brief Converts an object in such a way that a given cast becomes viable.
+     * @tparam Type Type to which the cast is requested.
+     * @return True if there exists a viable conversion, false otherwise.
+     */
+template<typename Type>
+bool allow_cast() {
+if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); other) {
+if(other.storage.owner()) {
+std::swap(*this, other);
+return true;
+}
+
+return (static_cast<constness_as_t<any, std::remove_reference_t<const Type>> &>(storage).data() != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc any::emplace */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+release();
+vtable = &basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+storage.emplace<Type>(std::forward<Args>(args)...);
+node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve();
+}
+
+/*! @copydoc any::assign */
+bool assign(const meta_any &other);
+
+/*! @copydoc any::assign */
+bool assign(meta_any &&other);
+
+/*! @copydoc any::reset */
+void reset() {
+release();
+vtable = &basic_vtable<void>;
+storage.reset();
+node = nullptr;
+}
+
+/**
+     * @brief Returns a sequence container proxy.
+     * @return A sequence container proxy for the underlying object.
+     */
+[[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_sequence_container proxy;
+vtable(operation::seq, detached, &proxy);
+return proxy;
+}
+
+/*! @copydoc as_sequence_container */
+[[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_sequence_container proxy;
+vtable(operation::seq, detached, &proxy);
+return proxy;
+}
+
+/**
+     * @brief Returns an associative container proxy.
+     * @return An associative container proxy for the underlying object.
+     */
+[[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_associative_container proxy;
+vtable(operation::assoc, detached, &proxy);
+return proxy;
+}
+
+/*! @copydoc as_associative_container */
+[[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT {
+any detached = storage.as_ref();
+meta_associative_container proxy;
+vtable(operation::assoc, detached, &proxy);
+return proxy;
+}
+
+/**
+     * @brief Indirection operator for dereferencing opaque objects.
+     * @return A wrapper that shares a reference to an unmanaged object if the
+     * wrapped element is dereferenceable, an invalid meta any otherwise.
+     */
+[[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT {
+meta_any ret{};
+vtable(operation::deref, storage, &ret);
+return ret;
+}
+
+/**
+     * @brief Returns false if a wrapper is invalid, true otherwise.
+     * @return False if the wrapper is invalid, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+/*! @copydoc any::operator== */
+[[nodiscard]] bool operator==(const meta_any &other) const {
+return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage);
+}
+
+/*! @copydoc any::as_ref */
+[[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT {
+return meta_any{*this, storage.as_ref()};
+}
+
+/*! @copydoc any::as_ref */
+[[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT {
+return meta_any{*this, storage.as_ref()};
+}
+
+/*! @copydoc any::owner */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return storage.owner();
+}
+
+private:
+any storage;
+internal::meta_type_node *node;
+vtable_type *vtable;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, typename... Args>
+meta_any make_meta(Args &&...args) {
+return meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<typename Type>
+meta_any forward_as_meta(Type &&value) {
+return meta_any{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+/**
+ * @brief Opaque pointers to instances of any type.
+ *
+ * A handle doesn't perform copies and isn't responsible for the contained
+ * object. It doesn't prolong the lifetime of the pointed instance.<br/>
+ * Handles are used to generate references to actual objects when needed.
+ */
+struct meta_handle {
+/*! @brief Default constructor. */
+meta_handle() = default;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+meta_handle(const meta_handle &) = delete;
+
+/*! @brief Default move constructor. */
+meta_handle(meta_handle &&) = default;
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This meta handle.
+     */
+meta_handle &operator=(const meta_handle &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This meta handle.
+     */
+meta_handle &operator=(meta_handle &&) = default;
+
+/**
+     * @brief Creates a handle that points to an unmanaged object.
+     * @tparam Type Type of object to use to initialize the handle.
+     * @param value An instance of an object to use to initialize the handle.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
+meta_handle(Type &value) ENTT_NOEXCEPT
+: meta_handle{} {
+if constexpr(std::is_same_v<std::decay_t<Type>, meta_any>) {
+any = value.as_ref();
+} else {
+any.emplace<Type &>(value);
+}
+}
+
+/**
+     * @brief Returns false if a handle is invalid, true otherwise.
+     * @return False if the handle is invalid, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(any);
+}
+
+/**
+     * @brief Access operator for accessing the contained opaque object.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] meta_any *operator->() {
+return &any;
+}
+
+/*! @copydoc operator-> */
+[[nodiscard]] const meta_any *operator->() const {
+return &any;
+}
+
+private:
+meta_any any;
+};
+
+/*! @brief Opaque wrapper for properties of any type. */
+struct meta_prop {
+/*! @brief Node type. */
+using node_type = internal::meta_prop_node;
+
+/**
+     * @brief Constructs an instance from a given node.
+     * @param curr The underlying node with which to construct the instance.
+     */
+meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/**
+     * @brief Returns the stored key as a const reference.
+     * @return A wrapper containing the key stored with the property.
+     */
+[[nodiscard]] meta_any key() const {
+return node->id.as_ref();
+}
+
+/**
+     * @brief Returns the stored value by copy.
+     * @return A wrapper containing the value stored with the property.
+     */
+[[nodiscard]] meta_any value() const {
+return node->value;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for data members. */
+struct meta_data {
+/*! @brief Node type. */
+using node_type = internal::meta_data_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/*! @copydoc meta_type::id */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the number of setters available.
+     * @return The number of setters available.
+     */
+[[nodiscard]] size_type arity() const ENTT_NOEXCEPT {
+return node->arity;
+}
+
+/**
+     * @brief Indicates whether a data member is constant or not.
+     * @return True if the data member is constant, false otherwise.
+     */
+[[nodiscard]] bool is_const() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_const);
+}
+
+/**
+     * @brief Indicates whether a data member is static or not.
+     * @return True if the data member is static, false otherwise.
+     */
+[[nodiscard]] bool is_static() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_static);
+}
+
+/*! @copydoc meta_any::type */
+[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT;
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.<br/>
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param instance An opaque instance of the underlying type.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(meta_handle instance, Type &&value) const {
+return node->set && node->set(std::move(instance), std::forward<Type>(value));
+}
+
+/**
+     * @brief Gets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.
+     *
+     * @param instance An opaque instance of the underlying type.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(meta_handle instance) const {
+return node->get(std::move(instance));
+}
+
+/**
+     * @brief Returns the type accepted by the i-th setter.
+     * @param index Index of the setter of which to return the accepted type.
+     * @return The type accepted by the i-th setter.
+     */
+[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT;
+
+/**
+     * @brief Returns a range to visit registered meta properties.
+     * @return An iterable range to visit registered meta properties.
+     */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for registered meta properties.
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+for(auto curr: prop()) {
+if(curr.key() == key) {
+return curr;
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for member functions. */
+struct meta_func {
+/*! @brief Node type. */
+using node_type = internal::meta_func_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/*! @copydoc meta_type::id */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the number of arguments accepted by a member function.
+     * @return The number of arguments accepted by the member function.
+     */
+[[nodiscard]] size_type arity() const ENTT_NOEXCEPT {
+return node->arity;
+}
+
+/**
+     * @brief Indicates whether a member function is constant or not.
+     * @return True if the member function is constant, false otherwise.
+     */
+[[nodiscard]] bool is_const() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_const);
+}
+
+/**
+     * @brief Indicates whether a member function is static or not.
+     * @return True if the member function is static, false otherwise.
+     */
+[[nodiscard]] bool is_static() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_static);
+}
+
+/**
+     * @brief Returns the return type of a member function.
+     * @return The return type of the member function.
+     */
+[[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT;
+
+/**
+     * @brief Returns the type of the i-th argument of a member function.
+     * @param index Index of the argument of which to return the type.
+     * @return The type of the i-th argument of a member function.
+     */
+[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT;
+
+/**
+     * @brief Invokes the underlying function, if possible.
+     *
+     * To invoke a member function, the parameters must be such that a cast or
+     * conversion to the required types is possible. Otherwise, an empty and
+     * thus invalid wrapper is returned.<br/>
+     * It must be possible to cast the instance to the parent type of the member
+     * function.
+     *
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @param sz Number of parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const {
+return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{};
+}
+
+/**
+     * @copybrief invoke
+     *
+     * @sa invoke
+     *
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+meta_any invoke(meta_handle instance, Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return invoke(std::move(instance), arguments, sizeof...(Args));
+}
+
+/*! @copydoc meta_data::prop */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for registered meta properties.
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+for(auto curr: prop()) {
+if(curr.key() == key) {
+return curr;
+}
+}
+
+return nullptr;
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+private:
+const node_type *node;
+};
+
+/*! @brief Opaque wrapper for types. */
+class meta_type {
+template<auto Member, typename Pred>
+[[nodiscard]] std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const {
+std::decay_t<decltype(node->*Member)> candidate{};
+size_type extent{sz + 1u};
+bool ambiguous{};
+
+for(auto *curr = (node->*Member); curr; curr = curr->next) {
+if(pred(curr) && curr->arity == sz) {
+size_type direct{};
+size_type ext{};
+
+for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) {
+const auto type = args[next].type();
+const auto other = curr->arg(next);
+
+if(const auto &info = other.info(); info == type.info()) {
+++direct;
+} else {
+ext += internal::find_by<&node_type::base>(info, type.node)
+|| internal::find_by<&node_type::conv>(info, type.node)
+|| (type.node->conversion_helper && other.node->conversion_helper);
+}
+}
+
+if((direct + ext) == sz) {
+if(ext < extent) {
+candidate = curr;
+extent = ext;
+ambiguous = false;
+} else if(ext == extent) {
+ambiguous = true;
+}
+}
+}
+}
+
+return (candidate && !ambiguous) ? candidate : decltype(candidate){};
+}
+
+public:
+/*! @brief Node type. */
+using node_type = internal::meta_type_node;
+/*! @brief Node type. */
+using base_node_type = internal::meta_base_node;
+/*! @brief Unsigned integer type. */
+using size_type = typename node_type::size_type;
+
+/*! @copydoc meta_prop::meta_prop */
+meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT
+: node{curr} {}
+
+/**
+     * @brief Constructs an instance from a given base node.
+     * @param curr The base node with which to construct the instance.
+     */
+meta_type(const base_node_type *curr) ENTT_NOEXCEPT
+: node{curr ? curr->type : nullptr} {}
+
+/**
+     * @brief Returns the type info object of the underlying type.
+     * @return The type info object of the underlying type.
+     */
+[[nodiscard]] const type_info &info() const ENTT_NOEXCEPT {
+return *node->info;
+}
+
+/**
+     * @brief Returns the identifier assigned to a type.
+     * @return The identifier assigned to the type.
+     */
+[[nodiscard]] id_type id() const ENTT_NOEXCEPT {
+return node->id;
+}
+
+/**
+     * @brief Returns the size of the underlying type if known.
+     * @return The size of the underlying type if known, 0 otherwise.
+     */
+[[nodiscard]] size_type size_of() const ENTT_NOEXCEPT {
+return node->size_of;
+}
+
+/**
+     * @brief Checks whether a type refers to an arithmetic type or not.
+     * @return True if the underlying type is an arithmetic type, false
+     * otherwise.
+     */
+[[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_arithmetic);
+}
+
+/**
+     * @brief Checks whether a type refers to an array type or not.
+     * @return True if the underlying type is an array type, false otherwise.
+     */
+[[nodiscard]] bool is_array() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_array);
+}
+
+/**
+     * @brief Checks whether a type refers to an enum or not.
+     * @return True if the underlying type is an enum, false otherwise.
+     */
+[[nodiscard]] bool is_enum() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_enum);
+}
+
+/**
+     * @brief Checks whether a type refers to a class or not.
+     * @return True if the underlying type is a class, false otherwise.
+     */
+[[nodiscard]] bool is_class() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_class);
+}
+
+/**
+     * @brief Checks whether a type refers to a pointer or not.
+     * @return True if the underlying type is a pointer, false otherwise.
+     */
+[[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_pointer);
+}
+
+/**
+     * @brief Checks whether a type is a pointer-like type or not.
+     * @return True if the underlying type is a pointer-like one, false
+     * otherwise.
+     */
+[[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_pointer_like);
+}
+
+/**
+     * @brief Checks whether a type refers to a sequence container or not.
+     * @return True if the type is a sequence container, false otherwise.
+     */
+[[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_sequence_container);
+}
+
+/**
+     * @brief Checks whether a type refers to an associative container or not.
+     * @return True if the type is an associative container, false otherwise.
+     */
+[[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT {
+return !!(node->traits & internal::meta_traits::is_meta_associative_container);
+}
+
+/**
+     * @brief Checks whether a type refers to a recognized class template
+     * specialization or not.
+     * @return True if the type is a recognized class template specialization,
+     * false otherwise.
+     */
+[[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT {
+return (node->templ != nullptr);
+}
+
+/**
+     * @brief Returns the number of template arguments.
+     * @return The number of template arguments.
+     */
+[[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT {
+return node->templ ? node->templ->arity : size_type{};
+}
+
+/**
+     * @brief Returns a tag for the class template of the underlying type.
+     *
+     * @sa meta_class_template_tag
+     *
+     * @return The tag for the class template of the underlying type.
+     */
+[[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT {
+return node->templ ? node->templ->type : meta_type{};
+}
+
+/**
+     * @brief Returns the type of the i-th template argument of a type.
+     * @param index Index of the template argument of which to return the type.
+     * @return The type of the i-th template argument of a type.
+     */
+[[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT {
+return index < template_arity() ? node->templ->arg(index) : meta_type{};
+}
+
+/**
+     * @brief Returns a range to visit registered top-level base meta types.
+     * @return An iterable range to visit registered top-level base meta types.
+     */
+[[nodiscard]] meta_range<meta_type, internal::meta_base_node> base() const ENTT_NOEXCEPT {
+return node->base;
+}
+
+/**
+     * @brief Lookup function for registered base meta types.
+     * @param id Unique identifier.
+     * @return The registered base meta type for the given identifier, if any.
+     */
+[[nodiscard]] meta_type base(const id_type id) const {
+return internal::find_by<&node_type::base>(id, node);
+}
+
+/**
+     * @brief Returns a range to visit registered top-level meta data.
+     * @return An iterable range to visit registered top-level meta data.
+     */
+[[nodiscard]] meta_range<meta_data> data() const ENTT_NOEXCEPT {
+return node->data;
+}
+
+/**
+     * @brief Lookup function for registered meta data.
+     *
+     * Registered meta data of base classes will also be visited.
+     *
+     * @param id Unique identifier.
+     * @return The registered meta data for the given identifier, if any.
+     */
+[[nodiscard]] meta_data data(const id_type id) const {
+return internal::find_by<&node_type::data>(id, node);
+}
+
+/**
+     * @brief Returns a range to visit registered top-level functions.
+     * @return An iterable range to visit registered top-level functions.
+     */
+[[nodiscard]] meta_range<meta_func> func() const ENTT_NOEXCEPT {
+return node->func;
+}
+
+/**
+     * @brief Lookup function for registered meta functions.
+     *
+     * Registered meta functions of base classes will also be visited.<br/>
+     * In case of overloaded functions, the first one with the required
+     * identifier will be returned.
+     *
+     * @param id Unique identifier.
+     * @return The registered meta function for the given identifier, if any.
+     */
+[[nodiscard]] meta_func func(const id_type id) const {
+return internal::find_by<&node_type::func>(id, node);
+}
+
+/**
+     * @brief Creates an instance of the underlying type, if possible.
+     *
+     * Parameters are such that a cast or conversion to the required types is
+     * possible. Otherwise, an empty and thus invalid wrapper is returned.<br/>
+     * If suitable, the implicitly generated default constructor is used.
+     *
+     * @param args Parameters to use to construct the instance.
+     * @param sz Number of parameters to use to construct the instance.
+     * @return A wrapper containing the new instance, if any.
+     */
+[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
+const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; });
+return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{});
+}
+
+/**
+     * @copybrief construct
+     *
+     * @sa construct
+     *
+     * @tparam Args Types of arguments to use to construct the instance.
+     * @param args Parameters to use to construct the instance.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+[[nodiscard]] meta_any construct(Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return construct(arguments, sizeof...(Args));
+}
+
+/**
+     * @brief Invokes a function given an identifier, if possible.
+     *
+     * It must be possible to cast the instance to the parent type of the member
+     * function.
+     *
+     * @sa meta_func::invoke
+     *
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @param sz Number of parameters to use to invoke the function.
+     * @return A wrapper containing the returned value, if any.
+     */
+meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
+const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; });
+
+for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) {
+candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; });
+}
+
+return candidate ? candidate->invoke(std::move(instance), args) : meta_any{};
+}
+
+/**
+     * @copybrief invoke
+     *
+     * @sa invoke
+     *
+     * @param id Unique identifier.
+     * @tparam Args Types of arguments to use to invoke the function.
+     * @param instance An opaque instance of the underlying type.
+     * @param args Parameters to use to invoke the function.
+     * @return A wrapper containing the new instance, if any.
+     */
+template<typename... Args>
+meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
+meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
+return invoke(id, std::move(instance), arguments, sizeof...(Args));
+}
+
+/**
+     * @brief Sets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.<br/>
+     * The type of the value is such that a cast or conversion to the type of
+     * the variable is possible. Otherwise, invoking the setter does nothing.
+     *
+     * @tparam Type Type of value to assign.
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @param value Parameter to use to set the underlying variable.
+     * @return True in case of success, false otherwise.
+     */
+template<typename Type>
+bool set(const id_type id, meta_handle instance, Type &&value) const {
+const auto candidate = data(id);
+return candidate && candidate.set(std::move(instance), std::forward<Type>(value));
+}
+
+/**
+     * @brief Gets the value of a given variable.
+     *
+     * It must be possible to cast the instance to the parent type of the data
+     * member.
+     *
+     * @param id Unique identifier.
+     * @param instance An opaque instance of the underlying type.
+     * @return A wrapper containing the value of the underlying variable.
+     */
+[[nodiscard]] meta_any get(const id_type id, meta_handle instance) const {
+const auto candidate = data(id);
+return candidate ? candidate.get(std::move(instance)) : meta_any{};
+}
+
+/**
+     * @brief Returns a range to visit registered top-level meta properties.
+     * @return An iterable range to visit registered top-level meta properties.
+     */
+[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT {
+return node->prop;
+}
+
+/**
+     * @brief Lookup function for meta properties.
+     *
+     * Properties of base classes are also visited.
+     *
+     * @param key The key to use to search for a property.
+     * @return The registered meta property for the given key, if any.
+     */
+[[nodiscard]] meta_prop prop(meta_any key) const {
+return internal::find_by<&internal::meta_type_node::prop>(key, node);
+}
+
+/**
+     * @brief Returns true if an object is valid, false otherwise.
+     * @return True if the object is valid, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return !(node == nullptr);
+}
+
+/**
+     * @brief Checks if two objects refer to the same type.
+     * @param other The object with which to compare.
+     * @return True if the objects refer to the same type, false otherwise.
+     */
+[[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT {
+return (!node && !other.node) || (node && other.node && *node->info == *other.node->info);
+}
+
+private:
+const node_type *node;
+};
+
+/**
+ * @brief Checks if two objects refer to the same type.
+ * @param lhs An object, either valid or not.
+ * @param rhs An object, either valid or not.
+ * @return False if the objects refer to the same node, true otherwise.
+ */
+[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT {
+return node;
+}
+
+template<typename... Args>
+meta_any meta_any::invoke(const id_type id, Args &&...args) const {
+return type().invoke(id, *this, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+meta_any meta_any::invoke(const id_type id, Args &&...args) {
+return type().invoke(id, *this, std::forward<Args>(args)...);
+}
+
+template<typename Type>
+bool meta_any::set(const id_type id, Type &&value) {
+return type().set(id, *this, std::forward<Type>(value));
+}
+
+[[nodiscard]] inline meta_any meta_any::get(const id_type id) const {
+return type().get(id, *this);
+}
+
+[[nodiscard]] inline meta_any meta_any::get(const id_type id) {
+return type().get(id, *this);
+}
+
+[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const {
+if(const auto &info = type.info(); node && *node->info == info) {
+return as_ref();
+} else if(node) {
+for(auto *it = node->conv; it; it = it->next) {
+if(*it->type->info == info) {
+return it->conv(*this);
+}
+}
+
+if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) {
+// exploits the fact that arithmetic types and enums are also default constructible
+auto other = type.construct();
+ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found");
+const auto value = node->conversion_helper(nullptr, storage.data());
+other.node->conversion_helper(other.storage.data(), &value);
+return other;
+}
+
+for(auto *it = node->base; it; it = it->next) {
+const auto as_const = it->cast(as_ref());
+
+if(auto other = as_const.allow_cast(type); other) {
+return other;
+}
+}
+}
+
+return {};
+}
+
+inline bool meta_any::assign(const meta_any &other) {
+auto value = other.allow_cast(node);
+return value && storage.assign(std::move(value.storage));
+}
+
+inline bool meta_any::assign(meta_any &&other) {
+if(*node->info == *other.node->info) {
+return storage.assign(std::move(other.storage));
+}
+
+return assign(std::as_const(other));
+}
+
+[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT {
+return node->type;
+}
+
+[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT {
+return node->ret;
+}
+
+[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT {
+return index < arity() ? node->arg(index) : meta_type{};
+}
+
+[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT {
+return index < arity() ? node->arg(index) : meta_type{};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+class meta_sequence_container::meta_iterator final {
+friend class meta_sequence_container;
+
+using deref_fn_type = meta_any(const any &, const std::ptrdiff_t);
+
+template<typename It>
+static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) {
+return meta_any{std::in_place_type<typename std::iterator_traits<It>::reference>, any_cast<const It &>(value)[pos]};
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = meta_any;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+meta_iterator() ENTT_NOEXCEPT
+: deref{},
+offset{},
+handle{} {}
+
+template<typename It>
+explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT
+: deref{&deref_fn<It>},
+offset{init},
+handle{std::move(iter)} {}
+
+meta_iterator &operator++() ENTT_NOEXCEPT {
+return ++offset, *this;
+}
+
+meta_iterator operator++(int value) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+offset += ++value;
+return orig;
+}
+
+meta_iterator &operator--() ENTT_NOEXCEPT {
+return --offset, *this;
+}
+
+meta_iterator operator--(int value) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+offset -= ++value;
+return orig;
+}
+
+[[nodiscard]] reference operator*() const {
+return deref(handle, offset);
+}
+
+[[nodiscard]] pointer operator->() const {
+return operator*();
+}
+
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(handle);
+}
+
+[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
+return offset == other.offset;
+}
+
+[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+deref_fn_type *deref;
+difference_type offset;
+any handle;
+};
+
+class meta_associative_container::meta_iterator final {
+enum class operation : std::uint8_t {
+incr,
+deref
+};
+
+using vtable_type = void(const operation, const any &, std::pair<meta_any, meta_any> *);
+
+template<bool KeyOnly, typename It>
+static void basic_vtable(const operation op, const any &value, std::pair<meta_any, meta_any> *other) {
+switch(op) {
+case operation::incr:
+++any_cast<It &>(const_cast<any &>(value));
+break;
+case operation::deref:
+const auto &it = any_cast<const It &>(value);
+if constexpr(KeyOnly) {
+other->first.emplace<decltype(*it)>(*it);
+} else {
+other->first.emplace<decltype((it->first))>(it->first);
+other->second.emplace<decltype((it->second))>(it->second);
+}
+break;
+}
+}
+
+public:
+using difference_type = std::ptrdiff_t;
+using value_type = std::pair<meta_any, meta_any>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+
+meta_iterator() ENTT_NOEXCEPT
+: vtable{},
+handle{} {}
+
+template<bool KeyOnly, typename It>
+meta_iterator(std::integral_constant<bool, KeyOnly>, It iter) ENTT_NOEXCEPT
+: vtable{&basic_vtable<KeyOnly, It>},
+handle{std::move(iter)} {}
+
+meta_iterator &operator++() ENTT_NOEXCEPT {
+vtable(operation::incr, handle, nullptr);
+return *this;
+}
+
+meta_iterator operator++(int) ENTT_NOEXCEPT {
+meta_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const {
+reference other;
+vtable(operation::deref, handle, &other);
+return other;
+}
+
+[[nodiscard]] pointer operator->() const {
+return operator*();
+}
+
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(handle);
+}
+
+[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
+return handle == other.handle;
+}
+
+[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+vtable_type *vtable;
+any handle;
+};
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Returns the meta value type of a container.
+ * @return The meta value type of the container.
+ */
+[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT {
+return value_type_node;
+}
+
+/**
+ * @brief Returns the size of a container.
+ * @return The size of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT {
+return size_fn(storage);
+}
+
+/**
+ * @brief Resizes a container to contain a given number of elements.
+ * @param sz The new size of the container.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_sequence_container::resize(const size_type sz) {
+return resize_fn(storage, sz);
+}
+
+/**
+ * @brief Clears the content of a container.
+ * @return True in case of success, false otherwise.
+ */
+inline bool meta_sequence_container::clear() {
+return resize_fn(storage, 0u);
+}
+
+/**
+ * @brief Returns an iterator to the first element of a container.
+ * @return An iterator to the first element of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() {
+return iter_fn(storage, false);
+}
+
+/**
+ * @brief Returns an iterator that is past the last element of a container.
+ * @return An iterator that is past the last element of the container.
+ */
+[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() {
+return iter_fn(storage, true);
+}
+
+/**
+ * @brief Inserts an element at a specified location of a container.
+ * @param it Iterator before which the element will be inserted.
+ * @param value Element value to insert.
+ * @return A possibly invalid iterator to the inserted element.
+ */
+inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) {
+return insert_fn(storage, it.offset, value);
+}
+
+/**
+ * @brief Removes a given element from a container.
+ * @param it Iterator to the element to remove.
+ * @return A possibly invalid iterator following the last removed element.
+ */
+inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
+return erase_fn(storage, it.offset);
+}
+
+/**
+ * @brief Returns a reference to the element at a given location of a container
+ * (no bounds checking is performed).
+ * @param pos The position of the element to return.
+ * @return A reference to the requested element properly wrapped.
+ */
+[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) {
+auto it = begin();
+it.operator++(static_cast<int>(pos) - 1);
+return *it;
+}
+
+/**
+ * @brief Returns false if a proxy is invalid, true otherwise.
+ * @return False if the proxy is invalid, true otherwise.
+ */
+[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(storage);
+}
+
+/**
+ * @brief Returns true if a container is also key-only, false otherwise.
+ * @return True if the associative container is also key-only, false otherwise.
+ */
+[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT {
+return key_only_container;
+}
+
+/**
+ * @brief Returns the meta key type of a container.
+ * @return The meta key type of the a container.
+ */
+[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT {
+return key_type_node;
+}
+
+/**
+ * @brief Returns the meta mapped type of a container.
+ * @return The meta mapped type of the a container.
+ */
+[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT {
+return mapped_type_node;
+}
+
+/*! @copydoc meta_sequence_container::value_type */
+[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT {
+return value_type_node;
+}
+
+/*! @copydoc meta_sequence_container::size */
+[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT {
+return size_fn(storage);
+}
+
+/*! @copydoc meta_sequence_container::clear */
+inline bool meta_associative_container::clear() {
+return clear_fn(storage);
+}
+
+/*! @copydoc meta_sequence_container::begin */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() {
+return iter_fn(storage, false);
+}
+
+/*! @copydoc meta_sequence_container::end */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() {
+return iter_fn(storage, true);
+}
+
+/**
+ * @brief Inserts an element (a key/value pair) into a container.
+ * @param key The key of the element to insert.
+ * @param value The value of the element to insert.
+ * @return A bool denoting whether the insertion took place.
+ */
+inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
+return insert_fn(storage, key, value);
+}
+
+/**
+ * @brief Removes the specified element from a container.
+ * @param key The key of the element to remove.
+ * @return A bool denoting whether the removal took place.
+ */
+inline bool meta_associative_container::erase(meta_any key) {
+return erase_fn(storage, key);
+}
+
+/**
+ * @brief Returns an iterator to the element with a given key, if any.
+ * @param key The key of the element to search.
+ * @return An iterator to the element with the given key, if any.
+ */
+[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
+return find_fn(storage, key);
+}
+
+/**
+ * @brief Returns false if a proxy is invalid, true otherwise.
+ * @return False if the proxy is invalid, true otherwise.
+ */
+[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(storage);
+}
+
+} // namespace entt
+
+#endif
+
+// #include "meta/node.hpp"
+#ifndef ENTT_META_NODE_HPP
+#define ENTT_META_NODE_HPP
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "../core/enum.hpp"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/type_traits.hpp"
+
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+class meta_any;
+class meta_type;
+struct meta_handle;
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+enum class meta_traits : std::uint32_t {
+is_none = 0x0000,
+is_const = 0x0001,
+is_static = 0x0002,
+is_arithmetic = 0x0004,
+is_array = 0x0008,
+is_enum = 0x0010,
+is_class = 0x0020,
+is_pointer = 0x0040,
+is_meta_pointer_like = 0x0080,
+is_meta_sequence_container = 0x0100,
+is_meta_associative_container = 0x0200,
+_entt_enum_as_bitmask
+};
+
+struct meta_type_node;
+
+struct meta_prop_node {
+meta_prop_node *next;
+const meta_any &id;
+meta_any &value;
+};
+
+struct meta_base_node {
+meta_base_node *next;
+meta_type_node *const type;
+meta_any (*const cast)(meta_any) ENTT_NOEXCEPT;
+};
+
+struct meta_conv_node {
+meta_conv_node *next;
+meta_type_node *const type;
+meta_any (*const conv)(const meta_any &);
+};
+
+struct meta_ctor_node {
+using size_type = std::size_t;
+meta_ctor_node *next;
+const size_type arity;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+meta_any (*const invoke)(meta_any *const);
+};
+
+struct meta_data_node {
+using size_type = std::size_t;
+id_type id;
+const meta_traits traits;
+meta_data_node *next;
+meta_prop_node *prop;
+const size_type arity;
+meta_type_node *const type;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+bool (*const set)(meta_handle, meta_any);
+meta_any (*const get)(meta_handle);
+};
+
+struct meta_func_node {
+using size_type = std::size_t;
+id_type id;
+const meta_traits traits;
+meta_func_node *next;
+meta_prop_node *prop;
+const size_type arity;
+meta_type_node *const ret;
+meta_type (*const arg)(const size_type) ENTT_NOEXCEPT;
+meta_any (*const invoke)(meta_handle, meta_any *const);
+};
+
+struct meta_template_node {
+using size_type = std::size_t;
+const size_type arity;
+meta_type_node *const type;
+meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT;
+};
+
+struct meta_type_node {
+using size_type = std::size_t;
+const type_info *info;
+id_type id;
+const meta_traits traits;
+meta_type_node *next;
+meta_prop_node *prop;
+const size_type size_of;
+meta_any (*const default_constructor)();
+double (*const conversion_helper)(void *, const void *);
+const meta_template_node *const templ;
+meta_ctor_node *ctor{nullptr};
+meta_base_node *base{nullptr};
+meta_conv_node *conv{nullptr};
+meta_data_node *data{nullptr};
+meta_func_node *func{nullptr};
+void (*dtor)(void *){nullptr};
+};
+
+template<typename... Args>
+meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
+
+template<typename Type>
+class ENTT_API meta_node {
+static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
+
+[[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT {
+if constexpr(std::is_default_constructible_v<Type>) {
+return +[]() { return meta_any{std::in_place_type<Type>}; };
+} else {
+return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr);
+}
+}
+
+[[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT {
+if constexpr(std::is_arithmetic_v<Type>) {
+return +[](void *bin, const void *value) {
+return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
+};
+} else if constexpr(std::is_enum_v<Type>) {
+return +[](void *bin, const void *value) {
+return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
+};
+} else {
+return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr);
+}
+}
+
+[[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT {
+if constexpr(is_complete_v<meta_template_traits<Type>>) {
+static meta_template_node node{
+meta_template_traits<Type>::args_type::size,
+meta_node<typename meta_template_traits<Type>::class_type>::resolve(),
+[](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); }
+// tricks clang-format
+};
+
+return &node;
+} else {
+return nullptr;
+}
+}
+
+public:
+[[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT {
+static meta_type_node node{
+&type_id<Type>(),
+{},
+internal::meta_traits::is_none
+| (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none)
+| (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none)
+| (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none)
+| (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none)
+| (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none)
+| (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none)
+| (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none)
+| (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none),
+nullptr,
+nullptr,
+size_of_v<Type>,
+meta_default_constructor(),
+meta_conversion_helper(),
+meta_template_info()
+// tricks clang-format
+};
+
+return &node;
+}
+};
+
+template<typename... Args>
+[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
+meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...};
+return args[index + 1u];
+}
+
+template<auto Member, typename Type>
+[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT {
+for(auto *curr = node->*Member; curr; curr = curr->next) {
+if constexpr(std::is_same_v<Type, type_info>) {
+if(*curr->type->info == info_or_id) {
+return curr;
+}
+} else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) {
+if(curr->type->id == info_or_id) {
+return curr;
+}
+} else {
+if(curr->id == info_or_id) {
+return curr;
+}
+}
+}
+
+for(auto *curr = node->base; curr; curr = curr->next) {
+if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) {
+return ret;
+}
+}
+
+return nullptr;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+} // namespace entt
+
+#endif
+
+// #include "meta/pointer.hpp"
+#ifndef ENTT_META_POINTER_HPP
+#define ENTT_META_POINTER_HPP
+
+#include <memory>
+#include <type_traits>
+// #include "type_traits.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Makes plain pointers pointer-like types for the meta system.
+ * @tparam Type Element type.
+ */
+template<typename Type>
+struct is_meta_pointer_like<Type *>
+: std::true_type {};
+
+/**
+ * @brief Partial specialization used to reject pointers to arrays.
+ * @tparam Type Type of elements of the array.
+ * @tparam N Number of elements of the array.
+ */
+template<typename Type, std::size_t N>
+struct is_meta_pointer_like<Type (*)[N]>
+: std::false_type {};
+
+/**
+ * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta
+ * system.
+ * @tparam Type Element type.
+ */
+template<typename Type>
+struct is_meta_pointer_like<std::shared_ptr<Type>>
+: std::true_type {};
+
+/**
+ * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta
+ * system.
+ * @tparam Type Element type.
+ * @tparam Args Other arguments.
+ */
+template<typename Type, typename... Args>
+struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
+: std::true_type {};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/policy.hpp"
+#ifndef ENTT_META_POLICY_HPP
+#define ENTT_META_POLICY_HPP
+
+#include <type_traits>
+
+namespace entt {
+
+/*! @brief Empty class type used to request the _as ref_ policy. */
+struct as_ref_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename Type>
+static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as cref_ policy. */
+struct as_cref_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename Type>
+static constexpr bool value = std::is_reference_v<Type>;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as-is_ policy. */
+struct as_is_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename>
+static constexpr bool value = true;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+/*! @brief Empty class type used to request the _as void_ policy. */
+struct as_void_t {
+/**
+     * @cond TURN_OFF_DOXYGEN
+     * Internal details not to be documented.
+     */
+template<typename>
+static constexpr bool value = true;
+/**
+     * Internal details not to be documented.
+     * @endcond
+     */
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/range.hpp"
+#ifndef ENTT_META_RANGE_HPP
+#define ENTT_META_RANGE_HPP
+
+#include <cstddef>
+#include <iterator>
+// #include "../core/iterator.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename Node>
+struct meta_range_iterator final {
+using difference_type = std::ptrdiff_t;
+using value_type = Type;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using iterator_category = std::input_iterator_tag;
+using node_type = Node;
+
+meta_range_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+meta_range_iterator(node_type *head) ENTT_NOEXCEPT
+: it{head} {}
+
+meta_range_iterator &operator++() ENTT_NOEXCEPT {
+return (it = it->next), *this;
+}
+
+meta_range_iterator operator++(int) ENTT_NOEXCEPT {
+meta_range_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return it;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT {
+return it == other.it;
+}
+
+[[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT {
+return !(*this == other);
+}
+
+private:
+node_type *it;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Iterable range to use to iterate all types of meta objects.
+ * @tparam Type Type of meta objects returned.
+ * @tparam Node Type of meta nodes iterated.
+ */
+template<typename Type, typename Node = typename Type::node_type>
+struct meta_range final {
+/*! @brief Node type. */
+using node_type = Node;
+/*! @brief Input iterator type. */
+using iterator = internal::meta_range_iterator<Type, Node>;
+/*! @brief Constant input iterator type. */
+using const_iterator = iterator;
+
+/*! @brief Default constructor. */
+meta_range() ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Constructs a meta range from a given node.
+     * @param head The underlying node with which to construct the range.
+     */
+meta_range(node_type *head) ENTT_NOEXCEPT
+: node{head} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first meta object of the range.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return iterator{node};
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last meta object of the
+     * range.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return iterator{};
+}
+
+/*! @copydoc cend */
+[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+private:
+node_type *node{nullptr};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/resolve.hpp"
+#ifndef ENTT_META_RESOLVE_HPP
+#define ENTT_META_RESOLVE_HPP
+
+#include <algorithm>
+// #include "../core/type_info.hpp"
+
+// #include "ctx.hpp"
+
+// #include "meta.hpp"
+
+// #include "node.hpp"
+
+// #include "range.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Returns the meta type associated with a given type.
+ * @tparam Type Type to use to search for a meta type.
+ * @return The meta type associated with the given type, if any.
+ */
+template<typename Type>
+[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
+return internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve();
+}
+
+/**
+ * @brief Returns a range to use to visit all meta types.
+ * @return An iterable range to use to visit all meta types.
+ */
+[[nodiscard]] inline meta_range<meta_type> resolve() ENTT_NOEXCEPT {
+return *internal::meta_context::global();
+}
+
+/**
+ * @brief Returns the meta type associated with a given identifier, if any.
+ * @param id Unique identifier.
+ * @return The meta type associated with the given identifier, if any.
+ */
+[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT {
+for(auto &&curr: resolve()) {
+if(curr.id() == id) {
+return curr;
+}
+}
+
+return {};
+}
+
+/**
+ * @brief Returns the meta type associated with a given type info object.
+ * @param info The type info object of the requested type.
+ * @return The meta type associated with the given type info object, if any.
+ */
+[[nodiscard]] inline meta_type resolve(const type_info &info) ENTT_NOEXCEPT {
+for(auto &&curr: resolve()) {
+if(curr.info() == info) {
+return curr;
+}
+}
+
+return {};
+}
+
+} // namespace entt
+
+#endif
+
+// #include "meta/template.hpp"
+#ifndef ENTT_META_TEMPLATE_HPP
+#define ENTT_META_TEMPLATE_HPP
+
+// #include "../core/type_traits.hpp"
+
+
+namespace entt {
+
+/*! @brief Utility class to disambiguate class templates. */
+template<template<typename...> class>
+struct meta_class_template_tag {};
+
+/**
+ * @brief General purpose traits class for generating meta template information.
+ * @tparam Clazz Type of class template.
+ * @tparam Args Types of template arguments.
+ */
+template<template<typename...> class Clazz, typename... Args>
+struct meta_template_traits<Clazz<Args...>> {
+/*! @brief Wrapped class template. */
+using class_type = meta_class_template_tag<Clazz>;
+/*! @brief List of template arguments. */
+using args_type = type_list<Args...>;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "meta/type_traits.hpp"
+#ifndef ENTT_META_TYPE_TRAITS_HPP
+#define ENTT_META_TYPE_TRAITS_HPP
+
+#include <type_traits>
+#include <utility>
+
+namespace entt {
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * template information.
+ */
+template<typename>
+struct meta_template_traits;
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * sequence containers.
+ */
+template<typename>
+struct meta_sequence_container_traits;
+
+/**
+ * @brief Traits class template to be specialized to enable support for meta
+ * associative containers.
+ */
+template<typename>
+struct meta_associative_container_traits;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is a
+ * pointer-like type from the point of view of the meta system, false otherwise.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename>
+struct is_meta_pointer_like: std::false_type {};
+
+/**
+ * @brief Partial specialization to ensure that const pointer-like types are
+ * also accepted.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename Type>
+struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type Potentially pointer-like type.
+ */
+template<typename Type>
+inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value;
+
+} // namespace entt
+
+#endif
+
+// #include "meta/utility.hpp"
+#ifndef ENTT_META_UTILITY_HPP
+#define ENTT_META_UTILITY_HPP
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "meta.hpp"
+
+// #include "node.hpp"
+
+// #include "policy.hpp"
+
+
+namespace entt {
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename, typename>
+struct meta_function_descriptor;
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam Class Actual owner of the member function.
+ * @tparam Args Function arguments.
+ */
+template<typename Type, typename Ret, typename Class, typename... Args>
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = true;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam Class Actual owner of the member function.
+ * @tparam Args Function arguments.
+ */
+template<typename Type, typename Ret, typename Class, typename... Args>
+struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta data is associated.
+ * @tparam Class Actual owner of the data member.
+ * @tparam Ret Data member type.
+ */
+template<typename Type, typename Ret, typename Class>
+struct meta_function_descriptor<Type, Ret Class::*> {
+/*! @brief Meta data return type. */
+using return_type = Ret &;
+/*! @brief Meta data arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>;
+
+/*! @brief True if the meta data is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta data is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<Class, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ * @tparam MaybeType First function argument.
+ * @tparam Args Other function arguments.
+ */
+template<typename Type, typename Ret, typename MaybeType, typename... Args>
+struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>;
+};
+
+/**
+ * @brief Meta function descriptor.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Ret Function return type.
+ */
+template<typename Type, typename Ret>
+struct meta_function_descriptor<Type, Ret (*)()> {
+/*! @brief Meta function return type. */
+using return_type = Ret;
+/*! @brief Meta function arguments. */
+using args_type = type_list<>;
+
+/*! @brief True if the meta function is const, false otherwise. */
+static constexpr auto is_const = false;
+/*! @brief True if the meta function is static, false otherwise. */
+static constexpr auto is_static = true;
+};
+
+/**
+ * @brief Meta function helper.
+ *
+ * Converts a function type to be associated with a reflected type into its meta
+ * function descriptor.
+ *
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Candidate The actual function to associate with the reflected type.
+ */
+template<typename Type, typename Candidate>
+class meta_function_helper {
+template<typename Ret, typename... Args, typename Class>
+static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const);
+
+template<typename Ret, typename... Args, typename Class>
+static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...));
+
+template<typename Ret, typename Class>
+static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*);
+
+template<typename Ret, typename... Args>
+static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...));
+
+template<typename Class>
+static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class);
+
+public:
+/*! @brief The meta function descriptor of the given function. */
+using type = decltype(get_rid_of_noexcept(std::declval<Candidate>()));
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type Reflected type to which the meta function is associated.
+ * @tparam Candidate The actual function to associate with the reflected type.
+ */
+template<typename Type, typename Candidate>
+using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type;
+
+/**
+ * @brief Wraps a value depending on the given policy.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Type Type of value to wrap.
+ * @param value Value to wrap.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Policy = as_is_t, typename Type>
+meta_any meta_dispatch([[maybe_unused]] Type &&value) {
+if constexpr(std::is_same_v<Policy, as_void_t>) {
+return meta_any{std::in_place_type<void>};
+} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
+return meta_any{std::in_place_type<Type>, std::forward<Type>(value)};
+} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
+static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
+return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
+} else {
+static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
+return meta_any{std::forward<Type>(value)};
+}
+}
+
+/**
+ * @brief Returns the meta type of the i-th element of a list of arguments.
+ * @tparam Type Type list of the actual types of arguments.
+ * @return The meta type of the i-th element of the list of arguments.
+ */
+template<typename Type>
+[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT {
+return internal::meta_arg_node(Type{}, index);
+}
+
+/**
+ * @brief Sets the value of a given variable.
+ * @tparam Type Reflected type to which the variable is associated.
+ * @tparam Data The actual variable to set.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param value Parameter to use to set the variable.
+ * @return True in case of success, false otherwise.
+ */
+template<typename Type, auto Data>
+[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
+if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
+if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+using descriptor = meta_function_helper_t<Type, decltype(Data)>;
+using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>;
+
+if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
+std::invoke(Data, *clazz, value.cast<data_type>());
+return true;
+}
+} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
+using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>;
+
+if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
+if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) {
+std::invoke(Data, *clazz) = value.cast<data_type>();
+return true;
+}
+}
+} else {
+using data_type = std::remove_reference_t<decltype(*Data)>;
+
+if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
+if(value.allow_cast<data_type>()) {
+*Data = value.cast<data_type>();
+return true;
+}
+}
+}
+}
+
+return false;
+}
+
+/**
+ * @brief Gets the value of a given variable.
+ * @tparam Type Reflected type to which the variable is associated.
+ * @tparam Data The actual variable to get.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param instance An opaque instance of the underlying type, if required.
+ * @return A meta any containing the value of the underlying variable.
+ */
+template<typename Type, auto Data, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
+if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
+if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
+if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
+if(auto *clazz = instance->try_cast<Type>(); clazz) {
+return meta_dispatch<Policy>(std::invoke(Data, *clazz));
+}
+}
+
+if constexpr(std::is_invocable_v<decltype(Data), const Type &>) {
+if(auto *fallback = instance->try_cast<const Type>(); fallback) {
+return meta_dispatch<Policy>(std::invoke(Data, *fallback));
+}
+}
+}
+
+return meta_any{};
+} else if constexpr(std::is_pointer_v<decltype(Data)>) {
+if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
+return meta_any{};
+} else {
+return meta_dispatch<Policy>(*Data);
+}
+} else {
+return meta_dispatch<Policy>(Data);
+}
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename Policy, typename Candidate, typename... Args>
+[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) {
+if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) {
+std::invoke(candidate, args...);
+return meta_any{std::in_place_type<void>};
+} else {
+return meta_dispatch<Policy>(std::invoke(candidate, args...));
+}
+}
+
+template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
+[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) {
+using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
+
+if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
+if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
+if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+} else {
+if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
+return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
+}
+}
+
+return meta_any{};
+}
+
+template<typename Type, typename... Args, std::size_t... Index>
+[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) {
+if(((args + Index)->allow_cast<Args>() && ...)) {
+return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...};
+}
+
+return meta_any{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Tries to _invoke_ an object given a list of erased parameters.
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param candidate The actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) {
+return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+}
+
+/**
+ * @brief Tries to invoke a function given a list of erased parameters.
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param instance An opaque instance of the underlying type, if required.
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) {
+return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Actual type of the instance to construct.
+ * @tparam Args Types of arguments expected.
+ * @param args Parameters to use to construct the instance.
+ * @return A meta any containing the new instance, if any.
+ */
+template<typename Type, typename... Args>
+[[nodiscard]] meta_any meta_construct(meta_any *const args) {
+return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{});
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the object to _invoke_ is associated.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @tparam Candidate The type of the actual object to _invoke_.
+ * @param args Parameters to use to _invoke_ the object.
+ * @param candidate The actual object to _invoke_.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, typename Policy = as_is_t, typename Candidate>
+[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) {
+if constexpr(meta_function_helper_t<Type, Candidate>::is_static) {
+return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+} else {
+return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
+}
+}
+
+/**
+ * @brief Tries to construct an instance given a list of erased parameters.
+ * @tparam Type Reflected type to which the function is associated.
+ * @tparam Candidate The actual function to invoke.
+ * @tparam Policy Optional policy (no policy set by default).
+ * @param args Parameters to use to invoke the function.
+ * @return A meta any containing the returned value, if any.
+ */
+template<typename Type, auto Candidate, typename Policy = as_is_t>
+[[nodiscard]] meta_any meta_construct(meta_any *const args) {
+return meta_construct<Type, Policy>(Candidate, args);
+}
+
+} // namespace entt
+
+#endif
+
+// #include "platform/android-ndk-r17.hpp"
+#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
+#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+#ifdef __ANDROID__
+#    include <android/ndk-version.h>
+#    if __NDK_MAJOR__ == 17
+
+#        include <functional>
+#        include <type_traits>
+#        include <utility>
+
+namespace std {
+
+namespace internal {
+
+template<typename Func, typename... Args>
+constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{});
+
+template<typename, typename...>
+constexpr std::false_type is_invocable(...);
+
+template<typename Ret, typename Func, typename... Args>
+constexpr auto is_invocable_r(int)
+-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>;
+
+
+template<typename, typename, typename...>
+constexpr std::false_type is_invocable_r(...);
+
+} // namespace internal
+
+template<typename Func, typename... Args>
+struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {};
+
+template<typename Func, typename... Argsv>
+inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value;
+
+template<typename Ret, typename Func, typename... Args>
+struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {};
+
+template<typename Ret, typename Func, typename... Args>
+inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value;
+
+template<typename Func, typename... Args>
+struct invoke_result {
+using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
+};
+
+template<typename Func, typename... Args>
+using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
+
+} // namespace std
+
+#    endif
+#endif
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "poly/poly.hpp"
+#ifndef ENTT_POLY_POLY_HPP
+#define ENTT_POLY_POLY_HPP
+
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/any.hpp"
+#ifndef ENTT_CORE_ANY_HPP
+#define ENTT_CORE_ANY_HPP
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief A SBO friendly, type-safe container for single values of any type.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ */
+template<std::size_t Len, std::size_t Align>
+class basic_any {
+enum class operation : std::uint8_t {
+copy,
+move,
+transfer,
+assign,
+destroy,
+compare,
+get
+};
+
+enum class policy : std::uint8_t {
+owner,
+ref,
+cref
+};
+
+using storage_type = std::aligned_storage_t<Len + !Len, Align>;
+using vtable_type = const void *(const operation, const basic_any &, const void *);
+
+template<typename Type>
+static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+
+template<typename Type>
+static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
+static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
+const Type *element = nullptr;
+
+if constexpr(in_situ<Type>) {
+element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
+} else {
+element = static_cast<const Type *>(value.instance);
+}
+
+switch(op) {
+case operation::copy:
+if constexpr(std::is_copy_constructible_v<Type>) {
+static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
+}
+break;
+case operation::move:
+if constexpr(in_situ<Type>) {
+if(value.owner()) {
+return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
+}
+}
+
+return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
+case operation::transfer:
+if constexpr(std::is_move_assignable_v<Type>) {
+*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
+return other;
+}
+[[fallthrough]];
+case operation::assign:
+if constexpr(std::is_copy_assignable_v<Type>) {
+*const_cast<Type *>(element) = *static_cast<const Type *>(other);
+return other;
+}
+break;
+case operation::destroy:
+if constexpr(in_situ<Type>) {
+element->~Type();
+} else if constexpr(std::is_array_v<Type>) {
+delete[] element;
+} else {
+delete element;
+}
+break;
+case operation::compare:
+if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
+return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
+} else {
+return (element == other) ? other : nullptr;
+}
+case operation::get:
+return element;
+}
+
+return nullptr;
+}
+
+template<typename Type, typename... Args>
+void initialize([[maybe_unused]] Args &&...args) {
+if constexpr(!std::is_void_v<Type>) {
+info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
+
+if constexpr(std::is_lvalue_reference_v<Type>) {
+static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
+mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
+instance = (std::addressof(args), ...);
+} else if constexpr(in_situ<Type>) {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+new(&storage) Type{std::forward<Args>(args)...};
+} else {
+new(&storage) Type(std::forward<Args>(args)...);
+}
+} else {
+if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
+instance = new Type{std::forward<Args>(args)...};
+} else {
+instance = new Type(std::forward<Args>(args)...);
+}
+}
+}
+}
+
+basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
+: instance{other.data()},
+info{other.info},
+vtable{other.vtable},
+mode{pol} {}
+
+public:
+/*! @brief Size of the internal storage. */
+static constexpr auto length = Len;
+/*! @brief Alignment requirement. */
+static constexpr auto alignment = Align;
+
+/*! @brief Default constructor. */
+constexpr basic_any() ENTT_NOEXCEPT
+: instance{},
+info{&type_id<void>()},
+vtable{},
+mode{policy::owner} {}
+
+/**
+     * @brief Constructs a wrapper by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
+: basic_any{} {
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Constructs a wrapper from a given value.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
+basic_any(Type &&value)
+: basic_any{} {
+initialize<std::decay_t<Type>>(std::forward<Type>(value));
+}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+basic_any(const basic_any &other)
+: basic_any{} {
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_any(basic_any &&other) ENTT_NOEXCEPT
+: instance{},
+info{other.info},
+vtable{other.vtable},
+mode{other.mode} {
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+}
+}
+
+/*! @brief Frees the internal storage, whatever it means. */
+~basic_any() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This any object.
+     */
+basic_any &operator=(const basic_any &other) {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::copy, other, this);
+}
+
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This any object.
+     */
+basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
+reset();
+
+if(other.vtable) {
+other.vtable(operation::move, other, this);
+info = other.info;
+vtable = other.vtable;
+mode = other.mode;
+}
+
+return *this;
+}
+
+/**
+     * @brief Value assignment operator.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @param value An instance of an object to use to initialize the wrapper.
+     * @return This any object.
+     */
+template<typename Type>
+std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
+operator=(Type &&value) {
+emplace<std::decay_t<Type>>(std::forward<Type>(value));
+return *this;
+}
+
+/**
+     * @brief Returns the object type if any, `type_id<void>()` otherwise.
+     * @return The object type if any, `type_id<void>()` otherwise.
+     */
+[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+return *info;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @param req Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
+return *info == req ? data() : nullptr;
+}
+
+/**
+     * @brief Replaces the contained object by creating a new instance directly.
+     * @tparam Type Type of object to use to initialize the wrapper.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+reset();
+initialize<Type>(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+bool assign(const any &other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+return (vtable(operation::assign, *this, other.data()) != nullptr);
+}
+
+return false;
+}
+
+/*! @copydoc assign */
+bool assign(any &&other) {
+if(vtable && mode != policy::cref && *info == *other.info) {
+if(auto *val = other.data(); val) {
+return (vtable(operation::transfer, *this, val) != nullptr);
+} else {
+return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
+}
+}
+
+return false;
+}
+
+/*! @brief Destroys contained object */
+void reset() {
+if(vtable && owner()) {
+vtable(operation::destroy, *this, nullptr);
+}
+
+info = &type_id<void>();
+vtable = nullptr;
+mode = policy::owner;
+}
+
+/**
+     * @brief Returns false if a wrapper is empty, true otherwise.
+     * @return False if the wrapper is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return vtable != nullptr;
+}
+
+/**
+     * @brief Checks if two wrappers differ in their content.
+     * @param other Wrapper with which to compare.
+     * @return False if the two objects differ in their content, true otherwise.
+     */
+bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
+if(vtable && *info == *other.info) {
+return (vtable(operation::compare, *this, other.data()) != nullptr);
+}
+
+return (!vtable && !other.vtable);
+}
+
+/**
+     * @brief Aliasing constructor.
+     * @return A wrapper that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
+return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
+}
+
+/*! @copydoc as_ref */
+[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
+return basic_any{*this, policy::cref};
+}
+
+/**
+     * @brief Returns true if a wrapper owns its object, false otherwise.
+     * @return True if the wrapper owns its object, false otherwise.
+     */
+[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
+return (mode == policy::owner);
+}
+
+private:
+union {
+const void *instance;
+storage_type storage;
+};
+const type_info *info;
+vtable_type *vtable;
+policy mode;
+};
+
+/**
+ * @brief Checks if two wrappers differ in their content.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param lhs A wrapper, either empty or not.
+ * @param rhs A wrapper, either empty or not.
+ * @return True if the two wrappers differ in their content, false otherwise.
+ */
+template<std::size_t Len, std::size_t Align>
+[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Performs type-safe access to the contained object.
+ * @tparam Type Type to which conversion is required.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ * @param data Target any object.
+ * @return The element converted to the requested type.
+ */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
+// forces const on non-reference types to make them work also with wrappers for const references
+auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(*instance);
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
+if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
+return static_cast<Type>(std::move(*instance));
+} else {
+return any_cast<Type>(data);
+}
+} else {
+auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
+ENTT_ASSERT(instance, "Invalid instance");
+return static_cast<Type>(std::move(*instance));
+}
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+return static_cast<const Type *>(data->data(info));
+}
+
+/*! @copydoc any_cast */
+template<typename Type, std::size_t Len, std::size_t Align>
+Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
+const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+// last attempt to make wrappers for const references return their values
+return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
+}
+
+/**
+ * @brief Constructs a wrapper from a given type, passing it all arguments.
+ * @tparam Type Type of object to use to initialize the wrapper.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Args Types of arguments to use to construct the new instance.
+ * @param args Parameters to use to construct the instance.
+ * @return A properly initialized wrapper for an object of the given type.
+ */
+template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
+basic_any<Len, Align> make_any(Args &&...args) {
+return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
+}
+
+/**
+ * @brief Forwards its argument and avoids copies for lvalue references.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ * @tparam Type Type of argument to use to construct the new instance.
+ * @param value Parameter to use to construct the instance.
+ * @return A properly initialized and not necessarily owning wrapper.
+ */
+template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
+basic_any<Len, Align> forward_as_any(Type &&value) {
+return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_POLY_FWD_HPP
+#define ENTT_POLY_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+
+namespace entt {
+
+template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_poly;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Concept Concept descriptor.
+ */
+template<typename Concept>
+using poly = basic_poly<Concept>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/*! @brief Inspector class used to infer the type of the virtual table. */
+struct poly_inspector {
+/**
+     * @brief Generic conversion operator (definition only).
+     * @tparam Type Type to which conversion is requested.
+     */
+template<class Type>
+operator Type &&() const;
+
+/**
+     * @brief Dummy invocation function (definition only).
+     * @tparam Member Index of the function to invoke.
+     * @tparam Args Types of arguments to pass to the function.
+     * @param args The arguments to pass to the function.
+     * @return A poly inspector convertible to any type.
+     */
+template<std::size_t Member, typename... Args>
+poly_inspector invoke(Args &&...args) const;
+
+/*! @copydoc invoke */
+template<std::size_t Member, typename... Args>
+poly_inspector invoke(Args &&...args);
+};
+
+/**
+ * @brief Static virtual table factory.
+ * @tparam Concept Concept descriptor.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Alignment requirement.
+ */
+template<typename Concept, std::size_t Len, std::size_t Align>
+class poly_vtable {
+using inspector = typename Concept::template type<poly_inspector>;
+
+template<typename Ret, typename... Args>
+static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
+
+template<typename Ret, typename... Args>
+static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
+
+template<typename Ret, typename... Args>
+static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
+
+template<typename Ret, typename... Args>
+static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
+
+template<typename Ret, typename... Args>
+static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
+
+template<auto... Candidate>
+static auto make_vtable(value_list<Candidate...>) ENTT_NOEXCEPT
+-> decltype(std::make_tuple(vtable_entry(Candidate)...));
+
+template<typename... Func>
+[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) ENTT_NOEXCEPT {
+if constexpr(sizeof...(Func) == 0u) {
+return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
+} else if constexpr((std::is_function_v<Func> && ...)) {
+return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){};
+}
+}
+
+template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
+static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) ENTT_NOEXCEPT {
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+entry = +[](Any &, Args... args) -> Ret {
+return std::invoke(Candidate, std::forward<Args>(args)...);
+};
+} else {
+entry = +[](Any &instance, Args... args) -> Ret {
+return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...));
+};
+}
+}
+
+template<typename Type, auto... Index>
+[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+vtable_type impl{};
+(fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
+return impl;
+}
+
+using vtable_type = decltype(make_vtable(Concept{}));
+static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u;
+
+public:
+/*! @brief Virtual table type. */
+using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
+
+/**
+     * @brief Returns a static virtual table for a specific concept and type.
+     * @tparam Type The type for which to generate the virtual table.
+     * @return A static virtual table for the given concept and type.
+     */
+template<typename Type>
+[[nodiscard]] static type instance() ENTT_NOEXCEPT {
+static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
+static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
+
+if constexpr(is_mono_v) {
+return std::get<0>(vtable);
+} else {
+return &vtable;
+}
+}
+};
+
+/**
+ * @brief Poly base class used to inject functionalities into concepts.
+ * @tparam Poly The outermost poly class.
+ */
+template<typename Poly>
+struct poly_base {
+/**
+     * @brief Invokes a function from the static virtual table.
+     * @tparam Member Index of the function to invoke.
+     * @tparam Args Types of arguments to pass to the function.
+     * @param self A reference to the poly object that made the call.
+     * @param args The arguments to pass to the function.
+     * @return The return value of the invoked function, if any.
+     */
+template<std::size_t Member, typename... Args>
+[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const {
+const auto &poly = static_cast<const Poly &>(self);
+
+if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
+return poly.vtable(poly.storage, std::forward<Args>(args)...);
+} else {
+return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
+}
+}
+
+/*! @copydoc invoke */
+template<std::size_t Member, typename... Args>
+[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) {
+auto &poly = static_cast<Poly &>(self);
+
+if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
+static_assert(Member == 0u, "Unknown member");
+return poly.vtable(poly.storage, std::forward<Args>(args)...);
+} else {
+return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
+}
+}
+};
+
+/**
+ * @brief Shortcut for calling `poly_base<Type>::invoke`.
+ * @tparam Member Index of the function to invoke.
+ * @tparam Poly A fully defined poly object.
+ * @tparam Args Types of arguments to pass to the function.
+ * @param self A reference to the poly object that made the call.
+ * @param args The arguments to pass to the function.
+ * @return The return value of the invoked function, if any.
+ */
+template<std::size_t Member, typename Poly, typename... Args>
+decltype(auto) poly_call(Poly &&self, Args &&...args) {
+return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Static polymorphism made simple and within everyone's reach.
+ *
+ * Static polymorphism is a very powerful tool in C++, albeit sometimes
+ * cumbersome to obtain.<br/>
+ * This class aims to make it simple and easy to use.
+ *
+ * @note
+ * Both deduced and defined static virtual tables are supported.<br/>
+ * Moreover, the `poly` class template also works with unmanaged objects.
+ *
+ * @tparam Concept Concept descriptor.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ * @tparam Align Optional alignment requirement.
+ */
+template<typename Concept, std::size_t Len, std::size_t Align>
+class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
+/*! @brief A poly base is allowed to snoop into a poly object. */
+friend struct poly_base<basic_poly>;
+
+public:
+/*! @brief Concept type. */
+using concept_type = typename Concept::template type<poly_base<basic_poly>>;
+/*! @brief Virtual table type. */
+using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
+
+/*! @brief Default constructor. */
+basic_poly() ENTT_NOEXCEPT
+: storage{},
+vtable{} {}
+
+/**
+     * @brief Constructs a poly by directly initializing the new object.
+     * @tparam Type Type of object to use to initialize the poly.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
+: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
+vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
+
+/**
+     * @brief Constructs a poly from a given value.
+     * @tparam Type Type of object to use to initialize the poly.
+     * @param value An instance of an object to use to initialize the poly.
+     */
+template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
+basic_poly(Type &&value) ENTT_NOEXCEPT
+: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
+
+/**
+     * @brief Returns the object type if any, `type_id<void>()` otherwise.
+     * @return The object type if any, `type_id<void>()` otherwise.
+     */
+[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
+return storage.type();
+}
+
+/**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @return An opaque pointer the contained instance, if any.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/*! @copydoc data */
+[[nodiscard]] void *data() ENTT_NOEXCEPT {
+return storage.data();
+}
+
+/**
+     * @brief Replaces the contained object by creating a new instance directly.
+     * @tparam Type Type of object to use to initialize the poly.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+template<typename Type, typename... Args>
+void emplace(Args &&...args) {
+storage.template emplace<Type>(std::forward<Args>(args)...);
+vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+/*! @brief Destroys contained object */
+void reset() {
+storage.reset();
+vtable = {};
+}
+
+/**
+     * @brief Returns false if a poly is empty, true otherwise.
+     * @return False if the poly is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(storage);
+}
+
+/**
+     * @brief Returns a pointer to the underlying concept.
+     * @return A pointer to the underlying concept.
+     */
+[[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT {
+return this;
+}
+
+/*! @copydoc operator-> */
+[[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT {
+return this;
+}
+
+/**
+     * @brief Aliasing constructor.
+     * @return A poly that shares a reference to an unmanaged object.
+     */
+[[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT {
+basic_poly ref{};
+ref.storage = storage.as_ref();
+ref.vtable = vtable;
+return ref;
+}
+
+/*! @copydoc as_ref */
+[[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT {
+basic_poly ref{};
+ref.storage = storage.as_ref();
+ref.vtable = vtable;
+return ref;
+}
+
+private:
+basic_any<Len, Align> storage;
+vtable_type vtable;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "process/process.hpp"
+#ifndef ENTT_PROCESS_PROCESS_HPP
+#define ENTT_PROCESS_PROCESS_HPP
+
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Base class for processes.
+ *
+ * This class stays true to the CRTP idiom. Derived classes must specify what's
+ * the intended type for elapsed times.<br/>
+ * A process should expose publicly the following member functions whether
+ * required:
+ *
+ * * @code{.cpp}
+ *   void update(Delta, void *);
+ *   @endcode
+ *
+ *   It's invoked once per tick until a process is explicitly aborted or it
+ *   terminates either with or without errors. Even though it's not mandatory to
+ *   declare this member function, as a rule of thumb each process should at
+ *   least define it to work properly. The `void *` parameter is an opaque
+ *   pointer to user data (if any) forwarded directly to the process during an
+ *   update.
+ *
+ * * @code{.cpp}
+ *   void init();
+ *   @endcode
+ *
+ *   It's invoked when the process joins the running queue of a scheduler. This
+ *   happens as soon as it's attached to the scheduler if the process is a top
+ *   level one, otherwise when it replaces its parent if the process is a
+ *   continuation.
+ *
+ * * @code{.cpp}
+ *   void succeeded();
+ *   @endcode
+ *
+ *   It's invoked in case of success, immediately after an update and during the
+ *   same tick.
+ *
+ * * @code{.cpp}
+ *   void failed();
+ *   @endcode
+ *
+ *   It's invoked in case of errors, immediately after an update and during the
+ *   same tick.
+ *
+ * * @code{.cpp}
+ *   void aborted();
+ *   @endcode
+ *
+ *   It's invoked only if a process is explicitly aborted. There is no guarantee
+ *   that it executes in the same tick, this depends solely on whether the
+ *   process is aborted immediately or not.
+ *
+ * Derived classes can change the internal state of a process by invoking the
+ * `succeed` and `fail` protected member functions and even pause or unpause the
+ * process itself.
+ *
+ * @sa scheduler
+ *
+ * @tparam Derived Actual type of process that extends the class template.
+ * @tparam Delta Type to use to provide elapsed time.
+ */
+template<typename Derived, typename Delta>
+class process {
+enum class state : std::uint8_t {
+uninitialized = 0,
+running,
+paused,
+succeeded,
+failed,
+aborted,
+finished,
+rejected
+};
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::uninitialized>)
+-> decltype(std::declval<Target>().init(), void()) {
+static_cast<Target *>(this)->init();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::running>, Delta delta, void *data)
+-> decltype(std::declval<Target>().update(delta, data), void()) {
+static_cast<Target *>(this)->update(delta, data);
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::succeeded>)
+-> decltype(std::declval<Target>().succeeded(), void()) {
+static_cast<Target *>(this)->succeeded();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::failed>)
+-> decltype(std::declval<Target>().failed(), void()) {
+static_cast<Target *>(this)->failed();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::aborted>)
+-> decltype(std::declval<Target>().aborted(), void()) {
+static_cast<Target *>(this)->aborted();
+}
+
+void next(...) const ENTT_NOEXCEPT {}
+
+protected:
+/**
+     * @brief Terminates a process with success if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     */
+void succeed() ENTT_NOEXCEPT {
+if(alive()) {
+current = state::succeeded;
+}
+}
+
+/**
+     * @brief Terminates a process with errors if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     */
+void fail() ENTT_NOEXCEPT {
+if(alive()) {
+current = state::failed;
+}
+}
+
+/**
+     * @brief Stops a process if it's in a running state.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * running.
+     */
+void pause() ENTT_NOEXCEPT {
+if(current == state::running) {
+current = state::paused;
+}
+}
+
+/**
+     * @brief Restarts a process if it's paused.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * paused.
+     */
+void unpause() ENTT_NOEXCEPT {
+if(current == state::paused) {
+current = state::running;
+}
+}
+
+public:
+/*! @brief Type used to provide elapsed time. */
+using delta_type = Delta;
+
+/*! @brief Default destructor. */
+virtual ~process() ENTT_NOEXCEPT {
+static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
+}
+
+/**
+     * @brief Aborts a process if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     *
+     * @param immediately Requests an immediate operation.
+     */
+void abort(const bool immediately = false) {
+if(alive()) {
+current = state::aborted;
+
+if(immediately) {
+tick({});
+}
+}
+}
+
+/**
+     * @brief Returns true if a process is either running or paused.
+     * @return True if the process is still alive, false otherwise.
+     */
+[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
+return current == state::running || current == state::paused;
+}
+
+/**
+     * @brief Returns true if a process is already terminated.
+     * @return True if the process is terminated, false otherwise.
+     */
+[[nodiscard]] bool finished() const ENTT_NOEXCEPT {
+return current == state::finished;
+}
+
+/**
+     * @brief Returns true if a process is currently paused.
+     * @return True if the process is paused, false otherwise.
+     */
+[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
+return current == state::paused;
+}
+
+/**
+     * @brief Returns true if a process terminated with errors.
+     * @return True if the process terminated with errors, false otherwise.
+     */
+[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
+return current == state::rejected;
+}
+
+/**
+     * @brief Updates a process and its internal state if required.
+     * @param delta Elapsed time.
+     * @param data Optional data.
+     */
+void tick(const Delta delta, void *data = nullptr) {
+switch(current) {
+case state::uninitialized:
+next(std::integral_constant<state, state::uninitialized>{});
+current = state::running;
+break;
+case state::running:
+next(std::integral_constant<state, state::running>{}, delta, data);
+break;
+default:
+// suppress warnings
+break;
+}
+
+// if it's dead, it must be notified and removed immediately
+switch(current) {
+case state::succeeded:
+next(std::integral_constant<state, state::succeeded>{});
+current = state::finished;
+break;
+case state::failed:
+next(std::integral_constant<state, state::failed>{});
+current = state::rejected;
+break;
+case state::aborted:
+next(std::integral_constant<state, state::aborted>{});
+current = state::rejected;
+break;
+default:
+// suppress warnings
+break;
+}
+}
+
+private:
+state current{state::uninitialized};
+};
+
+/**
+ * @brief Adaptor for lambdas and functors to turn them into processes.
+ *
+ * Lambdas and functors can't be used directly with a scheduler for they are not
+ * properly defined processes with managed life cycles.<br/>
+ * This class helps in filling the gap and turning lambdas and functors into
+ * full featured processes usable by a scheduler.
+ *
+ * The signature of the function call operator should be equivalent to the
+ * following:
+ *
+ * @code{.cpp}
+ * void(Delta delta, void *data, auto succeed, auto fail);
+ * @endcode
+ *
+ * Where:
+ *
+ * * `delta` is the elapsed time.
+ * * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
+ * * `succeed` is a function to call when a process terminates with success.
+ * * `fail` is a function to call when a process terminates with errors.
+ *
+ * The signature of the function call operator of both `succeed` and `fail`
+ * is equivalent to the following:
+ *
+ * @code{.cpp}
+ * void();
+ * @endcode
+ *
+ * Usually users shouldn't worry about creating adaptors. A scheduler will
+ * create them internally each and avery time a lambda or a functor is used as
+ * a process.
+ *
+ * @sa process
+ * @sa scheduler
+ *
+ * @tparam Func Actual type of process.
+ * @tparam Delta Type to use to provide elapsed time.
+ */
+template<typename Func, typename Delta>
+struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func {
+/**
+     * @brief Constructs a process adaptor from a lambda or a functor.
+     * @tparam Args Types of arguments to use to initialize the actual process.
+     * @param args Parameters to use to initialize the actual process.
+     */
+template<typename... Args>
+process_adaptor(Args &&...args)
+: Func{std::forward<Args>(args)...} {}
+
+/**
+     * @brief Updates a process and its internal state if required.
+     * @param delta Elapsed time.
+     * @param data Optional data.
+     */
+void update(const Delta delta, void *data) {
+Func::operator()(
+delta,
+data,
+[this]() { this->succeed(); },
+[this]() { this->fail(); });
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "process/scheduler.hpp"
+#ifndef ENTT_PROCESS_SCHEDULER_HPP
+#define ENTT_PROCESS_SCHEDULER_HPP
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "process.hpp"
+#ifndef ENTT_PROCESS_PROCESS_HPP
+#define ENTT_PROCESS_PROCESS_HPP
+
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Base class for processes.
+ *
+ * This class stays true to the CRTP idiom. Derived classes must specify what's
+ * the intended type for elapsed times.<br/>
+ * A process should expose publicly the following member functions whether
+ * required:
+ *
+ * * @code{.cpp}
+ *   void update(Delta, void *);
+ *   @endcode
+ *
+ *   It's invoked once per tick until a process is explicitly aborted or it
+ *   terminates either with or without errors. Even though it's not mandatory to
+ *   declare this member function, as a rule of thumb each process should at
+ *   least define it to work properly. The `void *` parameter is an opaque
+ *   pointer to user data (if any) forwarded directly to the process during an
+ *   update.
+ *
+ * * @code{.cpp}
+ *   void init();
+ *   @endcode
+ *
+ *   It's invoked when the process joins the running queue of a scheduler. This
+ *   happens as soon as it's attached to the scheduler if the process is a top
+ *   level one, otherwise when it replaces its parent if the process is a
+ *   continuation.
+ *
+ * * @code{.cpp}
+ *   void succeeded();
+ *   @endcode
+ *
+ *   It's invoked in case of success, immediately after an update and during the
+ *   same tick.
+ *
+ * * @code{.cpp}
+ *   void failed();
+ *   @endcode
+ *
+ *   It's invoked in case of errors, immediately after an update and during the
+ *   same tick.
+ *
+ * * @code{.cpp}
+ *   void aborted();
+ *   @endcode
+ *
+ *   It's invoked only if a process is explicitly aborted. There is no guarantee
+ *   that it executes in the same tick, this depends solely on whether the
+ *   process is aborted immediately or not.
+ *
+ * Derived classes can change the internal state of a process by invoking the
+ * `succeed` and `fail` protected member functions and even pause or unpause the
+ * process itself.
+ *
+ * @sa scheduler
+ *
+ * @tparam Derived Actual type of process that extends the class template.
+ * @tparam Delta Type to use to provide elapsed time.
+ */
+template<typename Derived, typename Delta>
+class process {
+enum class state : std::uint8_t {
+uninitialized = 0,
+running,
+paused,
+succeeded,
+failed,
+aborted,
+finished,
+rejected
+};
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::uninitialized>)
+-> decltype(std::declval<Target>().init(), void()) {
+static_cast<Target *>(this)->init();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::running>, Delta delta, void *data)
+-> decltype(std::declval<Target>().update(delta, data), void()) {
+static_cast<Target *>(this)->update(delta, data);
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::succeeded>)
+-> decltype(std::declval<Target>().succeeded(), void()) {
+static_cast<Target *>(this)->succeeded();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::failed>)
+-> decltype(std::declval<Target>().failed(), void()) {
+static_cast<Target *>(this)->failed();
+}
+
+template<typename Target = Derived>
+auto next(std::integral_constant<state, state::aborted>)
+-> decltype(std::declval<Target>().aborted(), void()) {
+static_cast<Target *>(this)->aborted();
+}
+
+void next(...) const ENTT_NOEXCEPT {}
+
+protected:
+/**
+     * @brief Terminates a process with success if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     */
+void succeed() ENTT_NOEXCEPT {
+if(alive()) {
+current = state::succeeded;
+}
+}
+
+/**
+     * @brief Terminates a process with errors if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     */
+void fail() ENTT_NOEXCEPT {
+if(alive()) {
+current = state::failed;
+}
+}
+
+/**
+     * @brief Stops a process if it's in a running state.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * running.
+     */
+void pause() ENTT_NOEXCEPT {
+if(current == state::running) {
+current = state::paused;
+}
+}
+
+/**
+     * @brief Restarts a process if it's paused.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * paused.
+     */
+void unpause() ENTT_NOEXCEPT {
+if(current == state::paused) {
+current = state::running;
+}
+}
+
+public:
+/*! @brief Type used to provide elapsed time. */
+using delta_type = Delta;
+
+/*! @brief Default destructor. */
+virtual ~process() ENTT_NOEXCEPT {
+static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
+}
+
+/**
+     * @brief Aborts a process if it's still alive.
+     *
+     * The function is idempotent and it does nothing if the process isn't
+     * alive.
+     *
+     * @param immediately Requests an immediate operation.
+     */
+void abort(const bool immediately = false) {
+if(alive()) {
+current = state::aborted;
+
+if(immediately) {
+tick({});
+}
+}
+}
+
+/**
+     * @brief Returns true if a process is either running or paused.
+     * @return True if the process is still alive, false otherwise.
+     */
+[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
+return current == state::running || current == state::paused;
+}
+
+/**
+     * @brief Returns true if a process is already terminated.
+     * @return True if the process is terminated, false otherwise.
+     */
+[[nodiscard]] bool finished() const ENTT_NOEXCEPT {
+return current == state::finished;
+}
+
+/**
+     * @brief Returns true if a process is currently paused.
+     * @return True if the process is paused, false otherwise.
+     */
+[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
+return current == state::paused;
+}
+
+/**
+     * @brief Returns true if a process terminated with errors.
+     * @return True if the process terminated with errors, false otherwise.
+     */
+[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
+return current == state::rejected;
+}
+
+/**
+     * @brief Updates a process and its internal state if required.
+     * @param delta Elapsed time.
+     * @param data Optional data.
+     */
+void tick(const Delta delta, void *data = nullptr) {
+switch(current) {
+case state::uninitialized:
+next(std::integral_constant<state, state::uninitialized>{});
+current = state::running;
+break;
+case state::running:
+next(std::integral_constant<state, state::running>{}, delta, data);
+break;
+default:
+// suppress warnings
+break;
+}
+
+// if it's dead, it must be notified and removed immediately
+switch(current) {
+case state::succeeded:
+next(std::integral_constant<state, state::succeeded>{});
+current = state::finished;
+break;
+case state::failed:
+next(std::integral_constant<state, state::failed>{});
+current = state::rejected;
+break;
+case state::aborted:
+next(std::integral_constant<state, state::aborted>{});
+current = state::rejected;
+break;
+default:
+// suppress warnings
+break;
+}
+}
+
+private:
+state current{state::uninitialized};
+};
+
+/**
+ * @brief Adaptor for lambdas and functors to turn them into processes.
+ *
+ * Lambdas and functors can't be used directly with a scheduler for they are not
+ * properly defined processes with managed life cycles.<br/>
+ * This class helps in filling the gap and turning lambdas and functors into
+ * full featured processes usable by a scheduler.
+ *
+ * The signature of the function call operator should be equivalent to the
+ * following:
+ *
+ * @code{.cpp}
+ * void(Delta delta, void *data, auto succeed, auto fail);
+ * @endcode
+ *
+ * Where:
+ *
+ * * `delta` is the elapsed time.
+ * * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
+ * * `succeed` is a function to call when a process terminates with success.
+ * * `fail` is a function to call when a process terminates with errors.
+ *
+ * The signature of the function call operator of both `succeed` and `fail`
+ * is equivalent to the following:
+ *
+ * @code{.cpp}
+ * void();
+ * @endcode
+ *
+ * Usually users shouldn't worry about creating adaptors. A scheduler will
+ * create them internally each and avery time a lambda or a functor is used as
+ * a process.
+ *
+ * @sa process
+ * @sa scheduler
+ *
+ * @tparam Func Actual type of process.
+ * @tparam Delta Type to use to provide elapsed time.
+ */
+template<typename Func, typename Delta>
+struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func {
+/**
+     * @brief Constructs a process adaptor from a lambda or a functor.
+     * @tparam Args Types of arguments to use to initialize the actual process.
+     * @param args Parameters to use to initialize the actual process.
+     */
+template<typename... Args>
+process_adaptor(Args &&...args)
+: Func{std::forward<Args>(args)...} {}
+
+/**
+     * @brief Updates a process and its internal state if required.
+     * @param delta Elapsed time.
+     * @param data Optional data.
+     */
+void update(const Delta delta, void *data) {
+Func::operator()(
+delta,
+data,
+[this]() { this->succeed(); },
+[this]() { this->fail(); });
+}
+};
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Cooperative scheduler for processes.
+ *
+ * A cooperative scheduler runs processes and helps managing their life cycles.
+ *
+ * Each process is invoked once per tick. If a process terminates, it's
+ * removed automatically from the scheduler and it's never invoked again.<br/>
+ * A process can also have a child. In this case, the process is replaced with
+ * its child when it terminates if it returns with success. In case of errors,
+ * both the process and its child are discarded.
+ *
+ * Example of use (pseudocode):
+ *
+ * @code{.cpp}
+ * scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
+ *     // code
+ * }).then<my_process>(arguments...);
+ * @endcode
+ *
+ * In order to invoke all scheduled processes, call the `update` member function
+ * passing it the elapsed time to forward to the tasks.
+ *
+ * @sa process
+ *
+ * @tparam Delta Type to use to provide elapsed time.
+ */
+template<typename Delta>
+class scheduler {
+struct process_handler {
+using instance_type = std::unique_ptr<void, void (*)(void *)>;
+using update_fn_type = bool(scheduler &, std::size_t, Delta, void *);
+using abort_fn_type = void(scheduler &, std::size_t, bool);
+using next_type = std::unique_ptr<process_handler>;
+
+instance_type instance;
+update_fn_type *update;
+abort_fn_type *abort;
+next_type next;
+};
+
+struct continuation {
+continuation(process_handler *ref) ENTT_NOEXCEPT
+: handler{ref} {}
+
+template<typename Proc, typename... Args>
+continuation then(Args &&...args) {
+static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
+auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
+handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
+handler = handler->next.get();
+return *this;
+}
+
+template<typename Func>
+continuation then(Func &&func) {
+return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func));
+}
+
+private:
+process_handler *handler;
+};
+
+template<typename Proc>
+[[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) {
+auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
+process->tick(delta, data);
+
+if(process->rejected()) {
+return true;
+} else if(process->finished()) {
+if(auto &&handler = owner.handlers[pos]; handler.next) {
+handler = std::move(*handler.next);
+// forces the process to exit the uninitialized state
+return handler.update(owner, pos, {}, nullptr);
+}
+
+return true;
+}
+
+return false;
+}
+
+template<typename Proc>
+static void abort(scheduler &owner, std::size_t pos, const bool immediately) {
+static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
+}
+
+template<typename Proc>
+static void deleter(void *proc) {
+delete static_cast<Proc *>(proc);
+}
+
+public:
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+
+/*! @brief Default constructor. */
+scheduler() = default;
+
+/*! @brief Default move constructor. */
+scheduler(scheduler &&) = default;
+
+/*! @brief Default move assignment operator. @return This scheduler. */
+scheduler &operator=(scheduler &&) = default;
+
+/**
+     * @brief Number of processes currently scheduled.
+     * @return Number of processes currently scheduled.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return handlers.size();
+}
+
+/**
+     * @brief Returns true if at least a process is currently scheduled.
+     * @return True if there are scheduled processes, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return handlers.empty();
+}
+
+/**
+     * @brief Discards all scheduled processes.
+     *
+     * Processes aren't aborted. They are discarded along with their children
+     * and never executed again.
+     */
+void clear() {
+handlers.clear();
+}
+
+/**
+     * @brief Schedules a process for the next tick.
+     *
+     * Returned value is an opaque object that can be used to attach a child to
+     * the given process. The child is automatically scheduled when the process
+     * terminates and only if the process returns with success.
+     *
+     * Example of use (pseudocode):
+     *
+     * @code{.cpp}
+     * // schedules a task in the form of a process class
+     * scheduler.attach<my_process>(arguments...)
+     * // appends a child in the form of a lambda function
+     * .then([](auto delta, void *, auto succeed, auto fail) {
+     *     // code
+     * })
+     * // appends a child in the form of another process class
+     * .then<my_other_process>();
+     * @endcode
+     *
+     * @tparam Proc Type of process to schedule.
+     * @tparam Args Types of arguments to use to initialize the process.
+     * @param args Parameters to use to initialize the process.
+     * @return An opaque object to use to concatenate processes.
+     */
+template<typename Proc, typename... Args>
+auto attach(Args &&...args) {
+static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
+auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
+auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
+// forces the process to exit the uninitialized state
+ref.update(*this, handlers.size() - 1u, {}, nullptr);
+return continuation{&handlers.back()};
+}
+
+/**
+     * @brief Schedules a process for the next tick.
+     *
+     * A process can be either a lambda or a functor. The scheduler wraps both
+     * of them in a process adaptor internally.<br/>
+     * The signature of the function call operator should be equivalent to the
+     * following:
+     *
+     * @code{.cpp}
+     * void(Delta delta, void *data, auto succeed, auto fail);
+     * @endcode
+     *
+     * Where:
+     *
+     * * `delta` is the elapsed time.
+     * * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
+     * * `succeed` is a function to call when a process terminates with success.
+     * * `fail` is a function to call when a process terminates with errors.
+     *
+     * The signature of the function call operator of both `succeed` and `fail`
+     * is equivalent to the following:
+     *
+     * @code{.cpp}
+     * void();
+     * @endcode
+     *
+     * Returned value is an opaque object that can be used to attach a child to
+     * the given process. The child is automatically scheduled when the process
+     * terminates and only if the process returns with success.
+     *
+     * Example of use (pseudocode):
+     *
+     * @code{.cpp}
+     * // schedules a task in the form of a lambda function
+     * scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
+     *     // code
+     * })
+     * // appends a child in the form of another lambda function
+     * .then([](auto delta, void *, auto succeed, auto fail) {
+     *     // code
+     * })
+     * // appends a child in the form of a process class
+     * .then<my_process>(arguments...);
+     * @endcode
+     *
+     * @sa process_adaptor
+     *
+     * @tparam Func Type of process to schedule.
+     * @param func Either a lambda or a functor to use as a process.
+     * @return An opaque object to use to concatenate processes.
+     */
+template<typename Func>
+auto attach(Func &&func) {
+using Proc = process_adaptor<std::decay_t<Func>, Delta>;
+return attach<Proc>(std::forward<Func>(func));
+}
+
+/**
+     * @brief Updates all scheduled processes.
+     *
+     * All scheduled processes are executed in no specific order.<br/>
+     * If a process terminates with success, it's replaced with its child, if
+     * any. Otherwise, if a process terminates with an error, it's removed along
+     * with its child.
+     *
+     * @param delta Elapsed time.
+     * @param data Optional data.
+     */
+void update(const Delta delta, void *data = nullptr) {
+for(auto pos = handlers.size(); pos; --pos) {
+const auto curr = pos - 1u;
+
+if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) {
+std::swap(handlers[curr], handlers.back());
+handlers.pop_back();
+}
+}
+}
+
+/**
+     * @brief Aborts all scheduled processes.
+     *
+     * Unless an immediate operation is requested, the abort is scheduled for
+     * the next tick. Processes won't be executed anymore in any case.<br/>
+     * Once a process is fully aborted and thus finished, it's discarded along
+     * with its child, if any.
+     *
+     * @param immediately Requests an immediate operation.
+     */
+void abort(const bool immediately = false) {
+for(auto pos = handlers.size(); pos; --pos) {
+const auto curr = pos - 1u;
+handlers[curr].abort(*this, curr, immediately);
+}
+}
+
+private:
+std::vector<process_handler> handlers{};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "resource/cache.hpp"
+#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP
+#define ENTT_RESOURCE_RESOURCE_CACHE_HPP
+
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../container/dense_map.hpp"
+#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+
+namespace entt {
+
+template<
+typename Key,
+typename Type,
+typename = std::hash<Key>,
+typename = std::equal_to<Key>,
+typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+typename Type,
+typename = std::hash<Type>,
+typename = std::equal_to<Type>,
+typename = std::allocator<Type>>
+class dense_set;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+using value_type = std::pair<Key, Type>;
+
+template<typename... Args>
+dense_map_node(const std::size_t pos, Args &&...args)
+: next{pos},
+element{std::forward<Args>(args)...} {}
+
+template<typename Allocator, typename... Args>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+: next{pos},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+std::size_t next;
+value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+template<typename>
+friend class dense_map_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_map_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_map_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_map_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_map_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_map_iterator operator--(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_map_iterator copy = *this;
+return (copy += value);
+}
+
+dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].element.first, it[value].element.second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->element.first, it->element.second};
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+template<typename>
+friend class dense_map_local_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].next, *this;
+}
+
+dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it[offset].element.first, it[offset].element.second};
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = internal::dense_map_node<Key, Type>;
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(key), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other, typename... Args>
+[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+template<typename Other, typename Arg>
+[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+it->second = std::forward<Arg>(value);
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
+for(; *curr != last; curr = &packed.first()[*curr].next) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Key;
+/*! @brief Mapped type of the container. */
+using mapped_type = Type;
+/*! @brief Key-value type of the container. */
+using value_type = std::pair<const Key, Type>;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the keys. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the keys for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Input iterator type. */
+using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_map()
+: dense_map(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const allocator_type &allocator)
+: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const allocator_type &allocator)
+: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_map{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_map(const dense_map &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_map(const dense_map &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_map(dense_map &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_map(dense_map &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(const dense_map &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(dense_map &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if the key does not exist.
+     * @param value A key-value pair eventually convertible to the value type.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value.first, value.second);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+}
+
+/**
+     * @copydoc insert
+     * @tparam Arg Type of the key-value pair to insert into the container.
+     */
+template<typename Arg>
+std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+insert(Arg &&value) {
+return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+}
+
+/**
+     * @brief Inserts elements into the container, if their keys do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Inserts an element into the container or assigns to the current
+     * element if the key already exists.
+     * @tparam Arg Type of the value to insert or assign.
+     * @param key A key used both to look up and to insert if not found.
+     * @param value A value to insert or assign.
+     * @return A pair consisting of an iterator to the element and a bool
+     * denoting whether the insertion took place.
+     */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+return insert_or_overwrite(key, std::forward<Arg>(value));
+}
+
+/*! @copydoc insert_or_assign */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+}
+
+/**
+     * @brief Constructs an element in-place, if the key does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+if constexpr(sizeof...(Args) == 0u) {
+return insert_or_do_nothing(key_type{});
+} else if constexpr(sizeof...(Args) == 1u) {
+return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+} else if constexpr(sizeof...(Args) == 2u) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+const auto index = key_to_bucket(node.element.first);
+
+if(auto it = constrained_find(node.element.first, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.next, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Inserts in-place if the key does not exist, does nothing if the
+     * key exists.
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param key A key used both to look up and to insert if not found.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+return insert_or_do_nothing(key, std::forward<Args>(args)...);
+}
+
+/*! @copydoc try_emplace */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(pos->first);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].element.first);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given key.
+     * @param key A key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const key_type &key) {
+for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+if(packed.second()(packed.first()[*curr].element.first, key)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].next;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_map &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Accesses a given element with bounds checking.
+     * @param key A key of an element to find.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &at(const key_type &key) {
+auto it = find(key);
+ENTT_ASSERT(it != end(), "Invalid key");
+return it->second;
+}
+
+/*! @copydoc at */
+[[nodiscard]] const mapped_type &at(const key_type &key) const {
+auto it = find(key);
+ENTT_ASSERT(it != cend(), "Invalid key");
+return it->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](const key_type &key) {
+return insert_or_do_nothing(key).first->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](key_type &&key) {
+return insert_or_do_nothing(std::move(key)).first->second;
+}
+
+/**
+     * @brief Finds an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const key_type &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const key_type &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Finds an element with a key that compares _equivalent_ to a given
+     * value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const key_type &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element with a key that
+     * compares _equivalent_ to a given value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given key.
+     * @param key The value of the key to examine.
+     * @return The bucket for the given key.
+     */
+[[nodiscard]] size_type bucket(const key_type &key) const {
+return key_to_bucket(key);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = key_to_bucket(packed.first()[pos].element.first);
+packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the keys.
+     * @return The function used to hash the keys.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare keys for equality.
+     * @return The function used to compare keys for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+: std::true_type {};
+
+} // namespace std
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_RESOURCE_FWD_HPP
+#define ENTT_RESOURCE_FWD_HPP
+
+#include <memory>
+
+namespace entt {
+
+template<typename>
+struct resource_loader;
+
+template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>>
+class resource_cache;
+
+template<typename>
+class resource;
+
+} // namespace entt
+
+#endif
+
+// #include "loader.hpp"
+#ifndef ENTT_RESOURCE_LOADEr_HPP
+#define ENTT_RESOURCE_LOADEr_HPP
+
+#include <memory>
+#include <utility>
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Transparent loader for shared resources.
+ * @tparam Type Type of resources created by the loader.
+ */
+template<typename Type>
+struct resource_loader {
+/*! @brief Result type. */
+using result_type = std::shared_ptr<Type>;
+
+/**
+     * @brief Constructs a shared pointer to a resource from its arguments.
+     * @tparam Args Types of arguments to use to construct the resource.
+     * @param args Parameters to use to construct the resource.
+     * @return A shared pointer to a resource of the given type.
+     */
+template<typename... Args>
+result_type operator()(Args &&...args) const {
+return std::make_shared<Type>(std::forward<Args>(args)...);
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "resource.hpp"
+#ifndef ENTT_RESOURCE_RESOURCE_HPP
+#define ENTT_RESOURCE_RESOURCE_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Basic resource handle.
+ *
+ * A handle wraps a resource and extends its lifetime. It also shares the same
+ * resource with all other handles constructed from the same element.<br/>
+ * As a rule of thumb, resources should never be copied nor moved. Handles are
+ * the way to go to push references around.
+ *
+ * @tparam Type Type of resource managed by a handle.
+ */
+template<typename Type>
+class resource {
+/*! @brief Resource handles are friends with each other. */
+template<typename>
+friend class resource;
+
+template<typename Other>
+static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
+
+public:
+/*! @brief Default constructor. */
+resource() ENTT_NOEXCEPT
+: value{} {}
+
+/**
+     * @brief Creates a handle from a weak pointer, namely a resource.
+     * @param res A weak pointer to a resource.
+     */
+explicit resource(std::shared_ptr<Type> res) ENTT_NOEXCEPT
+: value{std::move(res)} {}
+
+/*! @brief Default copy constructor. */
+resource(const resource &) ENTT_NOEXCEPT = default;
+
+/*! @brief Default move constructor. */
+resource(resource &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Aliasing constructor.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle with which to share ownership information.
+     * @param res Unrelated and unmanaged resources.
+     */
+template<typename Other>
+resource(const resource<Other> &other, Type &res) ENTT_NOEXCEPT
+: value{other.value, std::addressof(res)} {}
+
+/**
+     * @brief Copy constructs a handle which shares ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     */
+template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+resource(const resource<Other> &other) ENTT_NOEXCEPT
+: value{other.value} {}
+
+/**
+     * @brief Move constructs a handle which takes ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     */
+template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+resource(resource<Other> &&other) ENTT_NOEXCEPT
+: value{std::move(other.value)} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This resource handle.
+     */
+resource &operator=(const resource &) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This resource handle.
+     */
+resource &operator=(resource &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Copy assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     * @return This resource handle.
+     */
+template<typename Other>
+std::enable_if_t<is_acceptable_v<Other>, resource &>
+operator=(const resource<Other> &other) ENTT_NOEXCEPT {
+value = other.value;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     * @return This resource handle.
+     */
+template<typename Other>
+std::enable_if_t<is_acceptable_v<Other>, resource &>
+operator=(resource<Other> &&other) ENTT_NOEXCEPT {
+value = std::move(other.value);
+return *this;
+}
+
+/**
+     * @brief Returns a reference to the managed resource.
+     *
+     * @warning
+     * The behavior is undefined if the handle doesn't contain a resource.
+     *
+     * @return A reference to the managed resource.
+     */
+[[nodiscard]] Type &operator*() const ENTT_NOEXCEPT {
+return *value;
+}
+
+/*! @copydoc operator* */
+[[nodiscard]] operator Type &() const ENTT_NOEXCEPT {
+return *value;
+}
+
+/**
+     * @brief Returns a pointer to the managed resource.
+     * @return A pointer to the managed resource.
+     */
+[[nodiscard]] Type *operator->() const ENTT_NOEXCEPT {
+return value.get();
+}
+
+/**
+     * @brief Returns true if a handle contains a resource, false otherwise.
+     * @return True if the handle contains a resource, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(value);
+}
+
+/**
+     * @brief Returns the number of handles pointing the same resource.
+     * @return The number of handles pointing the same resource.
+     */
+[[nodiscard]] long use_count() const ENTT_NOEXCEPT {
+return value.use_count();
+}
+
+private:
+std::shared_ptr<Type> value;
+};
+
+/**
+ * @brief Compares two handles.
+ * @tparam Res Type of resource managed by the first handle.
+ * @tparam Other Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if both handles refer to the same resource, false otherwise.
+ */
+template<typename Res, typename Other>
+[[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) ENTT_NOEXCEPT {
+return (std::addressof(*lhs) == std::addressof(*rhs));
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Res Type of resource managed by the first handle.
+ * @tparam Other Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return False if both handles refer to the same registry, true otherwise.
+ */
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const resource<ILhs> &lhs, const resource<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, typename It>
+class resource_cache_iterator final {
+template<typename, typename>
+friend class resource_cache_iterator;
+
+public:
+using value_type = std::pair<id_type, resource<Type>>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+resource_cache_iterator() ENTT_NOEXCEPT = default;
+
+resource_cache_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+resource_cache_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+resource_cache_iterator operator++(int) ENTT_NOEXCEPT {
+resource_cache_iterator orig = *this;
+return ++(*this), orig;
+}
+
+resource_cache_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+resource_cache_iterator operator--(int) ENTT_NOEXCEPT {
+resource_cache_iterator orig = *this;
+return operator--(), orig;
+}
+
+resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+resource_cache_iterator copy = *this;
+return (copy += value);
+}
+
+resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].first, resource<Type>{it[value].second}};
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return (*this)[0];
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+friend std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+friend bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+friend bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
+[[nodiscard]] bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic cache for resources of any type.
+ * @tparam Type Type of resources managed by a cache.
+ * @tparam Loader Type of loader used to create the resources.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Loader, typename Allocator>
+class resource_cache {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
+using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
+
+public:
+/*! @brief Resource type. */
+using value_type = Type;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Loader type. */
+using loader_type = Loader;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+resource_cache()
+: resource_cache{loader_type{}} {}
+
+/**
+     * @brief Constructs an empty cache with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit resource_cache(const allocator_type &allocator)
+: resource_cache{loader_type{}, allocator} {}
+
+/**
+     * @brief Constructs an empty cache with a given allocator and loader.
+     * @param callable The loader to use.
+     * @param allocator The allocator to use.
+     */
+explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{})
+: pool{container_type{allocator}, callable} {}
+
+/*! @brief Default copy constructor. */
+resource_cache(const resource_cache &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+resource_cache(const resource_cache &other, const allocator_type &allocator)
+: pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
+
+/*! @brief Default move constructor. */
+resource_cache(resource_cache &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+resource_cache(resource_cache &&other, const allocator_type &allocator)
+: pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This cache.
+     */
+resource_cache &operator=(const resource_cache &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This cache.
+     */
+resource_cache &operator=(resource_cache &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return pool.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the cache. If the
+     * cache is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal cache.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return pool.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return pool.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the cache. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal cache.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return pool.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return pool.first().end();
+}
+
+/**
+     * @brief Returns true if a cache contains no resources, false otherwise.
+     * @return True if the cache contains no resources, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return pool.first().empty();
+}
+
+/**
+     * @brief Number of resources managed by a cache.
+     * @return Number of resources currently stored.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return pool.first().size();
+}
+
+/*! @brief Clears a cache. */
+void clear() ENTT_NOEXCEPT {
+pool.first().clear();
+}
+
+/**
+     * @brief Loads a resource, if its identifier does not exist.
+     *
+     * Arguments are forwarded directly to the loader and _consumed_ only if the
+     * resource doesn't already exist.
+     *
+     * @warning
+     * If the resource isn't loaded correctly, the returned handle could be
+     * invalid and any use of it will result in undefined behavior.
+     *
+     * @tparam Args Types of arguments to use to load the resource if required.
+     * @param id Unique resource identifier.
+     * @param args Arguments to use to load the resource if required.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> load(const id_type id, Args &&...args) {
+if(auto it = pool.first().find(id); it != pool.first().end()) {
+return {it, false};
+}
+
+return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...));
+}
+
+/**
+     * @brief Force loads a resource, if its identifier does not exist.
+     * @copydetails load
+     */
+template<typename... Args>
+std::pair<iterator, bool> force_load(const id_type id, Args &&...args) {
+return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true};
+}
+
+/**
+     * @brief Returns a handle for a given resource identifier.
+     *
+     * @warning
+     * There is no guarantee that the returned handle is valid.<br/>
+     * If it is not, any use will result in indefinite behavior.
+     *
+     * @param id Unique resource identifier.
+     * @return A handle for the given resource.
+     */
+[[nodiscard]] resource<const value_type> operator[](const id_type id) const {
+if(auto it = pool.first().find(id); it != pool.first().cend()) {
+return resource<const value_type>{it->second};
+}
+
+return {};
+}
+
+/*! @copydoc operator[] */
+[[nodiscard]] resource<value_type> operator[](const id_type id) {
+if(auto it = pool.first().find(id); it != pool.first().end()) {
+return resource<value_type>{it->second};
+}
+
+return {};
+}
+
+/**
+     * @brief Checks if a cache contains a given identifier.
+     * @param id Unique resource identifier.
+     * @return True if the cache contains the resource, false otherwise.
+     */
+[[nodiscard]] bool contains(const id_type id) const {
+return pool.first().contains(id);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto it = pool.first().begin();
+return pool.first().erase(it + (pos - const_iterator{it}));
+}
+
+/**
+     * @brief Removes the given elements from a cache.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto it = pool.first().begin();
+return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it}));
+}
+
+/**
+     * @brief Removes the given elements from a cache.
+     * @param id Unique resource identifier.
+     * @return Number of resources erased (either 0 or 1).
+     */
+size_type erase(const id_type id) {
+return pool.first().erase(id);
+}
+
+/**
+     * @brief Returns the loader used to create resources.
+     * @return The loader used to create resources.
+     */
+[[nodiscard]] loader_type loader() const {
+return pool.second();
+}
+
+private:
+compressed_pair<container_type, loader_type> pool;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "resource/loader.hpp"
+#ifndef ENTT_RESOURCE_LOADEr_HPP
+#define ENTT_RESOURCE_LOADEr_HPP
+
+#include <memory>
+#include <utility>
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Transparent loader for shared resources.
+ * @tparam Type Type of resources created by the loader.
+ */
+template<typename Type>
+struct resource_loader {
+/*! @brief Result type. */
+using result_type = std::shared_ptr<Type>;
+
+/**
+     * @brief Constructs a shared pointer to a resource from its arguments.
+     * @tparam Args Types of arguments to use to construct the resource.
+     * @param args Parameters to use to construct the resource.
+     * @return A shared pointer to a resource of the given type.
+     */
+template<typename... Args>
+result_type operator()(Args &&...args) const {
+return std::make_shared<Type>(std::forward<Args>(args)...);
+}
+};
+
+} // namespace entt
+
+#endif
+
+// #include "resource/resource.hpp"
+#ifndef ENTT_RESOURCE_RESOURCE_HPP
+#define ENTT_RESOURCE_RESOURCE_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Basic resource handle.
+ *
+ * A handle wraps a resource and extends its lifetime. It also shares the same
+ * resource with all other handles constructed from the same element.<br/>
+ * As a rule of thumb, resources should never be copied nor moved. Handles are
+ * the way to go to push references around.
+ *
+ * @tparam Type Type of resource managed by a handle.
+ */
+template<typename Type>
+class resource {
+/*! @brief Resource handles are friends with each other. */
+template<typename>
+friend class resource;
+
+template<typename Other>
+static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>;
+
+public:
+/*! @brief Default constructor. */
+resource() ENTT_NOEXCEPT
+: value{} {}
+
+/**
+     * @brief Creates a handle from a weak pointer, namely a resource.
+     * @param res A weak pointer to a resource.
+     */
+explicit resource(std::shared_ptr<Type> res) ENTT_NOEXCEPT
+: value{std::move(res)} {}
+
+/*! @brief Default copy constructor. */
+resource(const resource &) ENTT_NOEXCEPT = default;
+
+/*! @brief Default move constructor. */
+resource(resource &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Aliasing constructor.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle with which to share ownership information.
+     * @param res Unrelated and unmanaged resources.
+     */
+template<typename Other>
+resource(const resource<Other> &other, Type &res) ENTT_NOEXCEPT
+: value{other.value, std::addressof(res)} {}
+
+/**
+     * @brief Copy constructs a handle which shares ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     */
+template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+resource(const resource<Other> &other) ENTT_NOEXCEPT
+: value{other.value} {}
+
+/**
+     * @brief Move constructs a handle which takes ownership of the resource.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     */
+template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>>
+resource(resource<Other> &&other) ENTT_NOEXCEPT
+: value{std::move(other.value)} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This resource handle.
+     */
+resource &operator=(const resource &) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This resource handle.
+     */
+resource &operator=(resource &&) ENTT_NOEXCEPT = default;
+
+/**
+     * @brief Copy assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to copy from.
+     * @return This resource handle.
+     */
+template<typename Other>
+std::enable_if_t<is_acceptable_v<Other>, resource &>
+operator=(const resource<Other> &other) ENTT_NOEXCEPT {
+value = other.value;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator from foreign handle.
+     * @tparam Other Type of resource managed by the received handle.
+     * @param other The handle to move from.
+     * @return This resource handle.
+     */
+template<typename Other>
+std::enable_if_t<is_acceptable_v<Other>, resource &>
+operator=(resource<Other> &&other) ENTT_NOEXCEPT {
+value = std::move(other.value);
+return *this;
+}
+
+/**
+     * @brief Returns a reference to the managed resource.
+     *
+     * @warning
+     * The behavior is undefined if the handle doesn't contain a resource.
+     *
+     * @return A reference to the managed resource.
+     */
+[[nodiscard]] Type &operator*() const ENTT_NOEXCEPT {
+return *value;
+}
+
+/*! @copydoc operator* */
+[[nodiscard]] operator Type &() const ENTT_NOEXCEPT {
+return *value;
+}
+
+/**
+     * @brief Returns a pointer to the managed resource.
+     * @return A pointer to the managed resource.
+     */
+[[nodiscard]] Type *operator->() const ENTT_NOEXCEPT {
+return value.get();
+}
+
+/**
+     * @brief Returns true if a handle contains a resource, false otherwise.
+     * @return True if the handle contains a resource, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(value);
+}
+
+/**
+     * @brief Returns the number of handles pointing the same resource.
+     * @return The number of handles pointing the same resource.
+     */
+[[nodiscard]] long use_count() const ENTT_NOEXCEPT {
+return value.use_count();
+}
+
+private:
+std::shared_ptr<Type> value;
+};
+
+/**
+ * @brief Compares two handles.
+ * @tparam Res Type of resource managed by the first handle.
+ * @tparam Other Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return True if both handles refer to the same resource, false otherwise.
+ */
+template<typename Res, typename Other>
+[[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) ENTT_NOEXCEPT {
+return (std::addressof(*lhs) == std::addressof(*rhs));
+}
+
+/**
+ * @brief Compares two handles.
+ * @tparam Res Type of resource managed by the first handle.
+ * @tparam Other Type of resource managed by the second handle.
+ * @param lhs A valid handle.
+ * @param rhs A valid handle.
+ * @return False if both handles refer to the same registry, true otherwise.
+ */
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const resource<ILhs> &lhs, const resource<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace entt
+
+#endif
+
+// #include "signal/delegate.hpp"
+#ifndef ENTT_SIGNAL_DELEGATE_HPP
+#define ENTT_SIGNAL_DELEGATE_HPP
+
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_SIGNAL_FWD_HPP
+#define ENTT_SIGNAL_FWD_HPP
+
+#include <memory>
+
+namespace entt {
+
+template<typename>
+class delegate;
+
+template<typename = std::allocator<char>>
+class basic_dispatcher;
+
+template<typename>
+class emitter;
+
+class connection;
+
+struct scoped_connection;
+
+template<typename>
+class sink;
+
+template<typename Type, typename = std::allocator<Type *>>
+class sigh;
+
+/*! @brief Alias declaration for the most common use case. */
+using dispatcher = basic_dispatcher<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Ret, typename... Args>
+auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
+
+template<typename Ret, typename Type, typename... Args, typename Other>
+auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Type, typename... Other>
+auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
+
+template<typename... Type>
+using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
+
+template<typename... Class, typename Ret, typename... Args>
+[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
+return std::index_sequence_for<Class..., Args...>{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Used to wrap a function or a member of a specified type. */
+template<auto>
+struct connect_arg_t {};
+
+/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
+template<auto Func>
+inline constexpr connect_arg_t<Func> connect_arg{};
+
+/**
+ * @brief Basic delegate implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ */
+template<typename>
+class delegate;
+
+/**
+ * @brief Utility class to use to send around functions and members.
+ *
+ * Unmanaged delegate for function pointers and members. Users of this class are
+ * in charge of disconnecting instances before deleting them.
+ *
+ * A delegate can be used as a general purpose invoker without memory overhead
+ * for free functions possibly with payloads and bound or unbound members.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+class delegate<Ret(Args...)> {
+template<auto Candidate, std::size_t... Index>
+[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+public:
+/*! @brief Function type of the contained target. */
+using function_type = Ret(const void *, Args...);
+/*! @brief Function type of the delegate. */
+using type = Ret(Args...);
+/*! @brief Return type of the delegate. */
+using result_type = Ret;
+
+/*! @brief Default constructor. */
+delegate() ENTT_NOEXCEPT
+: instance{nullptr},
+fn{nullptr} {}
+
+/**
+     * @brief Constructs a delegate and connects a free function or an unbound
+     * member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
+connect<Candidate>();
+}
+
+/**
+     * @brief Constructs a delegate and connects a free function with payload or
+     * a bound member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
+connect<Candidate>(std::forward<Type>(value_or_instance));
+}
+
+/**
+     * @brief Constructs a delegate and connects an user defined function with
+     * optional payload.
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+connect(function, payload);
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a delegate.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+void connect() ENTT_NOEXCEPT {
+instance = nullptr;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+fn = [](const void *, Args... args) -> Ret {
+return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
+};
+} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
+fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
+} else {
+fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the delegate.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the delegate itself.
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid reference that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type &value_or_instance) ENTT_NOEXCEPT {
+instance = &value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * @sa connect(Type &)
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type *value_or_instance) ENTT_NOEXCEPT {
+instance = value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects an user defined function with optional payload to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of an instance overcomes
+     * the one of the delegate.<br/>
+     * The payload is returned as the first argument to the target function in
+     * all cases.
+     *
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+instance = payload;
+fn = function;
+}
+
+/**
+     * @brief Resets a delegate.
+     *
+     * After a reset, a delegate cannot be invoked anymore.
+     */
+void reset() ENTT_NOEXCEPT {
+instance = nullptr;
+fn = nullptr;
+}
+
+/**
+     * @brief Returns the instance or the payload linked to a delegate, if any.
+     * @return An opaque pointer to the underlying data.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return instance;
+}
+
+/**
+     * @brief Triggers a delegate.
+     *
+     * The delegate invokes the underlying function and returns the result.
+     *
+     * @warning
+     * Attempting to trigger an invalid delegate results in undefined
+     * behavior.
+     *
+     * @param args Arguments to use to invoke the underlying function.
+     * @return The value returned by the underlying function.
+     */
+Ret operator()(Args... args) const {
+ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
+return fn(instance, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Checks whether a delegate actually stores a listener.
+     * @return False if the delegate is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+// no need to also test instance
+return !(fn == nullptr);
+}
+
+/**
+     * @brief Compares the contents of two delegates.
+     * @param other Delegate with which to compare.
+     * @return False if the two contents differ, true otherwise.
+     */
+[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
+return fn == other.fn && instance == other.instance;
+}
+
+private:
+const void *instance;
+function_type *fn;
+};
+
+/**
+ * @brief Compares the contents of two delegates.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @param lhs A valid delegate object.
+ * @param rhs A valid delegate object.
+ * @return True if the two contents differ, false otherwise.
+ */
+template<typename Ret, typename... Args>
+[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ * @tparam Type Type of class or type of payload.
+ */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
+
+} // namespace entt
+
+#endif
+
+// #include "signal/dispatcher.hpp"
+#ifndef ENTT_SIGNAL_DISPATCHER_HPP
+#define ENTT_SIGNAL_DISPATCHER_HPP
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "../container/dense_map.hpp"
+#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+#ifndef ENTT_CONFIG_CONFIG_H
+#define ENTT_CONFIG_CONFIG_H
+
+// #include "version.h"
+#ifndef ENTT_CONFIG_VERSION_H
+#define ENTT_CONFIG_VERSION_H
+
+// #include "macro.h"
+#ifndef ENTT_CONFIG_MACRO_H
+#define ENTT_CONFIG_MACRO_H
+
+#define ENTT_STR(arg) #arg
+#define ENTT_XSTR(arg) ENTT_STR(arg)
+
+#endif
+
+
+#define ENTT_VERSION_MAJOR 3
+#define ENTT_VERSION_MINOR 10
+#define ENTT_VERSION_PATCH 0
+
+#define ENTT_VERSION \
+    ENTT_XSTR(ENTT_VERSION_MAJOR) \
+    "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
+
+#endif
+
+
+#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
+#    define ENTT_NOEXCEPT noexcept
+#    define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
+#    define ENTT_THROW throw
+#    define ENTT_TRY try
+#    define ENTT_CATCH catch(...)
+#else
+#    define ENTT_NOEXCEPT
+#    define ENTT_NOEXCEPT_IF(...)
+#    define ENTT_THROW
+#    define ENTT_TRY if(true)
+#    define ENTT_CATCH if(false)
+#endif
+
+#ifdef ENTT_USE_ATOMIC
+#    include <atomic>
+#    define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
+#else
+#    define ENTT_MAYBE_ATOMIC(Type) Type
+#endif
+
+#ifndef ENTT_ID_TYPE
+#    include <cstdint>
+#    define ENTT_ID_TYPE std::uint32_t
+#endif
+
+#ifndef ENTT_SPARSE_PAGE
+#    define ENTT_SPARSE_PAGE 4096
+#endif
+
+#ifndef ENTT_PACKED_PAGE
+#    define ENTT_PACKED_PAGE 1024
+#endif
+
+#ifdef ENTT_DISABLE_ASSERT
+#    undef ENTT_ASSERT
+#    define ENTT_ASSERT(...) (void(0))
+#elif !defined ENTT_ASSERT
+#    include <cassert>
+#    define ENTT_ASSERT(condition, ...) assert(condition)
+#endif
+
+#ifdef ENTT_NO_ETO
+#    define ENTT_IGNORE_IF_EMPTY false
+#else
+#    define ENTT_IGNORE_IF_EMPTY true
+#endif
+
+#ifdef ENTT_STANDARD_CPP
+#    define ENTT_NONSTD false
+#else
+#    define ENTT_NONSTD true
+#    if defined __clang__ || defined __GNUC__
+#        define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '='
+#        define ENTT_PRETTY_FUNCTION_SUFFIX ']'
+#    elif defined _MSC_VER
+#        define ENTT_PRETTY_FUNCTION __FUNCSIG__
+#        define ENTT_PRETTY_FUNCTION_PREFIX '<'
+#        define ENTT_PRETTY_FUNCTION_SUFFIX '>'
+#    endif
+#endif
+
+#if defined _MSC_VER
+#    pragma detect_mismatch("entt.version", ENTT_VERSION)
+#    pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
+#    pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
+#    pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
+#endif
+
+#endif
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/iterator.hpp"
+#ifndef ENTT_CORE_ITERATOR_HPP
+#define ENTT_CORE_ITERATOR_HPP
+
+#include <iterator>
+#include <memory>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Helper type to use as pointer with input iterators.
+ * @tparam Type of wrapped value.
+ */
+template<typename Type>
+struct input_iterator_pointer final {
+/*! @brief Pointer type. */
+using pointer = Type *;
+
+/*! @brief Default copy constructor, deleted on purpose. */
+input_iterator_pointer(const input_iterator_pointer &) = delete;
+
+/*! @brief Default move constructor. */
+input_iterator_pointer(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Constructs a proxy object by move.
+     * @param val Value to use to initialize the proxy object.
+     */
+input_iterator_pointer(Type &&val)
+: value{std::move(val)} {}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This proxy object.
+     */
+input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
+
+/**
+     * @brief Access operator for accessing wrapped values.
+     * @return A pointer to the wrapped value.
+     */
+[[nodiscard]] pointer operator->() ENTT_NOEXCEPT {
+return std::addressof(value);
+}
+
+private:
+Type value;
+};
+
+/**
+ * @brief Utility class to create an iterable object from a pair of iterators.
+ * @tparam It Type of iterator.
+ * @tparam Sentinel Type of sentinel.
+ */
+template<typename It, typename Sentinel = It>
+struct iterable_adaptor final {
+/*! @brief Value type. */
+using value_type = typename std::iterator_traits<It>::value_type;
+/*! @brief Iterator type. */
+using iterator = It;
+/*! @brief Sentinel type. */
+using sentinel = Sentinel;
+
+/*! @brief Default constructor. */
+iterable_adaptor() = default;
+
+/**
+     * @brief Creates an iterable object from a pair of iterators.
+     * @param from Begin iterator.
+     * @param to End iterator.
+     */
+iterable_adaptor(iterator from, sentinel to)
+: first{from},
+last{to} {}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     * @return An iterator to the first element of the range.
+     */
+[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+return first;
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     * @return An iterator to the element following the last element of the
+     * range.
+     */
+[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
+return last;
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
+return begin();
+}
+
+/*! @copydoc end */
+[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
+return end();
+}
+
+private:
+It first;
+Sentinel last;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "../core/memory.hpp"
+#ifndef ENTT_CORE_MEMORY_HPP
+#define ENTT_CORE_MEMORY_HPP
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/**
+ * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
+ * @tparam Type Pointer type.
+ * @param ptr Fancy or raw pointer.
+ * @return A raw pointer that represents the address of the original pointer.
+ */
+template<typename Type>
+[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
+if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
+return ptr;
+} else {
+return to_address(std::forward<Type>(ptr).operator->());
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
+lhs = rhs;
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
+lhs = std::move(rhs);
+}
+}
+
+/**
+ * @brief Utility function to design allocation-aware containers.
+ * @tparam Allocator Type of allocator.
+ * @param lhs A valid allocator.
+ * @param rhs Another valid allocator.
+ */
+template<typename Allocator>
+constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
+ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
+
+if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
+using std::swap;
+swap(lhs, rhs);
+}
+}
+
+/**
+ * @brief Checks whether a value is a power of two or not.
+ * @param value A value that may or may not be a power of two.
+ * @return True if the value is a power of two, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+return value && ((value & (value - 1)) == 0);
+}
+
+/**
+ * @brief Computes the smallest power of two greater than or equal to a value.
+ * @param value The value to use.
+ * @return The smallest power of two greater than or equal to the given value.
+ */
+[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
+ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
+std::size_t curr = value - (value != 0u);
+
+for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
+curr |= curr >> next;
+}
+
+return ++curr;
+}
+
+/**
+ * @brief Fast module utility function (powers of two only).
+ * @param value A value for which to calculate the modulus.
+ * @param mod _Modulus_, it must be a power of two.
+ * @return The common remainder.
+ */
+[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
+ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
+return value & (mod - 1u);
+}
+
+/**
+ * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
+ * @tparam Args Types of arguments to use to construct the object.
+ */
+template<typename Allocator>
+struct allocation_deleter: private Allocator {
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Pointer type. */
+using pointer = typename std::allocator_traits<Allocator>::pointer;
+
+/**
+     * @brief Inherited constructors.
+     * @param alloc The allocator to use.
+     */
+allocation_deleter(const allocator_type &alloc)
+: Allocator{alloc} {}
+
+/**
+     * @brief Destroys the pointed object and deallocates its memory.
+     * @param ptr A valid pointer to an object of the given type.
+     */
+void operator()(pointer ptr) {
+using alloc_traits = typename std::allocator_traits<Allocator>;
+alloc_traits::destroy(*this, to_address(ptr));
+alloc_traits::deallocate(*this, ptr, 1u);
+}
+};
+
+/**
+ * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
+ * @tparam Type Type of object to allocate for and to construct.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A properly initialized unique pointer with a custom deleter.
+ */
+template<typename Type, typename Allocator, typename... Args>
+auto allocate_unique(Allocator &allocator, Args &&...args) {
+static_assert(!std::is_array_v<Type>, "Array types are not supported");
+
+using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
+using allocator_type = typename alloc_traits::allocator_type;
+
+allocator_type alloc{allocator};
+auto ptr = alloc_traits::allocate(alloc, 1u);
+
+ENTT_TRY {
+alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
+}
+ENTT_CATCH {
+alloc_traits::deallocate(alloc, ptr, 1u);
+ENTT_THROW;
+}
+
+return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
+}
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type>
+struct uses_allocator_construction {
+template<typename Allocator, typename... Params>
+static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
+if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
+return std::forward_as_tuple(std::forward<Params>(params)...);
+} else {
+static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
+return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
+} else {
+static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
+return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
+}
+}
+}
+};
+
+template<typename Type, typename Other>
+struct uses_allocator_construction<std::pair<Type, Other>> {
+using type = std::pair<Type, Other>;
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
+return std::make_tuple(
+std::piecewise_construct,
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
+std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
+}
+
+template<typename Allocator>
+static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
+}
+
+template<typename Allocator, typename First, typename Second>
+static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
+return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Prepares the argument list needed to
+ * create an object of a given type by means of uses-allocator construction.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
+return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+/**
+ * @brief Uses-allocator construction utility (waiting for C++20).
+ *
+ * Primarily intended for internal use. Creates an object of a given type by
+ * means of uses-allocator construction at an uninitialized memory location.
+ *
+ * @tparam Type Type of object to create.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param value Memory location in which to place the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return A pointer to the newly created object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
+return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+
+namespace entt {
+
+template<
+typename Key,
+typename Type,
+typename = std::hash<Key>,
+typename = std::equal_to<Key>,
+typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+typename Type,
+typename = std::hash<Type>,
+typename = std::equal_to<Type>,
+typename = std::allocator<Type>>
+class dense_set;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+using value_type = std::pair<Key, Type>;
+
+template<typename... Args>
+dense_map_node(const std::size_t pos, Args &&...args)
+: next{pos},
+element{std::forward<Args>(args)...} {}
+
+template<typename Allocator, typename... Args>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+: next{pos},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+template<typename Allocator>
+dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+: next{other.next},
+element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+std::size_t next;
+value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+template<typename>
+friend class dense_map_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_iterator() ENTT_NOEXCEPT
+: it{} {}
+
+dense_map_iterator(const It iter) ENTT_NOEXCEPT
+: it{iter} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it} {}
+
+dense_map_iterator &operator++() ENTT_NOEXCEPT {
+return ++it, *this;
+}
+
+dense_map_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return ++(*this), orig;
+}
+
+dense_map_iterator &operator--() ENTT_NOEXCEPT {
+return --it, *this;
+}
+
+dense_map_iterator operator--(int) ENTT_NOEXCEPT {
+dense_map_iterator orig = *this;
+return operator--(), orig;
+}
+
+dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+it += value;
+return *this;
+}
+
+dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+dense_map_iterator copy = *this;
+return (copy += value);
+}
+
+dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+return (*this += -value);
+}
+
+dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+return (*this + -value);
+}
+
+[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
+return {it[value].element.first, it[value].element.second};
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it->element.first, it->element.second};
+}
+
+template<typename ILhs, typename IRhs>
+friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+template<typename ILhs, typename IRhs>
+friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+template<typename>
+friend class dense_map_local_iterator;
+
+using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+using value_type = std::pair<first_type, second_type>;
+using pointer = input_iterator_pointer<value_type>;
+using reference = value_type;
+using difference_type = std::ptrdiff_t;
+using iterator_category = std::input_iterator_tag;
+
+dense_map_local_iterator() ENTT_NOEXCEPT
+: it{},
+offset{} {}
+
+dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
+: it{iter},
+offset{pos} {}
+
+template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
+: it{other.it},
+offset{other.offset} {}
+
+dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
+return offset = it[offset].next, *this;
+}
+
+dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
+dense_map_local_iterator orig = *this;
+return ++(*this), orig;
+}
+
+[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+return operator*();
+}
+
+[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+return {it[offset].element.first, it[offset].element.second};
+}
+
+[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
+return offset;
+}
+
+private:
+It it;
+std::size_t offset;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return lhs.index() == rhs.index();
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+static constexpr float default_threshold = 0.875f;
+static constexpr std::size_t minimum_capacity = 8u;
+
+using node_type = internal::dense_map_node<Key, Type>;
+using alloc_traits = typename std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+template<typename Other>
+[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
+return fast_mod(sparse.second()(key), bucket_count());
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return begin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return end();
+}
+
+template<typename Other>
+[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+if(packed.second()(it->first, key)) {
+return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+}
+}
+
+return cend();
+}
+
+template<typename Other, typename... Args>
+[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+template<typename Other, typename Arg>
+[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+const auto index = key_to_bucket(key);
+
+if(auto it = constrained_find(key, index); it != end()) {
+it->second = std::forward<Arg>(value);
+return std::make_pair(it, false);
+}
+
+packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+sparse.first()[index] = packed.first().size() - 1u;
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+
+void move_and_pop(const std::size_t pos) {
+if(const auto last = size() - 1u; pos != last) {
+packed.first()[pos] = std::move(packed.first().back());
+size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
+for(; *curr != last; curr = &packed.first()[*curr].next) {}
+*curr = pos;
+}
+
+packed.first().pop_back();
+}
+
+void rehash_if_required() {
+if(size() > (bucket_count() * max_load_factor())) {
+rehash(bucket_count() * 2u);
+}
+}
+
+public:
+/*! @brief Key type of the container. */
+using key_type = Key;
+/*! @brief Mapped type of the container. */
+using mapped_type = Type;
+/*! @brief Key-value type of the container. */
+using value_type = std::pair<const Key, Type>;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Type of function to use to hash the keys. */
+using hasher = Hash;
+/*! @brief Type of function to use to compare the keys for equality. */
+using key_equal = KeyEqual;
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Input iterator type. */
+using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+/*! @brief Input iterator type. */
+using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+/*! @brief Constant input iterator type. */
+using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+/*! @brief Default constructor. */
+dense_map()
+: dense_map(minimum_capacity) {}
+
+/**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const allocator_type &allocator)
+: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const allocator_type &allocator)
+: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param allocator The allocator to use.
+     */
+dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
+: dense_map{bucket_count, hash, key_equal{}, allocator} {}
+
+/**
+     * @brief Constructs an empty container with a given allocator, hash
+     * function, compare function and user supplied minimal number of buckets.
+     * @param bucket_count Minimal number of buckets.
+     * @param hash Hash function to use.
+     * @param equal Compare function to use.
+     * @param allocator The allocator to use.
+     */
+explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+: sparse{allocator, hash},
+packed{allocator, equal},
+threshold{default_threshold} {
+rehash(bucket_count);
+}
+
+/*! @brief Default copy constructor. */
+dense_map(const dense_map &) = default;
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+dense_map(const dense_map &other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+threshold{other.threshold} {}
+
+/*! @brief Default move constructor. */
+dense_map(dense_map &&) = default;
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+dense_map(dense_map &&other, const allocator_type &allocator)
+: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+threshold{other.threshold} {}
+
+/**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(const dense_map &) = default;
+
+/**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+dense_map &operator=(dense_map &&) = default;
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return sparse.first().get_allocator();
+}
+
+/**
+     * @brief Returns an iterator to the beginning.
+     *
+     * The returned iterator points to the first instance of the internal array.
+     * If the array is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first instance of the internal array.
+     */
+[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/*! @copydoc cbegin */
+[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
+return cbegin();
+}
+
+/*! @copydoc begin */
+[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
+return packed.first().begin();
+}
+
+/**
+     * @brief Returns an iterator to the end.
+     *
+     * The returned iterator points to the element following the last instance
+     * of the internal array. Attempting to dereference the returned iterator
+     * results in undefined behavior.
+     *
+     * @return An iterator to the element following the last instance of the
+     * internal array.
+     */
+[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/*! @copydoc cend */
+[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
+return cend();
+}
+
+/*! @copydoc end */
+[[nodiscard]] iterator end() ENTT_NOEXCEPT {
+return packed.first().end();
+}
+
+/**
+     * @brief Checks whether a container is empty.
+     * @return True if the container is empty, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return packed.first().empty();
+}
+
+/**
+     * @brief Returns the number of elements in a container.
+     * @return Number of elements in a container.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return packed.first().size();
+}
+
+/*! @brief Clears the container. */
+void clear() ENTT_NOEXCEPT {
+sparse.first().clear();
+packed.first().clear();
+rehash(0u);
+}
+
+/**
+     * @brief Inserts an element into the container, if the key does not exist.
+     * @param value A key-value pair eventually convertible to the value type.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+std::pair<iterator, bool> insert(const value_type &value) {
+return insert_or_do_nothing(value.first, value.second);
+}
+
+/*! @copydoc insert */
+std::pair<iterator, bool> insert(value_type &&value) {
+return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+}
+
+/**
+     * @copydoc insert
+     * @tparam Arg Type of the key-value pair to insert into the container.
+     */
+template<typename Arg>
+std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+insert(Arg &&value) {
+return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+}
+
+/**
+     * @brief Inserts elements into the container, if their keys do not exist.
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     */
+template<typename It>
+void insert(It first, It last) {
+for(; first != last; ++first) {
+insert(*first);
+}
+}
+
+/**
+     * @brief Inserts an element into the container or assigns to the current
+     * element if the key already exists.
+     * @tparam Arg Type of the value to insert or assign.
+     * @param key A key used both to look up and to insert if not found.
+     * @param value A value to insert or assign.
+     * @return A pair consisting of an iterator to the element and a bool
+     * denoting whether the insertion took place.
+     */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+return insert_or_overwrite(key, std::forward<Arg>(value));
+}
+
+/*! @copydoc insert_or_assign */
+template<typename Arg>
+std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+}
+
+/**
+     * @brief Constructs an element in-place, if the key does not exist.
+     *
+     * The element is also constructed when the container already has the key,
+     * in which case the newly constructed object is destroyed immediately.
+     *
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+if constexpr(sizeof...(Args) == 0u) {
+return insert_or_do_nothing(key_type{});
+} else if constexpr(sizeof...(Args) == 1u) {
+return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+} else if constexpr(sizeof...(Args) == 2u) {
+return insert_or_do_nothing(std::forward<Args>(args)...);
+} else {
+auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+const auto index = key_to_bucket(node.element.first);
+
+if(auto it = constrained_find(node.element.first, index); it != end()) {
+packed.first().pop_back();
+return std::make_pair(it, false);
+}
+
+std::swap(node.next, sparse.first()[index]);
+rehash_if_required();
+
+return std::make_pair(--end(), true);
+}
+}
+
+/**
+     * @brief Inserts in-place if the key does not exist, does nothing if the
+     * key exists.
+     * @tparam Args Types of arguments to forward to the constructor of the
+     * element.
+     * @param key A key used both to look up and to insert if not found.
+     * @param args Arguments to forward to the constructor of the element.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+return insert_or_do_nothing(key, std::forward<Args>(args)...);
+}
+
+/*! @copydoc try_emplace */
+template<typename... Args>
+std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Removes an element from a given position.
+     * @param pos An iterator to the element to remove.
+     * @return An iterator following the removed element.
+     */
+iterator erase(const_iterator pos) {
+const auto diff = pos - cbegin();
+erase(pos->first);
+return begin() + diff;
+}
+
+/**
+     * @brief Removes the given elements from a container.
+     * @param first An iterator to the first element of the range of elements.
+     * @param last An iterator past the last element of the range of elements.
+     * @return An iterator following the last removed element.
+     */
+iterator erase(const_iterator first, const_iterator last) {
+const auto dist = first - cbegin();
+
+for(auto from = last - cbegin(); from != dist; --from) {
+erase(packed.first()[from - 1u].element.first);
+}
+
+return (begin() + dist);
+}
+
+/**
+     * @brief Removes the element associated with a given key.
+     * @param key A key value of an element to remove.
+     * @return Number of elements removed (either 0 or 1).
+     */
+size_type erase(const key_type &key) {
+for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+if(packed.second()(packed.first()[*curr].element.first, key)) {
+const auto index = *curr;
+*curr = packed.first()[*curr].next;
+move_and_pop(index);
+return 1u;
+}
+}
+
+return 0u;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given container.
+     * @param other Container to exchange the content with.
+     */
+void swap(dense_map &other) {
+using std::swap;
+swap(sparse, other.sparse);
+swap(packed, other.packed);
+swap(threshold, other.threshold);
+}
+
+/**
+     * @brief Accesses a given element with bounds checking.
+     * @param key A key of an element to find.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &at(const key_type &key) {
+auto it = find(key);
+ENTT_ASSERT(it != end(), "Invalid key");
+return it->second;
+}
+
+/*! @copydoc at */
+[[nodiscard]] const mapped_type &at(const key_type &key) const {
+auto it = find(key);
+ENTT_ASSERT(it != cend(), "Invalid key");
+return it->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](const key_type &key) {
+return insert_or_do_nothing(key).first->second;
+}
+
+/**
+     * @brief Accesses or inserts a given element.
+     * @param key A key of an element to find or insert.
+     * @return A reference to the mapped value of the requested element.
+     */
+[[nodiscard]] mapped_type &operator[](key_type &&key) {
+return insert_or_do_nothing(std::move(key)).first->second;
+}
+
+/**
+     * @brief Finds an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+[[nodiscard]] iterator find(const key_type &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+[[nodiscard]] const_iterator find(const key_type &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Finds an element with a key that compares _equivalent_ to a given
+     * value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return An iterator to an element with the given key. If no such element
+     * is found, a past-the-end iterator is returned.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+find(const Other &key) {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/*! @copydoc find */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+find(const Other &key) const {
+return constrained_find(key, key_to_bucket(key));
+}
+
+/**
+     * @brief Checks if the container contains an element with a given key.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+[[nodiscard]] bool contains(const key_type &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Checks if the container contains an element with a key that
+     * compares _equivalent_ to a given value.
+     * @tparam Other Type of the key value of an element to search for.
+     * @param key Key value of an element to search for.
+     * @return True if there is such an element, false otherwise.
+     */
+template<typename Other>
+[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+contains(const Other &key) const {
+return (find(key) != cend());
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] const_local_iterator begin(const size_type index) const {
+return cbegin(index);
+}
+
+/**
+     * @brief Returns an iterator to the beginning of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the beginning of the given bucket.
+     */
+[[nodiscard]] local_iterator begin(const size_type index) {
+return {packed.first().begin(), sparse.first()[index]};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
+return cend(index);
+}
+
+/**
+     * @brief Returns an iterator to the end of a given bucket.
+     * @param index An index of a bucket to access.
+     * @return An iterator to the end of the given bucket.
+     */
+[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+}
+
+/**
+     * @brief Returns the number of buckets.
+     * @return The number of buckets.
+     */
+[[nodiscard]] size_type bucket_count() const {
+return sparse.first().size();
+}
+
+/**
+     * @brief Returns the maximum number of buckets.
+     * @return The maximum number of buckets.
+     */
+[[nodiscard]] size_type max_bucket_count() const {
+return sparse.first().max_size();
+}
+
+/**
+     * @brief Returns the number of elements in a given bucket.
+     * @param index The index of the bucket to examine.
+     * @return The number of elements in the given bucket.
+     */
+[[nodiscard]] size_type bucket_size(const size_type index) const {
+return static_cast<size_type>(std::distance(begin(index), end(index)));
+}
+
+/**
+     * @brief Returns the bucket for a given key.
+     * @param key The value of the key to examine.
+     * @return The bucket for the given key.
+     */
+[[nodiscard]] size_type bucket(const key_type &key) const {
+return key_to_bucket(key);
+}
+
+/**
+     * @brief Returns the average number of elements per bucket.
+     * @return The average number of elements per bucket.
+     */
+[[nodiscard]] float load_factor() const {
+return size() / static_cast<float>(bucket_count());
+}
+
+/**
+     * @brief Returns the maximum average number of elements per bucket.
+     * @return The maximum average number of elements per bucket.
+     */
+[[nodiscard]] float max_load_factor() const {
+return threshold;
+}
+
+/**
+     * @brief Sets the desired maximum average number of elements per bucket.
+     * @param value A desired maximum average number of elements per bucket.
+     */
+void max_load_factor(const float value) {
+ENTT_ASSERT(value > 0.f, "Invalid load factor");
+threshold = value;
+rehash(0u);
+}
+
+/**
+     * @brief Reserves at least the specified number of buckets and regenerates
+     * the hash table.
+     * @param count New number of buckets.
+     */
+void rehash(const size_type count) {
+auto value = (std::max)(count, minimum_capacity);
+value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
+
+if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+sparse.first().resize(sz);
+std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
+
+for(size_type pos{}, last = size(); pos < last; ++pos) {
+const auto index = key_to_bucket(packed.first()[pos].element.first);
+packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+}
+}
+}
+
+/**
+     * @brief Reserves space for at least the specified number of elements and
+     * regenerates the hash table.
+     * @param count New number of elements.
+     */
+void reserve(const size_type count) {
+packed.first().reserve(count);
+rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
+}
+
+/**
+     * @brief Returns the function used to hash the keys.
+     * @return The function used to hash the keys.
+     */
+[[nodiscard]] hasher hash_function() const {
+return sparse.second();
+}
+
+/**
+     * @brief Returns the function used to compare keys for equality.
+     * @return The function used to compare keys for equality.
+     */
+[[nodiscard]] key_equal key_eq() const {
+return packed.second();
+}
+
+private:
+compressed_pair<sparse_container_type, hasher> sparse;
+compressed_pair<packed_container_type, key_equal> packed;
+float threshold;
+};
+
+} // namespace entt
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+: std::true_type {};
+
+} // namespace std
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+#endif
+
+// #include "../core/compressed_pair.hpp"
+#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
+#define ENTT_CORE_COMPRESSED_PAIR_HPP
+
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "type_traits.hpp"
+#ifndef ENTT_CORE_TYPE_TRAITS_HPP
+#define ENTT_CORE_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Utility class to disambiguate overloaded functions.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+struct choice_t
+// Unfortunately, doxygen cannot parse such a construct.
+: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
+{};
+
+/*! @copybrief choice_t */
+template<>
+struct choice_t<0> {};
+
+/**
+ * @brief Variable template for the choice trick.
+ * @tparam N Number of choices available.
+ */
+template<std::size_t N>
+inline constexpr choice_t<N> choice{};
+
+/**
+ * @brief Identity type trait.
+ *
+ * Useful to establish non-deduced contexts in template argument deduction
+ * (waiting for C++20) or to provide types through function arguments.
+ *
+ * @tparam Type A type.
+ */
+template<typename Type>
+struct type_identity {
+/*! @brief Identity type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type.
+ */
+template<typename Type>
+using type_identity_t = typename type_identity<Type>::type;
+
+/**
+ * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
+ * @tparam Type The type of which to return the size.
+ * @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
+ */
+template<typename Type, typename = void>
+struct size_of: std::integral_constant<std::size_t, 0u> {};
+
+/*! @copydoc size_of */
+template<typename Type>
+struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
+: std::integral_constant<std::size_t, sizeof(Type)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type of which to return the size.
+ */
+template<typename Type>
+inline constexpr std::size_t size_of_v = size_of<Type>::value;
+
+/**
+ * @brief Using declaration to be used to _repeat_ the same type a number of
+ * times equal to the size of a given parameter pack.
+ * @tparam Type A type to repeat.
+ */
+template<typename Type, typename>
+using unpack_as_type = Type;
+
+/**
+ * @brief Helper variable template to be used to _repeat_ the same value a
+ * number of times equal to the size of a given parameter pack.
+ * @tparam Value A value to repeat.
+ */
+template<auto Value, typename>
+inline constexpr auto unpack_as_value = Value;
+
+/**
+ * @brief Wraps a static constant.
+ * @tparam Value A static constant.
+ */
+template<auto Value>
+using integral_constant = std::integral_constant<decltype(Value), Value>;
+
+/**
+ * @brief Alias template to facilitate the creation of named values.
+ * @tparam Value A constant value at least convertible to `id_type`.
+ */
+template<id_type Value>
+using tag = integral_constant<Value>;
+
+/**
+ * @brief A class to use to push around lists of types, nothing more.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list {
+/*! @brief Type list type. */
+using type = type_list;
+/*! @brief Compile-time number of elements in the type list. */
+static constexpr auto size = sizeof...(Type);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct type_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Index Index of the type to return.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<std::size_t Index, typename Type, typename... Other>
+struct type_list_element<Index, type_list<Type, Other...>>
+: type_list_element<Index - 1u, type_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Type First type provided by the type list.
+ * @tparam Other Other types provided by the type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_element<0u, type_list<Type, Other...>> {
+/*! @brief Searched type. */
+using type = Type;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the type to return.
+ * @tparam List Type list to search into.
+ */
+template<std::size_t Index, typename List>
+using type_list_element_t = typename type_list_element<Index, List>::type;
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @return A type list composed by the types of both the type lists.
+ */
+template<typename... Type, typename... Other>
+constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_cat;
+
+/*! @brief Concatenates multiple type lists. */
+template<>
+struct type_list_cat<> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ * @tparam List Other type lists, if any.
+ */
+template<typename... Type, typename... Other, typename... List>
+struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple type lists.
+ * @tparam Type Types provided by the type list.
+ */
+template<typename... Type>
+struct type_list_cat<type_list<Type...>> {
+/*! @brief A type list composed by the types of all the type lists. */
+using type = type_list<Type...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists to concatenate.
+ */
+template<typename... List>
+using type_list_cat_t = typename type_list_cat<List...>::type;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename>
+struct type_list_unique;
+
+/**
+ * @brief Removes duplicates types from a type list.
+ * @tparam Type One of the types provided by the given type list.
+ * @tparam Other The other types provided by the given type list.
+ */
+template<typename Type, typename... Other>
+struct type_list_unique<type_list<Type, Other...>> {
+/*! @brief A type list without duplicate types. */
+using type = std::conditional_t<
+(std::is_same_v<Type, Other> || ...),
+typename type_list_unique<type_list<Other...>>::type,
+type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
+};
+
+/*! @brief Removes duplicates types from a type list. */
+template<>
+struct type_list_unique<type_list<>> {
+/*! @brief A type list without duplicate types. */
+using type = type_list<>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Type A type list.
+ */
+template<typename Type>
+using type_list_unique_t = typename type_list_unique<Type>::type;
+
+/**
+ * @brief Provides the member constant `value` to true if a type list contains a
+ * given type, false otherwise.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+struct type_list_contains;
+
+/**
+ * @copybrief type_list_contains
+ * @tparam Type Types provided by the type list.
+ * @tparam Other Type to look for.
+ */
+template<typename... Type, typename Other>
+struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam List Type list.
+ * @tparam Type Type to look for.
+ */
+template<typename List, typename Type>
+inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct type_list_diff;
+
+/**
+ * @brief Computes the difference between two type lists.
+ * @tparam Type Types provided by the first type list.
+ * @tparam Other Types provided by the second type list.
+ */
+template<typename... Type, typename... Other>
+struct type_list_diff<type_list<Type...>, type_list<Other...>> {
+/*! @brief A type list that is the difference between the two type lists. */
+using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Type lists between which to compute the difference.
+ */
+template<typename... List>
+using type_list_diff_t = typename type_list_diff<List...>::type;
+
+/**
+ * @brief A class to use to push around lists of constant values, nothing more.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list {
+/*! @brief Value list type. */
+using type = value_list;
+/*! @brief Compile-time number of elements in the value list. */
+static constexpr auto size = sizeof...(Value);
+};
+
+/*! @brief Primary template isn't defined on purpose. */
+template<std::size_t, typename>
+struct value_list_element;
+
+/**
+ * @brief Provides compile-time indexed access to the values of a value list.
+ * @tparam Index Index of the value to return.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<std::size_t Index, auto Value, auto... Other>
+struct value_list_element<Index, value_list<Value, Other...>>
+: value_list_element<Index - 1u, value_list<Other...>> {};
+
+/**
+ * @brief Provides compile-time indexed access to the types of a type list.
+ * @tparam Value First value provided by the value list.
+ * @tparam Other Other values provided by the value list.
+ */
+template<auto Value, auto... Other>
+struct value_list_element<0u, value_list<Value, Other...>> {
+/*! @brief Searched value. */
+static constexpr auto value = Value;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Index Index of the value to return.
+ * @tparam List Value list to search into.
+ */
+template<std::size_t Index, typename List>
+inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @return A value list composed by the values of both the value lists.
+ */
+template<auto... Value, auto... Other>
+constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
+return {};
+}
+
+/*! @brief Primary template isn't defined on purpose. */
+template<typename...>
+struct value_list_cat;
+
+/*! @brief Concatenates multiple value lists. */
+template<>
+struct value_list_cat<> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<>;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the first value list.
+ * @tparam Other Values provided by the second value list.
+ * @tparam List Other value lists, if any.
+ */
+template<auto... Value, auto... Other, typename... List>
+struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
+};
+
+/**
+ * @brief Concatenates multiple value lists.
+ * @tparam Value Values provided by the value list.
+ */
+template<auto... Value>
+struct value_list_cat<value_list<Value...>> {
+/*! @brief A value list composed by the values of all the value lists. */
+using type = value_list<Value...>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam List Value lists to concatenate.
+ */
+template<typename... List>
+using value_list_cat_t = typename value_list_cat<List...>::type;
+
+/*! @brief Same as std::is_invocable, but with tuples. */
+template<typename, typename>
+struct is_applicable: std::false_type {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @copybrief is_applicable
+ * @tparam Func A valid function type.
+ * @tparam Tuple Tuple-like type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, template<typename...> class Tuple, typename... Args>
+struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Func, typename Args>
+inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
+
+/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
+template<typename, typename, typename>
+struct is_applicable_r: std::false_type {};
+
+/**
+ * @copybrief is_applicable_r
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename... Args>
+struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Ret The type to which the return type of the function should be
+ * convertible.
+ * @tparam Func A valid function type.
+ * @tparam Args The list of arguments to use to probe the function type.
+ */
+template<typename Ret, typename Func, typename Args>
+inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * complete, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_complete: std::false_type {};
+
+/*! @copydoc is_complete */
+template<typename Type>
+struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_complete_v = is_complete<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is an
+ * iterator, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_iterator: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_iterator_category: std::false_type {};
+
+template<typename Type>
+struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_iterator */
+template<typename Type>
+struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
+: internal::has_iterator_category<Type> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_iterator_v = is_iterator<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is both
+ * an empty and non-final class, false otherwise.
+ * @tparam Type The type to test
+ */
+template<typename Type>
+struct is_ebco_eligible
+: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if `Type::is_transparent`
+ * is valid and denotes a type, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_transparent: std::false_type {};
+
+/*! @copydoc is_transparent */
+template<typename Type>
+struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_transparent_v = is_transparent<Type>::value;
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * equality comparable, false otherwise.
+ * @tparam Type The type to test.
+ */
+template<typename Type, typename = void>
+struct is_equality_comparable: std::false_type {};
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename, typename = void>
+struct has_tuple_size_value: std::false_type {};
+
+template<typename Type>
+struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
+
+template<typename Type, std::size_t... Index>
+[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
+return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
+}
+
+template<typename>
+[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
+return true;
+}
+
+template<typename Type>
+[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
+if constexpr(is_iterator_v<Type>) {
+return true;
+} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
+return maybe_equality_comparable<Type>(choice<0>);
+} else {
+return is_equality_comparable<typename Type::value_type>::value;
+}
+}
+
+template<typename Type>
+[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
+if constexpr(has_tuple_size_value<Type>::value) {
+return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
+} else {
+return maybe_equality_comparable<Type>(choice<1>);
+}
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @copydoc is_equality_comparable */
+template<typename Type>
+struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
+: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
+
+/**
+ * @brief Helper variable template.
+ * @tparam Type The type to test.
+ */
+template<typename Type>
+inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
+
+/**
+ * @brief Transcribes the constness of a type to another type.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+struct constness_as {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::remove_const_t<To>;
+};
+
+/*! @copydoc constness_as */
+template<typename To, typename From>
+struct constness_as<To, const From> {
+/*! @brief The type resulting from the transcription of the constness. */
+using type = std::add_const_t<To>;
+};
+
+/**
+ * @brief Alias template to facilitate the transcription of the constness.
+ * @tparam To The type to which to transcribe the constness.
+ * @tparam From The type from which to transcribe the constness.
+ */
+template<typename To, typename From>
+using constness_as_t = typename constness_as<To, From>::type;
+
+/**
+ * @brief Extracts the class of a non-static member object or function.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+class member_class {
+static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...));
+
+template<typename Class, typename Ret, typename... Args>
+static Class *clazz(Ret (Class::*)(Args...) const);
+
+template<typename Class, typename Type>
+static Class *clazz(Type Class::*);
+
+public:
+/*! @brief The class of the given non-static member object or function. */
+using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
+};
+
+/**
+ * @brief Helper type.
+ * @tparam Member A pointer to a non-static member object or function.
+ */
+template<typename Member>
+using member_class_t = typename member_class<Member>::type;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Type, std::size_t, typename = void>
+struct compressed_pair_element {
+using reference = Type &;
+using const_reference = const Type &;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
+compressed_pair_element()
+: value{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: value{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: value{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return value;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return value;
+}
+
+private:
+Type value;
+};
+
+template<typename Type, std::size_t Tag>
+struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
+using reference = Type &;
+using const_reference = const Type &;
+using base_type = Type;
+
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
+compressed_pair_element()
+: base_type{} {}
+
+template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
+compressed_pair_element(Args &&args)
+: base_type{std::forward<Args>(args)} {}
+
+template<typename... Args, std::size_t... Index>
+compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
+: base_type{std::forward<Args>(std::get<Index>(args))...} {}
+
+[[nodiscard]] reference get() ENTT_NOEXCEPT {
+return *this;
+}
+
+[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
+return *this;
+}
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief A compressed pair.
+ *
+ * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
+ * reduce its final size to a minimum.
+ *
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+class compressed_pair final
+: internal::compressed_pair_element<First, 0u>,
+internal::compressed_pair_element<Second, 1u> {
+using first_base = internal::compressed_pair_element<First, 0u>;
+using second_base = internal::compressed_pair_element<Second, 1u>;
+
+public:
+/*! @brief The type of the first element that the pair stores. */
+using first_type = First;
+/*! @brief The type of the second element that the pair stores. */
+using second_type = Second;
+
+/**
+     * @brief Default constructor, conditionally enabled.
+     *
+     * This constructor is only available when the types that the pair stores
+     * are both at least default constructible.
+     *
+     * @tparam Dummy Dummy template parameter used for internal purposes.
+     */
+template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
+constexpr compressed_pair()
+: first_base{},
+second_base{} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+constexpr compressed_pair(const compressed_pair &other) = default;
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+constexpr compressed_pair(compressed_pair &&other) = default;
+
+/**
+     * @brief Constructs a pair from its values.
+     * @tparam Arg Type of value to use to initialize the first element.
+     * @tparam Other Type of value to use to initialize the second element.
+     * @param arg Value to use to initialize the first element.
+     * @param other Value to use to initialize the second element.
+     */
+template<typename Arg, typename Other>
+constexpr compressed_pair(Arg &&arg, Other &&other)
+: first_base{std::forward<Arg>(arg)},
+second_base{std::forward<Other>(other)} {}
+
+/**
+     * @brief Constructs a pair by forwarding the arguments to its parts.
+     * @tparam Args Types of arguments to use to initialize the first element.
+     * @tparam Other Types of arguments to use to initialize the second element.
+     * @param args Arguments to use to initialize the first element.
+     * @param other Arguments to use to initialize the second element.
+     */
+template<typename... Args, typename... Other>
+constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
+: first_base{std::move(args), std::index_sequence_for<Args...>{}},
+second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(const compressed_pair &other) = default;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This compressed pair object.
+     */
+constexpr compressed_pair &operator=(compressed_pair &&other) = default;
+
+/**
+     * @brief Returns the first element that a pair stores.
+     * @return The first element that a pair stores.
+     */
+[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
+return static_cast<first_base &>(*this).get();
+}
+
+/*! @copydoc first */
+[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
+return static_cast<const first_base &>(*this).get();
+}
+
+/**
+     * @brief Returns the second element that a pair stores.
+     * @return The second element that a pair stores.
+     */
+[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
+return static_cast<second_base &>(*this).get();
+}
+
+/*! @copydoc second */
+[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
+return static_cast<const second_base &>(*this).get();
+}
+
+/**
+     * @brief Swaps two compressed pair objects.
+     * @param other The compressed pair to swap with.
+     */
+void swap(compressed_pair &other) {
+using std::swap;
+swap(first(), other.first());
+swap(second(), other.second());
+}
+
+/**
+     * @brief Extracts an element from the compressed pair.
+     * @tparam Index An integer value that is either 0 or 1.
+     * @return Returns a reference to the first element if `Index` is 0 and a
+     * reference to the second element if `Index` is 1.
+     */
+template<std::size_t Index>
+decltype(auto) get() ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+
+/*! @copydoc get */
+template<std::size_t Index>
+decltype(auto) get() const ENTT_NOEXCEPT {
+if constexpr(Index == 0u) {
+return first();
+} else {
+static_assert(Index == 1u, "Index out of bounds");
+return second();
+}
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Type Type of value to use to initialize the first element.
+ * @tparam Other Type of value to use to initialize the second element.
+ */
+template<typename Type, typename Other>
+compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
+
+/**
+ * @brief Swaps two compressed pair objects.
+ * @tparam First The type of the first element that the pairs store.
+ * @tparam Second The type of the second element that the pairs store.
+ * @param lhs A valid compressed pair object.
+ * @param rhs A valid compressed pair object.
+ */
+template<typename First, typename Second>
+inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
+lhs.swap(rhs);
+}
+
+} // namespace entt
+
+// disable structured binding support for clang 6, it messes when specializing tuple_size
+#if !defined __clang_major__ || __clang_major__ > 6
+namespace std {
+
+/**
+ * @brief `std::tuple_size` specialization for `compressed_pair`s.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<typename First, typename Second>
+struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
+
+/**
+ * @brief `std::tuple_element` specialization for `compressed_pair`s.
+ * @tparam Index The index of the type to return.
+ * @tparam First The type of the first element that the pair stores.
+ * @tparam Second The type of the second element that the pair stores.
+ */
+template<size_t Index, typename First, typename Second>
+struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
+static_assert(Index < 2u, "Index out of bounds");
+};
+
+} // namespace std
+#endif
+
+#endif
+
+// #include "../core/fwd.hpp"
+#ifndef ENTT_CORE_FWD_HPP
+#define ENTT_CORE_FWD_HPP
+
+#include <cstdint>
+#include <type_traits>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
+class basic_any;
+
+/*! @brief Alias declaration for type identifiers. */
+using id_type = ENTT_ID_TYPE;
+
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+} // namespace entt
+
+#endif
+
+// #include "../core/type_info.hpp"
+#ifndef ENTT_CORE_TYPE_INFO_HPP
+#define ENTT_CORE_TYPE_INFO_HPP
+
+#include <string_view>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/attribute.h"
+#ifndef ENTT_CORE_ATTRIBUTE_H
+#define ENTT_CORE_ATTRIBUTE_H
+
+#ifndef ENTT_EXPORT
+#    if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
+#        define ENTT_EXPORT __declspec(dllexport)
+#        define ENTT_IMPORT __declspec(dllimport)
+#        define ENTT_HIDDEN
+#    elif defined __GNUC__ && __GNUC__ >= 4
+#        define ENTT_EXPORT __attribute__((visibility("default")))
+#        define ENTT_IMPORT __attribute__((visibility("default")))
+#        define ENTT_HIDDEN __attribute__((visibility("hidden")))
+#    else /* Unsupported compiler */
+#        define ENTT_EXPORT
+#        define ENTT_IMPORT
+#        define ENTT_HIDDEN
+#    endif
+#endif
+
+#ifndef ENTT_API
+#    if defined ENTT_API_EXPORT
+#        define ENTT_API ENTT_EXPORT
+#    elif defined ENTT_API_IMPORT
+#        define ENTT_API ENTT_IMPORT
+#    else /* No API */
+#        define ENTT_API
+#    endif
+#endif
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "hashed_string.hpp"
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+#include <cstddef>
+#include <cstdint>
+// #include "../config/config.h"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename>
+struct fnv1a_traits;
+
+template<>
+struct fnv1a_traits<std::uint32_t> {
+using type = std::uint32_t;
+static constexpr std::uint32_t offset = 2166136261;
+static constexpr std::uint32_t prime = 16777619;
+};
+
+template<>
+struct fnv1a_traits<std::uint64_t> {
+using type = std::uint64_t;
+static constexpr std::uint64_t offset = 14695981039346656037ull;
+static constexpr std::uint64_t prime = 1099511628211ull;
+};
+
+template<typename Char>
+struct basic_hashed_string {
+using value_type = Char;
+using size_type = std::size_t;
+using hash_type = id_type;
+
+const value_type *repr;
+size_type length;
+hash_type hash;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Zero overhead unique identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifiers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ *
+ * @warning
+ * This class doesn't take ownership of user-supplied strings nor does it make a
+ * copy of them.
+ *
+ * @tparam Char Character type.
+ */
+template<typename Char>
+class basic_hashed_string: internal::basic_hashed_string<Char> {
+using base_type = internal::basic_hashed_string<Char>;
+using hs_traits = internal::fnv1a_traits<id_type>;
+
+struct const_wrapper {
+// non-explicit constructor on purpose
+constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
+const Char *repr;
+};
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
+base_type base{str, 0u, hs_traits::offset};
+
+for(; str[base.length]; ++base.length) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
+}
+
+return base;
+}
+
+// Fowler–Noll–Vo hash function v. 1a - the good
+[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
+base_type base{str, len, hs_traits::offset};
+
+for(size_type pos{}; pos < len; ++pos) {
+base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
+}
+
+return base;
+}
+
+public:
+/*! @brief Character type. */
+using value_type = typename base_type::value_type;
+/*! @brief Unsigned integer type. */
+using size_type = typename base_type::size_type;
+/*! @brief Unsigned integer type. */
+using hash_type = typename base_type::hash_type;
+
+/**
+     * @brief Returns directly the numeric representation of a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
+return basic_hashed_string{str, len};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     * @return The numeric representation of the string.
+     */
+template<std::size_t N>
+[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
+return basic_hashed_string{str};
+}
+
+/**
+     * @brief Returns directly the numeric representation of a string.
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     * @return The numeric representation of the string.
+     */
+[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
+return basic_hashed_string{wrapper};
+}
+
+/*! @brief Constructs an empty hashed string. */
+constexpr basic_hashed_string() ENTT_NOEXCEPT
+: base_type{} {}
+
+/**
+     * @brief Constructs a hashed string from a string view.
+     * @param str Human-readable identifier.
+     * @param len Length of the string to hash.
+     */
+constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
+: base_type{helper(str, len)} {}
+
+/**
+     * @brief Constructs a hashed string from an array of const characters.
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifier.
+     */
+template<std::size_t N>
+constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
+: base_type{helper(str)} {}
+
+/**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const value_type *`.
+     *
+     * @warning
+     * The lifetime of the string is not extended nor is it copied.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
+: base_type{helper(wrapper.repr)} {}
+
+/**
+     * @brief Returns the size a hashed string.
+     * @return The size of the hashed string.
+     */
+[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
+return base_type::length;
+}
+
+/**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the hashed string.
+     */
+[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
+return base_type::repr;
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
+return base_type::hash;
+}
+
+/*! @copydoc data */
+[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
+return data();
+}
+
+/**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the hashed string.
+     */
+[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @param str Human-readable identifier.
+ * @param len Length of the string to hash.
+ */
+template<typename Char>
+basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifier.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() == rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings differ, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return lhs.value() < rhs.value();
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two hashed strings.
+ * @tparam Char Character type.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+template<typename Char>
+[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/*! @brief Aliases for common character types. */
+using hashed_string = basic_hashed_string<char>;
+
+/*! @brief Aliases for common character types. */
+using hashed_wstring = basic_hashed_string<wchar_t>;
+
+inline namespace literals {
+
+/**
+ * @brief User defined literal for hashed strings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed string.
+ */
+[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_string{str};
+}
+
+/**
+ * @brief User defined literal for hashed wstrings.
+ * @param str The literal without its suffix.
+ * @return A properly initialized hashed wstring.
+ */
+[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
+return hashed_wstring{str};
+}
+
+} // namespace literals
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct ENTT_API type_index final {
+[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
+static ENTT_MAYBE_ATOMIC(id_type) value{};
+return value++;
+}
+};
+
+template<typename Type>
+[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
+#if defined ENTT_PRETTY_FUNCTION
+std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
+auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
+auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
+return value;
+#else
+return std::string_view{""};
+#endif
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
+constexpr auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
+static const auto value = stripped_type_name<Type>();
+return value;
+}
+
+template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
+[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
+constexpr auto stripped = stripped_type_name<Type>();
+constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
+return value;
+}
+
+template<typename Type>
+[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
+static const auto value = [](const auto stripped) {
+return hashed_string::value(stripped.data(), stripped.size());
+}(stripped_type_name<Type>());
+return value;
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Type sequential identifier.
+ * @tparam Type Type for which to generate a sequential identifier.
+ */
+template<typename Type, typename = void>
+struct ENTT_API type_index final {
+/**
+     * @brief Returns the sequential identifier of a given type.
+     * @return The sequential identifier of a given type.
+     */
+[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+static const id_type value = internal::type_index::next();
+return value;
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type hash.
+ * @tparam Type Type for which to generate a hash value.
+ */
+template<typename Type, typename = void>
+struct type_hash final {
+/**
+     * @brief Returns the numeric representation of a given type.
+     * @return The numeric representation of the given type.
+     */
+#if defined ENTT_PRETTY_FUNCTION
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return internal::type_hash<Type>(0);
+#else
+[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
+return type_index<Type>::value();
+#endif
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/**
+ * @brief Type name.
+ * @tparam Type Type for which to generate a name.
+ */
+template<typename Type, typename = void>
+struct type_name final {
+/**
+     * @brief Returns the name of a given type.
+     * @return The name of the given type.
+     */
+[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
+return internal::type_name<Type>(0);
+}
+
+/*! @copydoc value */
+[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
+return value();
+}
+};
+
+/*! @brief Implementation specific information about a type. */
+struct type_info final {
+/**
+     * @brief Constructs a type info object for a given type.
+     * @tparam Type Type for which to construct a type info object.
+     */
+template<typename Type>
+constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
+: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
+alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
+
+/**
+     * @brief Type index.
+     * @return Type index.
+     */
+[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
+return seq;
+}
+
+/**
+     * @brief Type hash.
+     * @return Type hash.
+     */
+[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
+return identifier;
+}
+
+/**
+     * @brief Type name.
+     * @return Type name.
+     */
+[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
+return alias;
+}
+
+private:
+id_type seq;
+id_type identifier;
+std::string_view alias;
+};
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects are identical, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.hash() == rhs.hash();
+}
+
+/**
+ * @brief Compares the contents of two type info objects.
+ * @param lhs A type info object.
+ * @param rhs A type info object.
+ * @return True if the two type info objects differ, false otherwise.
+ */
+[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than the second, false otherwise.
+ */
+[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return lhs.index() < rhs.index();
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is less than or equal to the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(rhs < lhs);
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than the second, false
+ * otherwise.
+ */
+[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return rhs < lhs;
+}
+
+/**
+ * @brief Compares two type info objects.
+ * @param lhs A valid type info object.
+ * @param rhs A valid type info object.
+ * @return True if the first element is greater than or equal to the second,
+ * false otherwise.
+ */
+[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
+return !(lhs < rhs);
+}
+
+/**
+ * @brief Returns the type info object associated to a given type.
+ *
+ * The returned element refers to an object with static storage duration.<br/>
+ * The type doesn't need to be a complete type. If the type is a reference, the
+ * result refers to the referenced type. In all cases, top-level cv-qualifiers
+ * are ignored.
+ *
+ * @tparam Type Type for which to generate a type info object.
+ * @return A reference to a properly initialized type info object.
+ */
+template<typename Type>
+[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
+if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
+static type_info instance{std::in_place_type<Type>};
+return instance;
+} else {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+}
+
+/*! @copydoc type_id */
+template<typename Type>
+[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
+return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
+}
+
+} // namespace entt
+
+#endif
+
+// #include "../core/utility.hpp"
+#ifndef ENTT_CORE_UTILITY_HPP
+#define ENTT_CORE_UTILITY_HPP
+
+#include <utility>
+// #include "../config/config.h"
+
+
+namespace entt {
+
+/*! @brief Identity function object (waiting for C++20). */
+struct identity {
+/*! @brief Indicates that this is a transparent function object. */
+using is_transparent = void;
+
+/**
+     * @brief Returns its argument unchanged.
+     * @tparam Type Type of the argument.
+     * @param value The actual argument.
+     * @return The submitted value as-is.
+     */
+template<class Type>
+[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
+return std::forward<Type>(value);
+}
+};
+
+/**
+ * @brief Constant utility to disambiguate overloaded members of a class.
+ * @tparam Type Type of the desired overload.
+ * @tparam Class Type of class to which the member belongs.
+ * @param member A valid pointer to a member.
+ * @return Pointer to the member.
+ */
+template<typename Type, typename Class>
+[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
+return member;
+}
+
+/**
+ * @brief Constant utility to disambiguate overloaded functions.
+ * @tparam Func Function type of the desired overload.
+ * @param func A valid pointer to a function.
+ * @return Pointer to the function.
+ */
+template<typename Func>
+[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
+return func;
+}
+
+/**
+ * @brief Helper type for visitors.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+struct overloaded: Func... {
+using Func::operator()...;
+};
+
+/**
+ * @brief Deduction guide.
+ * @tparam Func Types of function objects.
+ */
+template<class... Func>
+overloaded(Func...) -> overloaded<Func...>;
+
+/**
+ * @brief Basic implementation of a y-combinator.
+ * @tparam Func Type of a potentially recursive function.
+ */
+template<class Func>
+struct y_combinator {
+/**
+     * @brief Constructs a y-combinator from a given function.
+     * @param recursive A potentially recursive function.
+     */
+y_combinator(Func recursive)
+: func{std::move(recursive)} {}
+
+/**
+     * @brief Invokes a y-combinator and therefore its underlying function.
+     * @tparam Args Types of arguments to use to invoke the underlying function.
+     * @param args Parameters to use to invoke the underlying function.
+     * @return Return value of the underlying function, if any.
+     */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) const {
+return func(*this, std::forward<Args>(args)...);
+}
+
+/*! @copydoc operator()() */
+template<class... Args>
+decltype(auto) operator()(Args &&...args) {
+return func(*this, std::forward<Args>(args)...);
+}
+
+private:
+Func func;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+// #include "sigh.hpp"
+#ifndef ENTT_SIGNAL_SIGH_HPP
+#define ENTT_SIGNAL_SIGH_HPP
+
+#include <algorithm>
+#include <functional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "delegate.hpp"
+#ifndef ENTT_SIGNAL_DELEGATE_HPP
+#define ENTT_SIGNAL_DELEGATE_HPP
+
+#include <cstddef>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../core/type_traits.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename Ret, typename... Args>
+auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
+
+template<typename Ret, typename Type, typename... Args, typename Other>
+auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Ret, typename... Args, typename... Other>
+auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
+
+template<typename Class, typename Type, typename... Other>
+auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
+
+template<typename... Type>
+using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
+
+template<typename... Class, typename Ret, typename... Args>
+[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) {
+return std::index_sequence_for<Class..., Args...>{};
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/*! @brief Used to wrap a function or a member of a specified type. */
+template<auto>
+struct connect_arg_t {};
+
+/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
+template<auto Func>
+inline constexpr connect_arg_t<Func> connect_arg{};
+
+/**
+ * @brief Basic delegate implementation.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ */
+template<typename>
+class delegate;
+
+/**
+ * @brief Utility class to use to send around functions and members.
+ *
+ * Unmanaged delegate for function pointers and members. Users of this class are
+ * in charge of disconnecting instances before deleting them.
+ *
+ * A delegate can be used as a general purpose invoker without memory overhead
+ * for free functions possibly with payloads and bound or unbound members.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+class delegate<Ret(Args...)> {
+template<auto Candidate, std::size_t... Index>
+[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+template<auto Candidate, typename Type, std::size_t... Index>
+[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+return [](const void *payload, Args... args) -> Ret {
+[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
+};
+}
+
+public:
+/*! @brief Function type of the contained target. */
+using function_type = Ret(const void *, Args...);
+/*! @brief Function type of the delegate. */
+using type = Ret(Args...);
+/*! @brief Return type of the delegate. */
+using result_type = Ret;
+
+/*! @brief Default constructor. */
+delegate() ENTT_NOEXCEPT
+: instance{nullptr},
+fn{nullptr} {}
+
+/**
+     * @brief Constructs a delegate and connects a free function or an unbound
+     * member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
+connect<Candidate>();
+}
+
+/**
+     * @brief Constructs a delegate and connects a free function with payload or
+     * a bound member.
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
+connect<Candidate>(std::forward<Type>(value_or_instance));
+}
+
+/**
+     * @brief Constructs a delegate and connects an user defined function with
+     * optional payload.
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+connect(function, payload);
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a delegate.
+     * @tparam Candidate Function or member to connect to the delegate.
+     */
+template<auto Candidate>
+void connect() ENTT_NOEXCEPT {
+instance = nullptr;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+fn = [](const void *, Args... args) -> Ret {
+return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
+};
+} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
+fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
+} else {
+fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the delegate.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the delegate itself.
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid reference that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type &value_or_instance) ENTT_NOEXCEPT {
+instance = &value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * delegate.
+     *
+     * @sa connect(Type &)
+     *
+     * @tparam Candidate Function or member to connect to the delegate.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void connect(Type *value_or_instance) ENTT_NOEXCEPT {
+instance = value_or_instance;
+
+if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
+fn = [](const void *payload, Args... args) -> Ret {
+Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
+return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
+};
+} else {
+fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
+}
+}
+
+/**
+     * @brief Connects an user defined function with optional payload to a
+     * delegate.
+     *
+     * The delegate isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of an instance overcomes
+     * the one of the delegate.<br/>
+     * The payload is returned as the first argument to the target function in
+     * all cases.
+     *
+     * @param function Function to connect to the delegate.
+     * @param payload User defined arbitrary data.
+     */
+void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
+instance = payload;
+fn = function;
+}
+
+/**
+     * @brief Resets a delegate.
+     *
+     * After a reset, a delegate cannot be invoked anymore.
+     */
+void reset() ENTT_NOEXCEPT {
+instance = nullptr;
+fn = nullptr;
+}
+
+/**
+     * @brief Returns the instance or the payload linked to a delegate, if any.
+     * @return An opaque pointer to the underlying data.
+     */
+[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
+return instance;
+}
+
+/**
+     * @brief Triggers a delegate.
+     *
+     * The delegate invokes the underlying function and returns the result.
+     *
+     * @warning
+     * Attempting to trigger an invalid delegate results in undefined
+     * behavior.
+     *
+     * @param args Arguments to use to invoke the underlying function.
+     * @return The value returned by the underlying function.
+     */
+Ret operator()(Args... args) const {
+ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
+return fn(instance, std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Checks whether a delegate actually stores a listener.
+     * @return False if the delegate is empty, true otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+// no need to also test instance
+return !(fn == nullptr);
+}
+
+/**
+     * @brief Compares the contents of two delegates.
+     * @param other Delegate with which to compare.
+     * @return False if the two contents differ, true otherwise.
+     */
+[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
+return fn == other.fn && instance == other.instance;
+}
+
+private:
+const void *instance;
+function_type *fn;
+};
+
+/**
+ * @brief Compares the contents of two delegates.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @param lhs A valid delegate object.
+ * @param rhs A valid delegate object.
+ * @return True if the two contents differ, false otherwise.
+ */
+template<typename Ret, typename... Args>
+[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
+return !(lhs == rhs);
+}
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ */
+template<auto Candidate>
+delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Candidate Function or member to connect to the delegate.
+ * @tparam Type Type of class or type of payload.
+ */
+template<auto Candidate, typename Type>
+delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
+
+/**
+ * @brief Deduction guide.
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ */
+template<typename Ret, typename... Args>
+delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
+
+} // namespace entt
+
+#endif
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Sink class.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid signal handler type.
+ */
+template<typename Type>
+class sink;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Allocator>
+class sigh;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * It works directly with references to classes and pointers to member functions
+ * as well as pointers to free functions. Users of this class are in charge of
+ * disconnecting instances before deleting them.
+ *
+ * This class serves mainly two purposes:
+ *
+ * * Creating signals to use later to notify a bunch of listeners.
+ * * Collecting results from a set of functions like in a voting system.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sigh<Ret(Args...), Allocator> {
+/*! @brief A sink is allowed to modify a signal. */
+friend class sink<sigh<Ret(Args...), Allocator>>;
+
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type");
+using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Sink type. */
+using sink_type = sink<sigh<Ret(Args...), Allocator>>;
+
+/*! @brief Default constructor. */
+sigh()
+: sigh{allocator_type{}} {}
+
+/**
+     * @brief Constructs a signal handler with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit sigh(const allocator_type &allocator)
+: calls{allocator} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+sigh(const sigh &other)
+: calls{other.calls} {}
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+sigh(const sigh &other, const allocator_type &allocator)
+: calls{other.calls, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+sigh(sigh &&other) ENTT_NOEXCEPT
+: calls{std::move(other.calls)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: calls{std::move(other.calls), allocator} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This signal handler.
+     */
+sigh &operator=(const sigh &other) {
+calls = other.calls;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This signal handler.
+     */
+sigh &operator=(sigh &&other) ENTT_NOEXCEPT {
+calls = std::move(other.calls);
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given signal handler.
+     * @param other Signal handler to exchange the content with.
+     */
+void swap(sigh &other) {
+using std::swap;
+swap(calls, other.calls);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return calls.get_allocator();
+}
+
+/**
+     * @brief Instance type when it comes to connecting member functions.
+     * @tparam Class Type of class to which the member function belongs.
+     */
+template<typename Class>
+using instance_type = Class *;
+
+/**
+     * @brief Number of listeners connected to the signal.
+     * @return Number of listeners currently connected.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return calls.size();
+}
+
+/**
+     * @brief Returns false if at least a listener is connected to the signal.
+     * @return True if the signal has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return calls.empty();
+}
+
+/**
+     * @brief Triggers a signal.
+     *
+     * All the listeners are notified. Order isn't guaranteed.
+     *
+     * @param args Arguments to use to invoke listeners.
+     */
+void publish(Args... args) const {
+for(auto &&call: std::as_const(calls)) {
+call(args...);
+}
+}
+
+/**
+     * @brief Collects return values from the listeners.
+     *
+     * The collector must expose a call operator with the following properties:
+     *
+     * * The return type is either `void` or such that it's convertible to
+     *   `bool`. In the second case, a true value will stop the iteration.
+     * * The list of parameters is empty if `Ret` is `void`, otherwise it
+     *   contains a single element such that `Ret` is convertible to it.
+     *
+     * @tparam Func Type of collector to use, if any.
+     * @param func A valid function object.
+     * @param args Arguments to use to invoke listeners.
+     */
+template<typename Func>
+void collect(Func func, Args... args) const {
+for(auto &&call: calls) {
+if constexpr(std::is_void_v<Ret>) {
+if constexpr(std::is_invocable_r_v<bool, Func>) {
+call(args...);
+if(func()) { break; }
+} else {
+call(args...);
+func();
+}
+} else {
+if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
+if(func(call(args...))) { break; }
+} else {
+func(call(args...));
+}
+}
+}
+}
+
+private:
+container_type calls;
+};
+
+/**
+ * @brief Connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.
+ */
+class connection {
+/*! @brief A sink is allowed to create connection objects. */
+template<typename>
+friend class sink;
+
+connection(delegate<void(void *)> fn, void *ref)
+: disconnect{fn}, signal{ref} {}
+
+public:
+/*! @brief Default constructor. */
+connection() = default;
+
+/**
+     * @brief Checks whether a connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(disconnect);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+if(disconnect) {
+disconnect(signal);
+disconnect.reset();
+}
+}
+
+private:
+delegate<void(void *)> disconnect;
+void *signal{};
+};
+
+/**
+ * @brief Scoped connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.<br/>
+ * A scoped connection automatically breaks the link between the two objects
+ * when it goes out of scope.
+ */
+struct scoped_connection {
+/*! @brief Default constructor. */
+scoped_connection() = default;
+
+/**
+     * @brief Constructs a scoped connection from a basic connection.
+     * @param other A valid connection object.
+     */
+scoped_connection(const connection &other)
+: conn{other} {}
+
+/*! @brief Default copy constructor, deleted on purpose. */
+scoped_connection(const scoped_connection &) = delete;
+
+/**
+     * @brief Move constructor.
+     * @param other The scoped connection to move from.
+     */
+scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT
+: conn{std::exchange(other.conn, {})} {}
+
+/*! @brief Automatically breaks the link on destruction. */
+~scoped_connection() {
+conn.release();
+}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(const scoped_connection &) = delete;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The scoped connection to move from.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT {
+conn = std::exchange(other.conn, {});
+return *this;
+}
+
+/**
+     * @brief Acquires a connection.
+     * @param other The connection object to acquire.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(connection other) {
+conn = std::move(other);
+return *this;
+}
+
+/**
+     * @brief Checks whether a scoped connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(conn);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+conn.release();
+}
+
+private:
+connection conn;
+};
+
+/**
+ * @brief Sink class.
+ *
+ * A sink is used to connect listeners to signals and to disconnect them.<br/>
+ * The function type for a listener is the one of the signal to which it
+ * belongs.
+ *
+ * The clear separation between a signal and a sink permits to store the former
+ * as private data member without exposing the publish functionality to the
+ * users of the class.
+ *
+ * @warning
+ * Lifetime of a sink must not overcome that of the signal to which it refers.
+ * In any other case, attempting to use a sink results in undefined behavior.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sink<sigh<Ret(Args...), Allocator>> {
+using signal_type = sigh<Ret(Args...), Allocator>;
+using difference_type = typename signal_type::container_type::difference_type;
+
+template<auto Candidate, typename Type>
+static void release(Type value_or_instance, void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
+}
+
+template<auto Candidate>
+static void release(void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
+}
+
+public:
+/**
+     * @brief Constructs a sink that is allowed to modify a given signal.
+     * @param ref A valid reference to a signal object.
+     */
+sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT
+: offset{},
+signal{&ref} {}
+
+/**
+     * @brief Returns false if at least a listener is connected to the sink.
+     * @return True if the sink has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return signal->calls.empty();
+}
+
+/**
+     * @brief Returns a sink that connects before a given free function or an
+     * unbound member.
+     * @tparam Function A valid free function pointer.
+     * @return A properly initialized sink object.
+     */
+template<auto Function>
+[[nodiscard]] sink before() {
+delegate<Ret(Args...)> call{};
+call.template connect<Function>();
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a free function with payload
+     * or a bound member.
+     * @tparam Candidate Member or free function to look for.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<auto Candidate, typename Type>
+[[nodiscard]] sink before(Type &&value_or_instance) {
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type &value_or_instance) {
+return before(&value_or_instance);
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type *value_or_instance) {
+sink other{*this};
+
+if(value_or_instance) {
+const auto &calls = signal->calls;
+const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
+return delegate.data() == value_or_instance;
+});
+
+other.offset = calls.cend() - it;
+}
+
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before anything else.
+     * @return A properly initialized sink object.
+     */
+[[nodiscard]] sink before() {
+sink other{*this};
+other.offset = signal->calls.size();
+return other;
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a signal.
+     *
+     * The signal handler performs checks to avoid multiple connections for the
+     * same function.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate>
+connection connect() {
+disconnect<Candidate>();
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate>>();
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * signal.
+     *
+     * The signal isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the signal. On the other side, the signal handler performs
+     * checks to avoid multiple connections for the same function.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the signal itself.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate, typename Type>
+connection connect(Type &&value_or_instance) {
+disconnect<Candidate>(value_or_instance);
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate, Type>>(value_or_instance);
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Disconnects a free function or an unbound member from a signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     */
+template<auto Candidate>
+void disconnect() {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects a free function with payload or a bound member from a
+     * signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void disconnect(Type &&value_or_instance) {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type &value_or_instance) {
+disconnect(&value_or_instance);
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type *value_or_instance) {
+if(value_or_instance) {
+auto &calls = signal->calls;
+auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
+calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
+}
+}
+
+/*! @brief Disconnects all the listeners from a signal. */
+void disconnect() {
+signal->calls.clear();
+}
+
+private:
+difference_type offset;
+signal_type *signal;
+};
+
+/**
+ * @brief Deduction guide.
+ *
+ * It allows to deduce the signal handler type of a sink directly from the
+ * signal it refers to.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
+
+} // namespace entt
+
+#endif
+
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct basic_dispatcher_handler {
+virtual ~basic_dispatcher_handler() = default;
+virtual void publish() = 0;
+virtual void disconnect(void *) = 0;
+virtual void clear() ENTT_NOEXCEPT = 0;
+virtual std::size_t size() const ENTT_NOEXCEPT = 0;
+};
+
+template<typename Event, typename Allocator>
+class dispatcher_handler final: public basic_dispatcher_handler {
+static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
+
+using alloc_traits = std::allocator_traits<Allocator>;
+using signal_type = sigh<void(Event &), typename alloc_traits::template rebind_alloc<void (*)(Event &)>>;
+using container_type = std::vector<Event, typename alloc_traits::template rebind_alloc<Event>>;
+
+public:
+using allocator_type = Allocator;
+
+dispatcher_handler(const allocator_type &allocator)
+: signal{allocator},
+events{allocator} {}
+
+void publish() override {
+const auto length = events.size();
+
+for(std::size_t pos{}; pos < length; ++pos) {
+signal.publish(events[pos]);
+}
+
+events.erase(events.cbegin(), events.cbegin() + length);
+}
+
+void disconnect(void *instance) override {
+bucket().disconnect(instance);
+}
+
+void clear() ENTT_NOEXCEPT override {
+events.clear();
+}
+
+[[nodiscard]] auto bucket() ENTT_NOEXCEPT {
+using sink_type = typename sigh<void(Event &)>::sink_type;
+return sink_type{signal};
+}
+
+void trigger(Event event) {
+signal.publish(event);
+}
+
+template<typename... Args>
+void enqueue(Args &&...args) {
+if constexpr(std::is_aggregate_v<Event>) {
+events.push_back(Event{std::forward<Args>(args)...});
+} else {
+events.emplace_back(std::forward<Args>(args)...);
+}
+}
+
+std::size_t size() const ENTT_NOEXCEPT override {
+return events.size();
+}
+
+private:
+signal_type signal;
+container_type events;
+};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic dispatcher implementation.
+ *
+ * A dispatcher can be used either to trigger an immediate event or to enqueue
+ * events to be published all together once per tick.<br/>
+ * Listeners are provided in the form of member functions. For each event of
+ * type `Event`, listeners are such that they can be invoked with an argument of
+ * type `Event &`, no matter what the return type is.
+ *
+ * The dispatcher creates instances of the `sigh` class internally. Refer to the
+ * documentation of the latter for more details.
+ *
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Allocator>
+class basic_dispatcher {
+template<typename Event>
+using handler_type = internal::dispatcher_handler<Event, Allocator>;
+
+using key_type = id_type;
+// std::shared_ptr because of its type erased allocator which is pretty useful here
+using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
+
+using alloc_traits = std::allocator_traits<Allocator>;
+using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
+using container_type = dense_map<id_type, mapped_type, identity, std::equal_to<id_type>, container_allocator>;
+
+template<typename Event>
+[[nodiscard]] handler_type<Event> &assure(const id_type id) {
+auto &&ptr = pools.first()[id];
+
+if(!ptr) {
+const auto &allocator = pools.second();
+ptr = std::allocate_shared<handler_type<Event>>(allocator, allocator);
+}
+
+return static_cast<handler_type<Event> &>(*ptr);
+}
+
+template<typename Event>
+[[nodiscard]] const handler_type<Event> *assure(const id_type id) const {
+auto &container = pools.first();
+
+if(const auto it = container.find(id); it != container.end()) {
+return static_cast<const handler_type<Event> *>(it->second.get());
+}
+
+return nullptr;
+}
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+
+/*! @brief Default constructor. */
+basic_dispatcher()
+: basic_dispatcher{allocator_type{}} {}
+
+/**
+     * @brief Constructs a dispatcher with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit basic_dispatcher(const allocator_type &allocator)
+: pools{allocator, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+basic_dispatcher(basic_dispatcher &&other) ENTT_NOEXCEPT
+: pools{std::move(other.pools)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This dispatcher.
+     */
+basic_dispatcher &operator=(basic_dispatcher &&other) ENTT_NOEXCEPT {
+pools = std::move(other.pools);
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given dispatcher.
+     * @param other Dispatcher to exchange the content with.
+     */
+void swap(basic_dispatcher &other) {
+using std::swap;
+swap(pools, other.pools);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return pools.second();
+}
+
+/**
+     * @brief Returns the number of pending events for a given type.
+     * @tparam Event Type of event for which to return the count.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @return The number of pending events for the given type.
+     */
+template<typename Event>
+size_type size(const id_type id = type_hash<Event>::value()) const ENTT_NOEXCEPT {
+const auto *cpool = assure<Event>(id);
+return cpool ? cpool->size() : 0u;
+}
+
+/**
+     * @brief Returns the total number of pending events.
+     * @return The total number of pending events.
+     */
+size_type size() const ENTT_NOEXCEPT {
+size_type count{};
+
+for(auto &&cpool: pools.first()) {
+count += cpool.second->size();
+}
+
+return count;
+}
+
+/**
+     * @brief Returns a sink object for the given event and queue.
+     *
+     * A sink is an opaque object used to connect listeners to events.
+     *
+     * The function type for a listener is _compatible_ with:
+     * @code{.cpp}
+     * void(Event &);
+     * @endcode
+     *
+     * The order of invocation of the listeners isn't guaranteed.
+     *
+     * @sa sink
+     *
+     * @tparam Event Type of event of which to get the sink.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @return A temporary sink object.
+     */
+template<typename Event>
+[[nodiscard]] auto sink(const id_type id = type_hash<Event>::value()) {
+return assure<Event>(id).bucket();
+}
+
+/**
+     * @brief Triggers an immediate event of a given type.
+     * @tparam Event Type of event to trigger.
+     * @param event An instance of the given type of event.
+     */
+template<typename Event>
+void trigger(Event &&event = {}) {
+trigger(type_hash<std::decay_t<Event>>::value(), std::forward<Event>(event));
+}
+
+/**
+     * @brief Triggers an immediate event on a queue of a given type.
+     * @tparam Event Type of event to trigger.
+     * @param event An instance of the given type of event.
+     * @param id Name used to map the event queue within the dispatcher.
+     */
+template<typename Event>
+void trigger(const id_type id, Event &&event = {}) {
+assure<std::decay_t<Event>>(id).trigger(std::forward<Event>(event));
+}
+
+/**
+     * @brief Enqueues an event of the given type.
+     * @tparam Event Type of event to enqueue.
+     * @tparam Args Types of arguments to use to construct the event.
+     * @param args Arguments to use to construct the event.
+     */
+template<typename Event, typename... Args>
+void enqueue(Args &&...args) {
+enqueue_hint<Event>(type_hash<Event>::value(), std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Enqueues an event of the given type.
+     * @tparam Event Type of event to enqueue.
+     * @param event An instance of the given type of event.
+     */
+template<typename Event>
+void enqueue(Event &&event) {
+enqueue_hint(type_hash<std::decay_t<Event>>::value(), std::forward<Event>(event));
+}
+
+/**
+     * @brief Enqueues an event of the given type.
+     * @tparam Event Type of event to enqueue.
+     * @tparam Args Types of arguments to use to construct the event.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @param args Arguments to use to construct the event.
+     */
+template<typename Event, typename... Args>
+void enqueue_hint(const id_type id, Args &&...args) {
+assure<Event>(id).enqueue(std::forward<Args>(args)...);
+}
+
+/**
+     * @brief Enqueues an event of the given type.
+     * @tparam Event Type of event to enqueue.
+     * @param id Name used to map the event queue within the dispatcher.
+     * @param event An instance of the given type of event.
+     */
+template<typename Event>
+void enqueue_hint(const id_type id, Event &&event) {
+assure<std::decay_t<Event>>(id).enqueue(std::forward<Event>(event));
+}
+
+/**
+     * @brief Utility function to disconnect everything related to a given value
+     * or instance from a dispatcher.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type &value_or_instance) {
+disconnect(&value_or_instance);
+}
+
+/**
+     * @brief Utility function to disconnect everything related to a given value
+     * or instance from a dispatcher.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type *value_or_instance) {
+for(auto &&cpool: pools.first()) {
+cpool.second->disconnect(value_or_instance);
+}
+}
+
+/**
+     * @brief Discards all the events stored so far in a given queue.
+     * @tparam Event Type of event to discard.
+     * @param id Name used to map the event queue within the dispatcher.
+     */
+template<typename Event>
+void clear(const id_type id = type_hash<Event>::value()) {
+assure<Event>(id).clear();
+}
+
+/*! @brief Discards all the events queued so far. */
+void clear() ENTT_NOEXCEPT {
+for(auto &&cpool: pools.first()) {
+cpool.second->clear();
+}
+}
+
+/**
+     * @brief Delivers all the pending events of a given queue.
+     * @tparam Event Type of event to send.
+     * @param id Name used to map the event queue within the dispatcher.
+     */
+template<typename Event>
+void update(const id_type id = type_hash<Event>::value()) {
+assure<Event>(id).publish();
+}
+
+/*! @brief Delivers all the pending events. */
+void update() const {
+for(auto &&cpool: pools.first()) {
+cpool.second->publish();
+}
+}
+
+private:
+compressed_pair<container_type, allocator_type> pools;
+};
+
+} // namespace entt
+
+#endif
+
+// #include "signal/emitter.hpp"
+#ifndef ENTT_SIGNAL_EMITTER_HPP
+#define ENTT_SIGNAL_EMITTER_HPP
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <memory>
+#include <type_traits>
+#include <utility>
+// #include "../config/config.h"
+
+// #include "../container/dense_map.hpp"
+
+// #include "../core/fwd.hpp"
+
+// #include "../core/type_info.hpp"
+
+// #include "../core/utility.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief General purpose event emitter.
+ *
+ * The emitter class template follows the CRTP idiom. To create a custom emitter
+ * type, derived classes must inherit directly from the base class as:
+ *
+ * @code{.cpp}
+ * struct my_emitter: emitter<my_emitter> {
+ *     // ...
+ * }
+ * @endcode
+ *
+ * Pools for the type of events are created internally on the fly. It's not
+ * required to specify in advance the full list of accepted types.<br/>
+ * Moreover, whenever an event is published, an emitter provides the listeners
+ * with a reference to itself along with a reference to the event. Therefore
+ * listeners have an handy way to work with it without incurring in the need of
+ * capturing a reference to the emitter.
+ *
+ * @tparam Derived Actual type of emitter that extends the class template.
+ */
+template<typename Derived>
+class emitter {
+struct basic_pool {
+virtual ~basic_pool() = default;
+virtual bool empty() const ENTT_NOEXCEPT = 0;
+virtual void clear() ENTT_NOEXCEPT = 0;
+};
+
+template<typename Event>
+struct pool_handler final: basic_pool {
+static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
+
+using listener_type = std::function<void(Event &, Derived &)>;
+using element_type = std::pair<bool, listener_type>;
+using container_type = std::list<element_type>;
+using connection_type = typename container_type::iterator;
+
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT override {
+auto pred = [](auto &&element) { return element.first; };
+
+return std::all_of(once_list.cbegin(), once_list.cend(), pred)
+&& std::all_of(on_list.cbegin(), on_list.cend(), pred);
+}
+
+void clear() ENTT_NOEXCEPT override {
+if(publishing) {
+for(auto &&element: once_list) {
+element.first = true;
+}
+
+for(auto &&element: on_list) {
+element.first = true;
+}
+} else {
+once_list.clear();
+on_list.clear();
+}
+}
+
+connection_type once(listener_type listener) {
+return once_list.emplace(once_list.cend(), false, std::move(listener));
+}
+
+connection_type on(listener_type listener) {
+return on_list.emplace(on_list.cend(), false, std::move(listener));
+}
+
+void erase(connection_type conn) {
+conn->first = true;
+
+if(!publishing) {
+auto pred = [](auto &&element) { return element.first; };
+once_list.remove_if(pred);
+on_list.remove_if(pred);
+}
+}
+
+void publish(Event &event, Derived &ref) {
+container_type swap_list;
+once_list.swap(swap_list);
+
+publishing = true;
+
+for(auto &&element: on_list) {
+element.first ? void() : element.second(event, ref);
+}
+
+for(auto &&element: swap_list) {
+element.first ? void() : element.second(event, ref);
+}
+
+publishing = false;
+
+on_list.remove_if([](auto &&element) { return element.first; });
+}
+
+private:
+bool publishing{false};
+container_type once_list{};
+container_type on_list{};
+};
+
+template<typename Event>
+[[nodiscard]] pool_handler<Event> *assure() {
+if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
+auto *cpool = new pool_handler<Event>{};
+ptr.reset(cpool);
+return cpool;
+} else {
+return static_cast<pool_handler<Event> *>(ptr.get());
+}
+}
+
+template<typename Event>
+[[nodiscard]] const pool_handler<Event> *assure() const {
+const auto it = pools.find(type_hash<Event>::value());
+return (it == pools.cend()) ? nullptr : static_cast<const pool_handler<Event> *>(it->second.get());
+}
+
+public:
+/** @brief Type of listeners accepted for the given event. */
+template<typename Event>
+using listener = typename pool_handler<Event>::listener_type;
+
+/**
+     * @brief Generic connection type for events.
+     *
+     * Type of the connection object returned by the event emitter whenever a
+     * listener for the given type is registered.<br/>
+     * It can be used to break connections still in use.
+     *
+     * @tparam Event Type of event for which the connection is created.
+     */
+template<typename Event>
+struct connection: private pool_handler<Event>::connection_type {
+/** @brief Event emitters are friend classes of connections. */
+friend class emitter;
+
+/*! @brief Default constructor. */
+connection() ENTT_NOEXCEPT = default;
+
+/**
+         * @brief Creates a connection that wraps its underlying instance.
+         * @param conn A connection object to wrap.
+         */
+connection(typename pool_handler<Event>::connection_type conn)
+: pool_handler<Event>::connection_type{std::move(conn)} {}
+};
+
+/*! @brief Default constructor. */
+emitter() = default;
+
+/*! @brief Default destructor. */
+virtual ~emitter() ENTT_NOEXCEPT {
+static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template");
+}
+
+/*! @brief Default move constructor. */
+emitter(emitter &&) = default;
+
+/*! @brief Default move assignment operator. @return This emitter. */
+emitter &operator=(emitter &&) = default;
+
+/**
+     * @brief Emits the given event.
+     *
+     * All the listeners registered for the specific event type are invoked with
+     * the given event. The event type must either have a proper constructor for
+     * the arguments provided or be an aggregate type.
+     *
+     * @tparam Event Type of event to publish.
+     * @tparam Args Types of arguments to use to construct the event.
+     * @param args Parameters to use to initialize the event.
+     */
+template<typename Event, typename... Args>
+void publish(Args &&...args) {
+Event instance{std::forward<Args>(args)...};
+assure<Event>()->publish(instance, *static_cast<Derived *>(this));
+}
+
+/**
+     * @brief Registers a long-lived listener with the event emitter.
+     *
+     * This method can be used to register a listener designed to be invoked
+     * more than once for the given event type.<br/>
+     * The connection returned by the method can be freely discarded. It's meant
+     * to be used later to disconnect the listener if required.
+     *
+     * The listener is as a callable object that can be moved and the type of
+     * which is _compatible_ with `void(Event &, Derived &)`.
+     *
+     * @note
+     * Whenever an event is emitted, the emitter provides the listener with a
+     * reference to the derived class. Listeners don't have to capture those
+     * instances for later uses.
+     *
+     * @tparam Event Type of event to which to connect the listener.
+     * @param instance The listener to register.
+     * @return Connection object that can be used to disconnect the listener.
+     */
+template<typename Event>
+connection<Event> on(listener<Event> instance) {
+return assure<Event>()->on(std::move(instance));
+}
+
+/**
+     * @brief Registers a short-lived listener with the event emitter.
+     *
+     * This method can be used to register a listener designed to be invoked
+     * only once for the given event type.<br/>
+     * The connection returned by the method can be freely discarded. It's meant
+     * to be used later to disconnect the listener if required.
+     *
+     * The listener is as a callable object that can be moved and the type of
+     * which is _compatible_ with `void(Event &, Derived &)`.
+     *
+     * @note
+     * Whenever an event is emitted, the emitter provides the listener with a
+     * reference to the derived class. Listeners don't have to capture those
+     * instances for later uses.
+     *
+     * @tparam Event Type of event to which to connect the listener.
+     * @param instance The listener to register.
+     * @return Connection object that can be used to disconnect the listener.
+     */
+template<typename Event>
+connection<Event> once(listener<Event> instance) {
+return assure<Event>()->once(std::move(instance));
+}
+
+/**
+     * @brief Disconnects a listener from the event emitter.
+     *
+     * Do not use twice the same connection to disconnect a listener, it results
+     * in undefined behavior. Once used, discard the connection object.
+     *
+     * @tparam Event Type of event of the connection.
+     * @param conn A valid connection.
+     */
+template<typename Event>
+void erase(connection<Event> conn) {
+assure<Event>()->erase(std::move(conn));
+}
+
+/**
+     * @brief Disconnects all the listeners for the given event type.
+     *
+     * All the connections previously returned for the given event are
+     * invalidated. Using them results in undefined behavior.
+     *
+     * @tparam Event Type of event to reset.
+     */
+template<typename Event>
+void clear() {
+assure<Event>()->clear();
+}
+
+/**
+     * @brief Disconnects all the listeners.
+     *
+     * All the connections previously returned are invalidated. Using them
+     * results in undefined behavior.
+     */
+void clear() ENTT_NOEXCEPT {
+for(auto &&cpool: pools) {
+cpool.second->clear();
+}
+}
+
+/**
+     * @brief Checks if there are listeners registered for the specific event.
+     * @tparam Event Type of event to test.
+     * @return True if there are no listeners registered, false otherwise.
+     */
+template<typename Event>
+[[nodiscard]] bool empty() const {
+const auto *cpool = assure<Event>();
+return !cpool || cpool->empty();
+}
+
+/**
+     * @brief Checks if there are listeners registered with the event emitter.
+     * @return True if there are no listeners registered, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
+return cpool.second->empty();
+});
+}
+
+private:
+dense_map<id_type, std::unique_ptr<basic_pool>, identity> pools{};
+};
+
+} // namespace entt
+
+#endif
+
+// #include "signal/sigh.hpp"
+#ifndef ENTT_SIGNAL_SIGH_HPP
+#define ENTT_SIGNAL_SIGH_HPP
+
+#include <algorithm>
+#include <functional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+// #include "../config/config.h"
+
+// #include "delegate.hpp"
+
+// #include "fwd.hpp"
+
+
+namespace entt {
+
+/**
+ * @brief Sink class.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid signal handler type.
+ */
+template<typename Type>
+class sink;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * Primary template isn't defined on purpose. All the specializations give a
+ * compile-time error unless the template parameter is a function type.
+ *
+ * @tparam Type A valid function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Allocator>
+class sigh;
+
+/**
+ * @brief Unmanaged signal handler.
+ *
+ * It works directly with references to classes and pointers to member functions
+ * as well as pointers to free functions. Users of this class are in charge of
+ * disconnecting instances before deleting them.
+ *
+ * This class serves mainly two purposes:
+ *
+ * * Creating signals to use later to notify a bunch of listeners.
+ * * Collecting results from a set of functions like in a voting system.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sigh<Ret(Args...), Allocator> {
+/*! @brief A sink is allowed to modify a signal. */
+friend class sink<sigh<Ret(Args...), Allocator>>;
+
+using alloc_traits = std::allocator_traits<Allocator>;
+static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type");
+using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
+
+public:
+/*! @brief Allocator type. */
+using allocator_type = Allocator;
+/*! @brief Unsigned integer type. */
+using size_type = std::size_t;
+/*! @brief Sink type. */
+using sink_type = sink<sigh<Ret(Args...), Allocator>>;
+
+/*! @brief Default constructor. */
+sigh()
+: sigh{allocator_type{}} {}
+
+/**
+     * @brief Constructs a signal handler with a given allocator.
+     * @param allocator The allocator to use.
+     */
+explicit sigh(const allocator_type &allocator)
+: calls{allocator} {}
+
+/**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+sigh(const sigh &other)
+: calls{other.calls} {}
+
+/**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+sigh(const sigh &other, const allocator_type &allocator)
+: calls{other.calls, allocator} {}
+
+/**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+sigh(sigh &&other) ENTT_NOEXCEPT
+: calls{std::move(other.calls)} {}
+
+/**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT
+: calls{std::move(other.calls), allocator} {}
+
+/**
+     * @brief Copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This signal handler.
+     */
+sigh &operator=(const sigh &other) {
+calls = other.calls;
+return *this;
+}
+
+/**
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This signal handler.
+     */
+sigh &operator=(sigh &&other) ENTT_NOEXCEPT {
+calls = std::move(other.calls);
+return *this;
+}
+
+/**
+     * @brief Exchanges the contents with those of a given signal handler.
+     * @param other Signal handler to exchange the content with.
+     */
+void swap(sigh &other) {
+using std::swap;
+swap(calls, other.calls);
+}
+
+/**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
+return calls.get_allocator();
+}
+
+/**
+     * @brief Instance type when it comes to connecting member functions.
+     * @tparam Class Type of class to which the member function belongs.
+     */
+template<typename Class>
+using instance_type = Class *;
+
+/**
+     * @brief Number of listeners connected to the signal.
+     * @return Number of listeners currently connected.
+     */
+[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
+return calls.size();
+}
+
+/**
+     * @brief Returns false if at least a listener is connected to the signal.
+     * @return True if the signal has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return calls.empty();
+}
+
+/**
+     * @brief Triggers a signal.
+     *
+     * All the listeners are notified. Order isn't guaranteed.
+     *
+     * @param args Arguments to use to invoke listeners.
+     */
+void publish(Args... args) const {
+for(auto &&call: std::as_const(calls)) {
+call(args...);
+}
+}
+
+/**
+     * @brief Collects return values from the listeners.
+     *
+     * The collector must expose a call operator with the following properties:
+     *
+     * * The return type is either `void` or such that it's convertible to
+     *   `bool`. In the second case, a true value will stop the iteration.
+     * * The list of parameters is empty if `Ret` is `void`, otherwise it
+     *   contains a single element such that `Ret` is convertible to it.
+     *
+     * @tparam Func Type of collector to use, if any.
+     * @param func A valid function object.
+     * @param args Arguments to use to invoke listeners.
+     */
+template<typename Func>
+void collect(Func func, Args... args) const {
+for(auto &&call: calls) {
+if constexpr(std::is_void_v<Ret>) {
+if constexpr(std::is_invocable_r_v<bool, Func>) {
+call(args...);
+if(func()) { break; }
+} else {
+call(args...);
+func();
+}
+} else {
+if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
+if(func(call(args...))) { break; }
+} else {
+func(call(args...));
+}
+}
+}
+}
+
+private:
+container_type calls;
+};
+
+/**
+ * @brief Connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.
+ */
+class connection {
+/*! @brief A sink is allowed to create connection objects. */
+template<typename>
+friend class sink;
+
+connection(delegate<void(void *)> fn, void *ref)
+: disconnect{fn}, signal{ref} {}
+
+public:
+/*! @brief Default constructor. */
+connection() = default;
+
+/**
+     * @brief Checks whether a connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(disconnect);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+if(disconnect) {
+disconnect(signal);
+disconnect.reset();
+}
+}
+
+private:
+delegate<void(void *)> disconnect;
+void *signal{};
+};
+
+/**
+ * @brief Scoped connection class.
+ *
+ * Opaque object the aim of which is to allow users to release an already
+ * estabilished connection without having to keep a reference to the signal or
+ * the sink that generated it.<br/>
+ * A scoped connection automatically breaks the link between the two objects
+ * when it goes out of scope.
+ */
+struct scoped_connection {
+/*! @brief Default constructor. */
+scoped_connection() = default;
+
+/**
+     * @brief Constructs a scoped connection from a basic connection.
+     * @param other A valid connection object.
+     */
+scoped_connection(const connection &other)
+: conn{other} {}
+
+/*! @brief Default copy constructor, deleted on purpose. */
+scoped_connection(const scoped_connection &) = delete;
+
+/**
+     * @brief Move constructor.
+     * @param other The scoped connection to move from.
+     */
+scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT
+: conn{std::exchange(other.conn, {})} {}
+
+/*! @brief Automatically breaks the link on destruction. */
+~scoped_connection() {
+conn.release();
+}
+
+/**
+     * @brief Default copy assignment operator, deleted on purpose.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(const scoped_connection &) = delete;
+
+/**
+     * @brief Move assignment operator.
+     * @param other The scoped connection to move from.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT {
+conn = std::exchange(other.conn, {});
+return *this;
+}
+
+/**
+     * @brief Acquires a connection.
+     * @param other The connection object to acquire.
+     * @return This scoped connection.
+     */
+scoped_connection &operator=(connection other) {
+conn = std::move(other);
+return *this;
+}
+
+/**
+     * @brief Checks whether a scoped connection is properly initialized.
+     * @return True if the connection is properly initialized, false otherwise.
+     */
+[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
+return static_cast<bool>(conn);
+}
+
+/*! @brief Breaks the connection. */
+void release() {
+conn.release();
+}
+
+private:
+connection conn;
+};
+
+/**
+ * @brief Sink class.
+ *
+ * A sink is used to connect listeners to signals and to disconnect them.<br/>
+ * The function type for a listener is the one of the signal to which it
+ * belongs.
+ *
+ * The clear separation between a signal and a sink permits to store the former
+ * as private data member without exposing the publish functionality to the
+ * users of the class.
+ *
+ * @warning
+ * Lifetime of a sink must not overcome that of the signal to which it refers.
+ * In any other case, attempting to use a sink results in undefined behavior.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+class sink<sigh<Ret(Args...), Allocator>> {
+using signal_type = sigh<Ret(Args...), Allocator>;
+using difference_type = typename signal_type::container_type::difference_type;
+
+template<auto Candidate, typename Type>
+static void release(Type value_or_instance, void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
+}
+
+template<auto Candidate>
+static void release(void *signal) {
+sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
+}
+
+public:
+/**
+     * @brief Constructs a sink that is allowed to modify a given signal.
+     * @param ref A valid reference to a signal object.
+     */
+sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT
+: offset{},
+signal{&ref} {}
+
+/**
+     * @brief Returns false if at least a listener is connected to the sink.
+     * @return True if the sink has no listeners connected, false otherwise.
+     */
+[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
+return signal->calls.empty();
+}
+
+/**
+     * @brief Returns a sink that connects before a given free function or an
+     * unbound member.
+     * @tparam Function A valid free function pointer.
+     * @return A properly initialized sink object.
+     */
+template<auto Function>
+[[nodiscard]] sink before() {
+delegate<Ret(Args...)> call{};
+call.template connect<Function>();
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a free function with payload
+     * or a bound member.
+     * @tparam Candidate Member or free function to look for.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<auto Candidate, typename Type>
+[[nodiscard]] sink before(Type &&value_or_instance) {
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+
+const auto &calls = signal->calls;
+const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
+
+sink other{*this};
+other.offset = calls.cend() - it;
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type &value_or_instance) {
+return before(&value_or_instance);
+}
+
+/**
+     * @brief Returns a sink that connects before a given instance or specific
+     * payload.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid pointer that fits the purpose.
+     * @return A properly initialized sink object.
+     */
+template<typename Type>
+[[nodiscard]] sink before(Type *value_or_instance) {
+sink other{*this};
+
+if(value_or_instance) {
+const auto &calls = signal->calls;
+const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
+return delegate.data() == value_or_instance;
+});
+
+other.offset = calls.cend() - it;
+}
+
+return other;
+}
+
+/**
+     * @brief Returns a sink that connects before anything else.
+     * @return A properly initialized sink object.
+     */
+[[nodiscard]] sink before() {
+sink other{*this};
+other.offset = signal->calls.size();
+return other;
+}
+
+/**
+     * @brief Connects a free function or an unbound member to a signal.
+     *
+     * The signal handler performs checks to avoid multiple connections for the
+     * same function.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate>
+connection connect() {
+disconnect<Candidate>();
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate>>();
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Connects a free function with payload or a bound member to a
+     * signal.
+     *
+     * The signal isn't responsible for the connected object or the payload.
+     * Users must always guarantee that the lifetime of the instance overcomes
+     * the one of the signal. On the other side, the signal handler performs
+     * checks to avoid multiple connections for the same function.<br/>
+     * When used to connect a free function with payload, its signature must be
+     * such that the instance is the first argument before the ones used to
+     * define the signal itself.
+     *
+     * @tparam Candidate Function or member to connect to the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     * @return A properly initialized connection object.
+     */
+template<auto Candidate, typename Type>
+connection connect(Type &&value_or_instance) {
+disconnect<Candidate>(value_or_instance);
+
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+signal->calls.insert(signal->calls.end() - offset, std::move(call));
+
+delegate<void(void *)> conn{};
+conn.template connect<&release<Candidate, Type>>(value_or_instance);
+return {std::move(conn), signal};
+}
+
+/**
+     * @brief Disconnects a free function or an unbound member from a signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     */
+template<auto Candidate>
+void disconnect() {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>();
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects a free function with payload or a bound member from a
+     * signal.
+     * @tparam Candidate Function or member to disconnect from the signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<auto Candidate, typename Type>
+void disconnect(Type &&value_or_instance) {
+auto &calls = signal->calls;
+delegate<Ret(Args...)> call{};
+call.template connect<Candidate>(value_or_instance);
+calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type &value_or_instance) {
+disconnect(&value_or_instance);
+}
+
+/**
+     * @brief Disconnects free functions with payload or bound members from a
+     * signal.
+     * @tparam Type Type of class or type of payload.
+     * @param value_or_instance A valid object that fits the purpose.
+     */
+template<typename Type>
+void disconnect(Type *value_or_instance) {
+if(value_or_instance) {
+auto &calls = signal->calls;
+auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
+calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
+}
+}
+
+/*! @brief Disconnects all the listeners from a signal. */
+void disconnect() {
+signal->calls.clear();
+}
+
+private:
+difference_type offset;
+signal_type *signal;
+};
+
+/**
+ * @brief Deduction guide.
+ *
+ * It allows to deduce the signal handler type of a sink directly from the
+ * signal it refers to.
+ *
+ * @tparam Ret Return type of a function type.
+ * @tparam Args Types of arguments of a function type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Ret, typename... Args, typename Allocator>
+sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
+
+} // namespace entt
+
+#endif
\ No newline at end of file
diff --git a/tests/testfggl/util/guid.cpp b/tests/testfggl/util/guid.cpp
index e4dedf7b8280a2c2eceb322f38030adb5120ef2e..d70fa0f5dd2ca31241d4368ef68c95a5822cb01e 100644
--- a/tests/testfggl/util/guid.cpp
+++ b/tests/testfggl/util/guid.cpp
@@ -71,12 +71,16 @@ namespace {
 		auto value = fggl::util::hash_fnv1a_32("fggl::test::scoped");
 		auto value2 = fggl::util::hash_fnv1a_32("fggl::test::scoped");
 		EXPECT_EQ( value, value2 );
+		EXPECT_EQ( false, value < value2);
+		EXPECT_EQ( false, value2 < value);
 	}
 
 	TEST(UtilHash64, RepeatsAreEqual) {
 		auto value = fggl::util::hash_fnv1a_64("fggl::test::scoped");
 		auto value2 = fggl::util::hash_fnv1a_64("fggl::test::scoped");
 		EXPECT_EQ( value, value2 );
+		EXPECT_EQ( false, value < value2);
+		EXPECT_EQ( false, value2 < value);
 	}
 
 	// GUID tests
@@ -84,6 +88,24 @@ namespace {
 		auto guid = fggl::util::make_guid("test");
 		auto guidSuffix = "test"_fid;
 		EXPECT_EQ(guid, guidSuffix);
+		EXPECT_EQ( false, guid < guidSuffix);
+		EXPECT_EQ( false, guidSuffix < guid);
+	}
+
+	TEST(GuidTest, OrderingLE) {
+		auto guid1 = fggl::util::GUID::make(0);
+		auto guid2 = fggl::util::GUID::make(1);
+		EXPECT_NE(guid1, guid2);
+		EXPECT_EQ( true, guid1 < guid2);
+		EXPECT_EQ( false, guid2 < guid1);
+	}
+
+	TEST(GuidTest, OrderingGE) {
+		auto guid1 = fggl::util::GUID::make(15);
+		auto guid2 = fggl::util::GUID::make(36);
+		EXPECT_NE(guid1, guid2);
+		EXPECT_EQ( true, guid1 < guid2);
+		EXPECT_EQ( false, guid2 < guid1);
 	}
 
 	TEST(GuidTest, GuidToString) {