added util::bitset and working on implementing generate_spirv_parser
authorJacob Lifshay <programmerjake@gmail.com>
Tue, 13 Jun 2017 04:07:36 +0000 (21:07 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Tue, 13 Jun 2017 04:07:36 +0000 (21:07 -0700)
15 files changed:
src/CMakeLists.txt
src/demo/demo.cpp
src/generate_spirv_parser/ast.h
src/generate_spirv_parser/generate.cpp
src/generate_spirv_parser/generate_spirv_parser.cpp
src/generate_spirv_parser/parser.cpp
src/generate_spirv_parser/parser.h
src/json/json.cpp
src/json/json.h
src/json/parser.cpp
src/json/parser.h
src/util/CMakeLists.txt
src/util/bit_intrinsics.h
src/util/bitset.cpp [new file with mode: 0644]
src/util/bitset.h [new file with mode: 0644]

index a4c39440dc7144bfbb88c69a48db859efcb3ee07..ddee6581b9f8b079a7cd7b754586fcace5590588 100644 (file)
@@ -19,6 +19,7 @@
 # SOFTWARE.
 #
 cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 add_subdirectory(spirv)
 add_subdirectory(demo)
 add_subdirectory(json)
index 601571fb299db19c33ed01047e7e43fb7b228c42..2ef361664a03bf3a0ee5c9a68c460cfda50d774c 100644 (file)
@@ -26,8 +26,8 @@
 #include <array>
 #include <type_traits>
 #include <string>
-#include "../spirv/spirv.h"
-#include "../util/optional.h"
+#include "spirv/spirv.h"
+#include "util/optional.h"
 
 namespace vulkan_cpu
 {
index 3228746941f1e21e0ecf0e37f6d795444e043c91..81e650ee3068047d49c4189846327f37d3979a32 100644 (file)
@@ -24,7 +24,7 @@
 #ifndef GENERATE_SPIRV_PARSER_AST_H_
 #define GENERATE_SPIRV_PARSER_AST_H_
 
-#include "../json/json.h"
+#include "json/json.h"
 #include <cstdint>
 #include <vector>
 #include <string>
index b39259c26981ce4aa05c7f6305748df612cbc8d4..9d1f8f7c349cf8cc3cde9b07f9f4c2149d0e46d1 100644 (file)
@@ -253,7 +253,8 @@ struct Spirv_header_generator final : public Generator
         state.open_output_file();
         write_file_comments(state, top_level.copyright);
         write_file_guard_start(state);
-        state << "#include <cstdint>\n";
+        state << "#include <cstdint>\n"
+                 "#include \"util/bitset.h\"\n";
         state << "\n";
         write_namespaces_start(state, spirv_namespace_names);
         state << "typedef std::uint32_t Word;\n";
@@ -272,7 +273,7 @@ struct Spirv_header_generator final : public Generator
             extensions_list.push_back(extension);
         std::sort(extensions_list.begin(), extensions_list.end());
         state << "\n"
-                 "enum class Extension\n"
+                 "enum class Extension : std::size_t\n"
                  "{\n";
         {
             auto push_indent = state.pushed_indent();
@@ -280,6 +281,10 @@ struct Spirv_header_generator final : public Generator
                 state << indent << extension << ",\n";
         }
         state << "};\n"
+                 "\n"
+                 "constexpr std::size_t Extension_count = "
+              << unsigned_dec_integer_literal(extensions_list.size())
+              << ";\n"
                  "\n"
                  "constexpr const char *get_extension_name(Extension extension) noexcept\n"
                  "{\n";
@@ -308,10 +313,8 @@ struct Spirv_header_generator final : public Generator
             });
         for(auto *operand_kind : operand_kinds)
         {
-            switch(operand_kind->category)
-            {
-            case ast::Operand_kinds::Operand_kind::Category::value_enum:
-            case ast::Operand_kinds::Operand_kind::Category::bit_enum:
+            if(util::holds_alternative<ast::Operand_kinds::Operand_kind::Enumerants>(
+                   operand_kind->value))
             {
                 auto &enumerants =
                     util::get<ast::Operand_kinds::Operand_kind::Enumerants>(operand_kind->value);
@@ -332,9 +335,6 @@ struct Spirv_header_generator final : public Generator
                 }
                 push_indent.finish();
                 state << "};\n";
-                break;
-            }
-#warning finish
             }
         }
 
@@ -351,4 +351,4 @@ std::unique_ptr<Generator> Generators::make_spirv_header_generator()
 }
 }
 }
-}
\ No newline at end of file
+}
index 07d371e9d28fa4e7200863021809fdd7ba7237f2..4a88efe0d8eb13badfdab67f8babfed6f9a0670b 100644 (file)
  *
  */
 #include <iostream>
-#include "../json/json.h"
-#include "../json/parser.h"
+#include "json/json.h"
+#include "json/parser.h"
 #include "parser.h"
-#include "../util/optional.h"
+#include "util/optional.h"
 #include "generate.h"
 
 namespace vulkan_cpu
index ee6fd29afb6d15e2a5d61cb0eb5f47d50f8bff88..8c96b89a6061e0577197fed4b16f3539700d27d1 100644 (file)
@@ -21,7 +21,7 @@
  *
  */
 #include "parser.h"
-#include "../util/optional.h"
+#include "util/optional.h"
 #include <sstream>
 #include <limits>
 #include <iostream>
index df3b9dc097e40b6359369e3ccb9e345a122779c0..acf2457e08c08225bde7df60f0b11990a726021e 100644 (file)
@@ -29,9 +29,9 @@
 #include <cassert>
 #include <string>
 #include <vector>
