Full Code of WG21-SG14/SG14 for AI

master c92614381100 cached
30 files
794.4 KB
205.9k tokens
371 symbols
1 requests
Download .txt
Showing preview only (819K chars total). Download the full file or copy to clipboard to get everything.
Repository: WG21-SG14/SG14
Branch: master
Commit: c92614381100
Files: 30
Total size: 794.4 KB

Directory structure:
gitextract_rzo9ruc3/

├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── Docs/
│   ├── Proposals/
│   │   ├── D0447R16 - Introduction of hive to the Standard Library.html
│   │   ├── Fixed_Point_Library_Proposal.md
│   │   ├── p0037.html
│   │   ├── rawstorage.html
│   │   ├── ring_proposal_r5.tex
│   │   ├── uninitialized.html
│   │   └── unstable_remove.html
│   ├── fixed_point.md
│   └── plf_licensing.txt
├── README.md
├── SG14/
│   ├── algorithm_ext.h
│   ├── flat_map.h
│   ├── flat_set.h
│   ├── inplace_function.h
│   ├── plf_colony.h
│   ├── ring.h
│   └── slot_map.h
└── SG14_test/
    ├── SG14_test.h
    ├── flat_map_test.cpp
    ├── flat_set_test.cpp
    ├── inplace_function_test.cpp
    ├── main.cpp
    ├── plf_colony_test.cpp
    ├── ring_test.cpp
    ├── slot_map_test.cpp
    ├── uninitialized_test.cpp
    └── unstable_remove_test.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# =========================
# Operating System Files
# =========================

# OSX
# =========================

.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear on external disk
.Spotlight-V100
.Trashes

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
*.tlog
VS2015/SG14/SG14_test/Release/vc140.pdb
*.obj
VS2015/SG14/SG14_test/Release/SG14_test.log
*.pdb
VS2015/SG14/SG14_test/Debug/vc120.idb
*.idb
*.ipdb
VS2015/SG14/Release/SG14_test.iobj
*.ilk
VS2015/SG14/Release/SG14_test.exe
*.exe
VS2015/SG14/.vs/SG14/v14/.suo
*.opensdf
VS2015/SG14/SG14.sdf
*.suo
VS2015/SG14/SG14.sdf
VS2015/SG14/SG14_test/Debug/SG14_test.log

# Xcode
xcuserdata

# CLion
cmake/.idea
/.vs
/.vs/SG14/v15
/.vs/slnx.sqlite
/.vs/SG14/v15/Browse.VC.opendb
/.vs/SG14/v15/Browse.VC.db
/.vs/ProjectSettings.json
/build/


================================================
FILE: .travis.yml
================================================
language: cpp

before_install:
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
      brew update;
      brew upgrade cmake;
      brew install cmake;
    fi
  - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
      eval "${MATRIX_EVAL}";
      DEPS_DIR="${TRAVIS_BUILD_DIR}/deps";
      mkdir ${DEPS_DIR} && cd ${DEPS_DIR};
      CMAKE_URL="https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.tar.gz";
      mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake;
      export PATH=${DEPS_DIR}/cmake/bin:${PATH};
      cd ..;
    fi

matrix:
  include:
    - os: windows
      env:
        - CONFIG=Release

    - os: windows
      env:
        - CONFIG=Debug

    - os: osx
      osx_image: xcode9.2
      env:
        - CONFIG=Release

    - os: osx
      osx_image: xcode9.2
      env:
        - CONFIG=Debug

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-5
            - g++-5
      env:
        - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5 && CONFIG=Debug"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-5
            - g++-5
      env:
        - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5 && CONFIG=Release"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-6
            - g++-6
      env:
        - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6 && CONFIG=Debug"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-6
            - g++-6
      env:
        - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6 && CONFIG=Release"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-7
            - g++-7
      env:
        - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && CONFIG=Debug"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-7
            - g++-7
      env:
        - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && CONFIG=Release"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-8
            - g++-8
      env:
        - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && CONFIG=Debug"

    - os: linux
      dist: trusty
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - gcc-8
            - g++-8
      env:
        - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && CONFIG=Release"

script:
  - mkdir build && cd build && cmake .. && cmake --build . && ./bin/sg14_tests

notifications:
  email:
    on_success: never
    on_failure: always


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.10)
project(sg14 CXX)

find_package(Threads REQUIRED)

# Prefer C++17, downgrade if it isn't available.
set(CMAKE_CXX_STANDARD_REQUIRED OFF)
set(CMAKE_CXX_STANDARD 17)

set(SG14_INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SG14")
set(SG14_TEST_SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SG14_test")

# Output binary to predictable location.
set(BINARY_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BINARY_OUT_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${BINARY_OUT_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${BINARY_OUT_DIR})

foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
	string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
	set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${BINARY_OUT_DIR})
	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${BINARY_OUT_DIR})
	set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${BINARY_OUT_DIR})
endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)

link_directories(${CMAKE_CURRENT_BINARY_DIR}/lib) # For future use with testing library.

##
# Project
##
add_library(${PROJECT_NAME} INTERFACE)

include(GNUInstallDirs)

target_include_directories(${PROJECT_NAME} INTERFACE
	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
	$<BUILD_INTERFACE:${SG14_INCLUDE_DIRECTORY}>
)

##
# Unit Tests
##
set(TEST_SOURCE_FILES
    ${SG14_TEST_SOURCE_DIRECTORY}/main.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/flat_map_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/flat_set_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/inplace_function_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/plf_colony_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/ring_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/slot_map_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/uninitialized_test.cpp
    ${SG14_TEST_SOURCE_DIRECTORY}/unstable_remove_test.cpp
)

set(TEST_NAME ${PROJECT_NAME}_tests)
add_executable(${TEST_NAME} ${TEST_SOURCE_FILES})
target_link_libraries(${TEST_NAME} ${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(${TEST_NAME} PRIVATE "${SG14_TEST_SOURCE_DIRECTORY}")

# Compile options
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
	target_compile_options(${TEST_NAME} PRIVATE -Wall -Wextra -Werror)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
	target_compile_options(${TEST_NAME} PRIVATE -Wall -Wextra -Werror)
	set_source_files_properties(${SG14_TEST_SOURCE_DIRECTORY}/plf_colony_test.cpp PROPERTIES
		COMPILE_FLAGS "-Wno-unused-parameter"
	)
	if (CMAKE_CXX_COMPILER_VERSION MATCHES "^7.*")
		set_source_files_properties(${SG14_TEST_SOURCE_DIRECTORY}/slot_map_test.cpp PROPERTIES
			COMPILE_FLAGS "-Wno-error=unused-variable -Wno-error=unused-but-set-variable") # Fix gcc7 issues with structured bindings
		message("Disabled -Wunused-variable and -Wunused-but-set-variable for gcc ${CMAKE_CXX_COMPILER_VERSION}.")
	endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
	set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TEST_NAME})
	target_compile_options(${TEST_NAME} PRIVATE /Zc:__cplusplus /permissive- /W4 /WX)
	add_definitions(-DNOMINMAX -D_SCL_SECURE_NO_WARNINGS)
	set_source_files_properties(${SG14_TEST_SOURCE_DIRECTORY}/plf_colony_test.cpp PROPERTIES
		COMPILE_FLAGS "/wd4127") # Disable conditional expression is constant, use if constexpr
endif()

install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}_targets)

install(EXPORT ${PROJECT_NAME}_targets
    NAMESPACE ${PROJECT_NAME}::
    FILE ${PROJECT_NAME}-config.cmake
    DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}"
)

install(DIRECTORY "${SG14_INCLUDE_DIRECTORY}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")



================================================
FILE: Docs/Proposals/D0447R16 - Introduction of hive to the Standard Library.html
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
  <meta name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <meta name="viewport" content="width=device-width">
  <meta content="True" name="HandheldFriendly">
  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
  <title>Introduction of std::hive to the standard library</title>
  <style type="text/css">
    	pre {
        overflow-x: auto;
        white-space: pre-wrap;
        word-wrap: break-word;
      }
      body {
         font-size: 12pt;
         font-weight: normal;
         font-style: normal;
                        font-family: serif;
         color: black;
         background-color: white;
         line-height: 1.2em;
         margin-left: 4em;
         margin-right: 2em;
      }
      /* paragraphs */

      p {
         padding: 0;
         line-height: 1.3em;
         margin-top: 1.2em;
         margin-bottom: 1em;
         text-align: left;
      }

      table  {
         margin-top: 3.8em;
         margin-bottom: 2em;
         text-align: left;
         table-layout:fixed;
         width:100%;
      }
      td {
      	overflow:auto;
        word-wrap:break-word;
      }

      /* headings */

      h1 {
         font-size: 195%;
         font-weight: bold;
         font-style: normal;
         font-variant: small-caps;
         line-height: 1.6em;
         text-align: left;
         padding: 0;
         margin-top: 3.5em;
         margin-bottom: 1.7em;
      }
      h2 {
         font-size: 122%;
         font-weight: bold;
         font-style: normal;
         text-decoration: underline;
         padding: 0;
         margin-top: 4.5em;
         margin-bottom: 1.1em;
      }
      h3 {
         font-size: 110%;
         font-weight: bold;
         font-style: normal;
         text-decoration: underline;
         padding: 0;
         margin-top: 4em;
         margin-bottom: 1.1em;
      }
      h4 {
         font-size: 100%;
         font-weight: bold;
         font-style: normal;
         padding: 0;
         margin-top: 4em;
         margin-bottom: 1.1em;
      }
      h5 {
         font-size: 90%;
         font-weight: bold;
         font-style: italic;
         padding: 0;
         margin-top: 3em;
         margin-bottom: 1em;
      }
      h6 {
         font-size: 80%;
         font-weight: bold;
         font-style: normal;
         padding: 0;
         margin-top: 1em;
         margin-bottom: 1em;
      }
      /* divisions */

      div {
         padding: 0;
         margin-top: 0em;
         margin-bottom: 0em;
      }
      ul {
         margin: 12pt 0pt 22pt 18pt;
         padding: 0pt 0pt 0pt 0pt;
         list-style-type: square;
         font-size: 98%;
      }
      ol {
         margin: 12pt 0pt 22pt 17pt;
         padding: 0pt 0pt 0pt 0pt;
      }
      li {
         margin: 0pt 0pt 10.5pt 0pt;
         padding: 0pt 0pt 0pt 0pt;
         text-indent: 0pt;
         display: list-item;
      }
      /* inline */

      strong {
         font-weight: bold;
      }
      sup,
      sub {
         vertical-align: baseline;
         position: relative;
         top: -0.4em;
         font-size: 70%;
      }
      sub {
         top: 0.4em;
      }
      em {
         font-style: italic;
      }
                code {
                    font-family: Courier New, Courier, monospace;
                    font-size: 90%;
                    padding: 0;
                    word-wrap:break-word;
                   }
      ins {
         background-color: yellow;
         text-decoration: underline;
      }
      del {
         text-decoration: line-through;
      }
      a:hover {
         color: #4398E1;
      }
      a:active {
         color: #4598E1;
         text-decoration: none;
      }
      a:link.review {
         color: #AAAAAF;
      }
      a:hover.review {
         color: #4398E1;
      }
      a:visited.review {
         color: #444444;
      }
      a:active.review {
         color: #AAAAAF;
         text-decoration: none;
      }
  </style>
</head>

<body>
Audience: LEWG, SG14, WG21<br>
Document number: D0447R16<br>
Date: 2021-06-21<br>
Project: Introduction of std::hive to the standard library<br>
Reply-to: Matthew Bentley &lt;mattreecebentley@gmail.com&gt;<br>


<h1>Introduction of std::hive to the standard library</h1>

<h2>Table of Contents</h2>
<ol type="I">
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#questions">Questions for the committee</a></li>
  <li><a href="#motivation">Motivation and Scope</a></li>
  <li><a href="#impact">Impact On the Standard</a></li>
  <li><a href="#design">Design Decisions</a></li>
  <li><a href="#technical">Technical Specification</a></li>
  <li><a href="#acknowledgements">Acknowledgements</a></li>
  <li>Appendixes:
    <ol type="A">
      <li><a href="#basicusage">Basic usage examples</a></li>
      <li><a href="#benchmarks">Reference implementation benchmarks</a></li>
      <li><a href="#faq">Frequently Asked Questions</a></li>
      <li><a href="#responses">Specific responses to previous committee feedback</a></li>
      <li><a href="#sg14gameengine">Typical game engine requirements</a></li>
      <li><a href="#timecomplexityexplanations">Time complexity requirement explanations</a></li>
		<li><a href="#yunoconstexpr">Why not constexpr?</a></li>
		<li><a href="#referencediff">Reference implementation differences and link</a></li>
    </ol>
  </li>
</ol>

<h2><a id="revisions"></a>Revision history</h2>
<ul>
  <li>R16: Range-constructor corrected to allow sentinels.</li>
  <li>R15: Added throw details to splice. Further design decisions information on reshape and splice. Assign() overload for sentinels (differing iterator types) added. Minor text snaffu corrections. Colony changed to hive based on D2332R0.</li>
  <li>R14: get_iterator_from_pointer changed to get_iterator - the pointer part is implied by the fact that it's the only argument. Added const_iterator overload for get_iterator - which takes a const_pointer and is a const function. Some wording corrections, additional design decisions information. HTML corrections.</li>
  <li>R13: Revisions based on committee feedback. Skipfield template parameter changed to priority enum in order to not over-specify container implementation. Other wording changes to reduce over-specifying implementation. Some non-member template functions moved to be friend functions. std::limits changed to std::hive_limits. block_limits() changed to block_capacity_limits().</li>
  <li>R12: Fill, range and initializer_list inserts changed to void return, since the insertions are not guaranteed to be sequential in terms of hive order and therefore returning an iterator to the first insertion is not useful. Non-default-value fill constructor changed to non-explicit to match other std:: containers. Correction to reserve() wording. Other minor corrections and clarity improvements.</li>
  <li>R11: Overhaul of technical specification to be more 'wording-like'. Minor
    alterations &amp; clarifications. Additional alternative approach added to
    Design Decisions under skipfield information. Overall rewording. Reordering
    based on feedback. Removal of some easily-replicated 'helper' functions.
    Change to noexcept guarantees. Assign added. get_block_capacity_limits and
    set_block_capacity_limits functions renamed to block_limits and reshape.
    Addition of block-limits default constructors. Reserve() and
    shrink_to_fit() reintroduced. Trim(), erase and erase_if overloads added.</li>
  <li>R10: Additional information about time complexity requirements added to
    appendix, some minor corrections to time complexity info. The 'bentley
    pattern' (this was always a temporary name) is renamed to the more astute
    'low-complexity jump-counting pattern'. Likewise the 'advanced
    jump-counting skipfield' is renamed to the 'high-complexity jump-counting
    pattern' - for reasoning behind this go <a href="https://plflib.org/blog.htm#whatsinaname">here</a>. Both refer to
    time complexity of operations, as opposed to algorithmic complexity. Some
    other corrections.</li>
  <li>R9: Link to Bentley pattern paper added, and is spellchecked now.</li>
  <li>R8: Correction to SIMD info. Correction to structure (missing appendices
    title, member functions and technical specification were conjoined,
    acknowledgments section had mysteriously gone missing since an earlier
    version, now restored and updated). Update intro. HTML corrections.</li>
  <li>R7: Minor changes to member functions.</li>
  <li>R6: Re-write. Reserve() and shrink_to_fit() removed from
  specification.</li>
  <li>R5: Additional note for reserve, re-write of introduction.</li>
  <li>R4: Addition of revision history and review feedback appendices. General
    rewording. Cutting of some dead wood. Addition of some more dead wood.
    Reversion to HTML, benchmarks moved to external URL, based on feedback.
    Change of font to Times New Roman based on looking at what other papers
    were using, though I did briefly consider Comic Sans. Change to insert
    specifications.</li>
  <li>R3: Jonathan Wakely's extensive technical critique has been actioned on,
    in both documentation and the reference implementation. "Be clearer about
    what operations this supports, early in the paper." - done (V. Technical
    Specifications). "Be clear about the O() time of each operation, early in
    the paper." - done for main operations, see V. Technical Specifications.
    Responses to some other feedbacks included in the foreword.</li>
  <li>R2: Rewording.</li>
</ul>

<h2><a id="introduction"></a>I. Introduction</h2>

<p>The purpose of a container in the standard library cannot be to provide the
optimal solution for all scenarios. Inevitably in fields such as
high-performance trading or gaming, the optimal solution within critical loops
will be a custom-made one that fits that scenario perfectly. However, outside
of the most critical of hot paths, there is a wide range of application for
more generalized solutions.</p>

<p>hive is a formalisation, extension and optimization of what is typically
known as a 'bucket array' container in game programming circles; similar
structures exist in various incarnations across the high-performance computing,
high performance trading, 3D simulation, physics simulation, robotics, server/client
application and particle simulation fields (see: <a href="https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/1iWHyVnsLBQ">https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/1iWHyVnsLBQ</a>).</p>

<p>The concept of a bucket array is: you have multiple memory blocks of
elements, and a boolean token for each element which denotes whether or not
that element is 'active' or 'erased', commonly known as a skipfield. If it is
'erased', it is skipped over during iteration. When all elements in a block are
erased, the block is removed, so that iteration does not lose performance by
having to skip empty blocks. If an insertion occurs when all the blocks are
full, a new memory block is allocated.</p>

<p>The advantages of this structure are as follows: because a skipfield is
used, no reallocation of elements is necessary upon erasure. Because the
structure uses multiple memory blocks, insertions to a full container also do
not trigger reallocations. This means that element memory locations stay stable
and iterators stay valid regardless of erasure/insertion. This is highly
desirable, for example, <a href="#sg14gameengine">in game programming</a>
because there are usually multiple elements in different containers which need
to reference each other during gameplay and elements are being inserted or
erased in real time.</p>

<p>Problematic aspects of a typical bucket array are that they tend to have a
fixed memory block size, do not re-use memory locations from erased elements,
and utilize a boolean skipfield. The fixed block size (as opposed to block
sizes with a growth factor) and lack of erased-element re-use leads to far more
allocations/deallocations than is necessary. Given that allocation is a costly
operation in most operating systems, this becomes important in
performance-critical environments. The boolean skipfield makes iteration time
complexity undefined, as there is no way of knowing ahead of time how many
erased elements occur between any two non-erased elements. This can create
variable latency during iteration. It also requires branching code, which may
cause issues on processors with deep pipelines and poor branch-prediction
failure performance.</p>

<p>A hive uses a non-boolean method for skipping erased elements, which allows for O(1) amortized iteration time complexity
and more-predictable iteration performance than a bucket array. It also
utilizes a growth factor for memory blocks and reuses erased element locations
upon insertion, which leads to fewer allocations/reallocations. Because it
reuses erased element memory space, the exact location of insertion is
undefined, unless no erasures have occurred or an equal number of erasures and
insertions have occurred (in which case the insertion location is the back of
the container). The container is therefore considered unordered but sortable.
Lastly, because there is no way of predicting in advance where erasures
('skips') may occur during iteration, an O(1) time complexity [ ] operator is
not necessarily possible (depending on implementation) and therefore, the container is bidirectional but not random-access.</p>

<p>There are two patterns for accessing stored elements in a hive: the first
is to iterate over the container and process each element (or skip some
elements using the advance/prev/next/iterator ++/-- functions). The second is
to store the iterator returned by the insert() function (or a pointer derived
from the iterator) in some other structure and access the inserted element in
that way. To better understand how insertion and erasure work in a hive, see
the following images.</p>

<h3>Insertion to back</h3>

<p>The following images demonstrate how insertion works in a hive compared to
a vector when size == capacity.</p>
<img src="https://plflib.org/vector_addition.gif" alt="Visual demonstration of inserting to a full vector"
style="max-width: 100%; height: auto;">
<img src="https://plflib.org/hive_addition.gif"
alt="Visual demonstration of inserting to a full hive"
style="max-width: 100%; height: auto;">

<h3>Non-back erasure</h3>

<p>The following images demonstrate how non-back erasure works in a hive
compared to a vector.</p>
<img src="https://plflib.org/vector_erasure.gif"
alt="Visual demonstration of randomly erasing from a vector"
style="max-width: 100%; height: auto;">
<img src="https://plflib.org/hive_erasure.gif"
alt="Visual demonstration of randomly erasing from a hive"
style="max-width: 100%; height: auto;">

<h2><a id="questions"></a>II. Questions for the Committee</h2>
<ol>
	<li>It is possible to make the memory() function constant time at a cost (see details in it's entry in the design decisions section of the paper) but since this is expected to be a seldom-used function I've decided not to do so and leave the time complexity as implementation-defined. If there are any objections, please state them. Also, memory_usage() has been suggested as a better name?</li>
	<li>The conditions under which memory blocks are retained by the erase() functions and added to the "reserved" pile instead of deallocated, is presently implementation-defined. Are there any objections to this? Should we define this? See the notes on erase() in Design Decisions, and the item in the FAQ. One option is to specify that only the current back block may be retained, however I feel like this should be implementation-defined.</li>
	<li>Given that this is a largely unordered container, should resize() be included? Currently it is not and I see no particular reason to do so, but if there are valid reasons let me know.</li>
	<li>Bikeshedding is welcome for reshape(). Was previously set_block_capacity_limits(), which is more clear, but does not describe the action when container already contains blocks which are outside of the newly-specified capacity limits. change_block_capacity_limits()?</li>
</ol>

<h2><a id="motivation"></a>III. Motivation and Scope</h2>

<p><i>Note: Throughout this document I will use the term 'link' to denote any
form of referencing between elements whether it be via
ids/iterators/pointers/indexes/references/etc.</i></p>

<p>There are situations where data is heavily interlinked, iterated over
frequently, and changing often. An example is the typical video game engine.
Most games will have a central generic 'entity' or 'actor' class, regardless of
their overall schema (an entity class does not imply an <a
href="https://en.wikipedia.org/wiki/Entity-component-system">ECS</a>).
Entity/actor objects tend to be 'has a'-style objects rather than 'is a'-style
objects, which link to, rather than contain, shared resources like sprites,
sounds and so on. Those shared resources are usually located in separate
containers/arrays so that they can re-used by multiple entities. Entities are
in turn referenced by other structures within a game engine, such as
quadtrees/octrees, level structures, and so on.</p>

<p>Entities may be erased at any time (for example, a wall gets destroyed and
no longer is required to be processed by the game's engine, so is erased) and
new entities inserted (for example, a new enemy is spawned). While this is all
happening the links between entities, resources and superstructures such as
levels and quadtrees, must stay valid in order for the game to run. The order
of the entities and resources themselves within the containers is, in the
context of a game, typically unimportant, so an unordered container is okay.</p>

<p>Unfortunately the container with the best iteration performance in the
standard library, vector<sup><a href="#benchmarks">[1]</a></sup>, loses pointer
validity to elements within it upon insertion, and pointer/index validity upon
erasure. This tends to lead to sophisticated and often restrictive workarounds
when developers attempt to utilize vector or similar containers under the above
circumstances.</p>

<p>std::list and the like are not suitable due to their poor locality, which
leads to poor cache performance during iteration. This is however an ideal
situation for a container such as hive, which has a high degree of locality.
Even though that locality can be punctuated by gaps from erased elements, it
still works out better in terms of iteration performance<sup><a
href="#benchmarks">[1]</a></sup> than every existing standard library container
other than deque/vector, regardless of the ratio of erased to non-erased
elements.</p>

<p>Some more specific requirements for containers in the context of game
development are listed in the <a href="#sg14gameengine">appendix</a>.</p>

<p>As another example, particle simulation (weather, physics etcetera) often
involves large clusters of particles which interact with external objects and
each other. The particles each have individual properties (spin, momentum,
direction etc) and are being created and destroyed continuously. Therefore the
order of the particles is unimportant, what is important is the speed of
erasure and insertion. No current standard library container has both strong
insertion and non-back erasure speed, so again this is a good match for
hive.</p>

<p><a href="https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/1iWHyVnsLBQ">Reports
from other fields</a> suggest that, because most developers aren't aware of
containers such as this, they often end up using solutions which are sub-par
for iterative performance such as std::map and std::list in order to preserve pointer
validity, when most of their processing work is actually iteration-based. So,
introducing this container would both create a convenient solution to these
situations, as well as increasing awareness of better-performing approaches in
general. It will also ease communication across fields, as opposed to the
current scenario where each field uses a similar container but each has a
different name for it.</p>



<h2><a id="impact"></a>IV. Impact On the Standard</h2>

<p>This is purely a library addition, requiring no changes to the language.</p>

<h2><a id="design"></a>V. Design Decisions</h2>

<p>The three core aspects of a hive from an abstract perspective are: </p>
<ol>
  <li>A collection of element memory blocks + metadata, to prevent reallocation
    during insertion (as opposed to a single memory block)</li>
  <li>A method of skipping erased elements in O(1) time during iteration (as opposed to reallocating subsequent elements during erasure)</li>
  <li>An erased-element location recording mechanism, to enable the re-use of
    memory from erased elements in subsequent insertions, which in turn
    increases cache locality and reduces the number of block
    allocations/deallocations</li>
</ol>

<p>Each memory block houses multiple elements. The metadata about each block
may or may not be allocated with the blocks themselves (could be contained in a
separate structure). This metadata should include at a minimum, the number of
non-erased elements within each block and the block's capacity - which allows the
container to know when the block is empty and needs to be removed from the
iterative chain, and also allows iterators to judge when the end of one block
has been reached. A non-boolean method of skipping over
erased elements during iteration while maintaining O(1) amortized iteration
time complexity is required (amortized due to block traversal, which would typically require a few more
operations). Finally, a mechanism for keeping track of elements which have been
erased must be present, so that those memory locations can be reused upon
subsequent element insertions.</p>

<p>The following aspects of a hive must be implementation-defined in order to
allow for variance and possible performance improvement, and to conform with
possible changes to C++ in the future:</p>
<ul>
  <li>the method used to skip erased elements</li>
  <li>time complexity of operations to update whatever metadata is associated with the skip method</li>
  <li>erasure-recording mechanism</li>
  <li>element memory block metadata</li>
  <li>iterator structure</li>
  <li>memory block growth factor</li>
  <li>time complexity of advance()/next()/prev()</li>
</ul>

<p>However the implementation of these <em>is</em> significantly constrained by
the requirements of the container (lack of reallocation, stable pointers to
non-erased elements regardless of erasures/insertions).</p>

<p>In terms of the <a href="https://plflib.org/colony.htm">reference
implementation</a> the specific structure and mechanisms have changed many
times over the course of development, however the interface to the container
and its time complexity guarantees have remained largely unchanged (with the
exception of the time complexity for updating skipfield nodes - which has not
impacted significantly on performance). So it is reasonably likely that
regardless of specific implementation, it will be possible to maintain this
general specification without obviating future improvements in implementation,
so long as time complexity guarantees for the above list are
implementation-defined.</p>

<p>Below I explain the reference implementation's approach in terms of the
three core aspects described above, along with descriptions of some
alternatives implementation approaches.</p>

<h4>1. Collection of element memory blocks + metadata</h4>

<p>In the reference implementation this is essentially a doubly-linked list of
'group' structs containing (a) a dynamically-allocated element memory block, (b) memory block metadata and (c)
a dynamically-allocated skipfield. The memory blocks and skipfields have a growth factor of 2 from one
group to the next. The metadata includes information necessary for an iterator
to iterate over hive elements, such as the last insertion point within the
memory block, and other information useful to specific functions, such as the
total number of non-erased elements in the node. This approach keeps the
operation of freeing empty memory blocks from the hive container at O(1) time
complexity. Further information is available <a
href="https://plflib.org/chained_group_allocation_pattern.htm">here</a>.</p>

<p>Using a vector of group structs with dynamically-allocated element memory blocks, using the swap-and-pop idiom where groups need to be erased from the iterative sequence, would not work. To explain, when a group becomes empty of elements, it must be removed from the sequence of groups, because otherwise you end up with highly-variable latency during iteration due to the need to skip over an unknown number of empty groups when traversing from one non-empty group to the next. Simply erasing the group will not suffice, as this would create a variable amount of latency during erasure when the group becomes empty, based on the number of groups after that group which would need to be reallocated backward in the vector. But even if one swapped the to-be-erased group with the back group, and then pop'd the to-be-erased group off the back, this would not solve the problem, as iterators require a stable pointer to the group they are traversing in order to traverse to the next group in the sequence. If an iterator pointed to an element in the back group, and the back group was swapped with the to-be-erased group, this would invalidate the iterator.</p>

<p>A vector of pointers to group structs is more-possible. Erasing groups would still have highly-variable latency due to reallocation, however the cost of reallocating pointers may be negligible depending on architecture. While the number of pointers can be expected to be low in most cases due to the growth factor in memory blocks, if the user has defined their own memory block capacity limits the number of pointers could be large, and this has to be taken into consideration. In this case using a pop-and-swap idiom is still not possible, because while it would not necessarily invalidate the internal references of an iterator pointing to an element within the back group, the sequence of blocks would be changed and therefore the iterator would be moved backwards in the iterative sequence.</p>

<p>A vector of memory blocks, as opposed to a vector of pointers to memory
blocks or a vector of group structs with dynamically-allocated memory blocks, would also not work, both due to the above points and because as it would (a) disallow a growth factor in the memory
blocks and (b) invalidate pointers to elements in subsequent blocks when a
memory block became empty of elements and was therefore removed from the
vector. In short, negating hive's beneficial aspects.</p>


<h4>2. A non-boolean method of skipping erased elements in O(1) time during iteration</h4>

<p>The reference implementation currently uses a skipfield pattern called the
<a href="https://plflib.org/matt_bentley_-_the_low_complexity_jump-counting_pattern.pdf">Low complexity jump-counting pattern</a>. This effectively encodes the length of runs of consecutive erased elements, into a skipfield, which allows for O(1) time
complexity during iteration. Since there is no branching involved in iterating
over the skipfield aside from end-of-block checks, it can be less problematic
computationally than a boolean skipfield (which has to branch for every
skipfield read) in terms of CPUs which don't handle branching or
branch-prediction failure efficiently (eg. Core2). It also does not have the variable latency associated with a boolean skipfield.</p>

<p>The pattern stores and modifies the run-lengths during insertion and erasure
with O(1) time complexity. It has a lot of similarities to the <a
href="https://plflib.org/matt_bentley_-_the_high_complexity_jump-counting_pattern.pdf">High
complexity jump-counting pattern</a>, which was a pattern previously used by
the reference implementation. Using the High complexity jump-counting pattern
is an alternative, though the skipfield update time complexity guarantees for
that pattern are effectively undefined, or between O(1) and O(skipfield length)
for each insertion/erasure. In practice those updates result in one
memcpy operation which resolves to a single block-copy operation, but it is
still a little slower than the Low complexity jump-counting pattern. The
method you use to skip erased elements will typically also have an effect on the type of
memory-reuse mechanism you can utilize.</p>

<p>A pure boolean skipfield is not usable because it makes iteration time
complexity undefined - it could for example result in thousands of branching
statements + skipfield reads for a single ++ operation in the case of many
consecutive erased elements. In the high-performance fields for which this
container was initially designed, this brings with it unacceptable latency.
However another strategy using a combination of a jump-counting <i>and</i>
boolean skipfield, which saves memory at the expense of computational
efficiency, is possible as follows:</p>
<ol>
  <li>Instead of storing the data for the low complexity jump-counting pattern
    in it's own skipfield, have a boolean bitfield indicating which elements
    are erased. Store the jump-counting data in the erased element's memory
    space instead (possibly alongside free list data).</li>
  <li>When iterating, check whether the element is erased or not using the
    boolean bitfield; if it is not erased, do nothing. If it is erased, read
    the jump value from the erased element's memory space and skip forward the
    appropriate number of nodes both in the element memory block and the
    boolean bitfield.</li>
</ol>

<p>This approach has the advantage of still performing O(1) iterations from one
non-erased element to the next, unlike a pure boolean skipfield approach, but
compared to a pure jump-counting approach introduces 3 additional costs per
iteration via (1) a branch operation when checking the bitfield, (2) an
additional read (of the erased element's memory space) and (3) a bitmasking
operation + bitshift to read the bit. But it does reduce the memory overhead of
the skipfield to 1 bit per-element, which reduces the cache load. An implementation and benchmarking would be required in order to establish how this approach compares to the current implementation's performance.</p>

<p>Another method worth mentioning is the use of a referencing array - for example, having a vector of elements, together with a vector of either indexes or pointers to those elements. When an element is erased, the vector of elements itself is not updated - no elements are reallocated. Meanwhile the referencing vector is updated and the index or pointer to the erased element is erased. When iteration occurs it iterates over the referencing vector, accessing each element in the element vector via the indexes/pointers. The disadvantages of this technique are (a) much higher memory usage, particularly for small elements and (b) highly-variable latency during erasure due to reallocation in the referencing array. Since once of the goals of hive is predictable latency, this is likely not suitable.</p>

<p><a href="http://bitsquid.blogspot.ca/2011/09/managing-decoupling-part-4-id-lookup.html">Packed arrays</a> are not worth mentioning as the iteration method is considered separate from the referencing mechanism, making them unsuitable for a std:: container.</p>


<h4>3. Erased-element location recording mechanism</h4>

<p>There are two valid approaches here; both involve per-memory-block <a
href="https://en.wikipedia.org/wiki/Free_list">free lists</a>, utilizing the
memory space of erased elements. The first approach forms a free list of all
erased elements. The second forms a free list of the first element in each
<i>run</i> of consecutive erased elements ("skipblocks", in terms of the
terminology used in the jump-counting pattern papers). The second can be more
efficient, but requires a doubly-linked free list rather than a singly-linked
free list, at least with a jump-counting skipfield - otherwise it would become an O(N) operation to update links in the
skipfield, when a skipblock expands or contracts during erasure or
insertion.</p>

<p>The reference implementation currently uses the second approach, using three
things to keep track of erased element locations:</p>
<ol type="a">
  <li>Metadata for each memory block includes a 'next block with erasures'
    pointer. The container itself contains a 'blocks with erasures' list-head
    pointer. These are used by the container to create an intrusive
    singly-linked list of memory blocks with erased elements which can be
    re-used for future insertions.</li>
  <li>Metadata for each memory block also includes a 'free list head' index
    number, which records the index (within the memory block) of the first
    element of the last-created skipblock - the 'head' skipblock.</li>
  <li>The memory space of the first erased element in each skipblock is
    reinterpret_cast'd via pointers as two index numbers, the first giving the
    index of the previous skipblock in that memory block, the second giving the
    index of the next skipblock in the sequence. In the case of the 'head'
    skipblock in the sequence, a unique number is used for the 'next' index.
    This forms a free list of runs of erased element memory locations which may
    be re-used.</li>
</ol>

<p>Using indexes for next and previous links, instead of pointers, reduces the necessary bit-depth of the next and previous links, thereby reducing the necessary over-alignment of the container's element type. If a global (ie. all memory blocks) free list were used, pointers would be necessary, as hive is bidirectional and does not support the [ ] operator. This would potentially increase the necessary over-alignment of the element type to 128 bits for a doubly-linked free list. A global free list would also decrease cache locality when traversing the free list by jumping between memory blocks.</p>

<p>Previous versions of the reference implementation used a singly-linked free
list of erased elements instead of a doubly-linked free list of skipblocks.
This was possible with the High complexity jump-counting pattern, but not
possible using the Low complexity jump-counting pattern as it cannot calculate
a skipblock's start node location from a middle node's value like the High
complexity pattern can. But using free-lists of skipblocks is a more efficient
approach as it requires fewer free list nodes. In addition, re-using only the start or end nodes of a skipblock is faster because it never splits a single skipblock in two (which would require adding a new skipblock to the free list).</p>

<p>One cannot use a stack of pointers (or similar) to erased elements for this
mechanism, as early versions of the reference implementation did, because this
can create allocations during erasure, which changes the exception guarantees
of erase(). One could instead scan all skipfields until an erased location was
found, or simply have the first item in the list above and then scan the first
available block, though both of these approaches would be slow.</p>

<p>In terms of the alternative <i>boolean + jump-counting skipfield</i>
approach described in the erased-element-skip-method section above, one could store both the
jump-counting data and free list data in any given erased element's memory
space, provided of course that elements are aligned to be wide enough to fit
both.</p>


<h3>Implementation of iterator class</h3>

<p>Any iterator implementation is going to be dependent on the erased-element-skipping mechanism used. The reference implementation's iterator stores a pointer to the current 'group' struct mentioned above, plus a pointer to the current element and a
pointer to its corresponding skipfield node. An alternative approach is to
store the group pointer + an index, since the index can indicate both the
offset from the memory block for the element, as well as the offset from the
start of the skipfield for the skipfield node. However multiple implementations
and benchmarks across many processors have shown this to be worse-performing
than the separate pointer-based approach, despite the increased memory cost for
the iterator class itself.</p>

<p>++ operation is as follows, utilising the reference implementation's
Low-complexity jump-counting pattern:</p>
<ol>
  <li>Add 1 to the existing element and skipfield pointers.</li>
  <li>Dereference skipfield pointer to get value of skipfield node, add value
    of skipfield node to both the skipfield pointer and the element pointer. If
    the node is erased, its value will be a positive integer indicating the
    number of nodes until the next non-erased node, if not erased it will be
    zero.</li>
  <li>If element pointer is now beyond end of element memory block, change
    group pointer to next group, element pointer to the start of the next
    group's element memory block, skipfield pointer to the start of the next
    group's skipfield. In case there is a skipblock at the beginning of this
    memory block, dereference skipfield pointer to get value of skipfield node
    and add value of skipfield node to both the skipfield pointer and the
    element pointer. There is no need to repeat the check for end of block, as
    the block would have been removed from the iteration sequence if it were
    empty of elements.</li>
</ol>

<p>-- operation is the same except both step 1 and 2 involve subtraction rather
than adding, and step 3 checks to see if the element pointer is now before the
beginning of the memory block. If so it traverses to the back of the previous
group, and subtracts the value of the back skipfield node from the element
pointer and skipfield pointer.</p>

<p>Iterators are bidirectional but also provide constant time
complexity &gt;, &lt;, &gt;=, &lt;= and &lt;=&gt; operators for convenience
(eg. in <code>for</code> loops when skipping over multiple elements per loop
and there is a possibility of going past a pre-determined end element). This is
achieved by keeping a record of the order of memory blocks. In the reference
implementation this is done by assigning a number to each memory block in its
metadata. In an implementation using a vector of pointers to memory blocks
instead of a linked list, one could use the position of the pointers within the
vector to determine this. Comparing relative order of the two iterators' memory
blocks via this number, then comparing the memory locations of the elements
themselves, if they happen to be in the same memory block, is enough to
implement all greater/lesser comparisons.</p>

<h3>Additional notes on specific functions</h3>
<ul>
  <li><code style="font-weight:bold">iterator insert</code> (all variants)<br>

    <p>Insertion re-uses previously-erased element memory locations when
    available, so position of insertion is effectively random unless no
    previous erasures have occurred, in which case all elements will be
    inserted linearly to the back of the container, at least in the current
    implementation. These details have been removed from the standard in order
    to allow leeway for potentially-better implementations in future - though
    it is expected that a hive will always reuse erased memory locations, it
    is impossible to predict optimal strategies for unknown future hardware.</p>
</li>
  <li><code style="font-weight:bold">void insert</code> (all variants)<br>

    <p>For range, fill and initializer_list insertion, it is not possible to guarantee that all the elements inserted will be sequential in the hive's iterative sequence, and therefore it is not considered useful to return an iterator to the first inserted element. There is a precedent for this in the various std:: map containers. Therefore these functions return void presently.</p>
    <p>For range insert and range constructors, thhe syntax has been modified compared to other containers in order to take two potentially-different iterator types in order to support sentinels and the like.</p>
</li>

  <li><code style="font-weight:bold">iterator erase</code> (all variants)<br>
  <p>Firstly it should be noted that erase may retain memory blocks which become completely empty of elements due to erasures, adding them to the set of unused memory blocks which are normally created by reserve(). Under what circumstances these memory blocks are retained rather than deallocated is implementation-defined - however given that small memory blocks have low cache locality compared to larger ones, from a performance perspective it is best to only retain the larger of the blocks currently allocated in the hive. In most cases this would mean the back block would almost always be retained. There is a lot of nuance to this, and it's also a matter of trading off complexity of implementation vs actual benchmarked speed vs latency. In my tests retaining both back blocks and 2nd-to-back blocks while ignoring actual capacity of blocks seems to have the best overall performance characteristics.</p>
  <p>There are three major performance advantages to retaining back blocks as opposed to any block - the first is that these will be, under most circumstances, the largest blocks in the hive (given the built-in growth factor) - the only exception to this is when splice is used, which may result in a smaller block following a larger block (implementation-dependent). Larger blocks == more cache locality during iteration, large numbers of erased elements notwithstanding. The second advantage is that in situations where elements are being inserted to and erased from the back of the hive (this assumes no erased element locations in other memory blocks, which would otherwise be used for insertions) continuously and in quick succession, retaining the back block avoids large numbers of deallocations/reallocations. The third advantage is that deallocations of larger blocks can, in part, be moved to non-critical code regions via trim(). Though ultimately if the user wants total control of when allocations and deallocations occur they would want to use a custom allocator.</p>
<p>Lastly, specifying a return iterator for range-erase may seem pointless, as no reallocation of elements occurs in erase so the return iterator will almost always be the <code>last</code> iterator of the <code>const_iterator first, const_iterator last</code> pair. However if <code>last</code> was <code>end()</code>, the new value of <code>end()</code> (if it has changed due to empty block removal) will be returned. In this case either the user submitted <code>end()</code> as <code>last</code>, or they incremented an iterator pointing to the final element in the hive and submitted that as <code>last</code>. The latter is the only valid reason to return an iterator from the function, as it may occur as part of a loop which is erasing elements and ends when <code>end()</code> is reached. If <code>end()</code> is changed by the erasure of an entire memory block, but the iterator being used in the loop does not accurately reflect <code>end()</code>'s new value, that iterator could iterate past <code>end()</code> and the loop would never finish.</li>

      <li><code style="font-weight:bold">void reshape(std::hive_limits block_capacity_limits);</code><br>
		  <p>This function updates the block capacity limits in the hive and, if necessary, changes any blocks which fall outside of those limits to be within the limits. For this reason it may trigger an exception with non-copyable/movable types, and also invalidate pointers/iterators/etc to elements.</p>
        <p>The order of elements post-reshape is not guaranteed to be stable in
        order to allow for optimizations. Specifically in the instance where a
        given element memory block no longer fits within the limits supplied by
        the user, depending on the state of the hive as a whole, the elements
        within that memory block could be reallocated to previously-erased
        element locations in other memory blocks which do fit within the
        supplied limits. Or they could be reallocated to the back of the final memory block.</p>
        <p>Additionally if there is empty capacity at the back of the last
        block in the container, at least some of the elements could be moved to
        that position rather than being reallocated to a new memory block. Both
        of these techniques increase cache locality by removing skipped memory
        spaces within existing memory blocks. However whether they are used is
        implementation-dependent.</p>
      </li>
      <li><code style="font-weight:bold">iterator get_iterator(pointer p) noexcept;<br>
		const_iterator get_iterator(const_pointer p) const noexcept;</code><br>
        <p>Because hive iterators are likely to be large, storing three
        pieces of data - current memory block, current element within memory
        block and potentially, current skipfield node - a program storing many
        links to elements within a hive may opt to dereference iterators to
        get pointers and store those instead of iterators, to save memory. This
        function reverses the process, giving an iterator which can then be
        used for operations such as erase. get_const_iterator was fielded as a workaround for the possibility of someone wanting to supply a non-const pointer
		  and get a const_iterator back, however <code>as_const</code> fulfills this same role when supplied to get_iterator and doesn't require expanding the interface of hive.</p>
      </li>
      <li><code style="font-weight:bold">void shrink_to_fit();</code>
        <p>A decision had to be made as to whether this function should, in the
        context of hive, be allowed to reallocate elements (as std::vector
        does) or simply trim off unused memory blocks (as std::deque does). Due
        to the fact that a large hive memory block could have as few as one
        remaining element after a series of erasures, it makes little sense to
        only trim unused blocks, and instead a shrink_to_fit is expected to
        reallocate all non-erased elements to as few memory blocks as possible
        in order to increase cache locality during iteration and reduce memory
        use. As with reshape(), the order of elements post-reshape is not
        guaranteed to be stable, to allow for potential optimizations. The
        trim() command is also introduced as a way to free unused memory blocks
        which have been previously reserved, without reallocating elements and
        invalidating iterators.</p>
      </li>
      <li><code style="font-weight:bold">void sort();</code>
		  <p>It is forseen that although the container has unordered insertion, there may be circumstances where sorting is desired. Because hive uses bidirectional iterators, using std::sort or similar is not possible. Therefore an internal sort routine is warranted, as it is with std::list. An implementation of the sort routine used in the reference implementation of hive can be found in a non-container-specific form at <a href="https://plflib.org/indiesort.htm">plflib.org/indiesort.htm</a> - see that page for the technique's advantages over the usual sort algorithms for non-random-access containers. Unfortunately to date there has been no interest in including this algorithm in the standard library. An allowance is made for sort to allocate memory if necessary, so that algorithms such as indiesort can be used internally.</p>
		</li>
      <li><code style="font-weight:bold">void splice(hive &amp;x);</code>
        <p>Whether <code>x</code>'s blocks are transferred to the beginning or
        end of <code>*this</code>'s iterative sequence, or interlaced in some way (for example, to preserve relative capacity growth-factor ordering of subsequent blocks) is implementation-defined. Better
        performance may be gained in some cases by allowing the source blocks
        to go to the front rather than the back, depending on how full the
        final block in <code>x</code>'s iterative sequence is. This is because
        unused elements that are not at the back of hive's iterative sequence
        will need to be marked as skipped, and skipping over large numbers of
        elements will incur a small performance disadvantage during iteration
        compared to skipping over a small number of elements, due to memory
        locality.</p>
        <p>This function is not noexcept for three reasons - the first is that a length_error exception may be thrown if any of the capacities of the source <code>x</code>'s blocks are outside of the range defined by the destination's (<code>*this</code>) minimum and maximum block capacity limits. Second is that an exception may be thrown if the allocators of the two hives are different. Third is that in the case of an implementation using a linked list of group structs (ala the reference implementation) transferring blocks involves no allocation, however in the case of an implementation using a vector of pointers to blocks, an additional allocation may have to be made if the group pointer vector isn't of sufficient capacity to accomodate pointers to the spliced blocks from the source.</p>
      </li>
      <li><code style="font-weight:bold">size_type memory() const noexcept;</code>
		  <p>A hive uses memory block metadata and may use a skipfield, both which are
        implementation-defined, so it is not possible for a user to estimate
        internal memory usage from size(), sizeof() or capacity(). This function fulfills
        that role. Because some types of elements may allocate their own memory
        dynamically (eg. std::hive&lt;std::vector&gt;) only the static
        allocation of each element is included in this functions byte count.</p>
      <p>This function can be made constant time by adding a counter to the hive that keeps track of the number of reserved memory blocks available, or by having a vector of pointers to memory blocks instead an intrusive linked list of memory blocks. However in the case of the reference implementation which uses linked lists, the counter metadata would only be used by this function and since this function is not expected to be in heavy use, the time complexity of this function is left as implementation-defined to allow flexibility.</p>
		</li>
      <li>Non-member function overloads for <code style="font-weight:bold">advance, prev and next</code> (all variants)<br>

        <p>For these functions, complexity is dependent on state of hive, position of iterator and
        amount of distance, but in many cases will be less than linear, and may
        be constant. To explain: it is necessary in a hive to store metadata
        both about the capacity of each block (for the purpose of iteration)
        and how many non-erased elements are present within the block (for the
        purpose of removing blocks from the iterative chain once they become
        empty). For this reason, intermediary blocks between the iterator's
        initial block and its final destination block (if these are not the
        same block, and if the initial block and final block are not
        immediately adjacent) can be skipped rather than iterated linearly
        across, by using the "number of non-erased elements" metadata.</p>
        <p>This means that the only linear time operations are any iterations
        within the initial block and the final block. However if either the
        initial or final block have no erased elements (as determined by
        comparing whether the block's capacity metadata and the block's "number
        of non-erased elements" metadata are equal), linear iteration can be
        skipped for that block and pointer/index math used instead to determine
        distances, reducing complexity to constant time. Hence the best case
        for this operation is constant time, the worst is linear to the
        distance.</p>
      </li>
      <li>Non-member function overloads for <code style="font-weight:bold">distance</code> (all variants)<br>

        <p>The same considerations which apply to advance, prev and next also
        apply to distance - intermediary blocks between first and last's blocks
        can be skipped in constant time and their "number of non-erased
        elements" metadata added to the cumulative distance count, while
        first's block and last's block (if they are not the same block) must be
        linearly iterated across unless either block has no erased elements, in
        which case the operation becomes pointer/index math and is reduced to
        constant time for that block. In addition, if first's block is not the
        same as last's block, and last is equal to end() or --end(), or is the
        last element in that block, last's block's elements can also counted
        from the "number of non-erased elements" metadata rather than via
        iteration.</p>
      </li>
      <li>Priority template parameter for the container<br>
      <p>This forms a non-binding request for the container to prioritize either performance or memory use, supplied in the form of a scoped enum. The reference implementation uses a regular un-scoped enum, as it must also work under C++03. In terms of the reference implementation the priority parameter changes the skipfield type from unsigned short (performance) to unsigned char (memory use) - which in turn changes the maximum block limits, because in the reference implementation the block capacities are limited to numeric_limits&lt;skipfield_type&gt;::max. The maximum block capacity limit affects iteration performance, due to a greater or lesser number of elements being able to be sequential in memory, and the subsequent effects on cache. For small numbers of elements ie. under 1000, unsigned char also will be faster in addition to needing less memory, due to the lowered cache usage and the fact that the maximum block capacity limit is not significantly limiting cache locality at this point. Hence, prioritizing for performance may not necessarily be faster in all circumstances, but should be faster in most - if in fact the request is actioned upon, and it is not guaranteed to be actioned upon in all implementations.</p>
      <p>There is a point of diminishing returns in terms of how many elements can be stored sequentially in memory and how that impacts performance, due to the limits of cache size - hence it was found that increasing the skipfield type to unsigned int and thence increasing the block capacity limit, did not have a performance advantage on any number of elements.</p>
      </li>

    </ul>


	<h3>Results of implementation</h3>

  <p>In practical application the reference implementation is generally faster
  for insertion and (non-back) erasure than current standard library
  containers, and generally faster for iteration than any container except
  vector and deque. For full details, see <a href="#benchmarks">benchmarks</a>.</p>


  <h2><a id="technical"></a>VI. Technical Specification</h2>

  <p>Suggested location of hive in the standard is 22.3, Sequence
  Containers.</p>

  <h3>22.3.7 Header <code>&lt;hive&gt;</code> synopsis [hive.syn]</h3>

  <div style="background: #ffffff; overflow:auto; width:auto; border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
  <pre style="margin: 0; line-height: 125%">
#include &lt;initializer_list&gt; // see 17.10.2
#include &lt;compare&gt; // see 17.11.1
#include &lt;concepts&gt; // see 18.3
#include &lt;stdexcept&gt; // see 19.2
#include &lt;utility&gt; // see 20.2.1
#include &lt;memory&gt; // see 20.10

namespace std {
   // 22.3.14, class template hive

	struct hive_limits;
	enum class hive_priority;

   template &lt;class T, class Allocator = allocator&lt;T&gt;, hive_priority priority = hive_priority::performance&gt; class hive;

   namespace pmr {
      template &lt;class T&gt;
      using hive = std::hive&lt;T, polymorphic_allocator&lt;T&gt;&gt;;
   }
}</pre>
  </div>

  <h4><a id="iteratorinvalidation"></a>Iterator Invalidation</h4>

  <table border="1">
    <tbody>
      <tr>
        <td>All read-only operations, swap, std::swap, splice, operator=
          &amp;&amp; (source), reserve, trim</td>
        <td>Never.</td>
      </tr>
      <tr>
        <td>clear, operator= &amp; (destination), operator= &amp;&amp;
          (destination)</td>
        <td>Always.</td>
      </tr>
      <tr>
        <td>reshape</td>
        <td>Only if memory blocks exist whose capacities do not fit within the
          supplied limits.</td>
      </tr>
      <tr>
        <td>shrink_to_fit</td>
        <td>Only if capacity() != size().</td>
      </tr>
      <tr>
        <td>erase</td>
        <td>Only for the erased element. If an iterator is == end() it may be
          invalidated if the back element of the hive is erased (similar to
          deque (22.3.9)).
			 Likewise if a reverse_iterator is == rend() it may be invalidated if the front element of the hive is erased.
			 The same applies with cend() and crend() for const_iterator and const_reverse_iterator respectively.
			 </td>
      </tr>
      <tr>
        <td>insert, emplace</td>
        <td>If an iterator is == end() or == begin() it may be invalidated by a subsequent insert/emplace.
		  		Likewise if a reverse_iterator is == rend() or == rbegin() it may be
          invalidated by a subsequent insert/emplace.
			 The same rules apply with cend(), cbegin() and crend(), crbegin() for const_iterator and const_reverse_iterator respectively.
			 </td>
      </tr>
    </tbody>
  </table>


  <h3>22.3.14 Class template <code>hive</code> [hive]</h3>

  <h4>22.3.14.1 Class template <code>hive</code> overview [hive.overview]</h4>
<ol>
  <li>A hive is a sequence container that allows constant-time insert and
    erase operations. Insertion location is the back of the container when no
    erasures have occured. When erasures have occured it will re-use existing
    erased element memory spaces where possible and insert to those locations.
    Storage management is handled automatically and is specifically organized
    in multiple blocks of sequential elements. Unlike vectors (22.3.12) and
    deques (22.3.9), fast random access to hive elements is not supported,
    but specializations of advance/next/prev give access which can be
    better than linear time in the number of elements traversed.</li>
  <li>Erasures are processed using implementation-defined strategies for skipping erased elements during iteration, rather than reallocating subsequent elements as is expected in a vector or deque.</li>
  <li>Memory block element capacities have an implementation-defined growth
    factor, for example a new block's capacity could be equal to the summed capacities of the existing
    blocks.</li>
  <li>Limits can be placed on the minimum and maximum element capacities of
    memory blocks, both by a user and by an implementation. Minimum capacity
    shall be no more than maximum capacity. When limits are not specified by a
    user, the implementation's default limits are used. Where user-specified limits do
    not fit within the implementation's limits (ie. user minimum is less than
    implementation minimum or user maximum is more than implementation maximum)
    an exception is thrown. User-specified limits can be supplied to a
    constructor or to the reshape() function, using the
    <code>std::hive_limits</code> struct with its <code>min</code> and
    <code>max</code> members set to the minimum and maximum element capacity
    limits respectively. The current limits in a hive instance can be
    obtained from block_capacity_limits().</li>
  <li>A hive satisfies all of the requirements of a container, of a
    reversible container (given in two tables in 22.2), of a sequence
    container, including most of the optional sequence container requirements
    (22.2.3), and of an allocator-aware container (Table 78). The exceptions
    are the <code>operator[]</code> and <code>at</code> member functions, which
    are not provided.</li>
  <li>hive iterators satisfy bidirectional requirements but also provide
    relational operators &lt;, &lt;=, &gt;, &gt;= and &lt;=&gt; which compare
    the relative ordering of two iterators in the sequence of a hive
  instance.</li>
  <li>Iterator operations ++ and -- take constant amortized time, other iterator operations take constant time.</li>
</ol>
<code>template &lt;class T, class Allocator = std::allocator&lt;T&gt;, priority Priority = priority::performance&gt; class hive</code>

<p><code><b>T</b></code> - the element type. In general T shall meet the
requirements of <a
href="https://en.cppreference.com/w/cpp/named_req/Erasable">Erasable</a>, <a
href="https://en.cppreference.com/w/cpp/named_req/CopyAssignable">CopyAssignable</a>
and <a
href="https://en.cppreference.com/w/cpp/named_req/CopyConstructible">CopyConstructible</a>.<br>
However, if emplace is utilized to insert elements into the hive, and no
functions which involve copying or moving are utilized, T is only required to
meet the requirements of <a href="https://en.cppreference.com/w/cpp/named_req/Erasable">Erasable</a>.<br>
If move-insert is utilized instead of emplace, T shall also meet the
requirements of <a
href="https://en.cppreference.com/w/cpp/named_req/MoveConstructible">MoveConstructible</a>.<br>
<br>
<code><b>Allocator</b></code> - an allocator that is used to acquire memory to
store the elements. The type shall meet the requirements of <a
href="https://en.cppreference.com/w/cpp/named_req/Allocator">Allocator</a>. The
behavior is undefined if <code>Allocator::value_type</code> is not the same as
T.<br>
<br>
<code><b>Priority</b></code> - if set to <code>priority::memory_use</code> this is a non-binding request to prioritize lowered memory usage over container performance. [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. If this feature is implemented, it is <i>not</i> specified that the container shall have better performance when using priority::performance instead of priority::memory_usage in <i>all</i> scenarios, but that it shall have better performance in <i>most</i> scenarios. - end note ]</p>

<div style="background: #ffffff; overflow:auto; width:auto; border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin: 0; line-height: 125%">namespace std {

struct hive_limits
{
	size_t min, max;
	hive_limits(size_t minimum, size_t maximum) noexcept : min(minimum), max(maximum) {}
};


enum struct hive_priority { performance, memory_use };


template &lt;class T, class Allocator = allocator&lt;T&gt;, hive_priority Priority = hive_priority::performance&gt;
class hive {
public:

  // types
  using value_type = T;
  using allocator_type = Allocator;
  using pointer = typename allocator_traits&lt;Allocator&gt;::pointer;
  using const_pointer = typename allocator_traits&lt;Allocator&gt;::const_pointer;
  using reference = value_type&amp;;
  using const_reference = const value_type&amp;;
  using size_type = implementation-defined; // see 22.2
  using difference_type = implementation-defined; // see 22.2
  using iterator = implementation-defined; // see 22.2
  using const_iterator = implementation-defined; // see 22.2
  using reverse_iterator = implementation-defined; // see 22.2
  using const_reverse_iterator = implementation-defined; // see 22.2



  hive() noexcept(noexcept(Allocator())) : hive(Allocator()) { }
  explicit hive(std::hive_limits block_capacity_limits) noexcept(noexcept(Allocator())) : hive(Allocator()) { }
  explicit hive(const Allocator&amp;) noexcept;
  explicit hive(std::hive_limits block_capacity_limits, const Allocator&amp;) noexcept;
  explicit hive(size_type n, std::hive_limits block_capacity_limits = implementation-defined, const Allocator&amp; = Allocator());
  hive(size_type n, const T&amp; value, std::hive_limits block_capacity_limits = implementation-defined, const Allocator&amp; = Allocator());
  template&lt;class InputIterator1, class InputIterator2&gt;
    hive(InputIterator1 first, InputIterator2 last, std::hive_limits block_capacity_limits = implementation-defined, const Allocator&amp; = Allocator());
  hive(const hive&amp; x);
  hive(hive&amp;&amp;) noexcept;
  hive(const hive&amp;, const Allocator&amp;);
  hive(hive&amp;&amp;, const Allocator&amp;);
  hive(initializer_list&lt;T&gt;, std::hive_limits block_capacity_limits = implementation-defined, const Allocator&amp; = Allocator());
  ~hive() noexcept;
  hive&amp; operator= (const hive&amp; x);
  hive&amp; operator= (hive&amp;&amp; x) noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_move_assignment::value || allocator_traits&lt;Allocator&gt;::is_always_equal::value);
  hive&amp; operator= (initializer_list&lt;T&gt;);
  template&lt;class InputIterator1, class InputIterator2&gt; void assign(InputIterator1 first, InputIterator2 last);
  void assign(size_type n, const T&amp; t);
  void assign(initializer_list&lt;T&gt;);
  allocator_type get_allocator() const noexcept;



  // iterators
  iterator               begin() noexcept;
  const_iterator         begin() const noexcept;
  iterator               end() noexcept;
  const_iterator         end() const noexcept;
  reverse_iterator       rbegin() noexcept;
  const_reverse_iterator rbegin() const noexcept;
  reverse_iterator       rend() noexcept;
  const_reverse_iterator rend() const noexcept;

  const_iterator         cbegin() const noexcept;
  const_iterator         cend() const noexcept;
  const_reverse_iterator crbegin() const noexcept;
  const_reverse_iterator crend() const noexcept;


  // capacity
  [[nodiscard]] bool empty() const noexcept;
  size_type size() const noexcept;
  size_type max_size() const noexcept;
  size_type capacity() const noexcept;
  size_type memory() const noexcept;
  void reserve(size_type n);
  void shrink_to_fit();
  void trim() noexcept;


  // modifiers
  template &lt;class... Args&gt; iterator emplace(Args&amp;&amp;... args);
  iterator insert(const T&amp; x);
  iterator insert(T&amp;&amp; x);
  void insert(size_type n, const T&amp; x);
  template &lt;class InputIterator1, class InputIterator2&gt; void insert(InputIterator1 first, InputIterator2 last);
  void insert(initializer_list&lt;T&gt; il);
  iterator erase(const_iterator position);
  iterator erase(const_iterator first, const_iterator last);
  void swap(hive&amp;) noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_swap::value || allocator_traits&lt;Allocator&gt;::is_always_equal::value);
  void clear() noexcept;


  // hive operations
  void splice(hive &amp;x);

  std::hive_limits block_capacity_limits() const noexcept;
  void reshape(std::hive_limits block_capacity_limits);

  iterator get_iterator(pointer p) noexcept;
  const_iterator get_iterator(const_pointer p) const noexcept;

  void sort();
  template &lt;class Compare&gt; void sort(Compare comp);

  friend bool operator== (const hive &amp;x, const hive &amp;y);
  friend bool operator!= (const hive &amp;x, const hive &amp;y);


  class iterator
  {
    friend void advance(iterator &amp;it, Distance n);
    friend iterator next(iterator it, difference_type distance = 1);
    friend iterator prev(iterator it, difference_type distance = 1);
    friend difference_type distance(iterator first, iterator last);
  }


  class const_iterator
  {
    friend void advance(const_iterator &amp;it, Distance n);
    friend const_iterator next(const_iterator it, difference_type distance = 1);
    friend const_iterator prev(const_iterator it, difference_type distance = 1);
    friend difference_type distance(const_iterator first, const_iterator last);
  }


  class reverse_iterator
  {
    friend void advance(reverse_iterator &amp;it, Distance n);
    friend reverse_iterator next(reverse_iterator it, difference_type distance = 1);
    friend reverse_iterator prev(reverse_iterator it, difference_type distance = 1);
    friend difference_type distance(reverse_iterator first, reverse_iterator last);
  }


  class const_reverse_iterator
  {
    friend void advance(const_reverse_iterator &amp;it, Distance n);
    friend const_reverse_iterator next(const_reverse_iterator it, difference_type distance = 1);
    friend const_reverse_iterator prev(const_reverse_iterator it, difference_type distance = 1);
    friend difference_type distance(const_reverse_iterator first, const_reverse_iterator last);
  }


  // swap
  friend void swap(hive&amp; x, hive&amp; y)
    noexcept(noexcept(x.swap(y)));


  // erase
  template &lt;class Predicate&gt;
    friend size_type erase_if(hive&amp; c, Predicate pred);
  template &lt;class U&gt;
    friend size_type erase(hive&amp; c, const U&amp; value);
}


template&lt;class InputIterator, class Allocator = allocator&lt;iter-value-type &lt;InputIterator&gt;&gt;&gt;
  hive(InputIterator, InputIterator, Allocator = Allocator())
    -> hive&lt;iter-value-type &lt;InputIterator&gt;, Allocator&gt;;

</pre>
</div>


<h4>22.3.14.2 hive constructors, copy, and assignment [hive.cons]</h4>
<code style="font-weight:bold">explicit hive(const Allocator&amp;);</code>
<ol>
  <li>Effects: Constructs an empty hive, using the specified allocator.</li>
  <li>Complexity: Constant.</li>
</ol>
<br>

<code style="font-weight:bold">explicit hive(size_type n, const T&amp; value, std::hive_limits block_capacities = implementation-defined, const Allocator&amp; =Allocator());</code>
<ol start="3">
  <li>Preconditions: <code>T</code> shall be <i>Cpp17MoveInsertable</i> into
    <code>*this</code>.</li>
  <li>Effects: Constructs a hive with n copies of <code>value</code>, using
    the specified allocator.</li>
  <li>Complexity: Linear in n.</li>
  <li>Throws: <code>length_error</code> if <code>block_capacities.min</code> or
    <code>block_capacities.max</code> are outside the implementation's minimum
    and maximum element memory block capacity limits, or if
    <code>block_capacities.min &gt; block_capacities.max</code>.
  <li>Remarks: If <code>n</code> is larger than
    <code>block_capacities.min</code>, the capacity of the first block created
    will be the smaller of <code>n</code> or <code>block_capacities.max</code>.</li>
</ol>
<br>

<pre><code style="font-weight:bold">template &lt;class InputIterator1, class InputIterator2&gt;
  hive(InputIterator1 first, InputIterator2 last, std::hive_limits block_capacities = implementation-defined, const Allocator&amp; = Allocator());</code></pre>
<ol start="8">
  <li>Preconditions: <code>InputIterator1</code> shall be <code>std::equality_comparable_with InputIterator2</code>.</li>
  <li>Effects: Constructs a hive equal to the range [first, last), using the
    specified allocator.</li>
  <li>Complexity: Linear in distance(first, last).</li>
  <li>Throws: <code>length_error</code> if <code>block_capacities.min</code> or
    <code>block_capacities.max</code> are outside the implementation's minimum
    and maximum element memory block capacity limits, or if
    <code>block_capacities.min &gt; block_capacities.max</code>. Or
  <li>Remarks: If iterators are random-access, let <code>n</code> be last -
    first; if <code>n</code> is larger than <code>block_capacities.min</code>,
    the capacity of the first block created will be the smaller of
    <code>n</code> or <code>block_capacities.max</code>.</li>
</ol>


<h4>22.3.14.3 hive capacity [hive.capacity]</h4>

<code style="font-weight:bold">size_type capacity() const noexcept;</code>
<ol>
  <li>Returns: The total number of elements that the hive can currently
    contain without needing to allocate more memory blocks.</li>
</ol>
<br>

<code style="font-weight:bold">size_type memory() const noexcept;</code>
<ol start="2">
  <li>Returns: The memory use, in bytes, of the container as a whole,
    including elements but not including any dynamic allocation incurred by
    those elements.</li>
</ol>
<br>

<code style="font-weight:bold">void reserve(size_type n);</code>
<ol start="3">
  <li>Effects: A directive that informs a hive of a planned change in size,
    so that it can manage the storage allocation accordingly. Since minimum and
    maximum memory block sizes can be specified by users, after
    <code>reserve()</code>, <code>capacity()</code> is not guaranteed to be
    equal to the argument of <code>reserve()</code>, may be greater. Does not
    cause reallocation of elements.</li>
  <li>Complexity: It does not change the size of the sequence and creates at
    most <code>(n / block_capacity_limits().max) + 1</code> allocations.</li>
  <li>Throws: <code>length_error</code> if <code>n &gt; max_size()</code><sup><a href="#r223">223</a></sup>.</li>
</ol>
<p style="font-size: 90%"><a id="r223"></a>223) reserve() uses Allocator::allocate() which may throw an appropriate exception.</p>
<br>

<code style="font-weight:bold">void shrink_to_fit();</code>
<ol start="6">
  <li>Preconditions: <code>T</code> is <i>Cpp17MoveInsertable</i> into
    <code>*this</code>.</li>
  <li>Effects: shrink_to_fit is a non-binding request to reduce
    <code>capacity()</code> to be closer to <code>size()</code>. [ Note: The
    request is non-binding to allow latitude for implementation-specific
    optimizations. - end note ] It does not increase <code>capacity()</code>,
    but may reduce <code>capacity()</code> by causing reallocation. It may move
    elements from multiple memory blocks and consolidate them into a smaller
    number of memory blocks.<br>
    If an exception is thrown other than by the move constructor of a
    non-<em>Cpp17CopyInsertable</em> T, there are no effects.</li>
  <li>Complexity: If reallocation happens, linear to the number of elements
    reallocated.</li>
  <li>Remarks: Reallocation invalidates all the references, pointers, and
    iterators referring to the elements reallocated as well as the past-the-end
    iterator. [Note: If no reallocation happens, they remain valid. &mdash;end
    note] The order of elements post-operation is not guaranteed to be stable.
  </li>
</ol>
<br>

<code style="font-weight:bold">void trim();</code>
<ol start="10">
  <li>Effects: Removes and deallocates empty memory blocks created by prior
    calls to <code>reserve()</code> or <code>erase()</code>. If such memory
    blocks are present, <code>capacity()</code> will be reduced.</li>
  <li>Complexity: Linear in the number of reserved blocks to deallocate.</li>
  <li>Remarks: Does not reallocate elements and no references, pointers or
    iterators referring to elements in the sequence will be invalidated.</li>
</ol>
<br>



<h4>22.3.14.4 hive modifiers [hive.modifiers]</h4>
<pre><code style="font-weight:bold">iterator insert(const T&amp; x);
iterator insert(T&amp;&amp; x);
void insert(size_type n, const T&amp; x);
template &lt;class InputIterator1, class InputIterator2&gt;
  void insert(InputIterator1 first, InputIterator2 last);
void insert(initializer_list&lt;T&gt;);
template &lt;class... Args&gt;
  iterator emplace(Args&amp;&amp;... args);</code></pre>
<ol>
  <li>Preconditions: For <code>template &lt;class InputIterator1, class InputIterator2&gt; void insert(InputIterator1 first, InputIterator2 last)</code>, <code>InputIterator1</code> shall be <code>std::equality_comparable_with InputIterator2</code>.</li>
  <li>Complexity: Insertion of a single element into a hive takes constant
    time and exactly one call to a constructor of <code>T</code>. Insertion of
    multiple elements into a hive is linear in the number of elements
    inserted, and the number of calls to the copy constructor or move
    constructor of <code>T</code> is exactly equal to the number of elements
    inserted.</li>
  <li>Remarks: Does not affect the validity of iterators and references, unless
    an iterator points to <code>end()</code>, in which case it may be
    invalidated. Likewise if a reverse_iterator points to <code>rend()</code>
    it may be invalidated. If an exception is thrown there are no effects.</li>
</ol>
<br>

<code style="font-weight:bold">iterator erase(const_iterator position);</code>
<ol start="3">
  <li>Effects: Invalidates only the iterators and references to the erased
    element.</li>
  <li>Complexity: Constant. [Note: operations pertaining to the updating of any data associated with the erased-elemment skipping mechanism is not factored into this; it is implementation-defined and may be constant, linear or otherwise defined. &mdash;end note]</li>
</ol>
<br>

<code style="font-weight:bold">iterator erase(const_iterator first, const_iterator last);</code>
<ol start="5">
  <li>Effects: Invalidates only the iterators and references to the erased
    elements. In some cases if an iterator is equal to <code>end()</code> and
    the back element of the hive is erased, that iterator may be invalidated.
    Likewise if a reverse_iterator is equal to <code>rend()</code> and the
    front element of the hive is erased, that reverse_iterator may be
    invalidated.</li>
  <li>Complexity: Linear in the number of elements erased for
    non-trivially-destructible types, for trivially-destructible types constant
    in best case and linear in worst case, approximating logarithmic in the
    number of elements erased on average.</li>
</ol>
<br>

<code style="font-weight:bold">void swap(hive&amp; x) noexcept(allocator_traits&lt;Allocator&gt;::propagate_on_container_swap::value || allocator_traits&lt;Allocator&gt;::is_always_equal::value);<br>
</code>
<ol start="7">
  <li>Effects: Exchanges the contents and <code>capacity()</code> of
    <code>*this</code> with that of <code>x</code>.</li>
  <li>Complexity: Constant time.</li>
</ol>


<h4>22.3.14.5 Operations [hive.operations]</h4>

<code style="font-weight:bold">void splice(hive &amp;x);</code>
<ol>
  <li>Preconditions: &amp;x != this.</li>
  <li>Effects: Inserts the contents of <code>x</code> into <code>*this</code>
    and <code>x</code> becomes empty. Pointers and references to the moved
    elements of <code>x</code> now refer to those same elements but as members
    of <code>*this</code>. Iterators referring to the moved elements will
    continue to refer to their elements, but they now behave as iterators into
    <code>*this</code>, not into <code>x</code>.</li>
  <li>Complexity: Constant time.</li>
  <li>Throws: <code>length_error</code> if any of <code>x</code>'s element memory block capacities are outside the current minimum and maximum element
    memory block capacity limits of <code>*this</code>.<sup><a href="#r223">223</a></sup></li>
</ol>
<br>

<code style="font-weight:bold">std::hive_limits block_capacity_limits() const noexcept;</code>
<ol start="4">
  <li>Effects: Returns a std::hive_limits struct with the <code>min</code> and
    <code>max</code> members set to the current minimum and maximum element
    memory block capacity limits of <code>*this</code>.</li>
  <li>Complexity: Constant time.</li>
</ol>
<br>

<code style="font-weight:bold">void reshape(std::hive_limits block_capacity_limits);</code><br>
<ol start="6">
  <li>Preconditions: <code>T</code> shall be <i>Cpp17MoveInsertable</i> into
    <code>*this</code>.<br>
  </li>
  <li>Effects: Sets minimum and maximum element memory block capacities to the
    min and max members of the supplied std::hive_limits struct. If the hive is
    not empty, adjusts existing memory block capacities to conform to the new
    minimum and maximum block capacities, where necessary. If existing memory
    block capacities are within the supplied minimum/maximum range, no
    reallocation of elements takes place. If they are not within the supplied
    range, elements are reallocated to new memory blocks which fit within the
    supplied range and the old memory blocks are deallocated. Order of elements
    is not guaranteed to be stable.</li>
  <li>Complexity: If no reallocation occurs, constant time. If reallocation
    occurs, complexity is linear in the number of elements reallocated.</li>
  <li>Throws: <code>length_error</code> if <code>block_capacities.min</code> or
    <code>block_capacities.max</code> are outside the implementation's minimum
    and maximum element memory block capacity limits, or if
    <code>block_capacities.min &gt; block_capacities.max</code>.<sup><a href="#r223">223</a></sup></li>
  <li>Remarks: The order of elements post-operation is not guaranteed to be
    stable (16.5.5.8).</li>
</ol>
<br>

<code style="font-weight:bold">iterator get_iterator(pointer p) noexcept;<br>
const_iterator get_iterator(const_pointer p) const noexcept;</code>
<ol start="11">
  <li>Effects: Returns an iterator or const_iterator pointing to the same element as the
    pointer or const_pointer. If <code>p</code> does not point to an element in
    <code>*this</code>, <code>end()</code> is returned.</li>
</ol>
<br>

<pre><code style="font-weight:bold">void sort();
template &lt;class Compare&gt;
  void sort(Compare comp);</code></pre>
<ol start="12">
  <li>Preconditions: <code>T</code> is <i>Cpp17MoveInsertable</i> into
    <code>*this</code>.</li>
  <li>Effects: Sorts the hive according to the <code>operator &lt;</code> or
    a <code>Compare</code> function object. If an exception is thrown, the
    order of the elements in <code>*this</code> is unspecified. Iterators and
    references may be invalidated.</li>
  <li>Complexity: Approximately N log N comparisons, where <code>N == size()</code>.</li>
  <li>Throws: <code>bad_alloc</code> if it fails to allocate any memory necessary for the sort process.</li>
  <li>Remarks: Not required to be stable (16.5.5.8). May allocate memory.</li>
</ol>



<h4>22.3.14.6 Specialized algorithms [hive.special]</h4>
<pre><code style="font-weight:bold">friend void swap(hive &amp;x, hive &amp;y) noexcept(noexcept(x.swap(y)));</code></pre>
<ol>
  <li>Effects: As if by <code>x.swap(y)</code>.</li>
  <li>Remarks: This function is to be found via argument-dependent
lookup only.</li>
</ol>
<br>

<pre><code style="font-weight:bold">friend bool operator== (const hive &amp;x, const hive &amp;y);
friend bool operator!= (const hive &amp;x, const hive &amp;y);</code></pre>
<ol start="3">
  <li>Returns: For ==, returns <code>True</code> if both containers have the same elements in the same iterative sequence, otherwise <code>False</code>.
  For !=, returns <code>True</code> if both containers do not have the same elements in the same iterative sequence, otherwise <code>False</code>.</li>
  <li>Remarks: These functions are to be found via argument-dependent lookup only.</li>
</ol>



<pre><code style="font-weight:bold">
class iterator
{
  friend void advance(iterator &amp;it, Distance n);
  friend iterator next(iterator it, difference_type distance = 1);
  friend iterator prev(iterator it, difference_type distance = 1);
  friend difference_type distance(iterator first, iterator last);
}


class const_iterator
{
  friend void advance(const_iterator &amp;it, Distance n);
  friend const_iterator next(const_iterator it, difference_type distance = 1);
  friend const_iterator prev(const_iterator it, difference_type distance = 1);
  friend difference_type distance(const_iterator first, const_iterator last);
}


class reverse_iterator
{
  friend void advance(reverse_iterator &amp;it, Distance n);
  friend reverse_iterator next(reverse_iterator it, difference_type distance = 1);
  friend reverse_iterator prev(reverse_iterator it, difference_type distance = 1);
  friend difference_type distance(reverse_iterator first, reverse_iterator last);
}


class const_reverse_iterator
{
  friend void advance(const_reverse_iterator &amp;it, Distance n);
  friend const_reverse_iterator next(const_reverse_iterator it, difference_type distance = 1);
  friend const_reverse_iterator prev(const_reverse_iterator it, difference_type distance = 1);
  friend difference_type distance(const_reverse_iterator first, const_reverse_iterator last);
}
</code></pre>
<ol start="5">
<li>Complexity: Constant in best case and linear in the number of elements traversed in worst case, approximating logarithmic in the number of elements traversed on average.</li>
  <li>Remarks: These functions are to be found via argument-dependent lookup only.</li>
</ol>
<br>


<h4>22.3.14.7 Erasure [hive.erasure]</h4>

<pre><code style="font-weight:bold">template &lt;class U&gt;
    friend size_type erase(hive&amp; c, const U&amp; value);</code></pre>
<ol>
  <li>Effects: All elements in the container which are equal to <code>value</code> are erased. Invalidates all references and iterators to the erased elements.</li>
  <li>Remarks: This function is to be found via argument-dependent lookup only.</li>
</ol>
<br>

<pre><code style="font-weight:bold">template &lt;class Predicate&gt;
    friend size_type erase_if(hive&amp; c, Predicate pred);</code></pre>
<ol start="3">
  <li>Effects: All elements in the container which match predicate <code>pred</code> are erased. Invalidates all references and iterators to the erased elements.</li>
  <li>Remarks: This function is to be found via argument-dependent lookup only.</li>
</ol>


<h2><a id="acknowledgements"></a>VII. Acknowledgements</h2>

<p>Matt would like to thank: Glen Fernandes and Ion Gaztanaga for restructuring
advice, Robert Ramey for documentation advice, various Boost and SG14 members for support, critiques and corrections, Baptiste Wicht for teaching me how to construct decent benchmarks, Jonathan Wakely, Sean Middleditch, Jens Maurer (very nearly a co-author at this point really),
Patrice Roy and Guy Davidson for standards-compliance advice and critiques, support, representation at meetings and bug reports, Henry Miller for getting me to clarify why the instrusive list/free list approach to memory location reuse is the most appropriate, Ville Voutilainen and Gasper Azman for help with the colony/hive rename paper, that ex-Lionhead guy for annoying me enough to force me to implement the original skipfield pattern, Jon Blow for some initial advice and Mike Acton for some influence, the community at large for giving me feedback and bug reports on the reference implementation.<br>
Also Nico Josuttis for doing such a great job in terms of explaining the general format of the structure to the committee.</p>


<h2>VIII. Appendices</h2>

<h3><a id="basicusage"></a>Appendix A - Basic usage examples</h3>

<p>Using <a href="https://plflib.org/colony.htm">reference implementation</a>.</p>

<div
style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin: 0; line-height: 125%"><code><span style="color: #557799">#include &lt;iostream&gt;</span>
<span style="color: #557799">#include &lt;numeric&gt;</span>
<span style="color: #557799">#include "plf_hive.h"</span>

<span style="color: #333399; font-weight: bold">int</span> <span style="color: #0066BB; font-weight: bold">main</span>(<span style="color: #333399; font-weight: bold">int</span> argc, <span style="color: #333399; font-weight: bold">char</span> <span style="color: #333333">**</span>argv)
{
  plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span><span style="color: #333333">&gt;</span> i_hive;

  <span style="color: #888888">// Insert 100 ints:</span>
  <span style="color: #008800; font-weight: bold">for</span> (<span style="color: #333399; font-weight: bold">int</span> i <span style="color: #333333">=</span> <span style="color: #0000DD; font-weight: bold">0</span>; i <span style="color: #333333">!=</span> <span style="color: #0000DD; font-weight: bold">100</span>; <span style="color: #333333">++</span>i)
  {
    i_hive.insert(i);
  }

  <span style="color: #888888">// Erase half of them:</span>
  <span style="color: #008800; font-weight: bold">for</span> (plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span><span style="color: #333333">&gt;::</span>iterator it <span style="color: #333333">=</span> i_hive.begin(); it <span style="color: #333333">!=</span> i_hive.end(); <span style="color: #333333">++</span>it)
  {
    it <span style="color: #333333">=</span> i_hive.erase(it);
  }

  std<span style="color: #333333">::</span>cout <span style="color: #333333">&lt;&lt;</span> <span style="background-color: #fff0f0">"Total: "</span> <span style="color: #333333">&lt;&lt;</span> std::accumulate(i_hive.begin(), i_hive.end(), 0) <span style="color: #333333">&lt;&lt;</span> std<span style="color: #333333">::</span>endl;
  std<span style="color: #333333">::</span>cin.get();
  <span style="color: #008800; font-weight: bold">return</span> <span style="color: #0000DD; font-weight: bold">0</span>;
} </code></pre>
</div>

<h4>Example demonstrating pointer stability</h4>

<div
style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;">
<pre style="margin: 0; line-height: 125%"><code><span style="color: #557799">#include &lt;iostream&gt;</span>
<span style="color: #557799">#include "plf_hive.h"</span>

<span style="color: #333399; font-weight: bold">int</span> <span style="color: #0066BB; font-weight: bold">main</span>(<span style="color: #333399; font-weight: bold">int</span> argc, <span style="color: #333399; font-weight: bold">char</span> <span style="color: #333333">**</span>argv)
{
  plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span><span style="color: #333333">&gt;</span> i_hive;
  plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span><span style="color: #333333">&gt;::</span>iterator it;
  plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span> <span style="color: #333333">*&gt;</span> p_hive;
  plf<span style="color: #333333">::</span>hive<span style="color: #333333">&lt;</span><span style="color: #333399; font-weight: bold">int</span> <span style="color: #333333">*&gt;::</span>iterator p_it;

  <span style="color: #888888">// Insert 100 ints to i_hive and pointers to those ints to p_hive:</span>
  <span style="color: #008800; font-weight: bold">for</span> (<span style="color: #333399; font-weight: bold">int</span> i <span style="color: #333333">=</span> <span style="color: #0000DD; font-weight: bold">0</span>; i <span style="color: #333333">!=</span> <span style="color: #0000DD; font-weight: bold">100</span>; <span style="color: #333333">++</span>i)
  {
    it <span style="color: #333333">=</span> i_hive.insert(i);
    p_hive.insert(<span style="color: #333333">&amp;</span>(<span style="color: #333333">*</span>it));
  }

  <span style="color: #888888">// Erase half of the ints:</span>
  <span style="color: #008800; font-weight: bold">for</span> (it <span style="color: #333333">=</span> i_hive.begin(); it <span style="color: #333333">!=</span> i_hive.end(); <span style="color: #333333">++</span>it)
  {
    it <span style="color: #333333">=</span> i_hive.erase(it);
  }

  <span style="color: #888888">// Erase half of the int pointers:</span>
  <span style="color: #008800; font-weight: bold">for</span> (p_it <span style="color: #333333">=</span> p_hive.begin(); p_it <span style="color: #333333">!=</span> p_hive.end(); <span style="color: #333333">++</span>p_it)
  {
    p_it <span style="color: #333333">=</span> p_hive.erase(p_it);
  }

  <span style="color: #888888">// Total the remaining ints via the pointer hive (pointers will still be valid even after insertions and erasures):</span>
  <span style="color: #333399; font-weight: bold">int</span> total <span style="color: #333333">=</span> <span style="color: #0000DD; font-weight: bold">0</span>;

  <span style="color: #008800; font-weight: bold">for</span> (p_it <span style="color: #333333">=</span> p_hive.begin(); p_it <span style="color: #333333">!=</span> p_hive.end(); <span style="color: #333333">++</span>p_it)
  {
    total <span style="color: #333333">+=</span> <span style="color: #333333">*</span>(<span style="color: #333333">*</span>p_it);
  }

  std<span style="color: #333333">::</span>cout <span style="color: #333333">&lt;&lt;</span> <span style="background-color: #fff0f0">"Total: "</span> <span style="color: #333333">&lt;&lt;</span> total <span style="color: #333333">&lt;&lt;</span> std<span style="color: #333333">::</span>endl;

  <span style="color: #008800; font-weight: bold">if</span> (total <span style="color: #333333">==</span> <span style="color: #0000DD; font-weight: bold">2500</span>)
  {
    std<span style="color: #333333">::</span>cout <span style="color: #333333">&lt;&lt;</span> <span style="background-color: #fff0f0">"Pointers still valid!"</span> <span style="color: #333333">&lt;&lt;</span> std<span style="color: #333333">::</span>endl;
  }

  std<span style="color: #333333">::</span>cin.get();
  <span style="color: #008800; font-weight: bold">return</span> <span style="color: #0000DD; font-weight: bold">0</span>;
} </code></pre>
</div>

<h3><a id="benchmarks"></a>Appendix B - Reference implementation benchmarks</h3>

<p>Benchmark results for the hive reference implementation under GCC on an Intel Xeon E3-1241 (Haswell) are <a
href="https://plflib.org/benchmarks_haswell_gcc.htm">here</a>.</p>

<p>Old benchmark results for an earlier version of hive under MSVC 2015
update 3, on an Intel Xeon E3-1241 (Haswell) are <a
href="https://plflib.org/benchmarks_haswell_msvc.htm">here</a>. There is no
commentary for the MSVC results.</p>

<h3><a id="faq"></a>Appendix C - Frequently Asked Questions</h3>
<ol>
  <li><h4>Where is it worth using a hive in place of other std::
    containers?</h4>
    <p>As mentioned, it is worthwhile for performance reasons in situations
    where the order of container elements is not important and:</p>
    <ol type="a">
      <li>Insertion order is unimportant</li>
      <li>Insertions and erasures to the container occur frequently in
        performance-critical code, <i><b>and</b></i> </li>
      <li>Links to non-erased container elements may not be invalidated by
        insertion or erasure.</li>
    </ol>
    <p>Under these circumstances a hive will generally out-perform other
    std:: containers. In addition, because it never invalidates pointer
    references to container elements (except when the element being pointed to
    has been previously erased) it may make many programming tasks involving
    inter-relating structures in an object-oriented or modular environment much
    faster, and could be considered in those circumstances.</p>
  </li>
  <li><h4>What are some examples of situations where a hive might improve
    performance?</h4>
    <p>Some ideal situations to use a hive: cellular/atomic simulation,
    persistent octtrees/quadtrees, game entities or destructible-objects in a
    video game, particle physics, anywhere where objects are being created and
    destroyed continuously. Also, anywhere where a vector of pointers to
    dynamically-allocated objects or a std::list would typically end up being
    used in order to preserve pointer stability but where order is
    unimportant.</p>
  </li>
  <li><h4>Is it similar to a deque?</h4>
    <p>A deque is reasonably dissimilar to a hive - being a double-ended
    queue, it requires a different internal framework. In addition, being a
    random-access container, having a growth factor for memory blocks in a
    deque is problematic (though not impossible). A deque and hive have no
    comparable performance characteristics except for insertion (assuming a
    good deque implementation). Deque erasure performance varies wildly
    depending on the implementation, but is generally similar to vector erasure
    performance. A deque invalidates pointers to subsequent container elements
    when erasing elements, which a hive does not, and guarantees ordered
    insertion.</p>
  </li>
  <li><h4>What are the thread-safe guarantees?</h4>
    <p>Unlike a std::vector, a hive can be read from and inserted into at the
    same time (assuming different locations for read and write), however it
    cannot be iterated over and written to at the same time. If we look at a
    (non-concurrent implementation of) std::vector's thread-safe matrix to see
    which basic operations can occur at the same time, it reads as follows
    (please note push_back() is the same as insertion in this regard):</p>

    <table border="1" cellspacing="3">
      <tbody>
        <tr>
          <td><b>std::vector</b></td>
          <td>Insertion</td>
          <td>Erasure</td>
          <td>Iteration</td>
          <td>Read</td>
        </tr>
        <tr>
          <td>Insertion</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
        </tr>
        <tr>
          <td>Erasure</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
        </tr>
        <tr>
          <td>Iteration</td>
          <td>No</td>
          <td>No</td>
          <td>Yes</td>
          <td>Yes</td>
        </tr>
        <tr>
          <td>Read</td>
          <td>No</td>
          <td>No</td>
          <td>Yes</td>
          <td>Yes</td>
        </tr>
      </tbody>
    </table>
    <p>In other words, multiple reads and iterations over iterators can happen
    simultaneously, but the potential reallocation and pointer/iterator
    invalidation caused by insertion/push_back and erasure means those
    operations cannot occur at the same time as anything else. </p>
    <p>hive on the other hand does not invalidate pointers/iterators to
    non-erased elements during insertion and erasure, resulting in the
    following matrix:</p>

    <table border="1" cellspacing="3">
      <tbody>
        <tr>
          <td><b>hive</b></td>
          <td>Insertion</td>
          <td>Erasure</td>
          <td>Iteration</td>
          <td>Read</td>
        </tr>
        <tr>
          <td>Insertion</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
          <td>Yes</td>
        </tr>
        <tr>
          <td>Erasure</td>
          <td>No</td>
          <td>No</td>
          <td>No</td>
          <td>Mostly*</td>
        </tr>
        <tr>
          <td>Iteration</td>
          <td>No</td>
          <td>No</td>
          <td>Yes</td>
          <td>Yes</td>
        </tr>
        <tr>
          <td>Read</td>
          <td>Yes</td>
          <td>Mostly*</td>
          <td>Yes</td>
          <td>Yes</td>
        </tr>
      </tbody>
    </table>
    <p><span style="font-size: 10pt">* Erasures will not invalidate iterators
    unless the iterator points to the erased element.</span></p>
    <p>In other words, reads may occur at the same time as insertions and
    erasures (provided that the element being erased is not the element being
    read), multiple reads and iterations may occur at the same time, but
    iterations may not occur at the same time as an erasure or insertion, as
    either of these may change the state of the skipfield which is being
    iterated over, if a skipfield is used in the implementation. Note that iterators pointing to end() may be invalidated by
    insertion.</p>
    <p>So, hive could be considered more inherently thread-safe than a
    (non-concurrent implementation of) std::vector, but still has some areas
    which would require mutexes or atomics to navigate in a multithreaded
    environment.</p>
  </li>
  <li><h4>Any pitfalls to watch out for?</h4>
    <p>Because erased-element memory locations may be reused by
    <code>insert()</code> and <code>emplace()</code>, insertion position is
    essentially random unless no erasures have been made, or an equal number of
    erasures and insertions have been made.</p>
  </li>
  <li><h4>What is the purpose of limiting memory block minimum and maximum
    sizes?</h4>
    <p>One reason might be to ensure that memory blocks match a certain
    processor's cache or memory pathway sizes. Another reason to do this is
    that it is slightly slower to obtain an erased-element location from the
    list of groups-with-erasures (subsequently utilising that group's free list
    of erased locations) and to reuse that space than to insert a new element
    to the back of the hive (the default behavior when there are no
    previously-erased elements). If there are any erased elements in active memory blocks at the moment of insertion, hive will recycle those memory locations.</p>
    <p>So if a block size is large, and many erasures occur but the block is
    not completely emptied, iterative performance might suffer due to large
    memory gaps between any two non-erased elements and subsequent drop in data
    locality and cache performance. In that scenario you may want to experiment
    with benchmarking and limiting the minimum/maximum sizes of the blocks,
    such that memory blocks are freed earlier and find the optimal size for the
    given use case.</p>
  </li>
  <li><h4>What is hive's Abstract Data Type (ADT)?</h4>
    <p>Though I am happy to be proven wrong I suspect hives/colonies/bucket arrays
    are their own abstract data type. Some have suggested it's ADT is of type
    bag, I would somewhat dispute this as it does not have typical bag
    functionality such as <a href="http://www.austincc.edu/akochis/cosc1320/bag.htm">searching based on
    value</a> (you can use std::find but it's o(n)) and adding this
    functionality would slow down other performance characteristics. <a
    href="https://en.wikipedia.org/wiki/Set_(abstract_data_type)#Multiset">Multisets/bags</a>
    are also not sortable (by means other than automatically by key value).
    hive does not utilize key values, is sortable, and does not provide the
    sort of functionality frequently associated with a bag (e.g. counting the
    number of times a specific value occurs).</p>
  </li>
  <li><h4><a id="remove_when_empty"></a>Why must blocks be removed from the iterative sequence when empty?</h4>
    <p>Two reasons:</p>
    <ol type="a">
      <li>Standards compliance: if blocks aren't removed then <code>++</code>
        and <code>--</code> iterator operations become undefined in terms of
        time complexity, making them non-compliant with the C++ standard. At
        the moment they are O(1) amortized, in the reference implementation this constitutes typically one update for both
        skipfield and element pointers, but two if a skipfield jump takes the
        iterator beyond the bounds of the current block and into the next
        block. But if empty blocks are allowed, there could be anywhere between
        1 and <code>std::numeric_limits&lt;size_type&gt;::max</code> empty
        blocks between the current element and the next. Essentially you get
        the same scenario as you do when iterating over a boolean skipfield. It
        would be possible to move these to the back of the hive as trailing
        blocks, or house them in a separate list or vector for future usage,
        but this may create performance issues if any of the blocks are not at
        their maximum size (see below).</li>
      <li>Performance: iterating over empty blocks is slower than them not
        being present, of course - but also if you have to allow for empty
        blocks while iterating, then you have to include a while loop in every
        iteration operation, which increases cache misses and code size. The
        strategy of removing blocks when they become empty also statistically
        removes (assuming randomized erasure patterns) smaller blocks from the
        hive before larger blocks, which has a net result of improving
        iteration, because with a larger block, more iterations within the
        block can occur before the end-of-block condition is reached and a jump
        to the next block (and subsequent cache miss) occurs. Lastly, pushing
        to the back of a hive, provided there is still space and no new block
        needs to be allocated, will be faster than recycling memory locations
        as each subsequent insertion occurs in a subsequent memory location
        (which is cache-friendlier) and also less computational work is
        necessary. If a block is removed from the iterative sequence its recyclable memory locations are
        also not usable, hence subsequent insertions are more likely to
        be pushed to the back of the hive.</li>
    </ol>
  </li>
  <li><h4>Why not reserve all empty memory blocks for future use during erasure, or None, rather than leaving this decision
    undefined by the specification?</h4>
    <p>The default scenario, for reasons of predictability, should be to free
    the memory block in most cases. However for the reasons described in the design decisions section on erase(), retaining the back block at least has performance and latency benefits.
    Therefore retaining no memory blocks is non-optimal in cases where the user is not using a custom allocator. Meanwhile, retaining All memory blocks is bad for performance as many small memory blocks will be retained, which decreases iterative performance due to lower cache locality.
    However, one perspective is that if a scenario calls for
    retaining memory blocks instead of deallocating them, this should be left
    to an allocator to manage. Otherwise you get unpredictable memory behavior
    across implementations, and this is one of the things that SG14 members
    have complained about consistently with STL implementations. This is currently an open topic for discussion.</p>
  </li>
  <li><h4>Memory block sizes - what are they based on, how do they expand,
    etc</h4>
    <p>While implementations are free to chose their own limits and strategies here,
	 in the reference implementation memory block sizes start from either the
    dynamically-defined default minimum size (8 elements, larger if the type stored is small) or an
    amount defined by the end user (with a minimum of 3 elements, as there is enough metadata per-block that less than 3 elements is generally a waste of memory unless the value_type is extremely large).
	 Subsequent block sizes then increase the <i>total capacity</i> of the hive by a
    factor of 2 (so, 1st block 8 elements, 2nd 8 elements, 3rd 16 elements, 4th
    32 elements etcetera) until the maximum block size is reached. The default
    maximum block size in the reference implementation is the maximum possible number that the skipfield
    bitdepth is capable of representing (std::numeric_limits&lt;skipfield_type&gt;::max()). By default the
    skipfield bitdepth is 16 so the maximum size of a block would be 65535
    elements in that context.</p>
    <p>The skipfield bitdepth was initially a template parameter which could be set to
    any unsigned integer - unsigned char, unsigned int, Uint_64, etc. Unsigned
    short (guaranteed to be at least 16 bit, equivalent to C++11's
    uint_least16_t type) was found to have the best performance in real-world
    testing on x86 and x86_64 platforms due to the balance between memory contiguousness, memory waste and
    the number of allocations. unsigned char was found to have better performance below 1000 elements and of course lower memory use. Other platforms have not been tested. Since only two values were considered useful, they've been replaced in newer versions by a <code>priority</code> parameter, which specifies whether the priority of the instantiation is memory use or performance. While this is not strictly true in the sense that unsigned char will also have better performance for under 1000 elements, it is a compromise in order to have the implementation reflect a standard which may enable other implementations which do not share the same performance characteristics.</p>
  </li>
  <li><h4><a id="simd"></a>Can a hive be used with SIMD instructions?</h4>
    <p>No and yes. Yes if you're careful, no if you're not.<br>
    On platforms which support scatter and gather operations via hardware (e.g.
    AVX512) you can use hive with SIMD as much as you want, using gather to
    load elements from disparate or sequential locations, directly into a SIMD
    register, in parallel. Then use scatter to push the post-SIMD-process
    values elsewhere after. On platforms which do not support this in hardware,
    you would need to manually implement a scalar gather-and-scatter operation
    which may be significantly slower.</p>
    <p>In situations where gather and scatter operations are too expensive,
    which require elements to be contiguous in memory for SIMD processing, this
    is more complicated. When you have a bunch of erasures in a hive, there's
    no guarantee that your objects will be contiguous in memory, even though
    they are sequential during iteration. Some of them may also be in different
    memory blocks to each other. In these situations if you want to use SIMD
    with hive, you must do the following:</p>
    <ul>
      <li>Set your minimum and maximum group sizes to multiples of the width of
        your target processor's SIMD instruction size. If it supports 8
        elements at once, set the group sizes to multiples of 8.</li>
      <li>Either never erase from the hive, or:<br>
        <ol>
          <li>Shrink-to-fit after you erase (will invalidate all pointers to
            elements within the hive).</li>
          <li>Only erase from the back or front of the hive, and only erase
            elements in multiples of the width of your SIMD instruction e.g. 8
            consecutive elements at once. This will ensure that the
            end-of-memory-block boundaries line up with the width of the SIMD
            instruction, provided you've set your min/max block sizes as
          above.</li>
        </ol>
      </li>
    </ul>
    <p>Generally if you want to use SIMD without gather/scatter, it's probably
    preferable to use a vector or an array.</p>
  </li>
</ol>

<h3><a id="responses" name="responses"></a>Appendix D - Specific responses to
previous committee feedback</h3>
<ol>
  <li><h4>Naming</h4>
    <p>See D2332R0.</p>
  </li>
  <li><h4>"Unordered and no associative lookup, so this only supports use cases
    where you're going to do something to every element."</h4>
    <p>As noted the container was originally designed for highly
    object-oriented situations where you have many elements in different
    containers linking to many other elements in other containers. This linking
    can be done with pointers or iterators in hive (insert returns an
    iterator which can be dereferenced to get a pointer, pointers can be
    converted into iterators with the supplied functions (for erase etc)) and
    because pointers/iterators stay stable regardless of insertion/erasure,
    this usage is unproblematic. You could say the pointer is equivalent to a
    key in this case (but without the overhead). That is the first access
    pattern, the second is straight iteration over the container, as you say.
    Secondly, the container does have (typically better than O(n))
    advance/next/prev implementations, so multiple elements can be skipped.</p>
  </li>
  <li><h4>"Do we really need the Priority template parameter?"</h4>
    <p>While technically a non-binding request, this parameter promotes the use of the container in heavily
    memory-constrained environments like embedded programming. In the context of the reference implementation this means switching the skipfield type from unsigned short to unsigned char, in other implementations it could mean something else.
	 See more explanation in V. Technical Specifications.
    Unfortunately this parameter also means <code>operator=</code>, swap and some
    other functions won't work between hives of the same type but with differing priorities.</p>
  </li>
  <li><h4>"Prove this is not an allocator"</h4>
    <p>I'm not really sure how to answer this, as I don't see the resemblance,
    unless you count maps, vectors etc as being allocators also. The only
    aspect of it which resembles what an allocator might do, is the memory
    re-use mechanism. It would be impossible for an allocator to perform a
    similar function while still allowing the container to iterate over the
    data linearly in memory, preserving locality, in the manner described in
    this document.</p>
  </li>
  <li><h4>"If this is for games, won't game devs just write their own versions
    for specific types in order to get a 1% speed increase anyway?"</h4>
    <p>This is true for many/most AAA game companies who are on the bleeding
    edge, but they also do this for vector etc, so they aren't the target
    audience of std:: for the most part; sub-AAA game companies are more likely
    to use third party/pre-existing tools. As mentioned earlier, this structure
    (bucket-array-like) crops up in <a
    href="https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/1iWHyVnsLBQ">many,
    many fields</a>, not just game dev. So the target audience is probably
    everyone other than AAA gaming, but even then, it facilitates communication
    across fields and companies as to this type of container, giving it a
    standardized name and understanding.</p>
  </li>
  <li><h4>"Is there active research in this problem space? Is it likely to
    change in future?"</h4>
    <p>The only current analysis has been around the question of whether it's
    possible for this specification to fail to allow for a better
    implementation in future. This is unlikely given the container's
    requirements and how this impacts on implementation. Bucket arrays have
    been around since the 1990s, there's been no significant innovation in them
    until now. I've been researching/working on hive since early 2015, and
    while I can't say for sure that a better implementation might not be
    possible, I am confident that no change should be necessary to the
    specification to allow for future implementations, if it is done correctly.
    </p>
    <p>The requirement of allowing no reallocations upon insertion or erasure,
    truncates possible implementation strategies significantly. Memory blocks
    have to be independently allocated so that they can be removed (when empty)
    without triggering reallocation of subsequent elements. There's limited
    numbers of ways to do that and keep track of the memory blocks at the same
    time. Erased element locations must be recorded (for future re-use by
    insertion) in a way that doesn't create allocations upon erasure, and
    there's limited numbers of ways to do this also. Multiple consecutive
    erased elements have to be skipped in O(1) time, and again there's limits
    to how many ways you can do that. That covers the three core aspects upon
    which this specification is based. See <a href="#design"></a>IV. Design
    Decisions for the various ways these aspects can be designed.</p>
    <p>The time complexity of updates to whatever erased-element skipping mechanism is used should, I think, be left
    implementation-defined, as defining time complexity may obviate better
    solutions which are faster but are not necessarily O(1). These updates
    would likely occur during erasure, insertion, splicing and container copying.</p>
  </li>
  <li><h4>Why not iterate across the memory blocks backwards to find the first block with erasures to reuse, during insert?</h4>
  <p>While this would statistically ensure that smaller blocks get deallocated first due to becoming empty faster than later blocks, it introduces uncertain latency issues during insert, particularly when custom memory block sizes are used and the number of elements is large. With the current implementation there is an intrusive list of blocks with erasures, and within each block's metadata there's a free list of skipblocks. When reusing the current head of the intrusive list determines the block, and the current head of that block's free list determines the skipblock to be reused. This means that the most recently erased element will be the first to reused. This works out well for two reasons: currently-contiguous sequences of elements will tend to stay that way, helping cache coherence, and when elements are erased and inserted in sequence those erased memory locations will tend to be already in the cache when inserting. Lastly, this structure involves a minimum of branching and checks, resulting in minimal latency during insertion and erasure.</p>
</ol>

<h3><a id="sg14gameengine"></a>Appendix E - Typical game engine
requirements</h3>

<p>Here are some more specific requirements with regards to game engines,
verified by game developers within SG14:</p>
<ol type="a">
  <li>Elements within data collections refer to elements within other data
    collections (through a variety of methods - indices, pointers, etc). These
    references must stay valid throughout the course of the game/level. Any
    container which causes pointer or index invalidation creates difficulties
    or necessitates workarounds.</li>
  <li>Order is unimportant for the most part. The majority of data is simply
    iterated over, transformed, referred to and utilized with no regard to
    order.</li>
  <li>Erasing or otherwise "deactivating" objects occurs frequently in
    performance-critical code. For this reason methods of erasure which create
    strong performance penalties are avoided.</li>
  <li>Inserting new objects in performance-critical code (during gameplay) is
    also common - for example, a tree drops leaves, or a player spawns in an
    online multiplayer game.</li>
  <li>It is not always clear in advance how many elements there will be in a
    container at the beginning of development, or at the beginning of a level
    during play. Genericized game engines in particular have to adapt to
    considerably different user requirements and scopes. For this reason
    extensible containers which can expand and contract in realtime are
    necessary.</li>
  <li>Due to the effects of cache on performance, memory storage which is
    more-or-less contiguous is preferred.</li>
  <li>Memory waste is avoided.</li>
</ol>

<p>std::vector in its default state does not meet these requirements due to:
</p>
<ol>
  <li>Poor (non-fill) single insertion performance (regardless of insertion
    position) due to the need for reallocation upon reaching capacity</li>
  <li>Insert invalidates pointers/iterators to all elements </li>
  <li>Erase invalidates pointers/iterators/indexes to all elements after the
    erased element</li>
</ol>

<p>Game developers therefore either develop custom solutions for each scenario
or implement workarounds for vector. The most common workarounds are most
likely the following or derivatives:</p>
<ol>
  <li>Using a boolean flag or similar to indicate the inactivity of an object
    (as opposed to actually erasing from the vector). Elements flagged as
    inactive are skipped during iteration.<br>
    <br>
    Advantages: Fast "deactivation". Easy to manage in multi-access
    environments.<br>
    Disadvantages: Can be slower to iterate due to branching.</li>
  <li>Using a vector of data and a secondary vector of indexes. When erasing,
    the erasure occurs only in the vector of indexes, not the vector of data.
    When iterating it iterates over the vector of indexes and accesses the data
    from the vector of data via the remaining indexes.<br>
    <br>
    Advantages: Fast iteration.<br>
    Disadvantages: Erasure still incurs some reallocation cost which can
    increase jitter.</li>
  <li>Combining a swap-with-back-element-and-pop approach to erasure with some form of
    dereferenced lookup system to enable contiguous element iteration
    (sometimes called a 'packed array': <a href="http://bitsquid.blogspot.ca/2011/09/managing-decoupling-part-4-id-lookup.html">http://bitsquid.blogspot.ca/2011/09/managing-decoupling-part-4-id-lookup.html</a>).
    <br>
    Advantages: Iteration is at standard vector speed.<br>
    Disadvantages: Erasure will be slow if objects are large and/or
    non-trivially copyable, thereby making swap costs large. All link-based
    access to elements incur additional costs due to the dereferencing system.
  </li>
</ol>

<p>hive brings a more generic solution to these contexts. While some
developers, particularly AAA developers, will almost always develop a custom
solution for specific use-cases within their engine, I believe most sub-AAA and
indie developers are more likely to rely on third party solutions. Regardless,
standardising the container will allow for greater cross-discipline
communication.</p>

<h3><a id="timecomplexityexplanations"></a>Appendix F - Time complexity
requirement explanations</h3>

<h5>Insert (single): O(1)</h5>

<p>One of the requirements of hive is that pointers to non-erased elements
stay valid regardless of insertion/erasure within the container. For this
reason the container must use multiple memory blocks. If a single memory block
were used, like in a std::vector, reallocation of elements would occur when the
container expanded (and the elements were copied to a larger memory block).
Instead, hive will insert into existing memory blocks when able, and create a
new memory block when all existing memory blocks are full. This keeps insertion
at O(1).</p>

<h5>Insert (multiple): O(N)</h5>

<p>Multiple insertions may allow an implementation to reserve suitably-sized
memory blocks in advance, reducing the number of allocations necessary (whereas
singular insertion would generally follow the implementation's block growth
pattern, possibly allocating more than necessary). However when it comes to
time complexity it has no advantages over singular insertion, is linear to the
number elements inserted.</p>

<h5>Erase (single): O(1)</h5>

<p>Erasure is a simple matter of destructing the element in question and
updating whatever data is associated with the erased-element skipping mechanism eg. the skipfield. Since we use a skipping mechanism to avoid erasures during
iterator, no reallocation of subsequent elements is necessary and the process
is O(1). Additionally, when using a Low-complexity jump-counting pattern the
skipfield update is also always O(1).</p>

<p>Note: When a memory block becomes empty of non-erased elements it must be
freed to the OS (or reserved for future insertions, depending on implementation)
and removed from the hive's sequence of memory blocks. It it was not, we
would end up with non-O(1) iteration, since there would be no way to predict
how many empty memory blocks there would be between the current memory block
being iterated over, and the next memory block with non-erased (active)
elements in it.</p>


<h5>Erase (multiple): O(N) for non-trivially-destructible types, for
trivially-destructible types between O(1) and O(N) depending on range
start/end, approximating O(log n) average</h5>

<p>In this case, where the element is non-trivially destructible, the time
complexity is O(N), with infrequent deallocation necessary from the removal of
an empty memory block as noted above. However where the elements are
trivially-destructible, if the range spans an entire memory block at any point,
that block and it's metadata can simply be removed without doing any
individual writes to it's metadata or individual destruction of elements,
potentially making this a O(1) operation.</p>

<p>In addition (when dealing with trivially-destructible types) for those
memory blocks where only a portion of elements are erased by the range, if no
prior erasures have occurred in that memory block you may be able to erase that range in
O(1) time, as, for example, if you are using a skipfield there will be no need to check the skipfield within the range for
previously erased elements. The reason you would need to check for previously
erased elements within that portion's range is so you can update the metadata
for that memory block to accurately reflect how many non-erased elements remain
within the block. The non-erased element-count metadata is necessary because
there is no other way to ascertain when a memory block is empty of non-erased
elements, and hence needs to be removed from the hive's iteration sequence.
The reasoning for why empty memory blocks must be removed is included in the
Erase(single) section, above.</p>

<p>However in most cases the erase range will not perfectly match the size of
all memory blocks, and with typical usage of a hive there is usually some
prior erasures in most memory blocks. So, for example, when dealing with a
hive of a trivially-destructible type, you might end up with a tail portion
of the first memory block in the erasure range being erased in O(N) time, the
second and intermediary memory block being completely erased and freed in O(1)
time, and only a small front portion of the third and final memory block in the
range being erased in O(N) time. Hence the time complexity for
trivially-destructible elements approximates O(log n) on average, being between
O(1) and O(N) depending on the start and end of the erasure range.</p>


<h5>std::find: O(N)</h5>

<p>This relies on basic iteration so is O(N).</p>


<h5>splice: O(1)</h5>

<p>hive only does full-container splicing, not partial-container splicing
(use range-insert with std::make_move_iterator to achieve the latter, albiet
with the loss of pointer validity to the moved range). When splicing, the
memory blocks from the source hive are transferred to the destination hive
without processing the individual elements. These blocks may either be placed
at the front of the hive or the end, depending on how full the source back
block is compared to the destination back block. If the destination back block
is more full ie. there is less unused space in it, it is better to put it at
the beginning of the source block - as otherwise this creates a larger gap to
skip during iteration which in turn affects cache locality. If there are unused
element memory spaces at the back of the destination container (ie. the final
memory block is not full) and a skipfield is used, the skipfield nodes corresponding to those empty
spaces must be altered to indicate that these are skipped elements.</p>


<h5>Iterator operators ++ and --: O(1) amortized</h5>

<p>Generally the time complexity is O(1), and if a skipfield pattern is used it must
allow for O(1) skipping of multiple erased elements. However every so often
iteration will involve a transistion to the next/previous memory block in the
hive's sequence of blocks, depending on whether we are doing ++ or --. At
this point a read of the next/previous memory block's corresponding skipfield would be
necessary, in case the front/back element(s) in that memory block are erased
and hence skipped. So for every block transition, 2 reads of the skipfield are
necessary instead of 1. Hence the time complexity is O(1) amortized.</p>

<p>If skipfields are used they must be per-element-memory-block and independent of subsequent/previous memory blocks, as
otherwise you end up with a vector for a skipfield, which would need a
range erased every time a memory block was removed from the hive (see notes
under Erase, above), and reallocation to a larger skipfield memory block when a
hive expanded. Both of these procedures carry reallocation costs, meaning you
could have thousands of skipfield nodes needing to be reallocated based on a
single erasure (from within a memory block which only had one non-erased
element left and hence would need to be removed from the hive). This is
unacceptable latency for any field involving high timing sensitivity (all of <a
href="https://lists.isocpp.org/mailman/listinfo.cgi/sg14/">SG14</a>).</p>


<h5>begin()/end(): O(1)</h5>

<p>For any implementation these should generally be stored as member variables
and so returning them is O(1).</p>


<h5>advance/next/prev: between O(1) and O(n), depending on current iterator
location, distance and implementation. Average for reference implementation
approximates O(log N).</h5>

<p>The reasoning for this is similar to that of Erase(multiple), above.
Complexity is dependent on state of hive, position of iterator and length of
<code>distance</code>, but in many cases will be less than linear. It is
necessary in a hive to store metadata both about the capacity of each block
(for the purpose of iteration) and how many non-erased elements are present
within the block (for the purpose of removing blocks from the iterative chain
once they become empty). For this reason, intermediary blocks between the
iterator's initial block and its final destination block (if these are not the
same block, and if the initial block and final block are not immediately
adjacent) can be skipped rather than iterated linearly across, by subtracting
the "number of non-erased elements" metadata from <code>distance</code> for
those blocks.</p>

<p>This means that the only linear time operations are any iterations within
the initial block and the final block. However if either the initial or final
block have no erased elements (as determined by comparing whether the block's
capacity metadata and the block's "number of non-erased elements" metadata are
equal), linear iteration can be skipped for that block and pointer/index math
used instead to determine distances, reducing complexity to constant time.
Hence the best case for this operation is constant time, the worst is linear to
the distance.</p>


<h5>distance: between O(1) and O(n), depending on current iterator location,
distance and implementation. Average for reference implementation approximates
O(log N).</h5>

<p>The same considerations which apply to advance, prev and next also apply to
distance - intermediary blocks between iterator1 and iterator2's blocks can be
skipped in constant time, if they exist. iterator1's block and iterator2's
block (if these are not the same block) must be linearly iterated across using
++ unless either block has no erased elements, in which case the operation
becomes pointer/index math and is reduced to constant time for that block. In
addition, if iterator1's block is not the same as iterator2's block, and
iterator2 is equal to end() or (end() - 1), or is the last element in that
block, iterator2's block's elements can also counted from the metadata rather
than iteration.</p>


<h3><a id="yunoconstexpr"></a>Appendix G - Why not constexpr?</h3>
<p>I am somewhat awkwardly forced into a position where I have to question and push back against the currently-unsubstantiated enthusiasm around constexpr containers and functions. At the time of writing there are no compilers which both support constexpr non-trivial destructors and also have a working implementation of a constexpr container. And until that is remedied, we won't really know what we're dealing with. My own testing in terms of making hive constexpr has not been encouraging. 2% performance decrease in un-altered benchmark code is common, and I suspect the common cause of this is caching values from compile-time when it is cheaper to calculate them on-the-fly than to return them from main memory. This suspicion is based on the substantial increases in executable size in the constexpr versions.</p>

<p>For an example of the latter, think about size() in std::vector. This can be calculated in most implementations by (vector.end_iterator.pointer - vector.memory_block), both of which will most likely be in cache at the time of calling size(). That's if size isn't a member variable or something.
Calculating a minus operation on stuff that's already in cache is about 100x faster than making a call out to main memory for a compile-time-stored value of this function, if that is necessary. Hence calculating size() will typically be faster than storing it, but a constexpr implementation and compiler currently won't make that distinction.</p>

<p>None of which is an issue if a container is being entirely used within a constexpr function which has been determined to be evaluated at compile time. The problems occur when constexpr containers are used in runtime code, but certain functions such as size() are determined to be able to be evaluated at compile time, and therefore have their results cached. This is not an okay situation. If there were a mechanism which specified that for a given class instance, it's constexpr functions may <b>not</b> be evaluated at compile time, then I would give the go-ahead. Similarly if there were a rule which stated that a class instance's member functions may only be evaluated at compile time if the class instance is instantiated and destructed at compile time, I would give the go-ahead. This is not the situation we have, and I can't support it.</p>

<p>Constexpr function calls:</p>
<ol type="a">
<li>shift the responsibility of storage of pre-calculated results from the programmer to the compiler, and remove the ability for the programmer to think about the cache locality/optimal storage of precalculated results</li>
<li>shift the decision of whether or not to evaluate at runtime/compile-time to the compiler, where in some cases doing it at compile-time and storing the result may decrease performance (see the <a href="https://fabiensanglard.net/doom3_documentation/DOOM-3-BFG-Technical-Note.pdf">Doom 3 BFG edition technical notes</a> for their experience with pre-calc'ing meshes vs calculating them on the fly)</li>
<li>may dramatically increase code size/file size in some cases if the return results of constexpr functions are large</li>
<li>may dramatically increase compile times</li>
<li>create the potential for cache pollution when constexpr functions return large amounts of data, or when functions returning small amounts of data are called many times</li>
</ol>

<p>Given this, and the performance issues mentioned above, I am reluctant to make hive constexpr-by-default. Time may sort these issues out, but I am personally happier for std::array and std::vector to be the "canaries in the coalmine" here. Certainly I won't be giving the go-ahead on any change that produces, or can produce, on current compilers, a 2% performance decrease in runtime code. Though I acknowledge the functionality of constexpr code may be useful to many.</p>


<h3><a id="referencediff"></a>Appendix H - Reference implementation differences and link</h3>
<p>The <a href="https://plflib.org/colony.htm">reference implementation</a> has a couple of key differences from the proposal, one is that it is named 'colony' by default, for historical and userbase reasons, and typedef'd to hive for optional usage under that name. This is only possible with C++11 and above due to the limits on template typedefs under C++98/03. Likewise the template parameter 'hive_priority' is a regular enum in the reference implementation, instead of a scoped enum, in order to be usable with C++98/03, and is 'colony_priority' by default with a typedef to hive_priority. Lastly the struct 'colony_limits' is also typedef'd to 'hive_limits'. Otherwise the reference implementation is or should be identical with the std::hive proposal.</p>


</body>
</html>


================================================
FILE: Docs/Proposals/Fixed_Point_Library_Proposal.md
================================================
**Document number**: LEWG, EWG, SG14, SG6: P0037R0  
**Date**: 2015-09-28  
**Project**: Programming Language C++, Library Evolution WG, SG14  
**Reply-to**: John McFarlane, [fixed-point@john.mcfarlane.name](mailto:fixed-point@john.mcfarlane.name)

# Fixed-Point Real Numbers

## I. Introduction

This proposal introduces a system for performing binary fixed-point
arithmetic using built-in integral types.

## II. Motivation

Floating-point types are an exceedingly versatile and widely supported
method of expressing real numbers on modern architectures.

However, there are certain situations where fixed-point arithmetic is
preferable. Some systems lack native floating-point registers and must
emulate them in software. Many others are capable of performing some
or all operations more efficiently using integer arithmetic. Certain
applications can suffer from the variability in precision which comes
from a dynamic radix point [\[1\]](http://www.pathengine.com/Contents/Overview/FundamentalConcepts/WhyIntegerCoordinates/page.php).
In situations where a variable exponent is not desired, it takes
valuable space away from the significand and reduces precision.

Built-in integer types provide the basis for an efficient
representation of binary fixed-point real numbers. However, laborious,
error-prone steps are required to normalize the results of certain
operations and to convert to and from fixed-point types.

A set of tools for defining and manipulating fixed-point types is
proposed. These tools are designed to make work easier for those who
traditionally use integers to perform low-level, high-performance
fixed-point computation.

## III. Impact On the Standard

This proposal is a pure library extension. It does not require
changes to any standard classes, functions or headers.

## IV. Design Decisions

The design is driven by the following aims in roughly descending
order:

1. to automate the task of using integer types to perform low-level
   binary fixed-point arithmetic;
2. to facilitate a style of code that is intuitive to anyone who is
   comfortable with integer and floating-point arithmetic;
3. to avoid type promotion, implicit conversion or other behavior that
   might lead to surprising results and
4. to preserve significant digits at the expense of insignificant
   digits, i.e. to prefer underflow to overflow.

### Class Template

Fixed-point numbers are specializations of

    template <class ReprType, int Exponent>
    class fixed_point;

where the template parameters are described as follows.

#### `ReprType` Type Template Parameter

This parameter identifies the capacity and signedness of the
underlying type used to represent the value. In other words, the size
of the resulting type will be `sizeof(ReprType)` and it will be
signed iff `is_signed<ReprType>::value` is true. The default is
`int`.

`ReprType` must be a fundamental integral type and should not be the
largest size. Suitable types include: `std::int8_t`, `std::uint8_t`,
`std::int16_t`, `std::uint16_t`, `std::int32_t` and `std::uint32_t`.
In limited situations, `std::int64_t` and `std::uint64_t` can be used.
The  reasons for these limitations relate to the difficulty in finding
a type that is suitable for performing lossless integer
multiplication.

#### `Exponent` Non-Type Template Parameter

The exponent of a fixed-point type is the equivalent of the exponent
field in a floating-point type and shifts the stored value by the
requisite number of bits necessary to produce the desired range. The
default value of `Exponent` is zero, giving `fixed_point<T>` the same
range as `T`.

The resolution of a specialization of `fixed_point` is

    pow(2, Exponent)

and the minimum and maximum values are

    std::numeric_limits<ReprType>::min() * pow(2, Exponent)

and

    std::numeric_limits<ReprType>::max() * pow(2, Exponent)

respectively.

Any usage that results in values of `Exponent` which lie outside the
range, (`INT_MIN / 2`, `INT_MAX / 2`), may result in undefined
behavior and/or overflow or underflow. This range of exponent values
is far in excess of the largest built-in floting-point type and should
be adequate for all intents and purposes.

### `make_fixed` and `make_ufixed` Helper Type

The `Exponent` template parameter is versatile and concise. It is an
intuitive scale to use when considering the full range of positive and
negative exponents a fixed-point type might possess. It also
corresponds to the exponent field of built-in floating-point types.

However, most fixed-point formats can be described more intuitively by
the cardinal number of integer and/or fractional digits they contain.
Most users will prefer to distinguish fixed-point types using these
parameters.

For this reason, two aliases are defined in the style of
`make_signed`.

These aliases are declared as:

    template <unsigned IntegerDigits, unsigned FractionalDigits = 0, bool IsSigned = true>
  	using make_fixed;

and

    template <unsigned IntegerDigits, unsigned FractionalDigits = 0>
    using make_ufixed;

They resolve to a `fixed_point` specialization with the given
signedness and number of integer and fractional digits. They may
contain additional integer and fractional digits.

For example, one could define and initialize an 8-bit, unsigned,
fixed-point variable with four integer digits and four fractional
digits:

    make_ufixed<4, 4> value { 15.9375 };

or a 32-bit, signed, fixed-point number with two integer digits and 29
fractional digits:

    make_fixed<2, 29> value { 3.141592653 };

### Conversion

Fixed-point numbers can be explicitly converted to and from built-in
arithmetic types.

While effort is made to ensure that significant digits are not lost
during conversion, no effort is made to avoid rounding errors.
Whatever would happen when converting to and from an integer type
largely applies to `fixed_point` objects also. For example:

    make_ufixed<4, 4>(.006) == make_ufixed<4, 4>(0)

...equates to `true` and is considered a acceptable rounding error.

### Operator Overloads

Any operators that might be applied to integer types can also be
applied to fixed-point types. A guiding principle of operator
overloads is that they perform as little run-time computation as is
practically possible.

With the exception of shift and comparison operators, binary operators
can take any combination of:

* one or two fixed-point arguments and
* zero or one arguments of any arithmetic type, i.e. a type for which
  `is_arithmetic` is true.

Where the inputs are not identical fixed-point types, a simple set of
promotion-like rules are applied to determine the return type:

1. If both arguments are fixed-point, a type is chosen which is the
   size of the larger type, is signed if either input is signed and
   has the maximum integer bits of the two inputs, i.e. cannot lose
   high-significance bits through conversion alone.
2. If one of the arguments is a floating-point type, then the type of
   the result is the smallest floating-point type of equal or greater
   size than the inputs.
3. If one of the arguments is an integral type, then the result is the
   other, fixed-point type.

Some examples:

    make_ufixed<5, 3>{8} + make_ufixed<4, 4>{3} == make_ufixed<5, 3>{11};  
    make_ufixed<5, 3>{8} + 3 == make_ufixed<5, 3>{11};  
    make_ufixed<5, 3>{8} + float{3} == float{11};  

The reasoning behind this choice is a combination of predictability
and performance. It is explained for each rule as follows:

1. ensures that the least computation is performed where fixed-point
   types are used exclusively. Aside from multiplication and division
   requiring shift operations, should require similar computational
   costs to equivalent integer operations;
2. loosely follows the promotion rules for mixed-mode arithmetic,
   ensures values with exponents far beyond the range of the
   fixed-point type are catered for and avoids costly conversion from
   floating-point to integer and
3. preserves the input fixed-point type whose range is far more likely
   to be of deliberate importance to the operation.

Shift operator overloads require an integer type as the right-hand
parameter and return a type which is adjusted to accommodate the new
value without risk of overflow or underflow.

Comparison operators convert the inputs to a common result type
following the rules above before performing a comparison and returning
`true` or `false`.

#### Overflow

Because arithmetic operators return a result of equal capacity to
their inputs, they carry a risk of overflow. For instance,

    make_fixed<4, 3>(15) + make_fixed<4, 3>(1)

causes overflow because because a type with 4 integer bits cannot
store a value of 16.

Overflow of any bits in a signed or unsigned fixed-point type is
classed as undefined behavior. This is a minor deviation from
built-in integer arithmetic where only signed overflow results in
undefined behavior.

#### Underflow

The other typical cause of lost bits is underflow where, for example,

    make_fixed<7, 0>(15) / make_fixed<7, 0>(2)

results in a value of 7. This results in loss of precision but is
generally considered acceptable.

However, when all bits are lost due to underflow, the value is said
to be flushed and this is classed as undefined behavior.

### Dealing With Overflow and Flushes

Errors resulting from overflow and flushes are two of the biggest
headaches related to fixed-point arithmetic. Integers suffer the same
kinds of errors but are somewhat easier to reason about as they lack
fractional digits. Floating-point numbers are largely shielded from
these errors by their variable exponent and implicit bit.

Three strategies for avoiding overflow in fixed-point types are
presented:

1. simply leave it to the user to avoid overflow;
2. promote the result to a larger type to ensure sufficient capacity
   or
3. adjust the exponent of the result upward to ensure that the top
   limit of the type is sufficient to preserve the most significant
   digits at the expense of the less significant digits.

For arithmetic operators, choice 1) is taken because it most closely
follows the behavior of integer types. Thus it should cause the least
surprise to the fewest users. This makes it far easier to reason
about in code where functions are written with a particular type in
mind. It also requires the least computation in most cases.

Choices 2) and 3) are more robust to overflow events. However, they
represent different trade-offs and neither one is the best fit in all
situations. For these reasons, they are presented as named functions.

#### Type Promotion

Function template, `promote`, borrows a term from the language
feature which avoids integer overflow prior to certain operations. It
takes a `fixed_point` object and returns the same value represented
by a larger `fixed_point` specialization.

For example,

    promote(make_fixed<5, 2>(15.5))

is equivalent to

    make_fixed<11, 4>(15.5)

Complimentary function template, `demote`, reverses the process,
returning a value of a smaller type.

#### Named Arithmetic Functions

The following named function templates can be used as a hassle-free
alternative to arithmetic operators in situations where the aim is
to avoid overflow.

Unary functions:

    trunc_reciprocal, trunc_square, trunc_sqrt,
    promote_reciprocal, promote_square

Binary functions:

    trunc_add, trunc_subtract, trunc_multiply, trunc_divide
    trunc_shift_left, trunc_shift_right,
    promote_add, promote_sub, promote_multiply, promote_divide

Some notes:

1. The `trunc_` functions return the result as a type no larger than
   the inputs and with an exponent adjusted to avoid overflow;
2. the `promote_` functions return the result as a type large enough
   to avoid overflow and underflow;
3. the `_multiply` and `_square` functions are not guaranteed to be
   available for 64-bit types;
4. the `_multiply` and `_square` functions produce undefined behavior
   when all input parameters are the *most negative number*;
5. the `_square` functions return an unsigned type;
6. the `_add`, `_subtract`, `_multiply` and `_divide` functions take
   heterogeneous `fixed_point` specializations;
7. the `_divide` and `_reciprocal` functions in no way guard against
   divide-by-zero errors;
8. the `trunc_shift_` functions return results of the same type as
   their first input parameter and
9. the list is by no means complete.

### Example

The following example calculates the magnitude of a 3-dimensional vector.

    template <class Fp>
    constexpr auto magnitude(const Fp & x, const Fp & y, const Fp & z)
    -> decltype(trunc_sqrt(trunc_add(trunc_square(x), trunc_square(y), trunc_square(z))))
    {
        return trunc_sqrt(trunc_add(trunc_square(x), trunc_square(y), trunc_square(z)));
    }

Calling the above function as follows

    static_cast<double>(magnitude(
        make_ufixed<4, 12>(1),
        make_ufixed<4, 12>(4),
        make_ufixed<4, 12>(9)));

returns the value, 9.890625.

## V. Technical Specification

### Header \<fixed_point\> Synopsis

    namespace std {
      template <class ReprType, int Exponent> class fixed_point;

      template <unsigned IntegerDigits, unsigned FractionalDigits = 0, bool IsSigned = true>
        using make_fixed;
      template <unsigned IntegerDigits, unsigned FractionalDigits = 0>
        using make_ufixed;
      template <class ReprType, int IntegerDigits>
        using make_fixed_from_repr;

      template <class FixedPoint>
        using promote_result;
      template <class FixedPoint>
        promote_result<FixedPoint>
          constexpr promote(const FixedPoint & from) noexcept;

      template <class FixedPoint>
        using demote_result;
      template <class FixedPoint>
        demote_result<FixedPoint>
          constexpr demote(const FixedPoint & from) noexcept;

      template <class ReprType, int Exponent>
      	constexpr bool operator==(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
        constexpr bool operator!=(
      	  const fixed_point<ReprType, Exponent> & lhs,
          const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr bool operator<(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr bool operator>(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr bool operator>=(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr bool operator<=(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;

      template <class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent> operator-(
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent> operator+(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent> operator-(
      	  const fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	fixed_point<ReprType, Exponent> & operator+=(
      	  fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	fixed_point<ReprType, Exponent> & operator-=(
      	  fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	fixed_point<ReprType, Exponent> & operator*=(
      	  fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;
      template <class ReprType, int Exponent>
      	fixed_point<ReprType, Exponent> & operator/=(
      	  fixed_point<ReprType, Exponent> & lhs,
      	  const fixed_point<ReprType, Exponent> & rhs) noexcept;

      template <class Lhs, class Rhs>
      	constexpr auto operator==(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator!=(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator<(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator>(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator>=(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator<=(const Lhs & lhs, const Rhs & rhs) noexcept;

      template <class Lhs, class Rhs>
      	constexpr auto operator+(
      	  const Lhs & lhs,
      	  const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs>
      	constexpr auto operator-(
      	  const Lhs & lhs,
      	  const Rhs & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class RhsReprType, int RhsExponent>
      	constexpr auto operator*(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class RhsReprType, int RhsExponent>
      	constexpr auto operator/(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class Integer>
      	constexpr auto operator*(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const Integer & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class Integer>
      	constexpr auto operator/(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const Integer & rhs) noexcept;
      template <class Integer, class RhsReprType, int RhsExponent>
      	constexpr auto operator*(
      	  const Integer & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class Integer, class RhsReprType, int RhsExponent>
      	constexpr auto operator/(
      	  const Integer & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class Float>
      	constexpr auto operator*(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const Float & rhs) noexcept;
      template <class LhsReprType, int LhsExponent, class Float>
      	constexpr auto operator/(
      	  const fixed_point<LhsReprType, LhsExponent> & lhs,
      	  const Float & rhs) noexcept;
      template <class Float, class RhsReprType, int RhsExponent>
      	constexpr auto operator*(
      	  const Float & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class Float, class RhsReprType, int RhsExponent>
      	constexpr auto operator/(
      	  const Float & lhs,
      	  const fixed_point<RhsReprType, RhsExponent> & rhs) noexcept;
      template <class LhsReprType, int Exponent, class Rhs>
      	fixed_point<LhsReprType, Exponent> & operator+=(fixed_point<LhsReprType, Exponent> & lhs, const Rhs & rhs) noexcept;
      template <class LhsReprType, int Exponent, class Rhs>
      	fixed_point<LhsReprType, Exponent> & operator-=(fixed_point<LhsReprType, Exponent> & lhs, const Rhs & rhs) noexcept;
      template <class LhsReprType, int Exponent>
      template <class Rhs, typename std::enable_if<std::is_arithmetic<Rhs>::value, int>::type Dummy>
      	fixed_point<LhsReprType, Exponent> &
      	fixed_point<LhsReprType, Exponent>::operator*=(const Rhs & rhs) noexcept;
      template <class LhsReprType, int Exponent>
      template <class Rhs, typename std::enable_if<std::is_arithmetic<Rhs>::value, int>::type Dummy>
      	fixed_point<LhsReprType, Exponent> &
      	fixed_point<LhsReprType, Exponent>::operator/=(const Rhs & rhs) noexcept;
      template <class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent>
      	  sqrt(const fixed_point<ReprType, Exponent> & x) noexcept;
      template <class FixedPoint, unsigned N = 2>
      	using trunc_add_result;

      template <class FixedPoint, class ... Tail>
      	trunc_add_result<FixedPoint, sizeof...(Tail) + 1>
          constexpr trunc_add(const FixedPoint & addend1, const Tail & ... addend_tail);
      template <class Lhs, class Rhs = Lhs>
      	using trunc_subtract_result;
      template <class Lhs, class Rhs>
      	trunc_subtract_result<Lhs, Rhs>
      	  constexpr trunc_subtract(const Lhs & minuend, const Rhs & subtrahend);
      template <class Lhs, class Rhs = Lhs>
      	using trunc_multiply_result;

      template <class Lhs, class Rhs>
      	trunc_multiply_result<Lhs, Rhs>
      	  constexpr trunc_multiply(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class FixedPointDividend, class FixedPointDivisor = FixedPointDividend>
      	using trunc_divide_result;
      template <class FixedPointDividend, class FixedPointDivisor>
      	trunc_divide_result<FixedPointDividend, FixedPointDivisor>
      	  constexpr trunc_divide(const FixedPointDividend & lhs, const FixedPointDivisor & rhs) noexcept;
      template <class FixedPoint>
      	using trunc_reciprocal_result;
      template <class FixedPoint>
      	trunc_reciprocal_result<FixedPoint>
      	  constexpr trunc_reciprocal(const FixedPoint & fixed_point) noexcept;
      template <class FixedPoint>
      	using trunc_square_result;

      template <class FixedPoint>
      	trunc_square_result<FixedPoint>
      	  constexpr trunc_square(const FixedPoint & root) noexcept;
      template <class FixedPoint>
      	using trunc_sqrt_result;
      template <class FixedPoint>
      	trunc_sqrt_result<FixedPoint>
      	  constexpr trunc_sqrt(const FixedPoint & square) noexcept;
      template <int Integer, class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent + Integer>
      	  trunc_shift_left(const fixed_point<ReprType, Exponent> & fp) noexcept;
      template <int Integer, class ReprType, int Exponent>
      	constexpr fixed_point<ReprType, Exponent - Integer>
      	  trunc_shift_right(const fixed_point<ReprType, Exponent> & fp) noexcept;
      template <class FixedPoint, unsigned N = 2>
      	using promote_add_result;

      template <class FixedPoint, class ... Tail>
      	promote_add_result<FixedPoint, sizeof...(Tail) + 1>
      	  constexpr promote_add(const FixedPoint & addend1, const Tail & ... addend_tail);
      template <class Lhs, class Rhs = Lhs>
      	using promote_subtract_result
      template <class Lhs, class Rhs>
      	promote_subtract_result<Lhs, Rhs>
      	  constexpr promote_subtract(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs = Lhs>
      	using promote_multiply_result;
      template <class Lhs, class Rhs>
      	promote_multiply_result<Lhs, Rhs>
      	  constexpr promote_multiply(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class Lhs, class Rhs = Lhs>
      	using promote_divide_result;
      template <class Lhs, class Rhs>
      	promote_divide_result<Lhs, Rhs>
      	  constexpr promote_divide(const Lhs & lhs, const Rhs & rhs) noexcept;
      template <class FixedPoint>
      	using promote_square_result;
      template <class FixedPoint>
      	promote_square_result<FixedPoint>
      	  constexpr promote_square(const FixedPoint & root) noexcept;
    }

#### `fixed_point<>` Class Template

    template <class ReprType = int, int Exponent = 0>
    class fixed_point
    {
    public:
      using repr_type = ReprType;

      constexpr static int exponent;
      constexpr static int digits;
      constexpr static int integer_digits;
      constexpr static int fractional_digits;

      fixed_point() noexcept;
      template <class S, typename std::enable_if<_impl::is_integral<S>::value, int>::type Dummy = 0>
        explicit constexpr fixed_point(S s) noexcept;
      template <class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0>
        explicit constexpr fixed_point(S s) noexcept;
      template <class FromReprType, int FromExponent>
        explicit constexpr fixed_point(const fixed_point<FromReprType, FromExponent> & rhs) noexcept;
      template <class S, typename std::enable_if<_impl::is_integral<S>::value, int>::type Dummy = 0>
        fixed_point & operator=(S s) noexcept;
      template <class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0>
        fixed_point & operator=(S s) noexcept;
      template <class FromReprType, int FromExponent>
        fixed_point & operator=(const fixed_point<FromReprType, FromExponent> & rhs) noexcept;

      template <class S, typename std::enable_if<_impl::is_integral<S>::value, int>::type Dummy = 0>
        explicit constexpr operator S() const noexcept;
      template <class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0>
        explicit constexpr operator S() const noexcept;
      explicit constexpr operator bool() const noexcept;

      template <class Rhs, typename std::enable_if<std::is_arithmetic<Rhs>::value, int>::type Dummy = 0>
        fixed_point &operator*=(const Rhs & rhs) noexcept;
      template <class Rhs, typename std::enable_if<std::is_arithmetic<Rhs>::value, int>::type Dummy = 0>
        fixed_point & operator/=(const Rhs & rhs) noexcept;

      constexpr repr_type data() const noexcept;
      static constexpr fixed_point from_data(repr_type repr) noexcept;
    };

## VI. Future Issues

### Library Support

Because the aim is to provide an alternative to existing arithmetic
types which are supported by the standard library, it is conceivable
that a future proposal might specialize existing class templates and
overload existing functions.

Possible candidates for overloading include the functions defined in
\<cmath\> and a templated specialization of `numeric_limits`. A new type
trait, `is_fixed_point`, would also be useful.

While `fixed_point` is intended to provide drop-in replacements to
existing built-ins, it may be preferable to deviate slightly from the
behavior of certain standard functions. For example, overloads of
functions from \<cmath\> will be considerably less concise, efficient
and versatile if they obey rules surrounding error cases. In
particular, the guarantee of setting `errno` in the case of an error
prevents a function from being defined as pure. This highlights a
wider issue surrounding the adoption of the functional approach and
compile-time computation that is beyond the scope of this document.

### Alternatives to Built-in Integer Types

The reason that `ReprType` is restricted to built-in integer types
is that a number of features require the use of a higher - or
lower-capacity type. Supporting alias templates are defined to
provide `fixed_point` with the means to invoke integer types of
specific capacity and signedness at compile time.

There is no general purpose way of deducing a higher or
lower-capacity type given a source type in the same manner as
`make_signed` and `make_unsigned`. If there were, this might be
adequate to allow alternative choices for `ReprType`.

### Bounded Integers

The bounded::integer library [\[2\]](http://doublewise.net/c++/bounded/)
exemplifies the benefits of keeping track of ranges of values in
arithmetic types at compile time.

To a limited extent, the `trunc_` functions defined here also keep
track of - and modify - the limits of values. However, a combination
of techniques is capable of producing superior results.

For instance, consider the following expression:

    make_ufixed<2, 6> three(3);
    auto n = trunc_square(trunc_square(three));

The type of `n` is `make_ufixed<8, 0>` but its value does not
exceed 81. Hence, an integer bit has been wasted. It may be possible
to track more accurate limits in the same manner as the
bounded::integer library in order to improve the precision of types
returned by `trunc_` functions. For this reason, the exact value of
the exponents of these return types is not given.

Notes:
* Bounded::integer is already supported by fixed-point library,
fp [\[3\]](https://github.com/mizvekov/fp).
* A similar library is the boost constrained_value library
[\[4\]](http://rk.hekko.pl/constrained_value/).

### Alternative Policies

The behavior of the types specialized from `fixed_point` represent
one sub-set of all potentially desirable behaviors. Alternative
characteristics include:

* different rounding strategies - other than truncation;
* overflow and underflow checks - possibly throwing exceptions;
* operator return type - adopting `trunc_` or `promote_` behavior;
* default-initialize to zero - currently uninitialized and
* saturation arithmetic - as opposed to modular arithmetic.

One way to extend `fixed_point` to cover these alternatives would be
to add non-type template parameters containing bit flags or enumerated
types. The default set of values would reflect `fixed_point` as it
stands currently.

## VII. Prior Art

Many examples of fixed-point support in C and C++ exist. While almost
all of them aim for low run-time cost and expressive alternatives to
raw integer manipulation, they vary greatly in detail and in terms of
their interface.

One especially interesting dichotomy is between solutions which offer
a discrete selection of fixed-point types and libraries which contain
a continuous range of exponents through type parameterization.

### N1169

One example of the former is found in proposal N1169
[\[5\]](http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1169.pdf),
the intent of which is to expose features found in certain embedded
hardware. It introduces a succinct set of language-level fixed-point
types and impose constraints on the number of integer or fractional
digits each can possess.

As with all examples of discrete-type fixed-point support, the limited
choice of exponents is a considerable restriction on the versatility
and expressiveness of the API.

Nevertheless, it may be possible to harness performance gains provided
by N1169 fixed-point types through explicit template specialization.
This is likely to be a valuable proposition to potential users of the
library who find themselves targeting platforms which support
fixed-point arithmetic at the hardware level.

### N3352

There are many other C++ libraries available which fall into the
latter category of continuous-range fixed-point arithmetic
[\[3\]](https://github.com/mizvekov/fp)
[\[6\]](http://www.codeproject.com/Articles/37636/Fixed-Point-Class)
[\[7\]](https://github.com/viboes/fixed_point). In particular, an
existing library proposal, N3352 [\[8\]](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3352.html),
aims to achieve very similar goals through similar means and warrants
closer comparison than N1169.

N3352 introduces four class templates covering the quadrant of signed
versus unsigned and fractional versus integer numeric types. It is
intended to replace built-in types in a wide variety of situations and
accordingly, is highly compile-time configurable in terms of how
rounding and overflow are handled. Parameters to these four class
templates include the storage in bits and - for fractional types - the
resolution.

The `fixed_point` class template could probably - with a few caveats -
be generated using the two fractional types, `nonnegative` and
`negatable`, replacing the `ReprType` parameter with the integer bit
count of `ReprType`, specifying `fastest` for the rounding mode and
specifying `undefined` as the overflow mode.

However, fixed_point more closely and concisely caters to the needs of
users who already use integer types and simply desire a more concise,
less error-prone form. It more closely follows the four design aims of
the library and - it can be argued - more closely follows the spirit
of the standard in its pursuit of zero-cost abstraction.

Some aspects of the design of the N3352 API which back up these
conclusion are that:

* the result of arithmetic operations closely resemble the `trunc_`
  function templates and are potentially more costly at run-time;
* the nature of the range-specifying template parameters - through
  careful framing in mathematical terms - abstracts away valuable
  information regarding machine-critical type size information;
* the breaking up of duties amongst four separate class templates
  introduces four new concepts and incurs additional mental load for
  relatively little gain while further detaching the interface from
  vital machine-level details and
* the absence of the most negative number from signed types reduces
  the capacity of all types by one.

The added versatility that the N3352 API provides regarding rounding
and overflow handling are of relatively low priority to users who
already bear the scars of battles with raw integer types.
Nevertheless, providing them as options to be turned on or off at
compile time is an ideal way to leave the choice in the hands of the
user.

Many high-performance applications - in which fixed-point is of
potential value - favor run-time checks during development which are
subsequently deactivated in production builds. The N3352 interface is
highly conducive to this style of development. It is an aim of the
fixed_point design to be similarly extensible in future revisions.

## VIII. Acknowledgements

Subgroup: Guy Davidson, Michael Wong  
Contributors: Ed Ainsley, Billy Baker, Lance Dyson, Marco Foco,
Clément Grégoire, Nicolas Guillemot, Matt Kinzelman, Joël Lamotte,
Sean Middleditch, Patrice Roy, Peter Schregle, Ryhor Spivak

## IX. References

1. Why Integer Coordinates?, <http://www.pathengine.com/Contents/Overview/FundamentalConcepts/WhyIntegerCoordinates/page.php>
2. C++ bounded::integer library, <http://doublewise.net/c++/bounded/>
3. fp, C++14 Fixed Point Library, <https://github.com/mizvekov/fp>
4. Boost Constrained Value Libarary, <http://rk.hekko.pl/constrained_value/>
5. N1169, Extensions to support embedded processors, <http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1169.pdf>
6. fpmath, Fixed Point Math Library, <http://www.codeproject.com/Articles/37636/Fixed-Point-Class>
7. Boost fixed_point (proposed), Fixed point integral and fractional types, <https://github.com/viboes/fixed_point>
8. N3352, C++ Binary Fixed-Point Arithmetic, <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3352.html>
9. fixed_point, Reference Implementation of P0037, <https://github.com/johnmcfarlane/fixed_point>

## X. Appendix 1: Reference Implementation

An in-development implementation of the fixed_point class template and
its essential supporting functions and types is available
[\[9\]](https://github.com/johnmcfarlane/fixed_point). It includes a
utility header containing such things as math and trigonometric
functions and a partial `numeric_limits` specialization. Compile-time
and run-time tests are included as well as benchmarking support. It is
the source of examples and measurements cited here.

## XI. Appendix 2: Performance

Despite a focus on usable interface and direct translation from
integer-based fixed-point operations, there is an overwhelming
expectation that the source code result in minimal instructions and
clock cycles. A few preliminary numbers are presented to give a very
early idea of how the API might perform.

Some notes:

* A few test functions were run, ranging from single arithmetic
  operations to basic geometric functions, performed against integer,
  floating-point and fixed-point types for comparison.
* Figures were taken from a single CPU, OS and compiler, namely:

      Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
      Target: x86_64-pc-linux-gnu
      Thread model: posix

* Fixed inputs were provided to each function, meaning that branch
  prediction rarely fails. Results may also not represent the full
  range of inputs.
* Details of the test harness used can be found in the source
  project mentioned in Appendix 1;
* Times are in nanoseconds;
* Code has not yet been optimized for performance.

### Types

Where applicable various combinations of integer, floating-point and
fixed-point types were tested with the following identifiers:

* `uint8_t`, `int8_t`, `uint16_t`, `int16_t`, `uint32_t`, `int32_t`,
  `uint64_t` and `int64_t` built-in integer types;
* `float`, `double` and `long double` built-in floating-point types;
* s3:4, u4:4, s7:8, u8:8, s15:16, u16:16, s31:32 and u32:32 format
  fixed-point types.

### Basic Arithmetic

Plus, minus, multiplication and division were tested in isolation
using a number of different numeric types with the following results:

name	cpu_time  
add(float)	1.78011  
add(double)	1.73966  
add(long double)	3.46011  
add(u4_4)	1.87726  
add(s3_4)	1.85051  
add(u8_8)	1.85417  
add(s7_8)	1.82057  
add(u16_16)	1.94194  
add(s15_16)	1.93463  
add(u32_32)	1.94674  
add(s31_32)	1.94446  
add(int8_t)	2.14857  
add(uint8_t)	2.12571  
add(int16_t)	1.9936  
add(uint16_t)	1.88229  
add(int32_t)	1.82126  
add(uint32_t)	1.76  
add(int64_t)	1.76  
add(uint64_t)	1.83223  
sub(float)	1.96617  
sub(double)	1.98491  
sub(long double)	3.55474  
sub(u4_4)	1.77006  
sub(s3_4)	1.72983  
sub(u8_8)	1.72983  
sub(s7_8)	1.72983  
sub(u16_16)	1.73966  
sub(s15_16)	1.85051  
sub(u32_32)	1.88229  
sub(s31_32)	1.87063  
sub(int8_t)	1.76  
sub(uint8_t)	1.74994  
sub(int16_t)	1.82126  
sub(uint16_t)	1.83794  
sub(int32_t)	1.89074  
sub(uint32_t)	1.85417  
sub(int64_t)	1.83703  
sub(uint64_t)	2.04914  
mul(float)	1.9376  
mul(double)	1.93097  
mul(long double)	102.446  
mul(u4_4)	2.46583  
mul(s3_4)	2.09189  
mul(u8_8)	2.08  
mul(s7_8)	2.18697  
mul(u16_16)	2.12571  
mul(s15_16)	2.10789  
mul(u32_32)	2.10789  
mul(s31_32)	2.10789  
mul(int8_t)	1.76  
mul(uint8_t)	1.78011  
mul(int16_t)	1.8432  
mul(uint16_t)	1.76914  
mul(int32_t)	1.78011  
mul(uint32_t)	2.19086  
mul(int64_t)	1.7696  
mul(uint64_t)	1.79017  
div(float)	5.12  
div(double)	7.64343  
div(long double)	8.304  
div(u4_4)	3.82171  
div(s3_4)	3.82171  
div(u8_8)	3.84  
div(s7_8)	3.8  
div(u16_16)	9.152  
div(s15_16)	11.232  
div(u32_32)	30.8434  
div(s31_32)	34  
div(int8_t)	3.82171  
div(uint8_t)	3.82171  
div(int16_t)	3.8  
div(uint16_t)	3.82171  
div(int32_t)	3.82171  
div(uint32_t)	3.81806  
div(int64_t)	10.2286  
div(uint64_t)	8.304  

Among the slowest types are `long double`. It is likely that they are
emulated in software. The next slowest operations are fixed-point
multiply and divide operations - especially with 64-bit types. This is
because values need to be promoted temporarily to double-width types.
This is a known fixed-point technique which inevitably experiences
slowdown where a 128-bit type is required on a 64-bit system.

Here is a section of the disassembly of the s15:16 multiply call:

    30:   mov    %r14,%rax  
          mov    %r15,%rax  
          movslq -0x28(%rbp),%rax  
          movslq -0x30(%rbp),%rcx  
          imul   %rax,%rcx  
          shr    $0x10,%rcx  
          mov    %ecx,-0x38(%rbp)  
          mov    %r12,%rax  
    4c:   movzbl (%rbx),%eax  
          cmp    $0x1,%eax  
        ↓ jne    68  
    54:   mov    0x8(%rbx),%rax  
          lea    0x1(%rax),%rcx  
          mov    %rcx,0x8(%rbx)  
          cmp    0x38(%rbx),%rax  
        ↑ jb     30

The two 32-bit numbers are multiplied together and the result shifted
down - much as it would if raw `int` values were used. The efficiency
of this operation varies with the exponent. An exponent of zero should
mean no shift at all.

### 3-Dimensional Magnitude Squared

A fast `sqrt` implementation has not yet been tested with
`fixed_point`. (The naive implementation takes over 300ns.) For this
reason, a magnitude-squared function is measured, combining multiply
and add operations:

    template <typename FP>
    constexpr FP magnitude_squared(const FP & x, const FP & y, const FP & z)
    {
        return x * x + y * y + z * z;
    }

Only real number formats are tested:

float  2.42606  
double  2.08  
long double  4.5056  
s3_4  2.768  
s7_8  2.77577  
s15_16  2.752  
s31_32  4.10331  

Again, the size of the type seems to have the largest impact.

### Circle Intersection

A similar operation includes a comparison and branch:

    template <typename Real>
    bool circle_intersect_generic(Real x1, Real y1, Real r1, Real x2, Real y2, Real r2)
    {
	    auto x_diff = x2 - x1;
    	auto y_diff = y2 - y1;
	    auto distance_squared = x_diff * x_diff + y_diff * y_diff;

	    auto touch_distance = r1 + r2;
    	auto touch_distance_squared = touch_distance * touch_distance;

	    return distance_squared <= touch_distance_squared;
    }

float	3.46011  
double	3.48  
long double	6.4  
s3_4	3.88  
s7_8	4.5312  
s15_16	3.82171  
s31_32	5.92  

Again, fixed-point and native performance are comparable.


================================================
FILE: Docs/Proposals/p0037.html
================================================
<!DOCTYPE html>
<html>
  <head>
      <meta charset="utf-8" />
      <title>Fixed_Point_Library_Proposal</title>
      <style>.markdown-preview, .markdown-preview[data-use-github-style] { font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 16px; line-height: 1.6; word-wrap: break-word; overflow: scroll; box-sizing: border-box; padding: 20px; background-color: rgb(255, 255, 255); }
.markdown-preview > :first-child, .markdown-preview[data-use-github-style] > :first-child { margin-top: 0px !important; }
.markdown-preview > :last-child, .markdown-preview[data-use-github-style] > :last-child { margin-bottom: 0px !important; }
.markdown-preview a:not([href]), .markdown-preview[data-use-github-style] a:not([href]) { color: inherit; text-decoration: none; }
.markdown-preview .absent, .markdown-preview[data-use-github-style] .absent { color: rgb(204, 0, 0); }
.markdown-preview .anchor, .markdown-preview[data-use-github-style] .anchor { position: absolute; top: 0px; left: 0px; display: block; padding-right: 6px; padding-left: 30px; margin-left: -30px; }
.markdown-preview .anchor:focus, .markdown-preview[data-use-github-style] .anchor:focus { outline: none; }
.markdown-preview h1, .markdown-preview[data-use-github-style] h1, .markdown-preview h2, .markdown-preview[data-use-github-style] h2, .markdown-preview h3, .markdown-preview[data-use-github-style] h3, .markdown-preview h4, .markdown-preview[data-use-github-style] h4, .markdown-preview h5, .markdown-preview[data-use-github-style] h5, .markdown-preview h6, .markdown-preview[data-use-github-style] h6 { position: relative; margin-top: 1em; margin-bottom: 16px; font-weight: bold; line-height: 1.4; }
.markdown-preview h1 .octicon-link, .markdown-preview[data-use-github-style] h1 .octicon-link, .markdown-preview h2 .octicon-link, .markdown-preview[data-use-github-style] h2 .octicon-link, .markdown-preview h3 .octicon-link, .markdown-preview[data-use-github-style] h3 .octicon-link, .markdown-preview h4 .octicon-link, .markdown-preview[data-use-github-style] h4 .octicon-link, .markdown-preview h5 .octicon-link, .markdown-preview[data-use-github-style] h5 .octicon-link, .markdown-preview h6 .octicon-link, .markdown-preview[data-use-github-style] h6 .octicon-link { display: none; color: rgb(0, 0, 0); vertical-align: middle; }
.markdown-preview h1:hover .anchor, .markdown-preview[data-use-github-style] h1:hover .anchor, .markdown-preview h2:hover .anchor, .markdown-preview[data-use-github-style] h2:hover .anchor, .markdown-preview h3:hover .anchor, .markdown-preview[data-use-github-style] h3:hover .anchor, .markdown-preview h4:hover .anchor, .markdown-preview[data-use-github-style] h4:hover .anchor, .markdown-preview h5:hover .anchor, .markdown-preview[data-use-github-style] h5:hover .anchor, .markdown-preview h6:hover .anchor, .markdown-preview[data-use-github-style] h6:hover .anchor { padding-left: 8px; margin-left: -30px; text-decoration: none; }
.markdown-preview h1:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h1:hover .anchor .octicon-link, .markdown-preview h2:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h2:hover .anchor .octicon-link, .markdown-preview h3:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h3:hover .anchor .octicon-link, .markdown-preview h4:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h4:hover .anchor .octicon-link, .markdown-preview h5:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h5:hover .anchor .octicon-link, .markdown-preview h6:hover .anchor .octicon-link, .markdown-preview[data-use-github-style] h6:hover .anchor .octicon-link { display: inline-block; }
.markdown-preview h1 tt, .markdown-preview[data-use-github-style] h1 tt, .markdown-preview h2 tt, .markdown-preview[data-use-github-style] h2 tt, .markdown-preview h3 tt, .markdown-preview[data-use-github-style] h3 tt, .markdown-preview h4 tt, .markdown-preview[data-use-github-style] h4 tt, .markdown-preview h5 tt, .markdown-preview[data-use-github-style] h5 tt, .markdown-preview h6 tt, .markdown-preview[data-use-github-style] h6 tt, .markdown-preview h1 code, .markdown-preview[data-use-github-style] h1 code, .markdown-preview h2 code, .markdown-preview[data-use-github-style] h2 code, .markdown-preview h3 code, .markdown-preview[data-use-github-style] h3 code, .markdown-preview h4 code, .markdown-preview[data-use-github-style] h4 code, .markdown-preview h5 code, .markdown-preview[data-use-github-style] h5 code, .markdown-preview h6 code, .markdown-preview[data-use-github-style] h6 code { font-size: inherit; }
.markdown-preview h1, .markdown-preview[data-use-github-style] h1 { padding-bottom: 0.3em; font-size: 2.25em; line-height: 1.2; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); }
.markdown-preview h1 .anchor, .markdown-preview[data-use-github-style] h1 .anchor { line-height: 1; }
.markdown-preview h2, .markdown-preview[data-use-github-style] h2 { padding-bottom: 0.3em; font-size: 1.75em; line-height: 1.225; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); }
.markdown-preview h2 .anchor, .markdown-preview[data-use-github-style] h2 .anchor { line-height: 1; }
.markdown-preview h3, .markdown-preview[data-use-github-style] h3 { font-size: 1.5em; line-height: 1.43; }
.markdown-preview h3 .anchor, .markdown-preview[data-use-github-style] h3 .anchor { line-height: 1.2; }
.markdown-preview h4, .markdown-preview[data-use-github-style] h4 { font-size: 1.25em; }
.markdown-preview h4 .anchor, .markdown-preview[data-use-github-style] h4 .anchor { line-height: 1.2; }
.markdown-preview h5, .markdown-preview[data-use-github-style] h5 { font-size: 1em; }
.markdown-preview h5 .anchor, .markdown-preview[data-use-github-style] h5 .anchor { line-height: 1.1; }
.markdown-preview h6, .markdown-preview[data-use-github-style] h6 { font-size: 1em; color: rgb(119, 119, 119); }
.markdown-preview h6 .anchor, .markdown-preview[data-use-github-style] h6 .anchor { line-height: 1.1; }
.markdown-preview p, .markdown-preview[data-use-github-style] p, .markdown-preview blockquote, .markdown-preview[data-use-github-style] blockquote, .markdown-preview ul, .markdown-preview[data-use-github-style] ul, .markdown-preview ol, .markdown-preview[data-use-github-style] ol, .markdown-preview dl, .markdown-preview[data-use-github-style] dl, .markdown-preview table, .markdown-preview[data-use-github-style] table, .markdown-preview pre, .markdown-preview[data-use-github-style] pre { margin-top: 0px; margin-bottom: 16px; }
.markdown-preview hr, .markdown-preview[data-use-github-style] hr { height: 4px; padding: 0px; margin: 16px 0px; border: 0px none; background-color: rgb(231, 231, 231); }
.markdown-preview ul, .markdown-preview[data-use-github-style] ul, .markdown-preview ol, .markdown-preview[data-use-github-style] ol { padding-left: 2em; }
.markdown-preview ul.no-list, .markdown-preview[data-use-github-style] ul.no-list, .markdown-preview ol.no-list, .markdown-preview[data-use-github-style] ol.no-list { padding: 0px; list-style-type: none; }
.markdown-preview ul ul, .markdown-preview[data-use-github-style] ul ul, .markdown-preview ul ol, .markdown-preview[data-use-github-style] ul ol, .markdown-preview ol ol, .markdown-preview[data-use-github-style] ol ol, .markdown-preview ol ul, .markdown-preview[data-use-github-style] ol ul { margin-top: 0px; margin-bottom: 0px; }
.markdown-preview li > p, .markdown-preview[data-use-github-style] li > p { margin-top: 16px; }
.markdown-preview dl, .markdown-preview[data-use-github-style] dl { padding: 0px; }
.markdown-preview dl dt, .markdown-preview[data-use-github-style] dl dt { padding: 0px; margin-top: 16px; font-size: 1em; font-style: italic; font-weight: bold; }
.markdown-preview dl dd, .markdown-preview[data-use-github-style] dl dd { padding: 0px 16px; margin-bottom: 16px; }
.markdown-preview blockquote, .markdown-preview[data-use-github-style] blockquote { padding: 0px 15px; color: rgb(119, 119, 119); border-left-width: 4px; border-left-style: solid; border-left-color: rgb(221, 221, 221); }
.markdown-preview blockquote > :first-child, .markdown-preview[data-use-github-style] blockquote > :first-child { margin-top: 0px; }
.markdown-preview blockquote > :last-child, .markdown-preview[data-use-github-style] blockquote > :last-child { margin-bottom: 0px; }
.markdown-preview table, .markdown-preview[data-use-github-style] table { display: block; width: 100%; overflow: auto; word-break: normal; }
.markdown-preview table th, .markdown-preview[data-use-github-style] table th { font-weight: bold; }
.markdown-preview table th, .markdown-preview[data-use-github-style] table th, .markdown-preview table td, .markdown-preview[data-use-github-style] table td { padding: 6px 13px; border: 1px solid rgb(221, 221, 221); }
.markdown-preview table tr, .markdown-preview[data-use-github-style] table tr { border-top-width: 1px; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: rgb(255, 255, 255); }
.markdown-preview table tr:nth-child(2n), .markdown-preview[data-use-github-style] table tr:nth-child(2n) { background-color: rgb(248, 248, 248); }
.markdown-preview img, .markdown-preview[data-use-github-style] img { max-width: 100%; box-sizing: border-box; }
.markdown-preview .emoji, .markdown-preview[data-use-github-style] .emoji { max-width: none; }
.markdown-preview span.frame, .markdown-preview[data-use-github-style] span.frame { display: block; overflow: hidden; }
.markdown-preview span.frame > span, .markdown-preview[data-use-github-style] span.frame > span { display: block; float: left; width: auto; padding: 7px; margin: 13px 0px 0px; overflow: hidden; border: 1px solid rgb(221, 221, 221); }
.markdown-preview span.frame span img, .markdown-preview[data-use-github-style] span.frame span img { display: block; float: left; }
.markdown-preview span.frame span span, .markdown-preview[data-use-github-style] span.frame span span { display: block; padding: 5px 0px 0px; clear: both; color: rgb(51, 51, 51); }
.markdown-preview span.align-center, .markdown-preview[data-use-github-style] span.align-center { display: block; overflow: hidden; clear: both; }
.markdown-preview span.align-center > span, .markdown-preview[data-use-github-style] span.align-center > span { display: block; margin: 13px auto 0px; overflow: hidden; text-align: center; }
.markdown-preview span.align-center span img, .markdown-preview[data-use-github-style] span.align-center span img { margin: 0px auto; text-align: center; }
.markdown-preview span.align-right, .markdown-preview[data-use-github-style] span.align-right { display: block; overflow: hidden; clear: both; }
.markdown-preview span.align-right > span, .markdown-preview[data-use-github-style] span.align-right > span { display: block; margin: 13px 0px 0px; overflow: hidden; text-align: right; }
.markdown-preview span.align-right span img, .markdown-preview[data-use-github-style] span.align-right span img { margin: 0px; text-align: right; }
.markdown-preview span.float-left, .markdown-preview[data-use-github-style] span.float-left { display: block; float: left; margin-right: 13px; overflow: hidden; }
.markdown-preview span.float-left span, .markdown-preview[data-use-github-style] span.float-left span { margin: 13px 0px 0px; }
.markdown-preview span.float-right, .markdown-preview[data-use-github-style] span.float-right { display: block; float: right; margin-left: 13px; overflow: hidden; }
.markdown-preview span.float-right > span, .markdown-preview[data-use-github-style] span.float-right > span { display: block; margin: 13px auto 0px; overflow: hidden; text-align: right; }
.markdown-preview code, .markdown-preview[data-use-github-style] code, .markdown-preview tt, .markdown-preview[data-use-github-style] tt { padding: 0.2em 0px; margin: 0px; font-size: 85%; border-radius: 3px; background-color: rgba(0, 0, 0, 0.0392157); }
.markdown-preview code::before, .markdown-preview[d
Download .txt
gitextract_rzo9ruc3/

├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── Docs/
│   ├── Proposals/
│   │   ├── D0447R16 - Introduction of hive to the Standard Library.html
│   │   ├── Fixed_Point_Library_Proposal.md
│   │   ├── p0037.html
│   │   ├── rawstorage.html
│   │   ├── ring_proposal_r5.tex
│   │   ├── uninitialized.html
│   │   └── unstable_remove.html
│   ├── fixed_point.md
│   └── plf_licensing.txt
├── README.md
├── SG14/
│   ├── algorithm_ext.h
│   ├── flat_map.h
│   ├── flat_set.h
│   ├── inplace_function.h
│   ├── plf_colony.h
│   ├── ring.h
│   └── slot_map.h
└── SG14_test/
    ├── SG14_test.h
    ├── flat_map_test.cpp
    ├── flat_set_test.cpp
    ├── inplace_function_test.cpp
    ├── main.cpp
    ├── plf_colony_test.cpp
    ├── ring_test.cpp
    ├── slot_map_test.cpp
    ├── uninitialized_test.cpp
    └── unstable_remove_test.cpp
Download .txt
SYMBOL INDEX (371 symbols across 17 files)

FILE: SG14/algorithm_ext.h
  function namespace (line 8) | namespace stdext

FILE: SG14/flat_map.h
  function namespace (line 39) | namespace stdext {
  function explicit (line 258) | explicit iter(KeyIt&& kit, MappedIt&& vit)
  type sorted_unique_t (line 274) | struct sorted_unique_t { explicit sorted_unique_t() = default; }
  function sorted_unique_t (line 277) | inline
  function class (line 323) | class value_compare {
  type containers (line 336) | struct containers {
  function flat_map (line 344) | flat_map(Compare()) {}
  function explicit (line 414) | explicit flat_map(const Compare& comp)
  function compare_ (line 415) | compare_(comp) {}
  function iterator (line 540) | iterator begin() noexcept { return flatmap_detail::make_iterator(c_.keys...
  function const_iterator (line 541) | const_iterator begin() const noexcept { return flatmap_detail::make_iter...
  function const_iterator (line 543) | const_iterator end() const noexcept { return flatmap_detail::make_iterat...
  function const_iterator (line 546) | const_iterator cend() const noexcept { return flatmap_detail::make_itera...
  function const_reverse_iterator (line 549) | const_reverse_iterator rbegin() const noexcept { return const_reverse_it...
  function const_reverse_iterator (line 551) | const_reverse_iterator rend() const noexcept { return const_reverse_iter...
  function empty (line 557) | [[nodiscard]]
  function size_type (line 561) | size_type max_size() const noexcept { return std::min<size_type>(c_.keys...
  function mapped_reference (line 567) | mapped_reference operator[](Key&& x) {
  function mapped_reference (line 571) | mapped_reference at(const Key& k) {
  function const_mapped_reference (line 579) | const_mapped_reference at(const Key& k) const {
  function iterator (line 617) | iterator insert(const_iterator position, const value_type& x) {
  function iterator (line 621) | iterator insert(const_iterator position, value_type&& x) {
  function insert (line 666) | void insert(std::initializer_list<value_type> il) {
  function insert (line 670) | void insert(stdext::sorted_unique_t s, std::initializer_list<value_type>...
  function containers (line 675) | containers extract() && {
  function size_type (line 892) | size_type count(const Key& k) const {
  function contains (line 902) | bool contains(const Key& k) const {
  function iterator (line 912) | iterator lower_bound(const Key& k) {
  function const_iterator (line 920) | const_iterator lower_bound(const Key& k) const {
  function iterator (line 948) | iterator upper_bound(const Key& k) {
  function const_iterator (line 956) | const_iterator upper_bound(const Key& k) const {
  function kit1 (line 985) | auto kit1 = std::partition_point(c_.keys.begin(), c_.keys.end(), [&](con...
  function kit2 (line 988) | auto kit2 = std::partition_point(kit1, c_.keys.end(), [&](const auto& el...
  function kit1 (line 1000) | auto kit1 = std::partition_point(c_.keys.begin(), c_.keys.end(), [&](con...
  function kit2 (line 1003) | auto kit2 = std::partition_point(kit1, c_.keys.end(), [&](const auto& el...
  function kit1 (line 1017) | auto kit1 = std::partition_point(c_.keys.begin(), c_.keys.end(), [&](con...
  function kit2 (line 1020) | auto kit2 = std::partition_point(kit1, c_.keys.end(), [&](const auto& el...
  function kit2 (line 1037) | auto kit2 = std::partition_point(kit1, c_.keys.end(), [&](const auto& el...

FILE: SG14/flat_set.h
  function namespace (line 39) | namespace stdext {
  type sorted_unique_t (line 129) | struct sorted_unique_t { explicit sorted_unique_t() = default; }
  function sorted_unique_t (line 132) | inline
  function flat_set (line 165) | flat_set(Compare()) {}
  function explicit (line 167) | explicit flat_set(KeyContainer ctr)
  function explicit (line 246) | explicit flat_set(const Compare& comp)
  function iterator (line 353) | iterator begin() noexcept { return c_.begin(); }
  function iterator (line 355) | iterator end() noexcept { return c_.end(); }
  function reverse_iterator (line 361) | reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
  function const_reverse_iterator (line 362) | const_reverse_iterator rbegin() const noexcept { return const_reverse_it...
  function const_reverse_iterator (line 364) | const_reverse_iterator rend() const noexcept { return const_reverse_iter...
  function empty (line 370) | [[nodiscard]]
  function iterator (line 415) | iterator insert(const_iterator, const Key& t) {
  function iterator (line 420) | iterator insert(const_iterator, Key&& t) {
  function insert (line 448) | void insert(std::initializer_list<Key> il) {
  function insert (line 452) | void insert(sorted_unique_t s, std::initializer_list<Key> il) {
  function KeyContainer (line 456) | KeyContainer extract() && {
  function replace (line 462) | void replace(KeyContainer&& ctr) {
  function iterator (line 466) | iterator erase(iterator position) {
  function iterator (line 470) | iterator erase(const_iterator position) {
  function size_type (line 474) | size_type erase(const Key& t) {
  function iterator (line 483) | iterator erase(const_iterator first, const_iterator last) {
  function clear (line 497) | void clear() noexcept {
  function iterator (line 504) | iterator find(const Key& t) {
  function const_iterator (line 512) | const_iterator find(const Key& t) const {
  function size_type (line 540) | size_type count(const Key& x) const {
  function contains (line 550) | bool contains(const Key& x) const {
  function iterator (line 560) | iterator lower_bound(const Key& t) {
  function const_iterator (line 566) | const_iterator lower_bound(const Key& t) const {
  function iterator (line 588) | iterator upper_bound(const Key& t) {
  function const_iterator (line 594) | const_iterator upper_bound(const Key& t) const {
  function lo (line 617) | auto lo = std::partition_point(this->begin(), this->end(), [&](const Key...
  function hi (line 620) | auto hi = std::partition_point(lo, this->end(), [&](const Key& elt) {
  function lo (line 627) | auto lo = std::partition_point(this->begin(), this->end(), [&](const Key...
  function hi (line 630) | auto hi = std::partition_point(lo, this->end(), [&](const Key& elt) {
  function lo (line 639) | auto lo = std::partition_point(this->begin(), this->end(), [&](const Key...
  function hi (line 642) | auto hi = std::partition_point(lo, this->end(), [&](const Key& elt) {
  function hi (line 654) | auto hi = std::partition_point(lo, this->end(), [&](const Key& elt) {

FILE: SG14/inplace_function.h
  function namespace (line 37) | namespace stdext {
  function true_type (line 142) | struct is_valid_inplace_dst : std::true_type
  function false_type (line 157) | struct is_invocable_r_impl : std::false_type {}
  function namespace (line 195) | namespace inplace_function_detail {
  function vtable_t (line 245) | static const vtable_t vt{inplace_function_detail::wrapper<C>{}}
  function C (line 248) | new (std::addressof(storage_)) C{std::forward<T>(closure)};
  function vtable_ptr_ (line 271) | inplace_function(std::nullptr_t) noexcept :
  function R (line 317) | R operator() (Args... args) const
  function operator (line 325) | constexpr bool operator== (std::nullptr_t) const noexcept
  function operator (line 330) | constexpr bool operator!= (std::nullptr_t) const noexcept
  function swap (line 340) | void swap(inplace_function& other) noexcept

FILE: SG14/plf_colony.h
  function namespace (line 243) | namespace plf
  type typename (line 273) | typedef typename choose<priority == plf::performance, unsigned short, un...
  type typename (line 280) | typedef typename std::aligned_storage<sizeof(element_type), (sizeof(elem...
  type element_type (line 282) | typedef element_type aligned_element_type;
  type typename (line 286) | typedef typename std::allocator_traits<allocator_type>::size_type				siz...
  type typename (line 287) | typedef typename std::allocator_traits<allocator_type>::difference_type ...
  type element_type (line 288) | typedef element_type &																		reference;
  type element_type (line 289) | typedef const element_type &																const_reference;
  type typename (line 290) | typedef typename std::allocator_traits<allocator_type>::pointer 				poin...
  type typename (line 291) | typedef typename std::allocator_traits<allocator_type>::const_pointer		c...
  type typename (line 293) | typedef typename allocator_type::size_type			size_type;
  type typename (line 294) | typedef typename allocator_type::difference_type	difference_type;
  type typename (line 295) | typedef typename allocator_type::reference			reference;
  type typename (line 296) | typedef typename allocator_type::const_reference	const_reference;
  type typename (line 297) | typedef typename allocator_type::pointer				pointer;
  type typename (line 298) | typedef typename allocator_type::const_pointer		const_pointer;
  type colony_iterator (line 304) | typedef colony_iterator<false>			iterator;
  type colony_iterator (line 305) | typedef colony_iterator<true>			const_iterator;
  type colony_reverse_iterator (line 310) | typedef colony_reverse_iterator<false>	reverse_iterator;
  type colony_reverse_iterator (line 311) | typedef colony_reverse_iterator<true>	const_reverse_iterator;
  type alignas (line 320) | struct alignas
  type aligned_allocation_struct (line 327) | struct aligned_allocation_struct
  type group (line 337) | struct group
  type item_index_tuple (line 338) | struct item_index_tuple
  type typename (line 342) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 343) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 344) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 345) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 346) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 347) | typedef typename std::allocator_traits<allocator_type>::template
  type typename (line 349) | typedef typename std::allocator_traits<aligned_element_allocator_type>::...
  type typename (line 350) | typedef typename std::allocator_traits<group_allocator_type>::pointer 		...
  type typename (line 351) | typedef typename std::allocator_traits<skipfield_allocator_type>::pointe...
  type typename (line 352) | typedef typename std::allocator_traits<aligned_struct_allocator_type>::p...
  type typename (line 353) | typedef typename std::allocator_traits<tuple_allocator_type>::pointer			...
  type typename (line 355) | typedef typename allocator_type::template rebind<aligned_element_type>::...
  type typename (line 356) | typedef typename allocator_type::template rebind<group>::other							gro...
  type typename (line 357) | typedef typename allocator_type::template rebind<skipfield_type>::other	...
  type typename (line 358) | typedef typename allocator_type::template rebind<char>::other							alig...
  type typename (line 359) | typedef typename allocator_type::template rebind<item_index_tuple>::othe...
  type typename (line 360) | typedef typename allocator_type::template rebind<unsigned char>::other		...
  type typename (line 362) | typedef typename aligned_element_allocator_type::pointer	aligned_pointer...
  type typename (line 363) | typedef typename group_allocator_type::pointer 				group_pointer_type;
  type typename (line 364) | typedef typename skipfield_allocator_type::pointer 		skipfield_pointer_t...
  type typename (line 365) | typedef typename aligned_struct_allocator_type::pointer	aligned_struct_p...
  type typename (line 366) | typedef typename tuple_allocator_type::pointer				tuple_pointer_type;
  function aligned_struct_allocator_type (line 372) | struct group : private aligned_struct_allocator_type	// ebco - inherit a...
  function PLF_FORCE_INLINE (line 1955) | inline PLF_FORCE_INLINE iterator begin() PLF_NOEXCEPT
  function PLF_FORCE_INLINE (line 1962) | inline PLF_FORCE_INLINE const_iterator begin() const PLF_NOEXCEPT
  function PLF_FORCE_INLINE (line 1976) | inline PLF_FORCE_INLINE const_iterator end() const PLF_NOEXCEPT
  function PLF_FORCE_INLINE (line 1990) | inline PLF_FORCE_INLINE const_iterator cend() const PLF_NOEXCEPT
  function reverse_iterator (line 2004) | inline reverse_iterator rend() PLF_NOEXCEPT
  function const_reverse_iterator (line 2011) | inline const_reverse_iterator crbegin() const PLF_NOEXCEPT
  function deallocate_group (line 2058) | inline void deallocate_group(group_pointer_type const the_group) PLF_NOE...
  function destroy_all_data (line 2066) | void destroy_all_data() PLF_NOEXCEPT
  function initialize (line 2115) | void initialize(const skipfield_type first_group_size)
  function update_skipblock (line 2125) | void update_skipblock(const iterator &new_location, const skipfield_type...
  function iterator (line 2251) | iterator new_location(groups_with_erasures_list_head, groups_with_erasur...
  function iterator (line 2293) | iterator insert(element_type &&element) // The move-insert function is n...

FILE: SG14/ring.h
  function namespace (line 8) | namespace sg14

FILE: SG14/slot_map.h
  function namespace (line 38) | namespace stdext {
  function set_index (line 72) | constexpr void set_index(Key& k, Integral value) { auto& [idx, gen] = k;...
  function increment_generation (line 73) | static constexpr void increment_generation(Key& k) { auto& [idx, gen] = ...
  function set_index (line 77) | constexpr void set_index(Key& k, Integral value) { using std::get; get<0...
  function increment_generation (line 78) | static constexpr void increment_generation(Key& k) { using std::get; ++g...
  function reference (line 116) | constexpr reference at(const key_type& key) {
  function const_reference (line 123) | constexpr const_reference at(const key_type& key) const {
  function reference (line 135) | constexpr reference operator[](const key_type& key)              { retur...
  function const_reference (line 136) | constexpr const_reference operator[](const key_type& key) const  { retur...
  function iterator (line 142) | constexpr iterator find(const key_type& key) {
  function const_iterator (line 154) | constexpr const_iterator find(const key_type& key) const {
  function iterator (line 170) | constexpr iterator find_unchecked(const key_type& key) {
  function const_iterator (line 175) | constexpr const_iterator find_unchecked(const key_type& key) const {
  function iterator (line 183) | constexpr iterator begin()                         { return values_.begi...
  function iterator (line 184) | constexpr iterator end()                           { return values_.end(...
  function reverse_iterator (line 189) | constexpr reverse_iterator rbegin()                { return values_.rbeg...
  function reverse_iterator (line 190) | constexpr reverse_iterator rend()                  { return values_.rend...
  function reserve (line 206) | constexpr void reserve(size_type n) {
  function reserve_slots (line 221) | constexpr void reserve_slots(size_type n) {
  function key_type (line 241) | constexpr key_type insert(const mapped_type& value)   { return this->emp...
  function key_type (line 242) | constexpr key_type insert(mapped_type&& value)        { return this->emp...
  function iterator (line 269) | constexpr iterator erase(iterator pos) { return this->erase(const_iterat...
  function iterator (line 270) | constexpr iterator erase(iterator first, iterator last) { return this->e...
  function iterator (line 271) | constexpr iterator erase(const_iterator pos) {
  function iterator (line 275) | constexpr iterator erase(const_iterator first, const_iterator last) {
  function size_type (line 286) | constexpr size_type erase(const key_type& key) {
  function clear (line 300) | constexpr void clear() {
  function swap (line 310) | constexpr void swap(slot_map& rhs) {
  function Container (line 323) | constexpr const Container<mapped_type>& c() const& noexcept { return val...
  function Container (line 325) | constexpr const Container<mapped_type>&& c() const&& noexcept { return s...
  function iterator (line 333) | constexpr iterator erase_slot_iter(slot_iterator slot_iter) {
  function key_index_type (line 363) | key_index_type next_available_slot_index_{}
  function key_index_type (line 364) | key_index_type last_available_slot_index_{}

FILE: SG14_test/SG14_test.h
  function namespace (line 6) | namespace sg14_test

FILE: SG14_test/flat_map_test.cpp
  type AmbiguousEraseWidget (line 15) | struct AmbiguousEraseWidget {
    method AmbiguousEraseWidget (line 16) | explicit AmbiguousEraseWidget(const char *s) : s_(s) {}
    method AmbiguousEraseWidget (line 19) | AmbiguousEraseWidget(T) : s_("notfound") {}
  type InstrumentedWidget (line 29) | struct InstrumentedWidget {
    method InstrumentedWidget (line 31) | InstrumentedWidget() = delete;
    method InstrumentedWidget (line 32) | InstrumentedWidget(const char *s) : s_(s) {}
    method InstrumentedWidget (line 33) | InstrumentedWidget(InstrumentedWidget&& o) noexcept : s_(std::move(o.s...
    method InstrumentedWidget (line 34) | InstrumentedWidget(const InstrumentedWidget& o) : s_(o.s_) { copy_ctor...
    method InstrumentedWidget (line 35) | InstrumentedWidget& operator=(InstrumentedWidget&& o) noexcept {
    method InstrumentedWidget (line 40) | InstrumentedWidget& operator=(const InstrumentedWidget&) = default;
    method str (line 45) | std::string str() const { return s_; }
  function AmbiguousEraseTest (line 54) | static void AmbiguousEraseTest()
  function ExtractDoesntSwapTest (line 69) | static void ExtractDoesntSwapTest()
  function MoveOperationsPilferOwnership (line 93) | static void MoveOperationsPilferOwnership()
  function SortedUniqueConstructionTest (line 130) | static void SortedUniqueConstructionTest()
  function TryEmplaceTest (line 147) | static void TryEmplaceTest()
  function VectorBoolSanityTest (line 205) | static void VectorBoolSanityTest()
  function free_function_less (line 242) | static bool free_function_less(const int& a, const int& b) {
  function flatmap_is_ctadable_from (line 247) | static auto flatmap_is_ctadable_from(int, Args&&... args)
  function flatmap_is_ctadable_from (line 254) | static auto flatmap_is_ctadable_from(long, Args&&...)
  function DeductionGuideTests (line 262) | static void DeductionGuideTests()
  function ConstructionTest (line 498) | static void ConstructionTest()
  function InsertOrAssignTest (line 566) | static void InsertOrAssignTest()
  function SpecialMemberTest (line 603) | static void SpecialMemberTest()
  function ComparisonOperatorsTest (line 621) | static void ComparisonOperatorsTest()
  function SearchTest (line 659) | static void SearchTest()
  function main (line 758) | int main()

FILE: SG14_test/flat_set_test.cpp
  type AmbiguousEraseWidget (line 14) | struct AmbiguousEraseWidget {
    method AmbiguousEraseWidget (line 22) | explicit AmbiguousEraseWidget(const char *s) : s_(s) {}
    method AmbiguousEraseWidget (line 23) | AmbiguousEraseWidget(iterator) : s_("notfound") {}
    method AmbiguousEraseWidget (line 24) | AmbiguousEraseWidget(const_iterator) : s_("notfound") {}
  type InstrumentedWidget (line 30) | struct InstrumentedWidget {
    method InstrumentedWidget (line 32) | InstrumentedWidget(const char *s) : s_(s) {}
    method InstrumentedWidget (line 33) | InstrumentedWidget(InstrumentedWidget&& o) : s_(std::move(o.s_)) { mov...
    method InstrumentedWidget (line 34) | InstrumentedWidget(const InstrumentedWidget& o) : s_(o.s_) { copy_ctor...
    method InstrumentedWidget (line 35) | InstrumentedWidget& operator=(InstrumentedWidget&&) = default;
    method InstrumentedWidget (line 36) | InstrumentedWidget& operator=(const InstrumentedWidget&) = default;
  function AmbiguousEraseTest (line 48) | static void AmbiguousEraseTest()
  function ExtractDoesntSwapTest (line 65) | static void ExtractDoesntSwapTest()
  type ThrowingSwapException (line 89) | struct ThrowingSwapException {}
  type ComparatorWithThrowingSwap (line 91) | struct ComparatorWithThrowingSwap {
    method ComparatorWithThrowingSwap (line 94) | ComparatorWithThrowingSwap(std::function<bool(int, int)> f) : cmp_(f) {}
    method swap (line 95) | void swap(ComparatorWithThrowingSwap& a, ComparatorWithThrowingSwap& b) {
  function ThrowingSwapDoesntBreakInvariants (line 106) | static void ThrowingSwapDoesntBreakInvariants()
  function VectorBoolSanityTest (line 129) | static void VectorBoolSanityTest()
  type VectorBoolEvilComparator (line 152) | struct VectorBoolEvilComparator {
  function VectorBoolEvilComparatorTest (line 163) | static void VectorBoolEvilComparatorTest()
  function MoveOperationsPilferOwnership (line 176) | static void MoveOperationsPilferOwnership()
  function ConstructionTest (line 214) | static void ConstructionTest()
  function SpecialMemberTest (line 268) | static void SpecialMemberTest()
  function main (line 330) | int main()

FILE: SG14_test/inplace_function_test.cpp
  type Functor (line 18) | struct Functor {
    method Functor (line 19) | Functor() {}
    method Functor (line 20) | Functor(const Functor&) { copied += 1; }
    method Functor (line 21) | Functor(Functor&&) noexcept { moved += 1; }
  type ConstFunctor (line 25) | struct ConstFunctor {
    method ConstFunctor (line 26) | ConstFunctor() {}
    method ConstFunctor (line 27) | ConstFunctor(const ConstFunctor&) { copied += 1; }
    method ConstFunctor (line 28) | ConstFunctor(ConstFunctor&&) noexcept { moved += 1; }
  function Foo (line 32) | void Foo(int i)
  function GlobalFunction (line 44) | static double GlobalFunction(const std::string& s, int i)
  function FunctionPointer (line 51) | static void FunctionPointer()
  function Lambda (line 68) | static void Lambda()
  function Bind (line 83) | static void Bind()
  type AnotherFunctor (line 98) | struct AnotherFunctor
    method AnotherFunctor (line 105) | AnotherFunctor() { mConstructorCalls++; }
    method AnotherFunctor (line 106) | AnotherFunctor(AnotherFunctor&&) noexcept { mConstructorCalls++; }
    method AnotherFunctor (line 107) | AnotherFunctor(const AnotherFunctor&) { mConstructorCalls++; }
  function FunctorDestruction (line 114) | static void FunctorDestruction()
  function Swapping (line 143) | static void Swapping()
  function Copying (line 169) | static void Copying()
  function ContainingStdFunction (line 193) | static void ContainingStdFunction()
  function SimilarTypeCopy (line 213) | static void SimilarTypeCopy()
  function AssignmentDifferentFunctor (line 239) | static void AssignmentDifferentFunctor()
  type ThrowingFunctor (line 264) | struct ThrowingFunctor {
    method reset (line 269) | static void reset(int k) {
    method check_countdown (line 275) | static void check_countdown() {
    method ThrowingFunctor (line 280) | ThrowingFunctor() { check_countdown(); ++constructed; }
    method ThrowingFunctor (line 281) | ThrowingFunctor(const ThrowingFunctor&) {
    method ThrowingFunctor (line 285) | ThrowingFunctor(ThrowingFunctor&&) noexcept { ++constructed; }
    method ThrowingFunctor (line 286) | ThrowingFunctor& operator=(const ThrowingFunctor&) = delete;
    method ThrowingFunctor (line 287) | ThrowingFunctor& operator=(ThrowingFunctor&&) = delete;
  function test_exception_safety (line 296) | static void test_exception_safety()
  function expected_alignment_for_capacity (line 338) | constexpr size_t expected_alignment_for_capacity()
  function test_struct_layout (line 349) | static void test_struct_layout()
  function test_nullptr (line 360) | static void test_nullptr()
  type oon_functor (line 380) | struct oon_functor {
    method oon_functor (line 383) | oon_functor(int i) : dummy(i) {}
  function test_overloaded_operator_new (line 392) | static void test_overloaded_operator_new()
  function test_move_construction_is_noexcept (line 403) | static void test_move_construction_is_noexcept()
  function test_move_construction_from_smaller_buffer_is_noexcept (line 415) | static void test_move_construction_from_smaller_buffer_is_noexcept()
  type test_bug_32072_C (line 426) | struct test_bug_32072_C
  type test_bug_32072 (line 427) | struct test_bug_32072 {
  function RvalueRefParameter (line 433) | static void RvalueRefParameter()
  function test_is_convertible (line 447) | static void test_is_convertible()
  function test_convertibility_with_qualified_call_operators (line 455) | static void test_convertibility_with_qualified_call_operators()
  function test_convertibility_with_lambdas (line 475) | static void test_convertibility_with_lambdas()
  type InstrumentedCopyConstructor (line 519) | struct InstrumentedCopyConstructor {
    method InstrumentedCopyConstructor (line 522) | InstrumentedCopyConstructor() = default;
    method InstrumentedCopyConstructor (line 523) | InstrumentedCopyConstructor(const InstrumentedCopyConstructor&) {
    method InstrumentedCopyConstructor (line 526) | InstrumentedCopyConstructor(InstrumentedCopyConstructor&&) noexcept {
  function test_return_by_move (line 534) | static void test_return_by_move()
  function test_is_invocable (line 560) | static void test_is_invocable()
  function overloaded_function (line 600) | static int overloaded_function(stdext::inplace_function<int()>) { return...
  function overloaded_function (line 601) | static int overloaded_function(stdext::inplace_function<int(int)>) { ret...
  function test_overloading_on_arity (line 602) | static void test_overloading_on_arity()
  function overloaded_function2 (line 608) | static int overloaded_function2(stdext::inplace_function<int(int)>) { re...
  function overloaded_function2 (line 609) | static int overloaded_function2(stdext::inplace_function<int(int*)>) { r...
  function test_overloading_on_parameter_type (line 610) | static void test_overloading_on_parameter_type()
  function overloaded_function3 (line 616) | static int overloaded_function3(stdext::inplace_function<int(int)>) { re...
  function overloaded_function3 (line 617) | static int overloaded_function3(stdext::inplace_function<int*(int)>) { r...
  function test_overloading_on_return_type (line 618) | static void test_overloading_on_return_type()
  function main (line 719) | int main()

FILE: SG14_test/main.cpp
  function main (line 9) | int main(int, char *[])

FILE: SG14_test/plf_colony_test.cpp
  function failpass (line 89) | inline void failpass(const char* test_type, bool condition)
  function message (line 99) | void message(const char *)
  function title1 (line 104) | void title1(const char*)
  function title2 (line 108) | void title2(const char*)
  type perfect_forwarding_test (line 115) | struct perfect_forwarding_test
    method perfect_forwarding_test (line 119) | perfect_forwarding_test(int&& /*perfect1*/, int& perfect2)
    method perfect_forwarding_test (line 126) | perfect_forwarding_test(T&& /*imperfect1*/, U&& /*imperfect2*/)
  type small_struct (line 133) | struct small_struct
    method small_struct (line 142) | small_struct(const int num) : number(num) {}
  class non_copyable_type (line 147) | class non_copyable_type
    method non_copyable_type (line 154) | non_copyable_type(int a) : i(a) {}
  type small_struct_non_trivial (line 164) | struct small_struct_non_trivial
    method small_struct_non_trivial (line 173) | small_struct_non_trivial(const int num) : number(num) {}
  type plf (line 182) | namespace plf
    function rand (line 189) | unsigned int rand()
  type sg14_test (line 205) | namespace sg14_test
    function plf_colony_test (line 208) | void plf_colony_test()
  function main (line 2021) | int main()

FILE: SG14_test/ring_test.cpp
  function basic_test (line 10) | static void basic_test()
  function filter_test (line 52) | static void filter_test()
  function iterator_regression_test (line 72) | static void iterator_regression_test()
  function copy_popper_test (line 110) | static void copy_popper_test()
  function reverse_iterator_test (line 123) | static void reverse_iterator_test()
  function main (line 178) | int main()

FILE: SG14_test/slot_map_test.cpp
  type TestKey (line 15) | namespace TestKey {
    type key_16_8_t (line 16) | struct key_16_8_t {
    type key_11_5_t (line 20) | struct key_11_5_t {  // C++17 only
    function get (line 26) | auto get(const K& k) { return get(k, std::integral_constant<int, I>{}); }
  type TestContainer (line 36) | namespace TestContainer {
    type Vector (line 39) | struct Vector {
      method Vector (line 51) | Vector() = default;
      method Vector (line 53) | Vector(const Vector& rhs) { *this = rhs; }
      method Vector (line 54) | Vector(Vector&& rhs) { *this = std::move(rhs); }
      method size (line 65) | unsigned size() const { return static_cast<unsigned int>(size_); }
      method T (line 66) | T *begin() { return data_.get(); }
      method T (line 67) | T *end() { return data_.get() + size_; }
      method T (line 68) | const T *begin() const { return data_.get(); }
      method T (line 69) | const T *end() const { return data_.get() + size_; }
      method pop_back (line 70) | void pop_back() {
      method emplace_back (line 77) | void emplace_back(U t) {
      method clear (line 84) | void clear() {
      method swap (line 88) | void swap(Vector& a, Vector& b) {
  type Monad (line 100) | struct Monad {
    method T (line 102) | static T value_of(const T& i) { return i; }
    method T (line 103) | static T from_value(const U& v) { return static_cast<T>(v); }
  type Monad<std::unique_ptr<T_>> (line 106) | struct Monad<std::unique_ptr<T_>> {
    method T_ (line 108) | static T_ value_of(const T& ptr) { return *ptr; }
    method T (line 109) | static T from_value(const U& v) { return std::make_unique<T_>(v); }
  function print_slot_map (line 113) | void print_slot_map(const stdext::slot_map<T, Key, Container>& sm)
  function U (line 133) | static U move_if_necessary(T& value) { return static_cast<U>(value); }
  function BasicTests (line 136) | static void BasicTests(T t1, T t2)
  function FullContainerStressTest (line 172) | static void FullContainerStressTest(TGen t)
  function InsertEraseStressTest (line 198) | static void InsertEraseStressTest(TGen t)
  function EraseInLoopTest (line 228) | static void EraseInLoopTest()
  function EraseRangeTest (line 253) | static void EraseRangeTest()
  function ReserveTest (line 290) | static void ReserveTest()
  function VerifyCapacityExists (line 313) | static void VerifyCapacityExists(bool expected)
  function VerifyCapacityExists (line 326) | void VerifyCapacityExists(Bool expected)
  function TypedefTests (line 334) | static void TypedefTests()
  function BoundsCheckingTest (line 431) | void BoundsCheckingTest()
  function GenerationsDontSkipTest (line 449) | static void GenerationsDontSkipTest()
  function IndexesAreUsedEvenlyTest (line 476) | static void IndexesAreUsedEvenlyTest()
  function main (line 655) | int main()

FILE: SG14_test/uninitialized_test.cpp
  type lifetest (line 13) | struct lifetest
    method lifetest (line 18) | lifetest()
    method lifetest (line 22) | lifetest(lifetest&& /*in*/) noexcept
    method reset (line 30) | static void reset()
    method test (line 39) | static void test(uint64_t, uint64_t, uint64_t)
    method test (line 44) | static void test(uint64_t inconstruct, uint64_t indestruct, uint64_t i...
  function value (line 57) | void value()
  function def (line 77) | void def()
  function main (line 108) | int main()

FILE: SG14_test/unstable_remove_test.cpp
  type foo (line 10) | struct foo
    method foo (line 13) | static foo make()
  function main (line 94) | int main()
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (865K chars).
[
  {
    "path": ".gitignore",
    "chars": 1108,
    "preview": "# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$"
  },
  {
    "path": ".travis.yml",
    "chars": 3067,
    "preview": "language: cpp\n\nbefore_install:\n  - if [[ \"$TRAVIS_OS_NAME\" == \"osx\" ]]; then\n      brew update;\n      brew upgrade cmake"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 3618,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(sg14 CXX)\n\nfind_package(Threads REQUIRED)\n\n# Prefer C++17, downgrade if it "
  },
  {
    "path": "Docs/Proposals/D0447R16 - Introduction of hive to the Standard Library.html",
    "chars": 137902,
    "preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n<html>\r\n<head>\r\n  <meta name=\"viewport\"\r\n  content=\"widt"
  },
  {
    "path": "Docs/Proposals/Fixed_Point_Library_Proposal.md",
    "chars": 41396,
    "preview": "**Document number**: LEWG, EWG, SG14, SG6: P0037R0  \n**Date**: 2015-09-28  \n**Project**: Programming Language C++, Libra"
  },
  {
    "path": "Docs/Proposals/p0037.html",
    "chars": 122725,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n      <meta charset=\"utf-8\" />\n      <title>Fixed_Point_Library_Proposal</title>\n      <"
  },
  {
    "path": "Docs/Proposals/rawstorage.html",
    "chars": 3191,
    "preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML><HEAD><TITLE>Extending raw_storage_iterator</TITLE>"
  },
  {
    "path": "Docs/Proposals/ring_proposal_r5.tex",
    "chars": 16232,
    "preview": "Document number: P0059R4\nDate: 2017-05-15\nReply-to: Guy Davidson, guy@hatcat.com\nReply-to: Arthur O’Dwyer, arthur.j.odwy"
  },
  {
    "path": "Docs/Proposals/uninitialized.html",
    "chars": 5262,
    "preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML><HEAD><TITLE>Extending memory management tools</TIT"
  },
  {
    "path": "Docs/Proposals/unstable_remove.html",
    "chars": 4706,
    "preview": "LEWG, SG14: D0041R0<br>\n11-9-2015<br>\nBrent Friedman<br>\nfourthgeek@gmail.com\n<h1>Unstable remove algorithms</h1>\n<h2>I."
  },
  {
    "path": "Docs/fixed_point.md",
    "chars": 582,
    "preview": "# Fixed-Point Real Numbers\n\n[John McFarlane](https://groups.google.com/a/isocpp.org/forum/#!profile/sg14/APn2wQdoie4ys78"
  },
  {
    "path": "Docs/plf_licensing.txt",
    "chars": 909,
    "preview": "All plf:: modules are provided under a zlib license:\n\nThis software is provided 'as-is', without any express or implied\n"
  },
  {
    "path": "README.md",
    "chars": 876,
    "preview": "# SG14\n[![Build Status](https://travis-ci.org/WG21-SG14/SG14.svg?branch=master)](https://travis-ci.org/WG21-SG14/SG14)\n\n"
  },
  {
    "path": "SG14/algorithm_ext.h",
    "chars": 3627,
    "preview": "#pragma once\n\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <utility>\n\nnamespace stdext\n{\n\ttemplat"
  },
  {
    "path": "SG14/flat_map.h",
    "chars": 56815,
    "preview": "/*\n * Boost Software License - Version 1.0 - August 17th, 2003\n *\n * Permission is hereby granted, free of charge, to an"
  },
  {
    "path": "SG14/flat_set.h",
    "chars": 30316,
    "preview": "/*\n * Boost Software License - Version 1.0 - August 17th, 2003\n *\n * Permission is hereby granted, free of charge, to an"
  },
  {
    "path": "SG14/inplace_function.h",
    "chars": 12289,
    "preview": "/*\n * Boost Software License - Version 1.0 - August 17th, 2003\n *\n * Permission is hereby granted, free of charge, to an"
  },
  {
    "path": "SG14/plf_colony.h",
    "chars": 180417,
    "preview": "// Copyright (c) 2021, Matthew Bentley (mattreecebentley@gmail.com) www.plflib.org\r\n\r\n// zLib license (https://www.zlib."
  },
  {
    "path": "SG14/ring.h",
    "chars": 15431,
    "preview": "#pragma once\n\n#include <cstddef>\n#include <type_traits>\n#include <iterator>\n#include <cassert>\n\nnamespace sg14\n{\n\ttempla"
  },
  {
    "path": "SG14/slot_map.h",
    "chars": 17225,
    "preview": "/*\n * Boost Software License - Version 1.0 - August 17th, 2003\n *\n * Permission is hereby granted, free of charge, to an"
  },
  {
    "path": "SG14_test/SG14_test.h",
    "chars": 349,
    "preview": "#if !defined SG14_TEST_2015_06_11_18_24\n#define SG14_TEST_2015_06_11_18_24\n\n#undef NDEBUG\n\nnamespace sg14_test\n{\n    voi"
  },
  {
    "path": "SG14_test/flat_map_test.cpp",
    "chars": 29993,
    "preview": "#include \"SG14_test.h\"\n#include \"flat_map.h\"\n#include <assert.h>\n#include <deque>\n#include <functional>\n#include <list>\n"
  },
  {
    "path": "SG14_test/flat_set_test.cpp",
    "chars": 10770,
    "preview": "#include \"SG14_test.h\"\n#include \"flat_set.h\"\n#include <assert.h>\n#include <deque>\n#include <functional>\n#if __has_includ"
  },
  {
    "path": "SG14_test/inplace_function_test.cpp",
    "chars": 26720,
    "preview": "#include \"SG14_test.h\"\n#include \"inplace_function.h\"\n#include <cassert>\n#include <memory>\n#include <string>\n#include <ty"
  },
  {
    "path": "SG14_test/main.cpp",
    "chars": 444,
    "preview": "#if defined(_MSC_VER)\n#include <SDKDDKVer.h>\n#endif\n\n#include <stdio.h>\n\n#include \"SG14_test.h\"\n\nint main(int, char *[])"
  },
  {
    "path": "SG14_test/plf_colony_test.cpp",
    "chars": 50457,
    "preview": "#define PLF_COLONY_TEST_DEBUG\r\n\r\n#if defined(_MSC_VER)\r\n\t#if _MSC_VER >= 1600\r\n\t\t#define PLF_TEST_MOVE_SEMANTICS_SUPPORT"
  },
  {
    "path": "SG14_test/ring_test.cpp",
    "chars": 5538,
    "preview": "#include \"SG14_test.h\"\n\n#include \"ring.h\"\n\n#include <array>\n#include <numeric>\n#include <string>\n#include <vector>\n\nstat"
  },
  {
    "path": "SG14_test/slot_map_test.cpp",
    "chars": 27001,
    "preview": "#include \"SG14_test.h\"\n#include \"slot_map.h\"\n#include <assert.h>\n#include <inttypes.h>\n#include <algorithm>\n#include <de"
  },
  {
    "path": "SG14_test/uninitialized_test.cpp",
    "chars": 2051,
    "preview": "#include \"SG14_test.h\"\n#include <vector>\n#include <array>\n#include <ctime>\n#include <iostream>\n#include <algorithm>\n#inc"
  },
  {
    "path": "SG14_test/unstable_remove_test.cpp",
    "chars": 2465,
    "preview": "#include \"SG14_test.h\"\n#include \"algorithm_ext.h\"\n#include <algorithm>\n#include <array>\n#include <chrono>\n#include <iost"
  }
]

About this extraction

This page contains the full source code of the WG21-SG14/SG14 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (794.4 KB), approximately 205.9k tokens, and a symbol index with 371 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!