-#include "../util/variant.h"
-#include "../json/json.h"
-#include "../json/parser.h"
+#include "util/variant.h"
+#include "json/json.h"
+#include "json/parser.h"
 
 namespace vulkan_cpu
 {
index 1c41d79964f9d2a978409ce2b17983a2d0e962b5..28f4ae5d4416696a75f034cc7c19005bb1ad9de9 100644 (file)
@@ -28,7 +28,7 @@
 #include <cassert>
 #include <tuple>
 #include <algorithm>
-#include "../util/soft_float.h"
+#include "util/soft_float.h"
 
 namespace vulkan_cpu
 {
index 7d9f4c0225f84f4da98bf44f3d66895d988209e6..15cbe05bc3db376ac04d1c2abdf0a3ff09f761be 100644 (file)
@@ -35,8 +35,8 @@
 #include <cassert>
 #include <cmath>
 #include <list>
-#include "../util/variant.h"
-#include "../util/optional.h"
+#include "util/variant.h"
+#include "util/optional.h"
 #include "location.h"
 
 namespace vulkan_cpu
index 238e302cf63bc29deb93b9581d73cef705e5e6ef..1481c922af5137869472a62547f9dc8596e1c32a 100644 (file)
@@ -26,7 +26,7 @@
 #include <cassert>
 #include <algorithm>
 #include <limits>
-#include "../util/soft_float.h"
+#include "util/soft_float.h"
 
 namespace vulkan_cpu
 {
index dc147710665e589b86bda3b01a943c9e93665cf6..31adb3efdb658d8a789c1f23c52e452af1b4eb8e 100644 (file)
@@ -32,7 +32,7 @@
 #include "json.h"
 #include "source.h"
 #include "location.h"
-#include "../util/optional.h"
+#include "util/optional.h"
 
 namespace vulkan_cpu
 {
index 387804f258cc837c8088b96e123082eba82af26a..42d814dd557c8e2b08b893d4f04d41044fcfee44 100644 (file)
@@ -20,6 +20,7 @@
 #
 cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
 set(sources bit_intrinsics.cpp
+            bitset.cpp
             copy_cv_ref.cpp
             in_place.cpp
             invoke.cpp
index 455004273851cd5dcc09cb2160c8e2a476475787..3ffd6a75ea158aaa3250122bf36b4a770f2be064 100644 (file)
 #if defined(__clang__)
 #if defined(__apple_build_version__)
 #if __clang_major__ > 5 || (__clang_major__ == 5 && __clang_minor__ >= 1)
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
 #endif
 #else
 #if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4)
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
 #endif
 #endif
 #elif defined(__INTEL_COMPILER)
 #warning figure out icc version numbers for constexpr __builtin_clz and __builtin_ctz
 #elif defined(__GNUC__)
 // gcc supports constexpr __builtin_clz and __builtin_ctz before it supports c++14
-#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED 1
+#define VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED 1
 #endif
 
 #if 1
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
-#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+#undef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
 #endif
 #endif
 
@@ -56,7 +56,7 @@ namespace util
 {
 constexpr std::uint32_t clz4(std::uint8_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 4 : __builtin_clz(v) - __builtin_clz(0x8U);
 #else
     typedef const std::uint_fast8_t LookupTableType[0x10];
@@ -70,7 +70,7 @@ constexpr std::uint32_t clz4(std::uint8_t v) noexcept
 
 constexpr std::uint32_t clz8(std::uint8_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 8 : __builtin_clz(v) - __builtin_clz(0x80U);
 #else
     return ((v & 0xF0) == 0) ? 4 + clz4(v) : clz4(v >> 4);
@@ -79,7 +79,7 @@ constexpr std::uint32_t clz8(std::uint8_t v) noexcept
 
 constexpr std::uint32_t clz16(std::uint16_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 16 : __builtin_clz(v) - (std::numeric_limits<int>::digits - 16);
 #else
     return ((v & 0xFF00U) == 0) ? 8 + clz8(v) : clz8(v >> 8);
@@ -88,7 +88,7 @@ constexpr std::uint32_t clz16(std::uint16_t v) noexcept
 
 constexpr std::uint32_t clz32(std::uint32_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 32 : __builtin_clzl(v) - (std::numeric_limits<long>::digits - 32);
 #else
     return ((v & 0xFFFF0000UL) == 0) ? 16 + clz16(v) : clz16(v >> 16);
@@ -97,7 +97,7 @@ constexpr std::uint32_t clz32(std::uint32_t v) noexcept
 
 constexpr std::uint32_t clz64(std::uint64_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 64 : __builtin_clzll(v) - (std::numeric_limits<long long>::digits - 64);
 #else
     return ((v & 0xFFFFFFFF00000000ULL) == 0) ? 32 + clz32(v) : clz32(v >> 32);
@@ -106,7 +106,7 @@ constexpr std::uint32_t clz64(std::uint64_t v) noexcept
 
 constexpr std::uint32_t ctz4(std::uint8_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 4 : __builtin_ctz(v);
 #else
     typedef const std::uint_fast8_t LookupTableType[0x10];
@@ -120,7 +120,7 @@ constexpr std::uint32_t ctz4(std::uint8_t v) noexcept
 
 constexpr std::uint32_t ctz8(std::uint8_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 8 : __builtin_ctz(v);
 #else
     return ((v & 0xF0) == 0) ? ctz4(v) : 4 + ctz4(v >> 4);
@@ -129,7 +129,7 @@ constexpr std::uint32_t ctz8(std::uint8_t v) noexcept
 
 constexpr std::uint32_t ctz16(std::uint16_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 16 : __builtin_ctz(v);
 #else
     return ((v & 0xFF00U) == 0) ? ctz8(v) : 8 + ctz8(v >> 8);
@@ -138,7 +138,7 @@ constexpr std::uint32_t ctz16(std::uint16_t v) noexcept
 
 constexpr std::uint32_t ctz32(std::uint32_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 32 : __builtin_ctzl(v);
 #else
     return ((v & 0xFFFF0000UL) == 0) ? ctz16(v) : 16 + ctz16(v >> 16);
@@ -147,12 +147,59 @@ constexpr std::uint32_t ctz32(std::uint32_t v) noexcept
 
 constexpr std::uint32_t ctz64(std::uint64_t v) noexcept
 {
-#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_SUPPORTED
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
     return v == 0 ? 64 : __builtin_ctzll(v);
 #else
     return ((v & 0xFFFFFFFF00000000ULL) == 0) ? ctz32(v) : 32 + ctz32(v >> 32);
 #endif
 }
+
+constexpr std::uint32_t popcount8(std::uint8_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+    return __builtin_popcount(v);
+#else
+    constexpr std::uint8_t lookup_table[0x10] = {
+        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+    };
+    return lookup_table[v & 0xF] + lookup_table[v >> 4];
+#endif
+}
+
+constexpr std::uint32_t popcount32(std::uint32_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+    return __builtin_popcountl(v);
+#else
+    constexpr std::uint32_t m1 = 0x5555'5555UL;
+    constexpr std::uint32_t m2 = 0x3333'3333UL;
+    constexpr std::uint32_t m4 = 0x0F0F'0F0FUL;
+    v -= (v >> 1) & m1;
+    v = (v & m2) + ((v >> 2) & m2);
+    v = (v & m4) + ((v >> 4) & m4);
+    return static_cast<std::uint32_t>(v * 0x0101'0101UL) >> 24;
+#endif
+}
+
+constexpr std::uint32_t popcount16(std::uint16_t v) noexcept
+{
+    return popcount32(v);
+}
+
+constexpr std::uint32_t popcount64(std::uint64_t v) noexcept
+{
+#ifdef VULKAN_CPU_UTIL_CONSTEXPR_BUILTIN_CLZ_CTZ_POPCOUNT_SUPPORTED
+    return __builtin_popcountll(v);
+#else
+    constexpr std::uint64_t m1 = 0x5555'5555'5555'5555ULL;
+    constexpr std::uint64_t m2 = 0x3333'3333'3333'3333ULL;
+    constexpr std::uint64_t m4 = 0x0F0F'0F0F'0F0F'0F0FULL;
+    v -= (v >> 1) & m1;
+    v = (v & m2) + ((v >> 2) & m2);
+    v = (v & m4) + ((v >> 4) & m4);
+    return static_cast<std::uint64_t>(v * 0x0101'0101'0101'0101ULL) >> 56;
+#endif
+}
 }
 }
 
diff --git a/src/util/bitset.cpp b/src/util/bitset.cpp
new file mode 100644 (file)
index 0000000..73f9405
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+// derived from
+// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.cpp
+
+#include "bitset.h"
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <random>
+#include <vector>
+#include <string>
+#include <utility>
+
+namespace vulkan_cpu
+{
+namespace util
+{
+namespace detail
+{
+#if 0
+#warning testing bitset
+struct Bitset_nontemplate_base::Tester final
+{
+    template <std::size_t Bit_count>
+    static void check_unused_bits(const bitset<Bit_count> &value)
+    {
+        constexpr Word_type unused_bits =
+            Bit_count % word_bit_count ? ~((1ULL << (Bit_count % word_bit_count)) - 1ULL) : 0;
+        assert((value.get_word(value.word_count - 1) & unused_bits) == 0);
+    }
+    static void check_unused_bits(const bitset<0> &)
+    {
+    }
+    template <std::size_t Bit_count>
+    static void test_default_construct()
+    {
+        bitset<Bit_count> value;
+        for(std::size_t i = 0; i < value.word_count; i++)
+            assert(value.get_word(i) == 0);
+        check_unused_bits(value);
+    }
+    template <std::size_t Bit_count>
+    static void test_construct_from_ull()
+    {
+        for(std::size_t i = 0; i < std::numeric_limits<unsigned long long>::digits; i++)
+        {
+            bitset<Bit_count> value(1ULL << i);
+            check_unused_bits(value);
+            assert(bitset<Bit_count>(1ULL << i).to_ullong() == (1ULL << i) || i >= Bit_count);
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_reference_assign()
+    {
+        std::default_random_engine re;
+        std::uniform_int_distribution<unsigned long long> distribution;
+        for(std::size_t i = 0; i < 1000; i++)
+        {
+            bitset<Bit_count> src(distribution(re)), dest;
+            for(std::size_t j = 0; j < Bit_count; j++)
+            {
+                dest[j] = src[j];
+                check_unused_bits(src);
+                check_unused_bits(dest);
+            }
+            assert(src == dest);
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_reference_flip()
+    {
+        if(Bit_count == 0)
+            return;
+        std::default_random_engine re;
+        std::vector<bool> vector;
+        vector.resize(Bit_count, false);
+        bitset<Bit_count> value;
+        for(std::size_t i = 0; i < 1000; i++)
+        {
+            std::size_t index = std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+            vector[index].flip();
+            value[index].flip();
+            check_unused_bits(value);
+            for(std::size_t j = 0; j < Bit_count; j++)
+                assert(value[index] == static_cast<bool>(vector[index]));
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_test()
+    {
+        std::default_random_engine re;
+        std::vector<bool> vector;
+        vector.resize(Bit_count, false);
+        bitset<Bit_count> value;
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                vector[index].flip();
+                value[index].flip();
+                check_unused_bits(value);
+            }
+        }
+        for(std::size_t i = 0; i < Bit_count + 1000; i++)
+        {
+            bool threw = false;
+            bool result = false;
+            try
+            {
+                result = value.test(i);
+            }
+            catch(std::out_of_range &)
+            {
+                threw = true;
+            }
+            if(i >= Bit_count)
+            {
+                assert(threw);
+            }
+            else
+            {
+                assert(!threw);
+                assert(result == vector[i]);
+            }
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_all_none_any_and_count_helper(const std::vector<bool> &vector,
+                                                   const bitset<Bit_count> &value)
+    {
+        std::size_t set_bit_count = 0;
+        for(std::size_t i = 0; i < Bit_count; i++)
+            if(vector[i])
+                set_bit_count++;
+        assert(value.all() == (set_bit_count == Bit_count));
+        assert(value.any() == (set_bit_count != 0));
+        assert(value.none() == (set_bit_count == 0));
+        assert(value.count() == set_bit_count);
+    }
+    template <std::size_t Bit_count>
+    static void test_all_none_any_and_count()
+    {
+        std::default_random_engine re;
+        std::vector<bool> vector;
+        vector.resize(Bit_count, false);
+        bitset<Bit_count> value;
+        test_all_none_any_and_count_helper(vector, value);
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                vector[index].flip();
+                value[index].flip();
+                check_unused_bits(value);
+                test_all_none_any_and_count_helper(vector, value);
+            }
+        }
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            value[i] = true;
+            vector[i] = true;
+            check_unused_bits(value);
+            test_all_none_any_and_count_helper(vector, value);
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_and_or_and_xor_helper(const std::vector<bool> &vector1,
+                                           const std::vector<bool> &vector2,
+                                           const bitset<Bit_count> &bitset1,
+                                           const bitset<Bit_count> &bitset2)
+    {
+        bitset<Bit_count> dest_bitset_and = bitset1 & bitset2;
+        bitset<Bit_count> dest_bitset_or = bitset1 | bitset2;
+        bitset<Bit_count> dest_bitset_xor = bitset1 ^ bitset2;
+        check_unused_bits(dest_bitset_and);
+        check_unused_bits(dest_bitset_or);
+        check_unused_bits(dest_bitset_xor);
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            assert(dest_bitset_and[i] == (vector1[i] && vector2[i]));
+            assert(dest_bitset_or[i] == (vector1[i] || vector2[i]));
+            assert(dest_bitset_xor[i] == (static_cast<bool>(vector1[i]) != vector2[i]));
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_and_or_and_xor()
+    {
+        std::default_random_engine re;
+        std::vector<bool> vector1, vector2;
+        vector1.resize(Bit_count, false);
+        vector2.resize(Bit_count, false);
+        bitset<Bit_count> bitset1, bitset2;
+        test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 2000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count * 2 - 1)(re);
+                bool is_second_bit_set = index >= Bit_count;
+                index %= Bit_count;
+                if(is_second_bit_set)
+                {
+                    vector2[index].flip();
+                    bitset2[index].flip();
+                }
+                else
+                {
+                    vector1[index].flip();
+                    bitset1[index].flip();
+                }
+                check_unused_bits(bitset1);
+                check_unused_bits(bitset2);
+                test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+            }
+        }
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            bitset1[i] = true;
+            vector1[i] = true;
+            check_unused_bits(bitset1);
+            check_unused_bits(bitset2);
+            test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+        }
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            bitset2[i] = true;
+            vector2[i] = true;
+            check_unused_bits(bitset1);
+            check_unused_bits(bitset2);
+            test_and_or_and_xor_helper(vector1, vector2, bitset1, bitset2);
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_not()
+    {
+        std::default_random_engine re;
+        std::vector<bool> vector;
+        vector.resize(Bit_count, false);
+        bitset<Bit_count> value;
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                vector[index].flip();
+                value[index].flip();
+                check_unused_bits(value);
+                bitset<Bit_count> bitset_not = ~value;
+                check_unused_bits(bitset_not);
+                for(std::size_t j = 0; j < Bit_count; j++)
+                    assert(vector[j] == !bitset_not[j]);
+            }
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_shift_helper(const std::vector<bool> &vector, const bitset<Bit_count> &value)
+    {
+        for(std::size_t shift_count = 0; shift_count < Bit_count * 2 + 1; shift_count++)
+        {
+            bitset<Bit_count> bitset_shifted_left = value << shift_count;
+            bitset<Bit_count> bitset_shifted_right = value >> shift_count;
+            check_unused_bits(bitset_shifted_left);
+            check_unused_bits(bitset_shifted_right);
+            for(std::size_t i = 0; i < Bit_count; i++)
+            {
+                assert(bitset_shifted_left[i]
+                       == (i < shift_count ? false : static_cast<bool>(vector[i - shift_count])));
+                assert(bitset_shifted_right[i] == (shift_count >= Bit_count - i ?
+                                                       false :
+                                                       static_cast<bool>(vector[i + shift_count])));
+            }
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_shift()
+    {
+        std::default_random_engine re;
+        std::vector<bool> vector;
+        vector.resize(Bit_count, false);
+        bitset<Bit_count> value;
+        test_shift_helper(vector, value);
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                vector[index].flip();
+                value[index].flip();
+                check_unused_bits(value);
+                test_shift_helper(vector, value);
+            }
+        }
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            value[i] = true;
+            vector[i] = true;
+            check_unused_bits(value);
+            test_shift_helper(vector, value);
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_global_set_and_reset()
+    {
+        bitset<Bit_count> value;
+        value.reset();
+        check_unused_bits(value);
+        assert(value.none());
+        value.set();
+        check_unused_bits(value);
+        assert(value.all());
+    }
+    template <std::size_t Bit_count>
+    static void test_find_helper(const std::string &string, const bitset<Bit_count> &value)
+    {
+        for(std::size_t i = 0; i < Bit_count; i++)
+            assert(string[i] == (value[i] ? '1' : '0'));
+        for(std::size_t start = 0; start <= Bit_count; start++)
+        {
+            assert(string.find_first_of('1', start) == value.find_first(true, start));
+            assert(string.find_first_not_of('1', start) == value.find_first(false, start));
+            assert(string.find_last_of('1', start) == value.find_last(true, start));
+            assert(string.find_last_not_of('1', start) == value.find_last(false, start));
+        }
+    }
+    template <std::size_t Bit_count>
+    static void test_find()
+    {
+        std::default_random_engine re;
+        std::string string;
+        string.resize(Bit_count, '0');
+        bitset<Bit_count> value;
+        test_find_helper(string, value);
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                string[index] = (string[index] == '0' ? '1' : '0');
+                value[index].flip();
+                check_unused_bits(value);
+                test_find_helper(string, value);
+            }
+        }
+        for(std::size_t i = 0; i < Bit_count; i++)
+        {
+            value[i] = true;
+            string[i] = '1';
+            check_unused_bits(value);
+            test_find_helper(string, value);
+        }
+        if(Bit_count != 0)
+        {
+            for(std::size_t i = 0; i < 1000; i++)
+            {
+                std::size_t index =
+                    std::uniform_int_distribution<std::size_t>(0, Bit_count - 1)(re);
+                string[index] = (string[index] == '0' ? '1' : '0');
+                value[index].flip();
+                check_unused_bits(value);
+                test_find_helper(string, value);
+            }
+        }
+    }
+    template <std::size_t Bit_count>
+    static char test()
+    {
+        std::cout << "testing bitset<" << Bit_count << ">" << std::endl;
+        test_default_construct<Bit_count>();
+        test_construct_from_ull<Bit_count>();
+        test_reference_assign<Bit_count>();
+        test_reference_flip<Bit_count>();
+        test_test<Bit_count>();
+        test_all_none_any_and_count<Bit_count>();
+        test_and_or_and_xor<Bit_count>();
+        test_not<Bit_count>();
+        test_shift<Bit_count>();
+        test_global_set_and_reset<Bit_count>();
+        test_find<Bit_count>();
+        return 0;
+    }
+    template <typename... Args>
+    static void test_helper(Args...)
+    {
+    }
+    template <std::size_t... Bit_counts>
+    static void test(std::index_sequence<Bit_counts...>)
+    {
+        test_helper(test<Bit_counts>()...);
+    }
+    struct Test_runner final
+    {
+        Test_runner()
+        {
+            test(std::make_index_sequence<128>());
+            std::exit(0);
+        }
+    };
+    static Test_runner test_runner;
+};
+Bitset_nontemplate_base::Tester::Test_runner Bitset_nontemplate_base::Tester::test_runner;
+#endif
+}
+}
+}
diff --git a/src/util/bitset.h b/src/util/bitset.h
new file mode 100644 (file)
index 0000000..ca93b74
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef UTIL_BITSET_H_
+#define UTIL_BITSET_H_
+
+#include "bit_intrinsics.h"
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <string>
+#include <iosfwd>
+#include <stdexcept>
+
+// I have my own bitset implementation because std::bitset is not completely constexpr.
+
+// derived from
+// https://github.com/programmerjake/hashlife-voxels/blob/0dd91021a5b9caeffb7849b2114dca89204876bd/util/bitset.h
+
+namespace vulkan_cpu
+{
+namespace util
+{
+template <std::size_t Bit_count>
+class bitset;
+namespace detail
+{
+class Bitset_nontemplate_base_helper
+{
+protected:
+    static constexpr bool is_power_of_2(std::size_t v) noexcept
+    {
+        return (v & (v - 1)) == 0;
+    }
+};
+
+struct Bitset_nontemplate_base : public Bitset_nontemplate_base_helper
+{
+protected:
+    struct Tester;
+
+public:
+    typedef std::uintptr_t Word_type;
+    static constexpr std::size_t word_bit_count = std::numeric_limits<Word_type>::digits;
+    static_assert(is_power_of_2(word_bit_count), "word_bit_count is not a power of 2");
+    static constexpr std::size_t constexpr_min(std::size_t a, std::size_t b) noexcept
+    {
+        return a > b ? b : a;
+    }
+    static constexpr std::size_t get_word_index(std::size_t bit_index) noexcept
+    {
+        return bit_index / word_bit_count;
+    }
+    static constexpr Word_type get_word_mask(std::size_t bit_index) noexcept
+    {
+        return static_cast<Word_type>(1) << (bit_index % word_bit_count);
+    }
+    static constexpr std::size_t get_word_count(std::size_t bit_count) noexcept
+    {
+        return (bit_count + (word_bit_count - 1)) / word_bit_count;
+    }
+};
+
+template <std::size_t Word_count>
+class Bitset_base : protected Bitset_nontemplate_base
+{
+protected:
+    static constexpr std::size_t word_count = Word_count;
+    Word_type words[Word_count]; // little endian order
+    constexpr Bitset_base() noexcept : words{}
+    {
+    }
+    constexpr Bitset_base(unsigned long long val) noexcept
+        : Bitset_base(
+              val,
+              std::make_index_sequence<constexpr_min(
+                  get_word_count(std::numeric_limits<unsigned long long>::digits), Word_count)>())
+    {
+    }
+    constexpr Word_type get_word(std::size_t word_index) const noexcept
+    {
+        return words[word_index];
+    }
+    constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept
+    {
+        words[word_index] = word_value;
+    }
+    constexpr bool equals(const Bitset_base &rt) const noexcept
+    {
+        for(std::size_t i = 0; i < word_count; i++)
+            if(words[i] != rt.words[i])
+                return false;
+        return true;
+    }
+
+private:
+    template <std::size_t... Indexes>
+    constexpr Bitset_base(unsigned long long val, std::index_sequence<Indexes...>) noexcept
+        : words{
+              static_cast<Word_type>(val >> Indexes * word_bit_count)...,
+          }
+    {
+    }
+};
+
+template <>
+class Bitset_base<0> : protected Bitset_nontemplate_base
+{
+protected:
+    static constexpr std::size_t word_count = 0;
+    constexpr Bitset_base() noexcept
+    {
+    }
+    constexpr Bitset_base(unsigned long long) noexcept
+    {
+    }
+    constexpr Word_type get_word(std::size_t word_index) const noexcept
+    {
+        return (static_cast<void>(word_index), 0);
+    }
+    constexpr void set_word(std::size_t word_index, Word_type word_value) noexcept
+    {
+        static_cast<void>(word_index);
+        static_cast<void>(word_value);
+    }
+    constexpr bool equals(const Bitset_base &rt) const noexcept
+    {
+        return true;
+    }
+
+public:
+    constexpr unsigned long long to_ullong() const
+    {
+        return 0;
+    }
+};
+}
+
+template <std::size_t Bit_count>
+class bitset final
+    : public detail::Bitset_base<detail::Bitset_nontemplate_base::get_word_count(Bit_count)>
+{
+private:
+    friend struct detail::Bitset_nontemplate_base::Tester;
+    static constexpr std::size_t bit_count = Bit_count;
+    typedef detail::Bitset_base<detail::Bitset_nontemplate_base::get_word_count(Bit_count)> Base;
+    using typename Base::Word_type;
+    using Base::word_count;
+    using Base::get_word;
+    using Base::set_word;
+    using Base::get_word_count;
+    using Base::get_word_mask;
+    using Base::get_word_index;
+    using Base::word_bit_count;
+
+private:
+    constexpr Word_type get_word_checked(std::size_t word_index) const noexcept
+    {
+        return word_index < word_count ? get_word(word_index) : 0;
+    }
+
+public:
+    constexpr bitset() noexcept : Base()
+    {
+    }
+    constexpr bitset(unsigned long long val) noexcept
+        : Base(bit_count >= std::numeric_limits<unsigned long long>::digits ?
+                   val :
+                   val & ((1ULL << bit_count) - 1ULL))
+    {
+    }
+    class reference final
+    {
+        template <std::size_t>
+        friend class bitset;
+
+    private:
+        bitset *base;
+        std::size_t bit_index;
+        constexpr reference(bitset *base, std::size_t bit_index) noexcept : base(base),
+                                                                            bit_index(bit_index)
+        {
+        }
+
+    public:
+        constexpr reference &operator=(
+            const reference &rt) noexcept // assigns referenced value, not this class
+        {
+            return operator=(static_cast<bool>(rt));
+        }
+        constexpr reference &operator=(bool value) noexcept
+        {
+            auto mask = get_word_mask(bit_index);
+            auto word = base->get_word(get_word_index(bit_index));
+            if(value)
+                word |= mask;
+            else
+                word &= ~mask;
+            base->set_word(get_word_index(bit_index), word);
+            return *this;
+        }
+        constexpr operator bool() const noexcept
+        {
+            return static_cast<const bitset *>(base)->operator[](bit_index);
+        }
+        constexpr bool operator~() const noexcept
+        {
+            return !operator bool();
+        }
+        constexpr reference &flip() noexcept
+        {
+            auto mask = get_word_mask(bit_index);
+            auto word = base->get_word(get_word_index(bit_index));
+            word ^= mask;
+            base->set_word(get_word_index(bit_index), word);
+            return *this;
+        }
+    };
+    constexpr bool operator==(const bitset &rt) const noexcept
+    {
+        return this->equals(rt);
+    }
+    constexpr bool operator!=(const bitset &rt) const noexcept
+    {
+        return !this->equals(rt);
+    }
+    constexpr reference operator[](std::size_t bit_index) noexcept
+    {
+        return reference(this, bit_index);
+    }
+    constexpr bool operator[](std::size_t bit_index) const noexcept
+    {
+        return get_word_mask(bit_index) & get_word(get_word_index(bit_index));
+    }
+    constexpr bool test(std::size_t bit_index) const
+    {
+        if(bit_index >= bit_count)
+            throw std::out_of_range("bit_index out of range in bitset::test");
+        return operator[](bit_index);
+    }
+    constexpr bool all() const noexcept
+    {
+        if(bit_count == 0)
+            return true;
+        for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+            if(get_word(i) != static_cast<Word_type>(-1))
+                return false;
+        return get_word(get_word_index(bit_count - 1))
+               == static_cast<Word_type>(static_cast<Word_type>(get_word_mask(bit_count - 1) << 1)
+                                         - 1);
+    }
+    constexpr bool any() const noexcept
+    {
+        if(bit_count == 0)
+            return false;
+        for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+            if(get_word(i) != 0)
+                return true;
+        return get_word(get_word_index(bit_count - 1)) != 0;
+    }
+    constexpr bool none() const noexcept
+    {
+        return !any();
+    }
+    constexpr std::size_t count() const noexcept
+    {
+        std::size_t retval = 0;
+        for(std::size_t i = 0; i < word_count; i++)
+        {
+            static_assert(
+                std::numeric_limits<Word_type>::max() <= std::numeric_limits<std::uint64_t>::max(),
+                "");
+            if(std::numeric_limits<Word_type>::max() <= std::numeric_limits<std::uint32_t>::max())
+                retval += popcount32(get_word(i));
+            else
+                retval += popcount64(get_word(i));
+        }
+        return retval;
+    }
+    constexpr std::size_t size() const noexcept
+    {
+        return bit_count;
+    }
+    constexpr bitset &operator&=(const bitset &rt) noexcept
+    {
+        for(std::size_t i = 0; i < word_count; i++)
+            set_word(i, get_word(i) & rt.get_word(i));
+        return *this;
+    }
+    friend constexpr bitset operator&(bitset a, const bitset &b) noexcept
+    {
+        return a &= b;
+    }
+    constexpr bitset &operator|=(const bitset &rt) noexcept
+    {
+        for(std::size_t i = 0; i < word_count; i++)
+            set_word(i, get_word(i) | rt.get_word(i));
+        return *this;
+    }
+    friend constexpr bitset operator|(bitset a, const bitset &b) noexcept
+    {
+        return a |= b;
+    }
+    constexpr bitset &operator^=(const bitset &rt) noexcept
+    {
+        for(std::size_t i = 0; i < word_count; i++)
+            set_word(i, get_word(i) ^ rt.get_word(i));
+        return *this;
+    }
+    friend constexpr bitset operator^(bitset a, const bitset &b) noexcept
+    {
+        return a ^= b;
+    }
+    constexpr bitset &flip() noexcept
+    {
+        for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+            set_word(i, ~get_word(i));
+        set_word(get_word_index(bit_count - 1),
+                 get_word(get_word_index(bit_count - 1))
+                     ^ static_cast<Word_type>(
+                           static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+        return *this;
+    }
+    constexpr bitset operator~() const noexcept
+    {
+        bitset retval = *this;
+        retval.flip();
+        return retval;
+    }
+    constexpr bitset &operator<<=(std::size_t shiftCount) noexcept
+    {
+        if(shiftCount >= bit_count)
+            return reset();
+        std::size_t shiftWord_count = shiftCount / word_bit_count;
+        std::size_t shiftBitCount = shiftCount % word_bit_count;
+        if(shiftBitCount == 0)
+        {
+            for(std::size_t i = word_count; i > 0; i--)
+            {
+                std::size_t index = i - 1;
+                set_word(index, get_word_checked(index - shiftWord_count));
+            }
+        }
+        else
+        {
+            for(std::size_t i = word_count; i > 0; i--)
+            {
+                std::size_t index = i - 1;
+                Word_type highWord = get_word_checked(index - shiftWord_count);
+                Word_type lowWord = get_word_checked(index - 1 - shiftWord_count);
+                set_word(
+                    index,
+                    (lowWord >> (word_bit_count - shiftBitCount)) | (highWord << shiftBitCount));
+            }
+        }
+        if(word_count != 0)
+            set_word(word_count - 1,
+                     get_word(word_count - 1)
+                         & static_cast<Word_type>(
+                               static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+        return *this;
+    }
+    constexpr bitset operator<<(std::size_t shiftCount) const noexcept
+    {
+        bitset retval = *this;
+        retval <<= shiftCount;
+        return retval;
+    }
+    constexpr bitset &operator>>=(std::size_t shiftCount) noexcept
+    {
+        if(shiftCount >= bit_count)
+            return reset();
+        std::size_t shiftWord_count = shiftCount / word_bit_count;
+        std::size_t shiftBitCount = shiftCount % word_bit_count;
+        if(shiftBitCount == 0)
+        {
+            for(std::size_t index = 0; index < word_count; index++)
+            {
+                set_word(index, get_word_checked(index + shiftWord_count));
+            }
+        }
+        else
+        {
+            for(std::size_t index = 0; index < word_count; index++)
+            {
+                Word_type highWord = get_word_checked(index + 1 + shiftWord_count);
+                Word_type lowWord = get_word_checked(index + shiftWord_count);
+                set_word(
+                    index,
+                    (lowWord >> shiftBitCount) | (highWord << (word_bit_count - shiftBitCount)));
+            }
+        }
+        return *this;
+    }
+    constexpr bitset operator>>(std::size_t shiftCount) const noexcept
+    {
+        bitset retval = *this;
+        retval >>= shiftCount;
+        return retval;
+    }
+    constexpr bitset &set() noexcept
+    {
+        if(word_count == 0)
+            return *this;
+        for(std::size_t i = 0; i < get_word_index(bit_count - 1); i++)
+            set_word(i, static_cast<Word_type>(-1));
+        set_word(
+            get_word_index(bit_count - 1),
+            static_cast<Word_type>(static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1));
+        return *this;
+    }
+    constexpr bitset &set(std::size_t bit_index, bool value = true)
+    {
+        if(bit_index >= bit_count)
+            throw std::out_of_range("bit_index out of range in bitset::set");
+        operator[](bit_index) = value;
+        return *this;
+    }
+    constexpr bitset &reset() noexcept
+    {
+        for(std::size_t i = 0; i < word_count; i++)
+            set_word(i, 0);
+        return *this;
+    }
+    constexpr bitset &reset(std::size_t bit_index)
+    {
+        if(bit_index >= bit_count)
+            throw std::out_of_range("bit_index out of range in bitset::reset");
+        operator[](bit_index) = false;
+        return *this;
+    }
+    constexpr bitset &flip(std::size_t bit_index)
+    {
+        if(bit_index >= bit_count)
+            throw std::out_of_range("bit_index out of range in bitset::flip");
+        operator[](bit_index).flip();
+        return *this;
+    }
+    constexpr unsigned long long to_ullong() const
+    {
+        unsigned long long retval = 0;
+        constexpr std::size_t ullBitCount = std::numeric_limits<unsigned long long>::digits;
+        for(std::size_t i = 0; i < word_count; i++)
+        {
+            if(i * word_bit_count >= ullBitCount)
+            {
+                if(get_word(i) != 0)
+                    throw std::overflow_error("bit set value too large in bitset::to_ullong");
+            }
+            else
+            {
+                auto word = get_word(i);
+                auto shiftedWord = static_cast<unsigned long long>(word) << i * word_bit_count;
+                if((shiftedWord >> i * word_bit_count) != word)
+                    throw std::overflow_error("bit set value too large in bitset::to_ullong");
+                retval |= shiftedWord;
+            }
+        }
+        return retval;
+    }
+    constexpr unsigned long to_ulong() const
+    {
+        unsigned long long retval = to_ullong();
+        if(retval > std::numeric_limits<unsigned long>::max())
+            throw std::overflow_error("bit set value too large in bitset::to_ulong");
+        return retval;
+    }
+    static constexpr std::size_t npos = -1; // not in std::bitset
+    constexpr std::size_t find_first(bool value, std::size_t start = 0) const
+        noexcept // not in std::bitset
+    {
+        if(start >= bit_count)
+            return npos;
+        constexpr std::size_t endWordIndex = get_word_index(bit_count - 1);
+        std::size_t startWordIndex = get_word_index(start);
+        auto startWord = get_word(startWordIndex);
+        if(!value)
+        {
+            if(startWordIndex == endWordIndex)
+                startWord ^= static_cast<Word_type>(
+                    static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+            else
+                startWord = ~startWord;
+        }
+        auto mask = get_word_mask(start);
+        for(std::size_t retval = start; mask != 0; mask <<= 1, retval++)
+        {
+            if(startWord & mask)
+                return retval;
+        }
+        if(startWordIndex == endWordIndex)
+            return npos;
+        for(std::size_t word_index = startWordIndex + 1; word_index < endWordIndex; word_index++)
+        {
+            auto word = get_word(word_index);
+            if(word == static_cast<Word_type>(value ? 0 : -1))
+                continue;
+            if(!value)
+                word = ~word;
+            mask = 1;
+            std::size_t retval = word_index * word_bit_count;
+            for(; mask != 0; mask <<= 1, retval++)
+            {
+                if(word & mask)
+                    break;
+            }
+            return retval;
+        }
+        auto endWord = get_word(endWordIndex);
+        if(!value)
+            endWord ^= static_cast<Word_type>(
+                static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+        if(endWord == 0)
+            return npos;
+        mask = 1;
+        std::size_t retval = endWordIndex * word_bit_count;
+        for(; mask != 0; mask <<= 1, retval++)
+        {
+            if(endWord & mask)
+                break;
+        }
+        return retval;
+    }
+    constexpr std::size_t find_last(bool value, std::size_t start = npos) const
+        noexcept // not in std::bitset
+    {
+        if(bit_count == 0)
+            return npos;
+        if(start >= bit_count)
+            start = bit_count - 1;
+        std::size_t startWordIndex = get_word_index(start);
+        auto startWord = get_word(startWordIndex);
+        if(!value)
+        {
+            if(startWordIndex == get_word_index(bit_count - 1))
+                startWord ^= static_cast<Word_type>(
+                    static_cast<Word_type>(get_word_mask(bit_count - 1) << 1) - 1);
+            else
+                startWord = ~startWord;
+        }
+        auto mask = get_word_mask(start);
+        for(std::size_t retval = start; mask != 0; mask >>= 1, retval--)
+        {
+            if(startWord & mask)
+                return retval;
+        }
+        for(std::size_t word_index = startWordIndex - 1, i = 0; i < startWordIndex;
+            word_index--, i++)
+        {
+            auto word = get_word(word_index);
+            if(word == static_cast<Word_type>(value ? 0 : -1))
+                continue;
+            if(!value)
+                word = ~word;
+            mask = get_word_mask(word_bit_count - 1);
+            std::size_t retval = word_index * word_bit_count + (word_bit_count - 1);
+            for(; mask != 0; mask >>= 1, retval--)
+            {
+                if(word & mask)
+                    break;
+            }
+            return retval;
+        }
+        return npos;
+    }
+    constexpr std::size_t hash() const noexcept // not in std::bitset
+    {
+        std::size_t retval = 0;
+        for(std::size_t i = 0; i < word_count; i++)
+        {
+            retval ^= get_word(i);
+        }
+        return retval;
+    }
+};
+
+template <std::size_t Bit_count>
+constexpr std::size_t bitset<Bit_count>::npos;
+}
+}
+
+namespace std
+{
+template <std::size_t Bit_count>
+struct hash<vulkan_cpu::util::bitset<Bit_count>>
+{
+    constexpr std::size_t operator()(const vulkan_cpu::util::bitset<Bit_count> &v) const noexcept
+    {
+        return v.hash();
+    }
+};
+}
+
+#endif /* UTIL_BITSET_H_ */