C++ abstractions on plain-old-data
 
 
 
Go to file
Justin Collier ba2bf92de6
minor fixes
2023-09-18 16:43:21 -07:00
src minor fixes 2023-09-18 16:43:21 -07:00
.clang-format build system adjustments, spans and arithmetic improvements, nttp removal, add k 2023-09-03 08:35:37 -07:00
.clangd re-organized library and added single-header and README generator 2023-08-31 10:04:02 -07:00
.gitignore re-organized library and added single-header and README generator 2023-08-31 10:04:02 -07:00
LICENSE fixed license, cleaned up readme for now 2023-08-01 15:19:10 -07:00
Makefile minor fixes 2023-09-18 16:43:21 -07:00
README.md minor fixes 2023-09-18 16:43:21 -07:00
pod.h minor fixes 2023-09-18 16:43:21 -07:00

README.md

                             ..
                           dF                  .uef^"
 .d``                u.   '88bu.             :d88E
 @8Ne.   .u    ...ue888b  '*88888bu          `888E
 %8888:u@88N   888R Y888r   ^"*8888N          888E .z8k
  `888I  888.  888R I888>  beWE "888L         888E~?888L
   888I  888I  888R I888>  888E  888E         888E  888E
   888I  888I  888R I888>  888E  888E         888E  888E
 uW888L  888' u8888cJ888   888E  888F    .    888E  888E
'*88888Nu88P   "*888*P"   .888N..888   .@8c   888E  888E
~ '88888F`       'Y"       `"888*""   '%888" m888N= 888>
   888 ^                      ""        ^*    `Y"   888
   *8E                                             J88"
   '8>     C++ abstractions on plain-old-data      @%
    "                                            :"
 Copyright (C) 2023 Justin Collier <m@jpcx.dev>
 License  AGPLv3+:  GNU  Affero GPL  version 3 or later
 <https://gnu.org/licenses/agpl-3.0.html>. This is free
 software:  you are free to change and redistribute it.
 There is NO WARRANTY,  to the extent permitted by law.

About

pod.h is a general-purpose C++20 utility library focused on plain-old-data (POD) aggregates with the goal of facilitating compiletime computation and testing, leveraging compiler optimizations, and simplifying value semantics.

Officially, PODs must have trivial and standard layout traits, meaning that they must be simple types with a predictable byte layout. POD classes cannot define custom constructors, destructors, assignment operators, and virtual functions, and their data members must not be references, must only be defined on one class in the inheritance chain, and must all have the same visibility.

For compatibilty reasons, pod.h extends this definition with its pod concept that describes trivial, standard layout, default constructible, non-array aggregates and scalars with a determinate byte layout. This additional set of constraints significantly reduces the set of types available, but allows for greater flexibility with respect to byte manipulation and generic usability.

// describes a strictly-constrained subset of plain-old-data (POD) types:
//
// - trivial (limited inheritance, no custom ctor/dtor/assn, no references)
// - standard layout                         (no mixed-access data members)
// - trivially default constructible                (no const data members)
// - non-const, non-volatile, non-reference           (no cvref qualifiers)
// - non-array aggregate or scalar           (fully public, singular value)
// - determinate byte layout             (subobject constraints; see below)
//
// "determinate byte layout" means that the type can be serialized and
// deserialized using std::bit_cast at compiletime, and that the produced
// bytes are fully accessible. this prohibits pointers, unions, implicit
// padding bytes, bools, empty structs, and any other indeterminate bytes.
//
// as these constraits can be quite restrictive, pd::tuple and
// pd::object are provided for easier generic data storage that
// inserts explicit padding bytes where the compiler would. empty
// structs can inherit from pd::empty to avoid needing to declare
// an explicit padding byte.
template <class T>
concept pod =
	// core POD traits
	std::is_trivial_v<T> && std::is_standard_layout_v<T> &&

	// generic T{} usability
	std::is_trivially_default_constructible_v<T> &&

	// no cvref qualifiers
	std::same_as<T, std::remove_cvref_t<T>> &&

	// single, fully public value
	!std::is_array_v<T> &&
	(std::is_aggregate_v<T> || std::is_scalar_v<T>)&&

	// runtime bit cast tests
	requires(T a, std::array<std::uint8_t, sizeof(T)> b) {
		// bytewise serializable and deserializable

		{ std::bit_cast<T>(b) };
		{ std::bit_cast<std::array<std::uint8_t, sizeof(T)>>(a) };
	} &&

	// compiletime bit cast tests using test and expect.
	//
	// at present, separate runtime constraints (above) are
	// still required to ensure that the concept evaluates.
	pd::test([] {
		using bytes = std::array<std::uint8_t, sizeof(T)>;

		// bytewise serializable and deserializable
		T     from_bytes = std::bit_cast<T>(bytes{});
		bytes to_bytes   = std::bit_cast<bytes>(T{});

		// fully determinate bytes
		std::ranges::fill(to_bytes, 0xFFu);
		from_bytes = std::bit_cast<T>(to_bytes);
		pd::expect(std::ranges::equal(to_bytes, std::bit_cast<bytes>(from_bytes)));
	});

These constraints place heavy restrictions on the types allowed, but help ensure several useful properites. Types that meet these requirements are:

  • inexpensive to copy and initialize (using builtin-memcpy)
  • usable in almost any context, including as NTTPs (pending full compiler support)
  • predictable w.r.t. construction, control flow, and lifetime
  • readily comparable (given a defaulted operator== or operator<=>)
  • simple to construct using aggregate or designated initialization
  • bytewise serializable and deserializable at compiletime

Notably, these restrictions make built-in RAII and move semantics impossible, which is one of the limitations that this library attempts to address.

Regardless, such types have simpler value semantics: forward and move become unnecessary and arguments can be accepted by const-lvref or copy-value without worrying about resource management. Both the programmer and compiler can be sure that copies result in an identical object and have no side-effects.

The entire library is centered around this concept and addresses these constraints with pod-focused utilities such as fully generic, pod data stores (pd::tuple, pd::object) and an ID-based, self-contained, relocatable allocator (pd::ialloc).

All library types are pods, except implementation and pd::unconstrained types.

Usage

pod.h is structured as a single-header include, generated by the Makefile using the headers in the src directory. Copy ./pod.h into your project directory or system include directory to use.

The library makes extensive use of C++20 features and requires #pragma pack support for its generic data stores. My development environment uses gcc 12 and clang 16 with the GNU stdlib on Linux.

Examples

Here are a few examples to showcase some of the unique features of this library, formatted as pd::test expressions that are executed at compiletime:

static_assert(pd::test([] {
	using namespace pd::aliases; // i32, u8, etc.

	// pd::object is a generic data store specialized by pd::kt keyed types
	auto v0 = pd::obj<pd::kt<"foo", i32>, pd::kt<"bar", u16>>();
	auto v1 = pd::obj<pd::kt<"qux", i8>, pd::kt<"quz", u64>>();

	get<"foo">(v0) = 82;
	get<"bar">(v0) = 18;
	get<0>(v1)     = -15;
	get<1>(v1)     = 888;

	// the pd::obj convenience function simplifies object construction
	auto v2   = pd::obj<"quux", "quuz">(v0, v1);
	auto vobj = pd::obj<"baz">(3.14) + v2 + pd::obj<"bang">(pd::to<u8>(42));

	// a structured representation of the vobj byte layout
	struct v {
		f64 baz;
		struct {
			i32 foo;
			u16 bar;
			u8  _[2]; // explicit padding is needed for full
			          // bit_cast compatibility, which is one of
			          // the major reasons why pd::object exists
		} quux;
		struct {
			i8  qux;
			u8  _[7];
			u64 quz;
		} quuz;
		u8 bang;
		u8 _[7];
	};

	// pd::objects have the same byte layout as struct with the same member types,
	// and can be serialized and deserialized (at compiletime) using std::bit_cast.
	// pod::to is a general cast function that falls back on std::bit_cast.
	auto vdata = to<v>(vobj);
	pd::expect(vdata.baz == 3.14);
	pd::expect(vdata.quux.foo == 82);
	pd::expect(vdata.quux.bar == 18);
	pd::expect(vdata.quuz.qux == -15);
	pd::expect(vdata.quuz.quz == 888);
	pd::expect(vdata.bang == 42);
}));

static_assert(pd::test([] {
	using namespace pd::aliases;

	// a fixed-size allocator that uses typed IDs instead of pointers
	pd::ialloc<i32, 24> m{};

	// an ienv is needed for dyn objects, like vector
	pd::unconstrained::ienv env{m};

	// a resizable array that needs ialloc to operate
	pd::vector<i32> v{};

	// creating a temporary (non-pod) range with reference to storage
	auto r = v(env);

	// standard vector behavior
	pd::expect(r.resize(5));
	std::ranges::copy(pd::arr(1, 2, 3, 4, 5), r.begin());
	r.pop_back();
	pd::expect(r.push_back(8080));
	std::ranges::copy(pd::arr(4, 3, 2, 1), std::back_inserter(r));
	pd::expect(std::ranges::equal(r, pd::arr(1, 2, 3, 4, 8080, 4, 3, 2, 1)));

	// frees memory allocated by v in m
	r.clear();
}));

// example pod types with dynamic data

struct sample : pd::vector<u16> {};

struct survey_ctx {
	using base_type = object<kt<"samples", pd::vector<sample>>>;

	template <of_ienv<sample, u16> Env>
	struct type {
		constexpr type(base_type &self, Env &env) noexcept
		    : _self{self},
		      _env{env} {
		}

		constexpr u16
		avg() const noexcept {
			usz sum = 0;
			usz ct  = 0;
			for (auto &&samp : samples()) {
				ct += samp.size();
				for (auto &&pt : samp(_env))
					sum += pt;
			}
			return sum / ct;
		}

		constexpr auto
		samples() noexcept {
			return get<"samples">(_self)(_env);
		}

		constexpr auto
		samples() const noexcept {
			return get<"samples">(_self)(_env);
		}

	   private:
		base_type &_self;
		Env       &_env;
	};
};

struct survey : dyn<survey_ctx> {
	constexpr pd::vector<sample> &
	samples() noexcept {
		return get<"samples">(*this);
	}

	constexpr pd::vector<sample> const &
	samples() const noexcept {
		return get<"samples">(*this);
	}
};

static_assert(pd::test([] {
	using namespace pd::aliases;

	pd::expect(pd::pod<sample>);
	pd::expect(pd::pod<survey>);

	// prepare memory needed
	pd::ialloc<u16, 8>      sample_m{};
	pd::ialloc<sample, 16>  survey_m{};
	pd::unconstrained::ienv env{sample_m, survey_m};

	// create pod dyn vector
	survey survey_info{};

	// non-pod, temporary wrapper around allocators
	auto ctx = survey_info(env);

	// prepare some data
	sample samp0{};
	pd::expect(samp0(env).push_back(42));
	pd::expect(samp0(env).push_back(64));
	pd::expect(samp0(env).push_back(96));

	// in this case, ctx samples returns a dyn ctx
	pd::expect(ctx.samples().push_back(samp0));

	// accessing data
	pd::expect(ctx.avg() == 67);

	// the entire memory space can be serialized, if need be
	pd::array<u8, sizeof sample_m + sizeof survey_m> ser{};
	{
		auto p0 = to<pd::array<u8, sizeof sample_m>>(sample_m);
		auto p1 = to<pd::array<u8, sizeof survey_m>>(survey_m);
		auto o  = std::ranges::copy(p0, ser.begin()).out;
		std::ranges::copy(p1, o);
	}
}));

Testing

All features are tested by inline static assertions, which means that if you can compile your project after including the file, the library has been tested. One of the main goals of this library is to make this kind of testing more practical and applicable to various types of projects. Run make test (on Linux) to verify, which attempts a build with both gcc and clang.

Synopsis

basic.utils

pd::expect: an assertion helper for pd::test (L50)

// an assertion helper for pd::test.
//
// essentially, this function throws an exception if b is false,
// which makes it a genuine assertion function. however, its main
// purpose is to conditionally break any static expression that
// calls it. this means that non-static conditions can be tested
// within a pd::test lambda body, causing the concept to fail.
//
// example:
//
//   static_assert(test<[] {
//      i32 v = 0;
//      expect(v++ == 0);
//      expect(v++ == 0); // causes test to fail
//   });
inline constexpr void
expect(bool b) {
	if (!b)
		throw "FAIL";
};

pd::test: a utility function for static testing (L98)

// a utility function for static testing.
//
// essentially, any n-ary (sizeof...(Args)) void lambda passed to this
// function will be evaluated at compiletime, and the function will return
// true if the lambda is a static expression, else false. as such, pd::expect
// is provided to conditionally break any static expression that calls it.
//
// there are two main ways of using this function, either within a concept
// to constrain compiletime behavior (see pd::pod), or within a static
// assertion for testing purposes.
//
// note: there are still compiler discrepancies regarding the behavior
//       of this function when used within a concept. make sure that you
//       constrain basic (runtime) behavior before use, or the concept
//       may fail to evaluate at all. the same issue happens when using
//       directly in a non-concept requires statement, so (for now), ensure
//       that your compiletime constraints match the following split format.
//
// example:
//
//     // concept example
//     template <class T, class V>
//     concept has_compiletime_value = //
//         requires(T const &v) {
//             // constrains runtime behavior
//             { v.value() } -> std::same_as<V>;
//         } && test<T>([](T const &v) { // can accept any copy-value or lvref
//             // ensures value() can execute at compiletime
//             v.value();
//         });
//
//     // static testing example
//     static_assert(test([] {
//         struct foo {
//             static constexpr int
//             value() {
//                 return 42;
//             }
//         };
//
//         struct bar {
//             static int // <-- no constexpr
//             value() {
//                 return 64;
//             }
//         };
//
//         expect(has_compiletime_value<foo, int>);
//         expect(!has_compiletime_value<bar, int>);
//     }));
//
//     // another static test example
//     static_assert(test([] {
//         expect(!test([] {
//             int w = 0;
//             expect(w++ == 0);
//             expect(w++ == 0); // fails the test
//         }));
//     }));
template <class... Args, class Expr>
	requires _impl::_test::expr<Expr, Args...>
[[nodiscard]] inline constexpr bool
test(Expr) {
	return _impl::_test::static_expr<Expr, Args...>;
}

basic.traits

pd::are_common: checks if std::common_type_t exists (L274)

// describes a group of types that share a common type that they
// are all implicitly convertible to (i.e. std::common_type_t).
template <class... Ts>
concept are_common =
    (sizeof...(Ts) == 0) || requires { typename std::common_type_t<Ts...>; };

pd::is: shorthand for std::same_as (L299)

// shorthand for std::same_as
template <class T, class U>
concept is = std::same_as<T, U>;

pd::are_same: pd::is for a collection of types (L328)

// describes a group of types that are exactly the same.
template <class... Ts>
concept are_same = _impl::_are_same::test<Ts...>::value;

pd::pod: a more constrained plain-old-data concept (L369)

// this concept, and the library as a whole, depends on pragma pack support
static_assert(_impl::_pod::pragma_pack_supported);

// describes a strictly-constrained subset of plain-old-data (POD) types:
//
// - trivial (limited inheritance, no custom ctor/dtor/assn, no references)
// - standard layout                         (no mixed-access data members)
// - trivially default constructible                (no const data members)
// - non-const, non-volatile, non-reference           (no cvref qualifiers)
// - non-array aggregate or scalar           (fully public, singular value)
// - determinate byte layout             (subobject constraints; see below)
//
// "determinate byte layout" means that the type can be serialized and
// deserialized using std::bit_cast at compiletime, and that the produced
// bytes are fully accessible. this prohibits pointers, unions, implicit
// padding bytes, bools, empty structs, and any other indeterminate bytes.
//
// as these constraits can be quite restrictive, pd::tuple and
// pd::object are provided for easier generic data storage that
// inserts explicit padding bytes where the compiler would. empty
// structs can inherit from pd::empty to avoid needing to declare
// an explicit padding byte.
template <class T>
concept pod =
    // core POD traits
    std::is_trivial_v<T> && std::is_standard_layout_v<T> &&

    // generic T{} usability
    std::is_trivially_default_constructible_v<T> &&

    // no cvref qualifiers
    std::same_as<T, std::remove_cvref_t<T>> &&

    // single, fully public value
    !std::is_array_v<T> &&
    (std::is_aggregate_v<T> || std::is_scalar_v<T>)&&

    // runtime bit cast tests
    requires(T a, std::array<std::uint8_t, sizeof(T)> b) {
	    // bytewise serializable and deserializable

	    { std::bit_cast<T>(b) };
	    { std::bit_cast<std::array<std::uint8_t, sizeof(T)>>(a) };
    } &&

    // compiletime bit cast tests using test and expect.
    //
    // at present, separate runtime constraints (above) are
    // still required to ensure that the concept evaluates.
    pd::test([] {
	    using bytes = std::array<std::uint8_t, sizeof(T)>;

	    // bytewise serializable and deserializable
	    T     from_bytes = std::bit_cast<T>(bytes{});
	    bytes to_bytes   = std::bit_cast<bytes>(T{});

	    // fully determinate bytes
	    std::ranges::fill(to_bytes, 0xFFu);
	    from_bytes = std::bit_cast<T>(to_bytes);
	    pd::expect(std::ranges::equal(to_bytes, std::bit_cast<bytes>(from_bytes)));
    });

pd::converts_to: checks compiletime static_cast capability (L623)

// a stronger std::convertible_to concept that constrains
// compiletime behavior and ensures both types are plain
template <class From, class To>
concept converts_to = pod<From> && pod<To> && std::convertible_to<From, To> && //
                      requires(From const &v) {
			      { static_cast<To>(v) } noexcept -> is<To>;
		      } && test<From>([](From const &v) {
			      auto _ = static_cast<To>(v);
		      });

pd::arithmetic: describes types with arithmetic traits (L779)

// describes pod types that behave like numbers, meaning that they
// are totally ordered, three-way comparable, and define all available
// arithmetic operators (noexcept and valid at compiletime).
//
// furthermore, they must be arithmetically consistent and totally ordered;
// T{} must be analogous to zero, ++T{} to one (which is greater than zero),
// etc. this constraint is verified at compiletime for values 0-7.
//
// arithmetic operators that return a new value (e.g. a + b, a++)
// must return a totally ordered and three-way comparable pod type that
// is convertible to T. this definition accounts for integer promotion.
//
// notably, this does not require the types to be arithmetic with
// int or any of the other built-in numeric literals. a type that
// is arithmetic with itself is considered number-like enough.
//
// examples:
//
//   static_assert(arithmetic<i32>);
//   static_assert(arithmetic<u8>);
//   static_assert(!arithmetic<bool>);   // we won't consider bool to be a number
//   static_assert(!arithmetic<void *>); // pointers aren't arithmetic either
//
//   // a minimal example of a custom arithmetic type:
//   struct x {
//       i32 v;
//
//       constexpr x   operator+(x const &o)  const noexcept { return {v + o.v}; }
//       constexpr x   operator-(x const &o)  const noexcept { return {v - o.v}; }
//       constexpr x   operator*(x const &o)  const noexcept { return {v * o.v}; }
//       constexpr x   operator/(x const &o)  const noexcept { return {v / o.v}; }
//       constexpr x & operator+=(x const &o) noexcept { v += o.v; return *this; }
//       constexpr x & operator-=(x const &o) noexcept { v -= o.v; return *this; }
//       constexpr x & operator*=(x const &o) noexcept { v *= o.v; return *this; }
//       constexpr x & operator/=(x const &o) noexcept { v /= o.v; return *this; }
//       constexpr x   operator++(int)    noexcept { auto w = v++; return {w};   }
//       constexpr x & operator++()       noexcept { ++v;          return *this; }
//       constexpr x   operator--(int)    noexcept { auto w = v--; return {w};   }
//       constexpr x & operator--()       noexcept { --v;          return *this; }
//       constexpr x   operator+()  const noexcept {               return x{v};  }
//       constexpr x   operator-()  const noexcept {               return x{-v}; }
//
//       constexpr std::strong_ordering operator<=>(x const &) const = default;
//   };
//
//   static_assert(arithmetic<x>);
template <class T>
concept arithmetic = pod<T> && std::totally_ordered<T> && std::three_way_comparable<T> &&
                     _impl::_arithmetic::basic_traits<T> && requires(T &l, T const &r) {
			     {
				     _impl::_arithmetic::test(l, r)
			     } noexcept -> is<std::bool_constant<true>>;
			     {
				     typename std::bool_constant<[] {
					     T        v{};
					     T       &l = v;
					     T const &r = v;
					     return _impl::_arithmetic::test(l, r).value;
				     }()>{}
			     } -> is<std::bool_constant<true>>;
		     };

pd::arithmetic_with: constrains mutually arithmetic behavior (L1176)

// describes two arithmetic types that are arithmetic with each other.
// see pd::arithmetic for details. this concept adds the following
// constraints:
//
// - must be totally ordered with each other
// - must be three-way comparable with each other
// - must have a common type, which is arithmetic
// - must be assignable from each other
// - binary arithmetic operators must accept T and U interchangeably
//
//
// examples:
//
//   static_assert(arithmetic_with<i32, i32>);
//   static_assert(!arithmetic_with<i32, u32>); // fails due to sign difference
//   static_assert(arithmetic_with<i32, i16>);  // size variation is OK
//   static_assert(arithmetic_with<i32, f32>);  // int/float arithmetic OK
//
//   // a minimal example of a custom type that is arithmetic with i32:
//   struct x {
//       i32 v;
//
//       auto operator<=>(x const &) const = default;
//
//       constexpr std::strong_ordering
//       operator<=>(i32 const &that) const noexcept {
//           return v <=> that;
//       }
//
//       constexpr operator i32() const noexcept {
//           return v;
//       }
//
//       constexpr x &
//       operator=(i32 const &that) noexcept {
//           v = that;
//           return *this;
//       }
//
//       constexpr x   operator+(x const &o)  const noexcept { return {v + o.v}; }
//       constexpr x   operator-(x const &o)  const noexcept { return {v - o.v}; }
//       constexpr x   operator*(x const &o)  const noexcept { return {v * o.v}; }
//       constexpr x   operator/(x const &o)  const noexcept { return {v / o.v}; }
//       constexpr x & operator+=(x const &o) noexcept { v += o.v; return *this; }
//       constexpr x & operator-=(x const &o) noexcept { v -= o.v; return *this; }
//       constexpr x & operator*=(x const &o) noexcept { v *= o.v; return *this; }
//       constexpr x & operator/=(x const &o) noexcept { v /= o.v; return *this; }
//       constexpr x   operator++(int) noexcept       { auto w = v++; return {w};    }
//       constexpr x & operator++()    noexcept       { ++v;          return *this;  }
//       constexpr x   operator--(int) noexcept       { auto w = v--; return {w};    }
//       constexpr x & operator--()    noexcept       { --v;          return *this;  }
//       constexpr x   operator+()     const noexcept {               return x{v};  }
//       constexpr x   operator-()     const noexcept {               return x{-v}; }
//
//       constexpr x   operator+(i32 const &o)  const noexcept { return {v + o}; }
//       constexpr x   operator-(i32 const &o)  const noexcept { return {v - o}; }
//       constexpr x   operator*(i32 const &o)  const noexcept { return {v * o}; }
//       constexpr x   operator/(i32 const &o)  const noexcept { return {v / o}; }
//       constexpr x & operator+=(i32 const &o) noexcept { v += o; return *this; }
//       constexpr x & operator-=(i32 const &o) noexcept { v -= o; return *this; }
//       constexpr x & operator*=(i32 const &o) noexcept { v *= o; return *this; }
//       constexpr x & operator/=(i32 const &o) noexcept { v /= o; return *this; }
//   };
//
//   static_assert(arithmetic<x>);
//   static_assert(arithmetic_with<x, i32>);
//   static_assert(arithmetic_with<x, i16>);  // OK; i16 is implicitly convertible
//                                            //     to i32 in all contexts
//   static_assert(!arithmetic_with<x, u32>); // NOT OK; sign difference prevents
//                                            //         safe implicit conversion
template <class T, class U>
concept arithmetic_with =
    arithmetic<T> && arithmetic<U> && std::totally_ordered_with<T, U> &&
    std::three_way_comparable_with<T, U> && std::common_with<T, U> &&
    arithmetic<std::common_type_t<T, U>> && std::assignable_from<T &, U const &> &&
    std::assignable_from<U &, T const &> && _impl::_arithmetic_with::basic_traits<T, U> &&
    requires(T &tl, T const &tr, U &ul, U const &ur) {
	    {
		    _impl::_arithmetic_with::test(tl, tr, ul, ur)
	    } noexcept -> is<std::bool_constant<true>>;
	    {
		    typename std::bool_constant<[] {
			    T        tv{};
			    U        uv{};
			    T       &tl = tv;
			    T const &tr = tv;
			    U       &ul = uv;
			    U const &ur = uv;
			    return _impl::_arithmetic_with::test(tl, tr, ul, ur).value;
		    }()>{}
	    } -> is<std::bool_constant<true>>;
    } && requires(T &tl, T const &tr, U &ul, U const &ur) {
	    {
		    _impl::_arithmetic_with::test(ul, ur, tl, tr)
	    } noexcept -> is<std::bool_constant<true>>;
	    {
		    typename std::bool_constant<[] {
			    T        tv{};
			    U        uv{};
			    T       &tl = tv;
			    T const &tr = tv;
			    U       &ul = uv;
			    U const &ur = uv;
			    return _impl::_arithmetic_with::test(ul, ur, tl, tr).value;
		    }()>{}
	    } -> is<std::bool_constant<true>>;
    };

pd::converts_from: reverse of pd::converts_to (L1774)

// same as converts_to with From as the concept target
template <class To, class From>
concept converts_from = converts_to<From, To>;

pd::isnt: opposite of pd::is (L1786)

// opposite of is
template <class T, class U>
concept isnt = !is<T, U>;

pd::pod_v: describes possibly cv-qualified pod values (L1798)

// matches pod possibly cv-qualified pod values
template <auto V>
concept pod_v = pod<std::remove_cv_t<decltype(V)>>;

pd::spans: describes contigous, sized, pod ranges (L1830)

// describes sized ranges that span over a contiguous, pod array.
// optionally checks the value type of the range.
template <class R, class T = void>
concept spans =
    std::ranges::contiguous_range<R> && std::ranges::sized_range<R> &&
    pod<std::remove_cvref_t<std::ranges::range_reference_t<R>>> &&
    (is<T, void> || (pod<std::remove_cvref_t<T>> &&
                     is<std::remove_reference_t<std::ranges::range_reference_t<R>>, T>));

basic.types

pd::aliases: shorthands for standard types (L1934)

namespace aliases {

	// shorthand for std::conditional_t
	template <bool Cond, class IfTrue, class IfFalse>
	using cond = std::conditional_t<Cond, IfTrue, IfFalse>;

	// shorthand for float
	using f32 = float;

	// shorthand for double
	using f64 = double;

	// shorthand for std::int16_t
	using i16 = std::int16_t;

	// shorthand for std::int32_t
	using i32 = std::int32_t;

	// shorthand for std::int64_t
	using i64 = std::int64_t;

	// shorthand for std::int8_t
	using i8 = std::int8_t;

	// shorthand for std::ptrdiff_t
	using isz = std::ptrdiff_t;

	// shorthand for std::remove_const_t
	template <class T>
	using rm_const = std::remove_const_t<T>;

	// shorthand for std::remove_cv_t
	template <class T>
	using rm_cv = std::remove_cv_t<T>;

	// shorthand for std::remove_cvref_t
	template <class T>
	using rm_cvref = std::remove_cvref_t<T>;

	// shorthand for std::remove_reference_t
	template <class T>
	using rm_ref = std::remove_reference_t<T>;

	// shorthand for std::uint16_t
	using u16 = std::uint16_t;

	// shorthand for std::uint32_t
	using u32 = std::uint32_t;

	// shorthand for std::uint64_t
	using u64 = std::uint64_t;

	// shorthand for std::uint8_t
	using u8 = std::uint8_t;

	// shorthand for std::size_t
	using usz = std::size_t;

} // namespace aliases

using aliases::cond;
using aliases::f32;
using aliases::f64;
using aliases::i16;
using aliases::i32;
using aliases::i64;
using aliases::i8;
using aliases::isz;
using aliases::rm_const;
using aliases::rm_cv;
using aliases::rm_cvref;
using aliases::rm_ref;
using aliases::u16;
using aliases::u32;
using aliases::u64;
using aliases::u8;
using aliases::usz;

pd::empty: an empty type with an explicit padding byte (L2020)

// an empty type with an explicit padding byte to satisfy plain
// constraints. if you want to write a pod struct without
// non-static data members, inherit from this type to avoid
// needing to declare an explicit padding byte.
struct empty {
	u8 _;

	[[nodiscard]] constexpr bool
	operator==(empty const &that) const noexcept {
		return this == &that;
	}

	[[nodiscard]] constexpr bool
	operator!=(empty const &that) const noexcept {
		return this != &that;
	}
};

pd::boolean: a boolean-testable pod value (L2050)

// a boolean-testable pod value that uses u8 as its internal representation.
// non-zero values are truthy, zero values are falsy.
struct boolean {
	u8 m_value;

	[[nodiscard]] static constexpr boolean
	from(bool b) noexcept {
		return {static_cast<u8>(b)};
	}

	constexpr boolean &
	operator=(bool b) noexcept {
		m_value = static_cast<u8>(b);
		return *this;
	}

	[[nodiscard]] constexpr bool
	operator*() const noexcept {
		return !!m_value;
	}

	[[nodiscard]] constexpr operator bool() const noexcept {
		return !!m_value;
	}
};

pd::common: shorthand for std::common_type_t (L2109)

// a constrained shorthand for std::common_type_t
template <class... Ts>
	requires are_common<Ts...> && (sizeof...(Ts) > 0)
using common = std::common_type_t<Ts...>;

pd::nil: an empty pod type, used as a generic indicator (L2130)

// an empty pod type and static instance, used as a generic indicator.
// compares as equal to all nil instances and unequal to everything else.
//
// used by the pd::optional class to indicate a falsy result.
inline constexpr struct nil : empty {
	[[nodiscard]] constexpr bool
	operator==(nil const &) const noexcept {
		return true;
	}

	[[nodiscard]] constexpr bool
	operator!=(nil const &) const noexcept {
		return false;
	}

	template <class T>
	[[nodiscard]] constexpr bool
	operator==(T const &) const noexcept {
		return false;
	}

	template <class T>
	[[nodiscard]] constexpr bool
	operator!=(T const &) const noexcept {
		return true;
	}

	template <class T>
	[[nodiscard]] inline friend constexpr bool
	operator==(T const &, nil const &) noexcept {
		return false;
	}

	template <class T>
	[[nodiscard]] inline friend constexpr bool
	operator!=(T const &, nil const &) noexcept {
		return true;
	}
} nil{};

pd::static_bool: a pod, statically-known boolean (L2194)

// a pod, statically-known boolean
template <bool StaticV>
struct static_bool : empty {
	static constexpr bool value = StaticV; // bool is not pod!
};

meta.traits

pd::exists: type existence verifiers (L2215)

// a collection of verifiers for type existence.
//
// ensures that the provided template can be specialized with the
// provided template arguments and that its result is a complete,
// default-constructible type.
namespace exists {

	// describes complete <class...> template specializations
	template <template <class...> class Tpl, class... A>
	concept t = requires {
		{ Tpl<A...>{} };
	};

	// describes complete <auto...> template specializations
	template <template <auto...> class Tpl, auto... A>
	concept v = requires {
		{ Tpl<A...>{} };
	};

	// describes complete <class, auto...> template specializations
	template <template <class, auto...> class Tpl, class A, auto... B>
	concept tv = requires {
		{ Tpl<A, B...>{} };
	};

	// describes complete <auto, class...> template specializations
	template <template <auto, class...> class Tpl, auto A, class... B>
	concept vt = requires {
		{ Tpl<A, B...>{} };
	};

	// describes complete <class, class, auto...> template specializations
	template <template <class, class, auto...> class Tpl, class A, class B, auto... C>
	concept ttv = requires {
		{ Tpl<A, B, C...>{} };
	};

	// describes complete <auto, class, auto...> template specializations
	template <template <auto, class, auto...> class Tpl, auto A, class B, auto... C>
	concept vtv = requires {
		{ Tpl<A, B, C...>{} };
	};

	// describes complete <auto, auto, class...> template specializations
	template <template <auto, auto, class...> class Tpl, auto A, auto B, class... C>
	concept vvt = requires {
		{ Tpl<A, B, C...>{} };
	};

	// describes complete <class, auto, class...> template specializations
	template <template <class, auto, class...> class Tpl, class A, auto B, class... C>
	concept tvt = requires {
		{ Tpl<A, B, C...>{} };
	};

	// describes complete <class, class, class, auto...> template specializations
	template <template <class, class, class, auto...> class Tpl, class A, class B,
	          class C, auto... D>
	concept tttv = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <class, auto, class, auto...> template specializations
	template <template <class, auto, class, auto...> class Tpl, class A, auto B,
	          class C, auto... D>
	concept tvtv = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <auto, class, class, auto...> template specializations
	template <template <auto, class, class, auto...> class Tpl, auto A, class B,
	          class C, auto... D>
	concept vttv = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <auto, auto, class, auto...> template specializations
	template <template <auto, auto, class, auto...> class Tpl, auto A, auto B,
	          class C, auto... D>
	concept vvtv = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <auto, auto, auto, class...> template specializations
	template <template <auto, auto, auto, class...> class Tpl, auto A, auto B, auto C,
	          class... D>
	concept vvvt = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <auto, class, auto, class...> template specializations
	template <template <auto, class, auto, class...> class Tpl, auto A, class B,
	          auto C, class... D>
	concept vtvt = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <class, auto, auto, class...> template specializations
	template <template <class, auto, auto, class...> class Tpl, class A, auto B,
	          auto C, class... D>
	concept tvvt = requires {
		{ Tpl<A, B, C, D...>{} };
	};

	// describes complete <class, class, auto, class...> template specializations
	template <template <class, class, auto, class...> class Tpl, class A, class B,
	          auto C, class... D>
	concept ttvt = requires {
		{ Tpl<A, B, C, D...>{} };
	};

} // namespace exists

pd::matches: template template parameter matchers (L2859)

// a collection of template template parameter matchers
namespace matches {

	// matches identical <class...> templates
	template <template <class...> class Tpl, template <class...> class Upl>
	concept t = _impl::_matches::t<Tpl, Upl>::value;

	// matches identical <auto...> templates
	template <template <auto...> class Tpl, template <auto...> class Upl>
	concept v = _impl::_matches::v<Tpl, Upl>::value;

	// matches identical <class, auto...> templates
	template <template <class, auto...> class Tpl,
	          template <class, auto...> class Upl>
	concept tv = _impl::_matches::tv<Tpl, Upl>::value;

	// matches identical <auto, class...> templates
	template <template <auto, class...> class Tpl,
	          template <auto, class...> class Upl>
	concept vt = _impl::_matches::vt<Tpl, Upl>::value;

	// matches identical <class, class, auto...> templates
	template <template <class, class, auto...> class Tpl,
	          template <class, class, auto...> class Upl>
	concept ttv = _impl::_matches::ttv<Tpl, Upl>::value;

	// matches identical <auto, class, auto...> templates
	template <template <auto, class, auto...> class Tpl,
	          template <auto, class, auto...> class Upl>
	concept vtv = _impl::_matches::vtv<Tpl, Upl>::value;

	// matches identical <auto, auto, class...> templates
	template <template <auto, auto, class...> class Tpl,
	          template <auto, auto, class...> class Upl>
	concept vvt = _impl::_matches::vvt<Tpl, Upl>::value;

	// matches identical <class, auto, class...> templates
	template <template <class, auto, class...> class Tpl,
	          template <class, auto, class...> class Upl>
	concept tvt = _impl::_matches::tvt<Tpl, Upl>::value;

	// matches identical <class, class, class, auto...> templates
	template <template <class, class, class, auto...> class Tpl,
	          template <class, class, class, auto...> class Upl>
	concept tttv = _impl::_matches::tttv<Tpl, Upl>::value;

	// matches identical <class, auto, class, auto...> templates
	template <template <class, auto, class, auto...> class Tpl,
	          template <class, auto, class, auto...> class Upl>
	concept tvtv = _impl::_matches::tvtv<Tpl, Upl>::value;

	// matches identical <auto, class, class, auto...> templates
	template <template <auto, class, class, auto...> class Tpl,
	          template <auto, class, class, auto...> class Upl>
	concept vttv = _impl::_matches::vttv<Tpl, Upl>::value;

	// matches identical <auto, auto, class, auto...> templates
	template <template <auto, auto, class, auto...> class Tpl,
	          template <auto, auto, class, auto...> class Upl>
	concept vvtv = _impl::_matches::vvtv<Tpl, Upl>::value;

	// matches identical <auto, auto, auto, class...> templates
	template <template <auto, auto, auto, class...> class Tpl,
	          template <auto, auto, auto, class...> class Upl>
	concept vvvt = _impl::_matches::vvvt<Tpl, Upl>::value;

	// matches identical <auto, class, auto, class...> templates
	template <template <auto, class, auto, class...> class Tpl,
	          template <auto, class, auto, class...> class Upl>
	concept vtvt = _impl::_matches::vtvt<Tpl, Upl>::value;

	// matches identical <class, auto, auto, class...> templates
	template <template <class, auto, auto, class...> class Tpl,
	          template <class, auto, auto, class...> class Upl>
	concept tvvt = _impl::_matches::tvvt<Tpl, Upl>::value;

	// matches identical <class, class, auto, class...> templates
	template <template <class, class, auto, class...> class Tpl,
	          template <class, class, auto, class...> class Upl>
	concept ttvt = _impl::_matches::ttvt<Tpl, Upl>::value;

} // namespace matches

pd::of: template specialization matchers (L3306)

// a collection of template specialization matchers
namespace of {

	// matches <class...> template specializations
	template <class T, template <class...> class Tpl>
	concept t = _impl::_of::t<Tpl, rm_cvref<T>>::value;

	// matches <auto...> template specializations
	template <class T, template <auto...> class Tpl>
	concept v = _impl::_of::v<Tpl, rm_cvref<T>>::value;

	// matches <class, auto...> template specializations
	template <class T, template <class, auto...> class Tpl>
	concept tv = _impl::_of::tv<Tpl, rm_cvref<T>>::value;

	// matches <auto, class...> template specializations
	template <class T, template <auto, class...> class Tpl>
	concept vt = _impl::_of::vt<Tpl, rm_cvref<T>>::value;

	// matches <class, class, auto...> template specializations
	template <class T, template <class, class, auto...> class Tpl>
	concept ttv = _impl::_of::ttv<Tpl, rm_cvref<T>>::value;

	// matches <auto, class, auto...> template specializations
	template <class T, template <auto, class, auto...> class Tpl>
	concept vtv = _impl::_of::vtv<Tpl, rm_cvref<T>>::value;

	// matches <auto, auto, class...> template specializations
	template <class T, template <auto, auto, class...> class Tpl>
	concept vvt = _impl::_of::vvt<Tpl, rm_cvref<T>>::value;

	// matches <class, auto, class...> template specializations
	template <class T, template <class, auto, class...> class Tpl>
	concept tvt = _impl::_of::tvt<Tpl, rm_cvref<T>>::value;

	// matches <class, class, class, auto...> template specializations
	template <class T, template <class, class, class, auto...> class Tpl>
	concept tttv = _impl::_of::tttv<Tpl, rm_cvref<T>>::value;

	// matches <class, auto, class, auto...> template specializations
	template <class T, template <class, auto, class, auto...> class Tpl>
	concept tvtv = _impl::_of::tvtv<Tpl, rm_cvref<T>>::value;

	// matches <auto, class, class, auto...> template specializations
	template <class T, template <auto, class, class, auto...> class Tpl>
	concept vttv = _impl::_of::vttv<Tpl, rm_cvref<T>>::value;

	// matches <auto, auto, class, auto...> template specializations
	template <class T, template <auto, auto, class, auto...> class Tpl>
	concept vvtv = _impl::_of::vvtv<Tpl, rm_cvref<T>>::value;

	// matches <auto, auto, auto, class...> template specializations
	template <class T, template <auto, auto, auto, class...> class Tpl>
	concept vvvt = _impl::_of::vvvt<Tpl, rm_cvref<T>>::value;

	// matches <auto, class, auto, class...> template specializations
	template <class T, template <auto, class, auto, class...> class Tpl>
	concept vtvt = _impl::_of::vtvt<Tpl, rm_cvref<T>>::value;

	// matches <class, auto, auto, class...> template specializations
	template <class T, template <class, auto, auto, class...> class Tpl>
	concept tvvt = _impl::_of::tvvt<Tpl, rm_cvref<T>>::value;

	// matches <class, class, auto, class...> template specializations
	template <class T, template <class, class, auto, class...> class Tpl>
	concept ttvt = _impl::_of::ttvt<Tpl, rm_cvref<T>>::value;

} // namespace of

pd::sequential: describes an arithmetic sequence of NTTPs (L3763)

// checks if Ns... is a sequence of same-typed POD NTTPs that follows
// a strictly increasing or decreasing arithmetic pattern, meaning
// that the difference between any two adjacent terms is the same.
//
// supports any pd::arithmetic type.
template <auto... Ns>
concept sequential = (pod_v<Ns> && ...) && are_same<rm_cv<decltype(Ns)>...> &&
                     _impl::_sequential::test<Ns...>;

meta.utils

pd::as: a non-copying, constrained static_cast (L3899)

// a non-copying, constrained static_cast.
// overload for pod references -> const lvref.
template <class To, pod From>
	requires is<To, From &> || is<To, From const &>
[[nodiscard]] inline constexpr To
as(From const &from) noexcept {
	return from;
}

// a non-copying, constrained static_cast.
// overload for derived pod references -> base non-const lvref.
template <class To, pod From>
	requires std::is_reference_v<To> && pod<rm_cvref<To>> &&
                 std::derived_from<From, rm_cvref<To>>
[[nodiscard]] inline constexpr To
as(From &from) noexcept {
	return from;
}

// a non-copying, constrained static_cast.
// overload for pod derived references -> base const lvref.
template <class To, pod From>
	requires(isnt<To, From &> && isnt<To, From const &>) && isnt<rm_ref<To>, To> &&
                is<rm_cvref<To> const, rm_ref<To>> &&
                std::derived_from<From, rm_cvref<To>>
[[nodiscard]] inline constexpr To
as(From const &from) noexcept {
	return from;
}

pd::tget: a variadic template type getter (L3982)

// indexes a sequence of type template args, e.g.:
//
//   static_assert(is<tget<0, i32, u64>, i32>);
//   static_assert(is<tget<1, i32, u64>, u64>);
template <usz I, class... Ts>
	requires(I < sizeof...(Ts))
using tget = typename _impl::_tget::impl<I, Ts...>::type;

pd::to: a copying, constrained static_cast (L4004)

// a copying, constrained static_cast.
// overload for pod copy-value static_cast conversion.
template <pod To, converts_to<To> From>
[[nodiscard]] inline constexpr To
to(From const &from) noexcept {
	return from;
}

// a copying, constrained static_cast.
// std::bit_cast fallback for same-sized types.
template <pod To, pod From>
	requires(!converts_to<From, To> && sizeof(To) == sizeof(From))
[[nodiscard]] inline constexpr To
to(From const &from) noexcept {
	return std::bit_cast<To>(from);
}

pd::typle_cat: concatenates a pd::typle sequence (L4097)

// concatenates a sequence of typles
template <of::t<typle>... Ty>
using typle_cat = _impl::_typle_cat::cat<Ty...>::type;

pd::typle_pred: a collection of pd::typle predicates (L4126)

// a collection of typle predicates.
// e.g.: typle<u8, i32, u64>::filter<typle_pred::is<i32>::tpl> -> typle<i32>
namespace typle_pred {
	// keeps elements that are T
	template <pod T>
	struct is : empty {
		template <pod U>
		struct tpl : empty {
			static constexpr bool value = pd::is<T, U>;
		};
	};

	// keeps elements that aren't T
	template <pod T>
	struct isnt : empty {
		template <pod U>
		struct tpl : empty {
			static constexpr bool value = pd::isnt<T, U>;
		};
	};

	// keeps elements that specialize type-only template Of
	template <template <class...> class Of>
	struct of : empty {
		template <pod T>
		struct tpl : empty {
			static constexpr bool value = pd::of::t<T, Of>;
		};
	};
} // namespace typle_pred

pd::vyple_cat: concatenates a pd::vyple sequence (L4196)

// concatenates a sequence of vyples
template <of::v<vyple>... Ty>
using vyple_cat = _impl::_vyple_cat::cat<Ty...>::type;

pd::vyple_pred: a collection of pd::vyple predicates (L4257)

// a collection of vyple predicates
// e.g. vyple<0, 1, 2, 3, 5, 192>::filter<vyple_pred::even> -> vyple<0, 2, 192>
namespace vyple_pred {

	// [vyple] keeps elements that are even
	template <auto V>
		requires pod_v<V> && _impl::_vyple_pred::modulo_2<V>
	struct even {
		static constexpr bool value = V % 2 == 0;
	};

	// [vyple] keeps elements that are odd
	template <auto V>
		requires pod_v<V> && _impl::_vyple_pred::modulo_2<V>
	struct odd {
		static constexpr bool value = V % 2 != 0;
	};

	// [vyple] keeps elements that are strictly less than V
	template <auto V>
		requires pod_v<V>
	struct lt {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_lt<V, W>
		struct tpl {
			static constexpr bool value = W < V;
		};
	};

	// [vyple] keeps elements that are strictly greater than V
	template <auto V>
		requires pod_v<V>
	struct gt {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_gt<V, W>
		struct tpl {
			static constexpr bool value = W > V;
		};
	};

	// [vyple] keeps elements that are less than or equal to than V
	template <auto V>
		requires pod_v<V>
	struct le {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_le<V, W>
		struct tpl {
			static constexpr bool value = W <= V;
		};
	};

	// [vyple] keeps elements that are greater than or equal to than V
	template <auto V>
		requires pod_v<V>
	struct ge {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_ge<V, W>
		struct tpl {
			static constexpr bool value = W >= V;
		};
	};

	// [vyple] keeps elements that are equal to V
	template <auto V>
		requires pod_v<V>
	struct eq {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_eq<V, W>
		struct tpl {
			static constexpr bool value = W == V;
		};
	};

	// [vyple] keeps elements that are not equal to V
	template <auto V>
		requires pod_v<V>
	struct ne {
		template <auto W>
			requires pod_v<V> && _impl::_vyple_pred::cmp_ne<V, W>
		struct tpl {
			static constexpr bool value = W != V;
		};
	};
} // namespace vyple_pred

meta.types

pd::typle: a generic collection of type parameters (L4548)

// empty typle specialization. see main specialization for docs
template <>
struct typle<> : empty {
	static constexpr usz size = 0;

	template <of::t<typle>... That>
	using cat = typle_cat<That...>;

	template <pod T>
	using push_front = typle<T>;

	template <pod T>
	using push_back = typle<T>;

	template <usz N, pod T = struct nil>
	using grow_front = _impl::_typle::grow_front<N, T>::type;

	template <usz N, pod T = struct nil>
	using grow_back = _impl::_typle::grow_back<N, T>::type;

	template <template <class> class M>
	using map = typle<>;

	template <template <class, class> class, pod Accum>
	using reduce = Accum;

	template <template <class> class Pred>
	using filter = typle<>;

	template <template <class> class Pred>
	static constexpr bool has_if = false;

	template <class T>
	static constexpr bool has = false;

	template <class = void>
	static constexpr bool distinct = true;

	template <template <class...> class Of>
		requires exists::t<Of>
	using as = Of<>;
};

// a sequence of POD types, stored as template parameters.
// provides various manipulation procedures for metaprogramming.
//
// examples:
//
//   static_assert(is<typle<i32>::push_back<u64>, typle<i32, u64>>);
//   static_assert(is<typle<i32, u64>::pop_front, typle<u64>>);
//   static_assert(is<typle<i32, u64, i8>::slice<2>, typle<i32, u64>>);
//   static_assert(is<typle<i32, u64, i8>::slice<1, 2>, typle<u64>>);
//   static_assert(is<typle<i32, u64, i8>::slice<1, 3>, typle<u64, i8>>);
template <pod... Ts>
struct typle : empty {
	// number of types
	static constexpr usz size = sizeof...(Ts);

	// gets the Ith type
	template <usz I>
		requires(I < size)
	using get = tget<I, Ts...>;

	// gets the first type.
	// e.g.: typle<i32, u64>::front<> // i32
	using front = typename typle::get<0>;

	// gets the last type
	// e.g.: typle<i32, u64>::back<> // u64
	using back = typename typle::get<size - 1>;

	// slices the typle using the range [B, E).
	// specified either as <End> (with Begin == 0), or <Begin, End>
	template <usz... SliceSpec>
		requires(_impl::_typle::slice_spec<size, SliceSpec...>)
	using slice =
	    _impl::_typle::slice<_impl::_typle::slice_traits<size, SliceSpec...>::begin,
	                         _impl::_typle::slice_traits<size, SliceSpec...>::end,
	                         Ts...>::type;

	// concatenates a sequence of typles to the end
	template <of::t<typle>... That>
	using cat = typle_cat<typle<Ts...>, That...>;

	// pushes a type to the front
	template <pod T>
	using push_front = typle<T, Ts...>;

	// pushes a type to the back
	template <pod T>
	using push_back = typle<Ts..., T>;

	// removes the first type (requires size > 0)
	// e.g.: typle<i32, u64>::pop_front<> // typle<u64>
	using pop_front = _impl::_typle::pop_front<Ts...>::type;

	// removes the last type (requires size > 0)
	// e.g.: typle<i32, u64>::pop_back<> // typle<i32>
	using pop_back = _impl::_typle::pop_back<typle<>, Ts...>::type;

	// places N new types at the front
	template <usz N, pod T = struct nil>
	using grow_front = _impl::_typle::grow_front<N, T, Ts...>::type;

	// places N new types at the back
	template <usz N, pod T = struct nil>
	using grow_back = _impl::_typle::grow_back<N, T, Ts...>::type;

	// applies a type transformation to each element.
	// M must have a pod typename M<T>::type for all T in Ts...
	// (std::make_unsigned is an example)
	template <template <class> class M>
		requires _impl::_typle::mapper<M, Ts...>
	using map = typle<typename M<Ts>::type...>;

	// performs a reduction of the typle into a single type.
	// R must have a pod typename R<Accum, T>::type for all T in Ts...,
	// where Accum is the previous result, and Val is the current type.
	template <template <class Acc, class T> class R, pod Accum>
		requires _impl::_typle::reducer<R, Accum, Ts...>
	using reduce = _impl::_typle::reduce<R, Accum, Ts...>::type;

	// performs a filtration of the typle into a new tuple.
	// R must have a static constexpr bool P<T>::value for all T in Ts...
	// Types yielding true are kept (in order); types yielding false are omitted.
	template <template <class T> class Pred>
		requires _impl::_typle::pred<Pred, Ts...> &&
	                     _impl::_typle::filters<Pred, Ts...>
	using filter = _impl::_typle::filter<Pred, Ts...>::type;

	// finds the index of the first element that matches the predicate Pred.
	// does not compile if the element doesn't exist; use has_if to check.
	template <template <class T> class Pred>
		requires _impl::_typle::pred<Pred, Ts...> &&
	                     _impl::_typle::finds_if<Pred, Ts...>
	static constexpr usz find_if = _impl::_typle::find_if<Pred, 0, Ts...>::idx;

	// finds the index of the first element that is T. does not
	// compile if the element doesn't exist; use has to check.
	template <class T>
		requires _impl::_typle::finds_if<typle_pred::is<T>::template tpl, Ts...>
	static constexpr usz find =
	    _impl::_typle::find_if<typle_pred::is<T>::template tpl, 0, Ts...>::idx;

	// checks if a type exists in the typle that matches predicate Pred
	template <template <class T> class Pred>
		requires _impl::_typle::pred<Pred, Ts...>
	static constexpr bool has_if = _impl::_typle::finds_if<Pred, Ts...>;

	// checks if type T exists in the typle.
	template <class T>
	static constexpr bool has = has_if<typle_pred::is<T>::template tpl>;

	// checks if all the elements in this typle are distinct (N^2 time)
	template <class = void>
	static constexpr bool distinct = _impl::_typle::distinct::eval<Ts...>;

	// specializes a template using the contained types
	template <template <class...> class Of>
		requires exists::t<Of, Ts...>
	using as = Of<Ts...>;
};

pd::vt: a generic NTTP-as-type wrapper (L4756)

// a simple type wrapper for non-type template parameters. allows values
// to specialize a template <class...>. this is useful for template template
// generics (which require template type/value specifity), and for type-related
// operations in general (such as storing values in a typle).
//
// notably, a template that is specialized by a vt instead of an NTTP will
// always specialize to a type that reflects the actual value inside the vt;
// for example, foo<vt<32>> is a different type than foo<vt<32u>>. this can
// be undesirable if you don't care what type the value is, but you can use
// of::v<vt> to help direct template specializations.
template <auto V>
	requires pod_v<V>
struct vt : empty {
	using type                  = rm_cv<decltype(V)>;
	static constexpr type value = V;

	[[nodiscard]] constexpr operator type() const noexcept {
		return value;
	}

	template <pod To>
		requires converts_to<type, To>
	using as_vt = vt<static_cast<To>(value)>;
};

pd::ty: shorthand for pd::typle (L5083)

// shorthand for typle
template <pod... Ts>
using ty = typle<Ts...>;

pd::vyple: a generic collection of NTTPs (L5149)

// empty vyple specialization. see main specialization for docs
template <>
struct vyple<> : empty {
	using values              = typle<>;
	using types               = typle<>;
	static constexpr usz size = 0;

	template <of::v<vyple>... That>
	using cat = vyple_cat<That...>;

	template <auto V>
		requires pod_v<V>
	using push_front = vyple<V>;

	template <auto V>
		requires pod_v<V>
	using push_back = vyple<V>;

	template <usz N, auto V = nil>
		requires pod_v<V>
	using grow_front =
	    _impl::_vyple::from<typename values::template grow_front<N, vt<V>>>::type;

	template <usz N, auto V = nil>
		requires pod_v<V>
	using grow_back =
	    _impl::_vyple::from<typename values::template grow_back<N, vt<V>>>::type;

	template <template <auto> class>
	using map = vyple<>;

	template <template <auto, auto> class, auto Accum>
		requires pod_v<Accum>
	static constexpr rm_cv<decltype(Accum)> reduce = Accum;

	template <template <auto V> class Pred>
	using filter = vyple<>;

	template <template <auto> class Pred>
	static constexpr bool has_if = false;

	template <auto V>
		requires pod_v<V>
	static constexpr bool has = false;

	template <template <auto...> class Of>
		requires exists::v<Of>
	using as = Of<>;
};

// a sequence of POD values, stored as non-type template parameters.
// provides various manipulation procedures for metaprogramming.
//
// examples:
//
//   static_assert(is<vyple<0>::push_back<5u>, vyple<0, 5u>>);
//   static_assert(is<vyple<0, 5u>::pop_front, vyple<5u>>);
//   static_assert(is<vyple<0, 5u, 'a'>::slice<2>, vyple<0, 5u>>);
//   static_assert(is<vyple<0, 5u, 'a'>::slice<1, 2>, vyple<5u>>);
//   static_assert(is<vyple<0, 5u, 'a'>::slice<1, 3>, vyple<5u, 'a'>>);
template <auto... Vs>
	requires(pod_v<Vs> && ...)
struct vyple : empty {
	// the underlying value storage (a vt typle)
	using values = typle<vt<Vs>...>;

	// a typle representation of the underlying types
	using types = typle<rm_cv<decltype(Vs)>...>;

	// number of values
	static constexpr usz size = values::size;

	// gets the Ith value
	template <usz I>
		requires(I < size)
	static constexpr types::template get<I> get = values::template get<I>::value;

	// gets the first value.
	// e.g.: vyple<16, 32u>::front<> // 16
	static constexpr types::template get<0> front = vyple::get<0>;

	// gets the last value.
	// e.g.: vyple<16, 32u>::back<> // 32u
	static constexpr types::template get<size - 1> back = vyple::get<size - 1>;

	// slices the vyple using the range [B, E)
	// specified either as <End> (with Begin == 0), or <Begin, End>
	template <usz... SliceSpec>
		requires(_impl::_typle::slice_spec<size, SliceSpec...>)
	using slice =
	    _impl::_vyple::from<typename values::template slice<SliceSpec...>>::type;

	// concates a sequence of vyples to the end
	template <of::v<vyple>... That>
	using cat = vyple_cat<vyple<Vs...>, That...>;

	// pushes a value to the front
	template <auto V>
		requires pod_v<V>
	using push_front = vyple<V, Vs...>;

	// pushes a value to the back
	template <auto V>
		requires pod_v<V>
	using push_back = vyple<Vs..., V>;

	// removes the first value (requires size > 0)
	using pop_front = _impl::_vyple::from<typename values::pop_front>::type;

	// removes the last value (requires size > 0)
	using pop_back = _impl::_vyple::from<typename values::pop_back>::type;

	// places N new values at the front
	template <usz N, auto V = nil>
		requires pod_v<V>
	using grow_front =
	    _impl::_vyple::from<typename values::template grow_front<N, vt<V>>>::type;

	// places N new values at the back
	template <usz N, auto V = nil>
		requires pod_v<V>
	using grow_back =
	    _impl::_vyple::from<typename values::template grow_back<N, vt<V>>>::type;

	// applies a value transformation to each element.
	// M must have a pod typename M<V>::value for all V in Vs...
	template <template <auto> class M>
		requires _impl::_vyple::mapper<M, Vs...>
	using map = vyple<M<Vs>::value...>;

	// performs a reduction of the vyple into a single value.
	// R must have a pod R<Accum, Val>::value for all V in Vs...,
	// where Accum is the previous result, and Val is the current value.
	template <template <auto Acc, auto Val> class R, auto Accum>
		requires _impl::_vyple::reducer<R, Accum, values>
	static constexpr
	    typename values::template reduce<_impl::_vyple::reduce<R>::template tpl,
	                                     vt<Accum>>::type reduce =
		values::template reduce<_impl::_vyple::reduce<R>::template tpl,
	                                vt<Accum>>::value;

	// performs a filtration of the vyple into a new vyple.
	// R must have a static constexpr bool P<T>::value for all T in Ts...
	// See pd::vyple_pred for examples of predicates.
	// Types yielding true are kept (in order); types yielding false are omitted.
	template <template <auto V> class Pred>
		requires _impl::_vyple::pred<Pred, Vs...> &&
	                     _impl::_vyple::filters<Pred, Vs...>
	using filter = _impl::_vyple::from<typename values::template filter<
	    _impl::_vyple::filter<Pred>::template tpl>>::type;

	// finds the index of the first element that matches the predicate Pred.
	// does not compile if the element doesn't exist; use has_if to check.
	template <template <auto V> class Pred>
		requires _impl::_vyple::pred<Pred, Vs...> &&
	                     (values::template has_if<
				 _impl::_vyple::tpred_for<Pred>::template tpl>)
	static constexpr usz find_if =
	    values::template find_if<_impl::_vyple::tpred_for<Pred>::template tpl>;

	// finds the index of the first element that is V. does not
	// compile if the element doesn't exist; use has to check.
	template <auto V>
		requires pod_v<V> && (values::template has<vt<V>>)
	static constexpr usz find = values::template find<vt<V>>;

	// checks if a value exists in the vyple that matches predicate Pred
	template <template <auto V> class Pred>
		requires _impl::_vyple::pred<Pred, Vs...>
	static constexpr bool has_if =
	    values::template has_if<_impl::_vyple::tpred_for<Pred>::template tpl>;

	// checks if value V exists in the vyple.
	template <auto V>
		requires pod_v<V>
	static constexpr bool has = values::template has<vt<V>>;

	// specializes a template using the contained values
	template <template <auto...> class Of>
		requires exists::v<Of, Vs...>
	using as = Of<Vs...>;
};

pd::vy: shorthand for pd::vyple (L5606)

// shorthand for vyple
template <auto... Vs>
	requires(pod_v<Vs> && ...)
using vy = vyple<Vs...>;

util.types

pd::sequence: a statically-verified arithmetic sequence (L5662)

template <auto... Ns>
	requires sequential<Ns...>
struct sequence;

template <>
struct sequence<> : empty {
	using values              = vyple<>;
	static constexpr usz size = 0;
};

template <auto N>
	requires sequential<N>
struct sequence<N> : empty {
	using values     = vyple<N>;
	using value_type = rm_cv<decltype(N)>;

	static constexpr value_type first = N;
	using rest                        = sequence<>;

	static constexpr usz size = 1;

	template <converts_from<value_type> U>
	[[nodiscard]] constexpr operator sequence<U{N}>() const noexcept {
		return {};
	}
};

// a statically-verified NTTP sequence that uses arithmetic operators
// to ensure validity. sequence cannot be specialized with a sequence of
// NTTPs that do not follow an arithmetic sequence or are not the same type.
//
// supports any pd::arithmetic type.
// use seq to generate new sequences.
//
// examples:
//
//  sequence<>           // valid
//  sequence<0>          // valid
//  sequence<0, 1, 2, 3> // valid
//  sequence<0, 1, 2, 2> // invalid, cannot exist
//  sequence<-3, -2, -1> // valid
//  sequence<-3, -2, -3> // invalid, cannot exist
template <auto N, auto... Ns>
	requires sequential<N, Ns...>
struct sequence<N, Ns...> : empty {
	using values     = vyple<N, Ns...>;
	using value_type = rm_cv<decltype(N)>;

	// the first term in the sequence
	static constexpr value_type first = N;

	// a sequence containing the rest of the terms
	// note: eventually, sequence::rest will yield the
	//       sequence<N> and sequence<> specializations,
	//       which have fewer properties than this one.
	using rest = vyple<Ns...>::template as<sequence>;

	// total size of the sequence
	static constexpr usz size = values::size;

	// whether or not the sequence is ascending
	static constexpr bool ascending = _impl::_sequence::traits<N, Ns...>::ascending;

	// the positive distance between each term
	static constexpr value_type step = _impl::_sequence::traits<N, Ns...>::step;

	// implicitly convertible to any sequence provided
	// that the values are equality comparable with
	// eachother and the numeric sequence is the same
	template <of::v<sequence> Seq>
		requires std::equality_comparable_with<value_type,
	                                               typename Seq::value_type> &&
	                 _impl::_sequence::values_eq<values, typename Seq::values>
	[[nodiscard]] constexpr operator Seq() const noexcept {
		return {};
	}
};

pd::seq: generates a pd::sequence (L5938)

// generates an arithmetic sequence according to a sequence specification:
//
//  - End [begin = {}, step = ++{}], or
//  - Begin, End      [step = ++{}], or
//  - Begin, End, Step
//
// spec types must be pd::arithmetic_with each other and convertible
// to an arithmetic common type, which becomes the sequence value_type.
//
// examples:
//
//   // standard integer sequences
//   seq<3>       -> sequence<0, 1, 2>
//   seq<3, 7>    -> sequence<3, 4, 5, 6>
//   seq<3, 7, 2> -> sequence<3, 5>
//
//   // custom type sequences
//   struct x {
//       constexpr std::strong_ordering operator<=>(x const &) const = default;
//
//       i32 v;
//
//       constexpr x   operator+(x const &o)  const noexcept { return {v + o.v}; }
//       constexpr x   operator-(x const &o)  const noexcept { return {v - o.v}; }
//       constexpr x   operator*(x const &o)  const noexcept { return {v * o.v}; }
//       constexpr x   operator/(x const &o)  const noexcept { return {v / o.v}; }
//       constexpr x & operator+=(x const &o) noexcept { v += o.v; return *this; }
//       constexpr x & operator-=(x const &o) noexcept { v -= o.v; return *this; }
//       constexpr x & operator*=(x const &o) noexcept { v *= o.v; return *this; }
//       constexpr x & operator/=(x const &o) noexcept { v /= o.v; return *this; }
//       constexpr x   operator++(int) noexcept       { auto w = v++; return {w};   }
//       constexpr x & operator++()    noexcept       { ++v;          return *this; }
//       constexpr x   operator--(int) noexcept       { auto w = v--; return {w};   }
//       constexpr x & operator--()    noexcept       { --v;          return *this; }
//       constexpr x   operator+()     const noexcept {               return x{v};  }
//       constexpr x   operator-()     const noexcept {               return x{-v}; }
//   };
//
//   seq<x{3}>             -> sequence<x{0}, x{1}, x{2}>
//   seq<x{3}, x{7}>       -> sequence<x{3}, x{4}, x{5}, x{6}>
//   seq<x{3}, x{7}, x{2}> -> sequence<x{3}, x{5}>
template <auto... SeqSpec>
	requires(pod_v<SeqSpec> && ...) && _impl::_seq::spec<SeqSpec...>
using seq = _impl::_seq::gen<SeqSpec...>;

pd::seq_for: shorthand for seq<sizeof...(Ts)>{} (L6132)

// shorthand for seq<sizeof...(Ts)>{}
template <class... Ts>
inline constexpr auto seq_for = seq<sizeof...(Ts)>{};

pd::tuple: a generic data container (L6391)

// a pd tuple with the same byte layout as a struct (if #pragma pack is supported).
// explicitly-defined padding bytes and packing ensures a 1:1 byte relationship
// between, for example, struct foo { T t; U u; V v; } and tuple<T, U, V>.
//
// If #pragma pack is missing, bytewise deserialization (.as()) is disabled.
// Bytewise deserialization also requires compiletime support, which is enforced
// by pd::test. Compiler support for this feature varies as C++20 is still being
// implemented.
//
// Due to its recursive definition and subobjects, aggregate construction is
// not recommended (e.g. tuple<int, int, int, int>{{{5}, {{6}, {{7}, {8}}}}})
// Instead, use pd::tup(...).
//
// examples:
//   static_assert(tup() + tup() == tup());
//   static_assert(tup(1, '3') + tup(0.7, arr(2)) == tup(1, '3', 0.7, arr(2)))
//   struct foo {
//       bool operator==(foo const &) const = default;
//       int               id;
//       char              option;
//       double            similarity;
//       array<int, vt<1>> siblings;
//   };
//   static_assert(foo{1, '3', 0.7, arr(2)} == tup(1, '3', 0.7, arr(2)).as<foo>());
template <pod... Ts>
	requires _impl::_tuple::fillable<_impl::_tuple::data<Ts...>, Ts...>
struct tuple {
	auto operator<=>(tuple const &) const = default;

	// the collection of types represented by this tuple
	using types = typle<Ts...>;

	// the number of elements contained
	static constexpr usz size = types::size;

	// fills up to size number of pod arguments
	// that are convertible to the contained types
	template <pod... Us>
		requires _impl::_tuple::fillable<_impl::_tuple::data<Ts...>, Us...>
	constexpr void
	fill(Us const &...vs) noexcept {
		_.fill(vs...);
	}

	// concatenates another tuple to the end
	template <pod... Us>
		requires _impl::_tuple::cattable<_impl::_tuple::data<Ts...>,
	                                         _impl::_tuple::data<Us...>>
	[[nodiscard]] constexpr tuple<Ts..., Us...>
	operator+(tuple<Us...> const &that) const noexcept {
		if constexpr (sizeof...(Us) == 0)
			return *this;
		else
			return {_impl::_tuple::cat(_, that._)};
	}

	// (friend) data accessor for non-const tuples
	template <usz I>
	[[nodiscard]] inline friend constexpr auto &
	get(tuple &o) noexcept {
		return o._.template get<I>();
	}

	// (friend) data accessor for const tuples
	template <usz I>
	[[nodiscard]] inline friend constexpr auto const &
	get(tuple const &o) noexcept {
		return o._.template get<I>();
	}

	template <pod As>
		requires(sizeof(tuple) == sizeof(As))
	[[nodiscard]] constexpr As
	as() noexcept {
		return std::bit_cast<As>(_);
	}

	// convertible to any tuple that can be filled by the elements in this
	// tuple. this includes same-sized tuples with convertible_from<Ts>...
	// elements, as well as larger-sized tuples with the same restriction
	// (any unpopulated elements are default-constructed).
	template <pod... Us>
		requires _impl::_tuple::fillable<_impl::_tuple::data<Us...>, Ts...>
	[[nodiscard]] constexpr operator tuple<Us...>() const noexcept {
		return fill_convert_to<tuple<Us...>>(seq_for<Ts...>);
	}

	_impl::_tuple::data<Ts...> _;

   private:
	template <class Res, usz... I>
	[[nodiscard]] constexpr Res
	fill_convert_to(sequence<I...>) const noexcept {
		Res res{};
		res.fill(get<I>(*this)...);
		return res;
	}
};

template <>
struct tuple<> : empty {
	using types = typle<>;

	[[nodiscard]] constexpr bool
	operator==(tuple const &) const noexcept {
		return true;
	}
	[[nodiscard]] constexpr bool
	operator!=(tuple const &) const noexcept {
		return false;
	}

	static constexpr usz size = 0;

	template <pod... Us>
	[[nodiscard]] constexpr tuple<Us...>
	operator+(tuple<Us...> const &that) const noexcept {
		return that;
	}
};

pd::unconstrained::string_literal: a non-pod string literal wrapper (L6615)

namespace unconstrained {

	// a non-pod string literal wrapper.
	// for storage, specialize pd::k with a string_literal instance.
	//
	// example:
	//
	//   template <unconstrained::string_literal K>
	//   struct foo {
	//       using key = k<K>;
	//   };
	template <usz Ext>
		requires(Ext > 0)
	struct string_literal {
		struct {
			char data[Ext];
		} const m_data;

	   public:
		[[nodiscard]] static constexpr usz
		size() noexcept {
			return Ext;
		}

		[[nodiscard]] constexpr char const *
		data() const noexcept {
			return m_data.data;
		}

		[[nodiscard]] constexpr char const *
		begin() const noexcept {
			return data();
		}

		[[nodiscard]] constexpr char const *
		end() const noexcept {
			return data() + size();
		}

		[[nodiscard]] constexpr string_literal(char const (&v)[Ext])
		    : m_data{[&v] {
			      rm_cvref<decltype(m_data)> res{};
			      std::ranges::copy(v, res.data);
			      expect(canonical(res.data));
			      return res;
		      }()} {
		}

	   private:
		[[nodiscard]] static constexpr bool
		canonical(char (&data)[Ext]) noexcept {
			for (usz i = 0; i < Ext - 1; ++i)
				if (!data[i])
					return false;
			return !data[Ext - 1];
		}
	};

} // namespace unconstrained

pd::k: a "key" type, specialized by a literal string (L6800)

// a statically-defined string key that stores its contents in its type.
//
// examples:
//     using foo = k<"foo!">;
//     static_assert(is<foo, k<"foo!">>);   // use type comparison for equality tests
//     static_assert(foo{} < k<"foo!!">{}); // or, use lexicographical comparison
//     static_assert(foo{} != k<"barbara">{});
//     static_assert(foo::size() == 4); // range does not include nullterm
template <unconstrained::string_literal K = "">
struct k : empty {

	using value_type      = char;
	using reference       = char const &;
	using pointer         = char const *;
	using iterator        = pointer;
	using const_reference = reference;
	using const_pointer   = pointer;
	using const_iterator  = iterator;
	using size_type       = usz;

	static constexpr auto base = K;

	[[nodiscard]] static constexpr size_type
	size() noexcept {
		return K.size() - 1;
	}

	[[nodiscard]] static constexpr const_pointer
	data() noexcept {
		return K.data();
	}

	[[nodiscard]] static constexpr const_iterator
	begin() noexcept {
		return data();
	}

	[[nodiscard]] static constexpr const_iterator
	end() noexcept {
		return data() + size();
	}

	[[nodiscard]] constexpr char
	operator[](usz i) const noexcept {
		return data()[i];
	}

	template <unconstrained::string_literal UK>
	[[nodiscard]] constexpr bool
	operator==(k<UK> const &) const noexcept {
		return is<k, k<UK>>;
	}

	template <unconstrained::string_literal UK>
	[[nodiscard]] constexpr bool
	operator!=(k<UK> const &) const noexcept {
		return isnt<k, k<UK>>;
	}

	template <unconstrained::string_literal UK>
	[[nodiscard]] constexpr std::strong_ordering
	operator<=>(k<UK> const &r) const noexcept {
		return std::lexicographical_compare_three_way(begin(), end(), r.begin(),
		                                              r.end());
	}
};

pd::kt: a statically-keyed type (L6903)

// a keyed type, specified by a pd::k key.
//
// e.g. using foo = kt<"foo!", i32>;
//      static_assert(is<foo::key, k<"foo!">>);
//      static_assert(is<foo::type, i32>);
template <unconstrained::string_literal K, pod T>
	requires exists::v<k, K>
struct kt : empty {
	using key  = k<K>;
	using type = T;
};

// shorthand for of::vt<T, kt>
template <class T>
concept of_kt = of::vt<T, kt>;

pd::kv: a statically-keyed constant value (L6934)

// a keyed value, specified by a pd::k key.
//
// e.g. using foo = kv<k<"answer">, 42>;
//      static_assert(is<foo::key, k<"answer">);
//      static_assert(foo::value == 42);
template <of::v<k> K, auto V>
	requires pod_v<V>
struct kv {
	using key                                 = K;
	static constexpr rm_cv<decltype(V)> value = V;
};

pd::tup: constructs a tuple from any pod arguments (L6962)

// constructs a tuple from any pod arguments
template <pod... Ts>
[[nodiscard]] inline constexpr tuple<Ts...>
tup(Ts const &...vs) noexcept {
	if constexpr (sizeof...(Ts) > 0) {
		tuple<Ts...> res{};
		res.fill(vs...);
		return res;
	} else {
		return {};
	}
}

util.accessors

pd::vget: a variadic argument getter (L7115)

// indexes a sequence of pod values, e.g.:
//
//   static_assert(vget<0>(84, 49u) == 84);
//   static_assert(vget<1>(84, 49u) == 49u);
template <usz I, pod... Args>
	requires(I < sizeof...(Args))
[[nodiscard]] inline constexpr auto
vget(Args const &...args) noexcept {
	return _impl::_vget::get<I>(args...);
}

algo.bits

pd::next_pow2: finds the next power of two (L7171)

// finds the next power of two greater than or equal to unsigned n
template <std::unsigned_integral T>
inline constexpr T
next_pow2(T n) noexcept {
	--n;
	for (T i = 1u; i < std::numeric_limits<T>::digits; i <<= 1u)
		n |= (n >> i);
	return ++n;
}

data.traits

pd::any_object: describes any actual or derived pd object (L7222)

// checks of T is any object; either a pd::object or derived from one.
template <class T>
concept any_object = _impl::_any_object::test<T>;

pd::dyn_ctx: describes context types for dyn objects (L7234)

// describes context types for dyn objects.
// see pd::dyn for more information and a simple example.
template <class Ctx>
concept dyn_ctx =
    // must have any object listed as its base type,
    any_object<typename Ctx::base_type> && // and:
    (
	// must have a <class...> template named type,
	matches::t<Ctx::template type, Ctx::template type> ||
	// or a complete, default-constructible type type
	requires() {
		{ typename Ctx::type{} };
	});

data.memory

pd::iallocates: describes pd::id-based allocators (L7259)

// describes `pd::id`-based allocators. see pd::ialloc for an example.
template <class Alloc, class T = void>
concept iallocates =
    pod<Alloc> &&
    (is<T, void> ? pod<typename Alloc::value_type>
                 : (pod<T> && is<T, typename Alloc::value_type>)) &&
    pod<typename Alloc::value_type> && of::t<typename Alloc::id, id> &&
    pod<typename Alloc::id::value_type> &&
    is<typename Alloc::value_type, typename Alloc::id::value_type> &&
    is<typename Alloc::pointer, typename Alloc::value_type *> &&
    is<typename Alloc::const_pointer, typename Alloc::value_type const *> &&
    is<typename Alloc::reference, typename Alloc::value_type &> &&
    is<typename Alloc::const_reference, typename Alloc::value_type const &> &&
    requires {
	    { Alloc::capacity() } noexcept -> is<usz>;
    } && Alloc::capacity() > 0 &&
    requires(Alloc &a, Alloc const &ac, usz n, typename Alloc::id id) {
	    { a.allocate(n) } noexcept -> is<typename Alloc::id>;
	    { a.deallocate(id, n) } noexcept -> is<void>;
	    { a[id] } noexcept -> is<typename Alloc::pointer>;
	    { ac[id] } noexcept -> is<typename Alloc::const_pointer>;
    } && test<Alloc, Alloc, Alloc, Alloc>([](Alloc &a0, Alloc &a1, Alloc &a2, Alloc &a3) {
	    auto _0 = Alloc::capacity();
	    auto _1 = a0.allocate(1);
	    a1.deallocate(a1.allocate(1), 1);
	    auto _2 = a2[a2.allocate(1)];
	    auto _3 = as<Alloc const &>(a3)[a3.allocate(1)];
    });

data.traits

pd::of_ienv: describes ienv types that contain a T allocator (L7309)

// describes ienv types that contain a T allocator.
// must have an allocator for each T in Ts...
template <class IEnv, class... Ts>
concept of_ienv = (pod<Ts> && ...) && _impl::_of_ienv::test<IEnv, Ts...>;

data.basics

pd::object: an inheritable, statically-keyed collection (L7332)

// given the inability for PODs to both inherit and define data members,
// pd::object is provided to serve as a base class that stores all of the
// data in the inheritance chain by defining a pd::tuple using the pd::kt
// specifiers that specialize the class. this allows for generic construction,
// data OOP, and reflection on non-static data members.
//
// objects are convertible to each other, as long as their keys match (or
// the conversion target has more keys), and their values are convertible.
//
// example:
//
//   template <of_kt... Deriv>
//   struct entity : object<kt<"id", i32>, Deriv...> {
//       constexpr i32 id() noexcept { return o<"id">(this); }
//   };
//
//   template <of_kt... Deriv>
//   struct person : entity<kt<"age", u8>, Deriv...> {
//       constexpr u8 age() noexcept { return o<"age">(this); }
//   };
//
//   static_assert(converts_to<object<kt<"id", i32>, kt<"age", i32>>, person<>>);
template <of_kt... Schema>
	requires(ty<typename Schema::key...>::template distinct<>)
struct object
    : ty<typename Schema::type...>::template as<tuple> { // an object is a tuple with
							 // statically-keyed indices
	auto operator<=>(object const &) const = default;

	using schema = ty<Schema...>;
	using keys   = ty<typename Schema::key...>;
	using types  = ty<typename Schema::type...>;
	using values = types::template as<tuple>;

	// (friend) data member accessor for const objects
	template <unconstrained::string_literal K>
		requires(keys::template has<k<K>>)
	[[nodiscard]] inline friend constexpr auto const &
	get(object const &o) noexcept {
		return get<keys::template find<k<K>>>(o);
	}

	// (friend) data member accessor for non-const objects
	template <unconstrained::string_literal K>
		requires(keys::template has<k<K>>)
	[[nodiscard]] inline friend constexpr auto &
	get(object &o) noexcept {
		return get<keys::template find<k<K>>>(o);
	}

	// object concatenation
	template <any_object Obj>
		requires(keys::template cat<typename Obj::keys>::template distinct<>)
	[[nodiscard]] constexpr
	    typename schema::template cat<typename Obj::schema>::template as<object>
	    operator+(Obj const &o) const noexcept {
		return {as<values const &>(*this) + as<typename Obj::values const &>(o)};
	}

	// implicitly convertible to any object, as long as the keys are the same
	// (or if the dest object has additional keys), and the values are convertible
	template <any_object Obj>
		requires(keys::size <= Obj::keys::size) &&
	                (is<keys, typename Obj::keys::template slice<keys::size>>) &&
	                converts_to<typename types::template as<tuple>,
	                            typename Obj::types::template as<tuple>>
	[[nodiscard]] constexpr operator Obj() const noexcept {
		return Obj{static_cast<typename Obj::types::template as<tuple>>(*this)};
	}
};

pd::array: a fixed-size, contiguous, pod array (L7556)

// a pod array.
//
// see pd::to<array>(...) and pd::arr(...) (which calls to<array>) for
// easy construction. args can be any set of types that are common with
// each other. the resulting array type is the common type of the arguments.
template <pod T, usz Size, of_kt... D>
struct array : object<kt<"data", _impl::_array::storage<T, Size>>, D...> {
	auto operator<=>(array const &) const = default;

	using value_type      = T;
	using size_type       = usz;
	using pointer         = T *;
	using const_pointer   = T const *;
	using reference       = T &;
	using const_reference = T const &;
	using iterator        = T *;
	using const_iterator  = T const *;

	[[nodiscard]] constexpr T *
	data() noexcept {
		return get<"data">(*this).data;
	}

	[[nodiscard]] constexpr T const *
	data() const noexcept {
		return get<"data">(*this).data;
	}

	[[nodiscard]] constexpr T *
	begin() noexcept {
		return data();
	}

	[[nodiscard]] constexpr T const *
	begin() const noexcept {
		return data();
	}

	[[nodiscard]] constexpr T const *
	cbegin() const noexcept {
		return data();
	}

	[[nodiscard]] constexpr T *
	end() noexcept {
		return begin() + size();
	}

	[[nodiscard]] constexpr T const *
	end() const noexcept {
		return begin() + size();
	}

	[[nodiscard]] constexpr T const *
	cend() const noexcept {
		return begin() + size();
	}

	[[nodiscard]] constexpr usz
	size() const noexcept {
		return Size;
	}

	[[nodiscard]] constexpr bool
	empty() const noexcept {
		return size() == 0;
	}

	[[nodiscard]] constexpr T &
	operator[](usz idx) noexcept {
		return data()[idx];
	}

	[[nodiscard]] constexpr T const &
	operator[](usz idx) const noexcept {
		return data()[idx];
	}

	template <converts_from<T> U, usz USize, of_kt... UD>
		requires(Size <= USize)
	[[nodiscard]] constexpr operator array<U, USize, UD...>() const noexcept {
		array<U, USize, UD...> res{};
		std::ranges::copy(*this, res.data());
		return res;
	}
};

pd::arr: generic array constructor (L7686)

// generic array constructor
template <struct nil = nil, pod... Ts> // <-- `nil` is for disambiguation, ignore
	requires((sizeof...(Ts) > 0) && are_common<Ts...>)
[[nodiscard]] inline constexpr array<common<Ts...>, sizeof...(Ts)>
arr(Ts const &...vs) noexcept {
	return {tup(_impl::_array::storage<common<Ts...>, sizeof...(Ts)>{
	    to<common<Ts...>>(vs)...})};
}

// generic array constructor, with specified size
template <usz Size, pod... Ts>
	requires((sizeof...(Ts) > 0) && (Size >= sizeof...(Ts)) && are_common<Ts...>)
[[nodiscard]] inline constexpr array<common<Ts...>, Size>
arr(Ts const &...vs) noexcept {
	return {
	    tup(_impl::_array::storage<common<Ts...>, Size>{to<common<Ts...>>(vs)...})};
}

// generic array constructor, with specified type
template <pod T, converts_to<T>... Ts>
[[nodiscard]] inline constexpr array<T, sizeof...(Ts)>
arr(Ts const &...vs) noexcept {
	return {tup(_impl::_array::storage<T, sizeof...(Ts)>{to<T>(vs)...})};
}

// generic array constructor, with specified size and type
template <pod T, usz Size, converts_to<T>... Ts>
	requires(Size >= sizeof...(Ts))
[[nodiscard]] inline constexpr array<T, Size>
arr(Ts const &...vs) noexcept {
	return {tup(_impl::_array::storage<T, Size>{to<T>(vs)...})};
}

pd::obj: generic object constructor (L7735)

template <of_kt... Schema>
[[nodiscard]] constexpr object<Schema...>
obj() noexcept {
	return {};
}

template <of_kt... Schema, pod... Ts>
[[nodiscard]] constexpr object<Schema...>
obj(Ts const &...vs) noexcept {
	return {tup(vs...)};
}

template <unconstrained::string_literal... Keys, pod... Ts>
	requires(sizeof...(Keys) == sizeof...(Ts))
[[nodiscard]] constexpr object<kt<Keys, Ts>...>
obj(Ts const &...vs) noexcept {
	return {tup(vs...)};
}

pd::optional: a boolean-testable, pod value wrapper (L7799)

// a boolean-testable, pod value wrapper
template <pod T, of_kt... D>
struct optional : object<kt<"value", T>, kt<"has_value", boolean>, D...> {
	bool operator==(optional const &) const = default;

	using value_type = T;

	[[nodiscard]] constexpr T &
	value() noexcept {
		return get<"value">(*this);
	}

	[[nodiscard]] constexpr T const &
	value() const noexcept {
		return get<"value">(*this);
	}

	[[nodiscard]] constexpr bool
	has_value() const noexcept {
		return get<"has_value">(*this);
	}

	[[nodiscard]] constexpr operator bool() const noexcept {
		return has_value();
	}

	[[nodiscard]] constexpr T &
	operator*() noexcept {
		return value();
	}

	[[nodiscard]] constexpr T const &
	operator*() const noexcept {
		return value();
	}

	[[nodiscard]] constexpr T *
	operator->() noexcept {
		return std::addressof(value());
	}

	[[nodiscard]] constexpr T const *
	operator->() const noexcept {
		return std::addressof(value());
	}

	template <pod ThatT, of_kt... ThatDeriv>
		requires converts_to<T, ThatT>
	[[nodiscard]] constexpr operator optional<ThatT, ThatDeriv...>() const noexcept {
		return {tup(static_cast<ThatT>(get<"value">(*this)),
		            get<"has_value">(*this))};
	}
};

// specialization for optional<struct nil>, which never has a value.
// implicitly convertible to any optional type (as falsy).
template <of_kt... D>
struct optional<struct nil, D...> : object<D...> {
	bool operator==(optional const &) const = default;

	using value_type = struct nil;

	[[nodiscard]] constexpr bool
	has_value() const noexcept {
		return false;
	}

	[[nodiscard]] constexpr operator bool() const noexcept {
		return false;
	}

	template <pod ThatT, of_kt... ThatDeriv>
	[[nodiscard]] constexpr operator optional<ThatT, ThatDeriv...>() const noexcept {
		if constexpr (is<ThatT, struct nil>)
			return {};
		else
			return {tup(ThatT{}, boolean{false})};
	}
};

pd::opt: shorthand for pd::to<optional> (L7909)

template <pod T>
[[nodiscard]] inline constexpr auto
opt(T const &v) noexcept {
	if constexpr (is<T, struct nil>)
		return optional<struct nil>{};
	else
		return optional<T>{tup(v, boolean{true})};
}

data.memory

pd::bitset: a fixed-size, packed range of bits (L8502)

// a fixed-size, random access range of bits, packed within 8-bit integers.
template <usz N, of_kt... Deriv>
	requires(N > 0)
struct bitset : array<u8, (N + 8 - 1) / 8, Deriv...> {
	bool operator==(bitset const &) const = default;

	// number of bytes contained within
	static constexpr usz underlying_size = (N + 8 - 1) / 8;

	using value_type      = bool;
	using size_type       = usz;
	using iterator        = _impl::_bitset::iterator;
	using const_iterator  = _impl::_bitset::const_iterator;
	using reference       = iterator::reference;
	using const_reference = const_iterator::reference;
	using pointer         = iterator;
	using const_pointer   = const_iterator;

	[[nodiscard]] constexpr iterator
	begin() noexcept {
		return iterator{this->data(), 0};
	}

	[[nodiscard]] constexpr const_iterator
	begin() const noexcept {
		return const_iterator{this->data(), 0};
	}

	[[nodiscard]] constexpr const_iterator
	cbegin() const noexcept {
		return begin();
	}

	[[nodiscard]] constexpr iterator
	end() noexcept {
		return begin() + size();
	}

	[[nodiscard]] constexpr const_iterator
	end() const noexcept {
		return cbegin() + size();
	}

	[[nodiscard]] constexpr const_iterator
	cend() const noexcept {
		return end();
	}

	[[nodiscard]] static constexpr usz
	size() noexcept {
		return N;
	}

	[[nodiscard]] constexpr reference
	operator[](size_type idx) noexcept {
		return begin()[idx];
	}

	[[nodiscard]] constexpr const_reference
	operator[](size_type idx) const noexcept {
		return begin()[idx];
	}

	// efficiently sets all bits from begin to end to value
	inline friend constexpr void
	fill(bitset &o, bool v) noexcept {
		fill(o.begin(), o.end(), v);
	}

	// effciently finds the first index of bool value
	[[nodiscard]] inline friend constexpr iterator
	find(bitset &o, bool v) noexcept {
		return find(o.begin(), o.end(), v);
	}

	// effciently finds the first index of bool value
	[[nodiscard]] inline friend constexpr const_iterator
	find(bitset const &o, bool v) noexcept {
		return find(o.cbegin(), o.cend(), v);
	}

	// efficiently copies bits from a bitset to a bitset iterator
	friend constexpr iterator
	copy(bitset const &src, iterator dst) noexcept {
		return copy(src.begin(), src.end(), dst);
	}

	template <usz UN, of_kt... UD>
		requires(N <= UN)
	[[nodiscard]] constexpr operator bitset<UN, UD...>() const noexcept {
		bitset<UN, UD...> res{};
		std::ranges::copy(as<array<u8, (N + 8 - 1) / 8, Deriv...> const &>(*this),
		                  res.data());
		return res;
	}
};

pd::dyn: a dynamic ienv context generator (L9140)

// a dynamic ienv context generator.
//
// provides a solution for working with multiple kinds of iallocated
// data using an ienv and base object. dyn is essentially a functor
// that creates a non-pod class that has access to memory management
// but stores its state in a pod object.
//
// an example:
//
//   // see pd::dyn_ctx for the full requirements
//   struct simple_ctx {
//       using base_type = object<kt<"id", id<i32>>>;
//
//       // type can be as complicated as it needs to be, as long
//       // as it can be deduced via braced-init construction
//       template <of_ienv<i32> Env>
//       struct type {
//           constexpr type(base_type &self, Env &env)
//                   : _self{self}, _env{env} {
//               if (!get<"id">(_self))
//                   get<"id">(_self) = env.template allocate<i32>(1);
//           }
//
//           constexpr void
//           relocate() noexcept {
//               auto next_id   = _env.template allocate<i32>(1);
//               *_env[next_id] = *(*this);
//               _env.deallocate(get<"id">(_self), 1);
//               get<"id">(_self) = next_id;
//           }
//
//           constexpr i32 &
//           operator*() noexcept {
//               return *_env[get<"id">(_self)];
//           }
//
//         private:
//           base_type &_self;
//           Env       &_env;
//       };
//   };
//
//   struct simple : dyn<simple_ctx, i32> { // simple inherits from dyn, which
//                                          // inherits from simple_ctx::base_type.
//                                          // its context requires an ienv that
//                                          // can allocate i32, at least.
//       constexpr pd::id<i32>
//       id() const noexcept {
//           return get<"id">(*this);
//       }
//   };
//
//   ialloc<i32, 4>      m{};    // actual storage pod
//   unconstrained::ienv env{m}; // non-pod working environment
//
//   auto foo = simple{};
//   auto ctx = foo(env); // dyn::operator() to create a new context
//                        // calls Ctx::type::type()
//
//   *ctx = 42;                  // ctx has full access to memory
//   expect(*m[foo.id()] == 42); // changes are reflected in the iallocator
//
//   auto prev_id = foo.id();
//   ctx.relocate();
//   expect(foo.id() != prev_id); // changes are reflected in the base object
template <dyn_ctx Ctx, pod... EnvT>
struct dyn : Ctx::base_type {
	template <of_ienv<EnvT...> Env>
	using ctx = _impl::_dyn::traits<Ctx, EnvT...>::template ctx<Env>::type;

	[[nodiscard]] constexpr auto
	operator()(of_ienv<EnvT...> auto &ienv) noexcept {
		return typename Ctx::type{*this, ienv};
	}

	[[nodiscard]] constexpr auto
	operator()(of_ienv<EnvT...> auto const &ienv) const noexcept {
		return typename Ctx::type{*this, ienv};
	}
};

pd::id: a typed, boolean-testable, index-based allocation id (L9229)

// a typed, boolean-testable, index-based allocation id.
// must be constructed with a 1-indexed value, as id{} is falsy.
// the underlying value is subtracted by one when accessing index.
template <pod T, of_kt... D>
struct id : object<kt<"ofs", usz>, D...> {
	using value_type = T;

	// comparable with itself
	bool operator==(id const &) const = default;

	// falsy if ofs is zero (falsy by default)
	[[nodiscard]] constexpr operator bool() const noexcept {
		return !!get<"ofs">(*this);
	}

	// access the offset (removes 1-indexing)
	[[nodiscard]] constexpr usz
	idx() const noexcept {
		return get<"ofs">(*this) - 1;
	}
};

pd::ialloc: a fixed-size, pd::id-based, pod allocator (L9269)

// a fixed-size, `pd::id`-based, pod memory allocator that stores elements internally.
// unlike pointers, IDs are pod and do not depend on the actual location of the
// underlying storage; ialloc objects can be moved without invalidating allocated IDs.
template <pod T, usz Cap, of_kt... D>
	requires(Cap > 0)
struct ialloc : object<kt<"alloc", bitset<Cap>>, kt<"data", array<T, Cap>>, D...> {

	using value_type      = T;
	using pointer         = T *;
	using reference       = T &;
	using const_pointer   = T const *;
	using const_reference = T const &;

	using id = pd::id<T>;

	// total available memory slots
	[[nodiscard]] static constexpr usz
	capacity() noexcept {
		return Cap;
	}

	// number of memory slots filled
	[[nodiscard]] constexpr usz
	size() const noexcept {
		return std::ranges::count(get<"alloc">(*this), true);
	}

	// allocate enough space for n contiguous elements.
	// returns id{} if not enough space is available.
	[[nodiscard]] constexpr id
	allocate(usz n) noexcept {
		if (n == 0)
			return id{};

		auto &alloc = get<"alloc">(*this);

		auto b = find(alloc, false);
		auto e = find(b, alloc.end(), true);

		while (static_cast<usz>(e - b) < n) {
			if (b == e)
				return id{};
			b = find(e, alloc.end(), false);
			e = find(b, alloc.end(), true);
		}

		if (b != e) {
			fill(b, b + n, true);
			return id{tup(b - alloc.begin() + 1)};
		}

		return id{};
	}

	// deallocate n elements given a previously-allocated id.
	constexpr void
	deallocate(id i, usz n) noexcept {
		auto b = get<"alloc">(*this).begin() + i.idx();
		fill(b, b + n, false);
	}

	// access the underlying storage given an id.
	[[nodiscard]] constexpr pointer
	operator[](id i) noexcept {
		return get<"data">(*this).data() + i.idx();
	}

	// access the underlying storage given an id
	[[nodiscard]] constexpr const_pointer
	operator[](id i) const noexcept {
		return get<"data">(*this).data() + i.idx();
	}
};

pd::unconstrained::ienv: a non-pod collection of iallocator references (L9534)

namespace unconstrained {

	// a non-pod collection of iallocator references.
	//
	// simplifies procedures that involve multiple types of iallocated data.
	// each allocator must have a distinct type.
	//
	// example:
	//
	//   ialloc<i32, 4> im{};
	//   ialloc<u8, 8>  um{};
	//   unconstrained::ienv m{im, um};
	//
	//   auto foo = m.allocate<i32>();
	//   auto bar = m.allocate<u8>();
	//
	//   *m[foo] = 42;
	//   *m[bar] = 0xFF;
	//
	//   expect(*im[foo] == 42);
	//   expect(*um[bar] == 0xFF);
	template <iallocates... Alloc>
		requires(sizeof...(Alloc) > 0) &&
	                (ty<typename Alloc::value_type...>::template distinct<>)
	struct ienv {
		bool operator==(ienv const &) const = default;

		using allocators = ty<Alloc...>;
		using types      = ty<typename Alloc::value_type...>;

		[[nodiscard]] constexpr ienv(Alloc &...alloc) noexcept : _m{&alloc...} {
		}

		template <pod T>
			requires(types::template has<T>)
		[[nodiscard]] constexpr T *
		operator[](id<T> id) noexcept {
			return (*std::get<types::template find<T>>(_m))[id];
		}

		template <pod T>
			requires(types::template has<T>)
		[[nodiscard]] constexpr T const *
		operator[](id<T> id) const noexcept {
			return (*std::get<types::template find<T>>(_m))[id];
		}

		template <pod T>
			requires(types::template has<T>)
		[[nodiscard]] constexpr usz
		capacity() const noexcept {
			return std::get<types::template find<T>>(_m)->capacity();
		}

		template <pod T>
			requires(types::template has<T>)
		[[nodiscard]] constexpr usz
		size() const noexcept {
			return std::get<types::template find<T>>(_m)->size();
		}

		template <pod T>
			requires(types::template has<T>)
		[[nodiscard]] constexpr id<T>
		allocate(usz n) noexcept {
			return std::get<types::template find<T>>(_m)->allocate(n);
		}

		template <pod T>
			requires(types::template has<T>)
		constexpr void
		deallocate(id<T> i, usz n) noexcept {
			return std::get<types::template find<T>>(_m)->deallocate(i, n);
		}

	   private:
		std::tuple<Alloc *...> _m;
	};

} // namespace unconstrained

pd::unconstrained::iptr: a non-owning ienv / iallocated pointer wrapper (L9756)

namespace unconstrained {

	// a non-owning ienv / iallocated pointer wrapper.
	// provides protected access to the underlying ienv.
	//
	// example
	//
	//   using unconstrained::ienv;
	//   using unconstrained::iptr;
	//
	//   ialloc<i32, 4> imem{};
	//   ienv           env{imem};
	//
	//   auto id = env.allocate<i32>(1);
	//   iptr p{env, id};
	//   *p = 42;
	//   expect(*imem[p.base()] == 42);
	//   expect(p.modifiable);
	//
	//   auto const &cenv = env;
	//
	//   iptr c{cenv, p.base()};
	//   expect(!c.modifiable);
	//   expect(*c == 42);
	template <class T, of_ienv<T> Env>
		requires pod<rm_const<T>>
	struct iptr {
		bool operator==(iptr const &) const = default;

		using env_type = rm_const<Env>;

		static constexpr bool modifiable = !std::is_const_v<Env>;

		using value_type           = T;
		using pointer              = cond<modifiable, T, T const> *;
		using reference            = cond<modifiable, T, T const> &;
		using const_pointer        = T const *;
		using const_reference      = T const &;
		using base_reference       = cond<modifiable, id<T>, id<T> const> &;
		using const_base_reference = id<T> const &;

		[[nodiscard]] constexpr iptr(Env &env, base_reference id) noexcept
		    : _env{&env},
		      _id{&id} {
		}

		constexpr iptr()                                      = delete;
		constexpr iptr(iptr const &)                          = default;
		[[nodiscard]] constexpr iptr(iptr &&)                 = default;
		constexpr ~iptr()                                     = default;
		constexpr iptr               &operator=(iptr const &) = default;
		[[nodiscard]] constexpr iptr &operator=(iptr &&)      = default;

		[[nodiscard]] constexpr reference
		operator*() noexcept {
			return *env()[base()];
		}

		[[nodiscard]] constexpr const_reference
		operator*() const noexcept {
			return *env()[base()];
		}

		[[nodiscard]] constexpr base_reference
		base() noexcept {
			return *_id;
		}

		[[nodiscard]] constexpr const_base_reference
		base() const noexcept {
			return *_id;
		}

	   protected:
		[[nodiscard]] constexpr Env &
		env() noexcept {
			return *_env;
		}

		[[nodiscard]] constexpr Env const &
		env() const noexcept {
			return *_env;
		}

	   private:
		Env                    *_env;
		rm_ref<base_reference> *_id;
	};

	template <class T, of_ienv<T> Env>
		requires pod<rm_const<T>>
	iptr(Env &, id<T> &) -> iptr<T, Env>;

	template <class T, of_ienv<T> Env>
		requires pod<rm_const<T>>
	iptr(Env const &, id<T> const &) -> iptr<T, Env const>;

} // namespace unconstrained

pd::unconstrained::ispan: a non-owning ienv / iallocated span wrapper (L9883)

namespace unconstrained {

	// a non-owning ienv / iallocated span wrapper.
	//
	// example
	//
	//   using unconstrained::ispan;
	//   using unconstrained::ienv;
	//
	//   ialloc<i32, 4> imem{};
	//   ienv           env{imem};
	//
	//   ispan sp{env, env.allocate<i32>(3), 3};
	//   expect(sp.modifiable);
	//
	//   sp[0] = 42;
	//   sp[1] = 64;
	//   sp[2] = sp.size();
	//
	//   auto const &cenv = env;
	//
	//   ispan cspan{cenv, sp.base(), sp.size()};
	//
	//   auto arr = std::array{42, 64, 3};
	//
	//   expect(is<decltype(cspan), ispan<i32, ienv<ialloc<i32, 4>> const>>);
	//   expect(std::ranges::equal(cspan, arr));
	//
	//   expect(cspan == arr);
	//   expect(cspan < std::array{42, 64, 4});
	//   expect(cspan > std::array{42, 64, 2});
	template <pod T, of_ienv<T> Env>
	struct ispan : iptr<T, Env> {
		static constexpr bool modifiable = iptr<T, Env>::modifiable;

		using value_type             = iptr<T, Env>::value_type;
		using pointer                = iptr<T, Env>::pointer;
		using const_pointer          = iptr<T, Env>::const_pointer;
		using reference              = iptr<T, Env>::reference;
		using const_reference        = iptr<T, Env>::const_reference;
		using base_reference         = iptr<T, Env>::base_reference;
		using const_base_reference   = iptr<T, Env>::const_base_reference;
		using iterator               = pointer;
		using const_iterator         = const_pointer;
		using reverse_iterator       = std::reverse_iterator<pointer>;
		using const_reverse_iterator = std::reverse_iterator<const_pointer>;
		using size_type              = usz;

		using size_reference = cond<modifiable, size_type, size_type const> &;
		using const_size_reference = size_type const &;

		[[nodiscard]] constexpr ispan(Env &env, base_reference data,
		                              size_reference sz) noexcept
		    : iptr<T, Env>{env, data},
		      _sz{&sz} {
		}

		constexpr ispan()                                       = delete;
		constexpr ispan(ispan const &)                          = default;
		[[nodiscard]] constexpr ispan(ispan &&)                 = default;
		constexpr ~ispan()                                      = default;
		constexpr ispan               &operator=(ispan const &) = default;
		[[nodiscard]] constexpr ispan &operator=(ispan &&)      = default;

		[[nodiscard]] constexpr size_reference
		size() noexcept {
			return *_sz;
		}

		[[nodiscard]] constexpr const_size_reference
		size() const noexcept {
			return *_sz;
		}

		[[nodiscard]] constexpr pointer
		data() noexcept {
			return &*(*this);
		}

		[[nodiscard]] constexpr const_pointer
		data() const noexcept {
			return &*(*this);
		}

		[[nodiscard]] constexpr iterator
		begin() noexcept {
			return data();
		}

		[[nodiscard]] constexpr const_iterator
		begin() const noexcept {
			return data();
		}

		[[nodiscard]] constexpr const_iterator
		cbegin() const noexcept {
			return data();
		}

		[[nodiscard]] constexpr iterator
		end() noexcept {
			return data() + size();
		}

		[[nodiscard]] constexpr const_iterator
		end() const noexcept {
			return data() + size();
		}

		[[nodiscard]] constexpr const_iterator
		cend() const noexcept {
			return data() + size();
		}

		[[nodiscard]] constexpr reverse_iterator
		rbegin() noexcept {
			return reverse_iterator{end()};
		}

		[[nodiscard]] constexpr const_reverse_iterator
		rbegin() const noexcept {
			return const_reverse_iterator{end()};
		}

		[[nodiscard]] constexpr const_reverse_iterator
		crbegin() const noexcept {
			return const_reverse_iterator{end()};
		}

		[[nodiscard]] constexpr reverse_iterator
		rend() noexcept {
			return reverse_iterator{begin()};
		}

		[[nodiscard]] constexpr const_reverse_iterator
		rend() const noexcept {
			return const_reverse_iterator{begin()};
		}

		[[nodiscard]] constexpr const_reverse_iterator
		crend() const noexcept {
			return const_reverse_iterator{begin()};
		}

		[[nodiscard]] constexpr reference
		operator[](size_type idx) noexcept {
			return data()[idx];
		}

		[[nodiscard]] constexpr const_reference
		operator[](size_type idx) const noexcept {
			return data()[idx];
		}

		template <spans<value_type> Rng>
			requires std::three_way_comparable<value_type>
		[[nodiscard]] constexpr auto
		operator<=>(Rng const &r) const noexcept {
			return std::lexicographical_compare_three_way(begin(), end(),
			                                              r.begin(), r.end());
		}

		template <spans<value_type> Rng>
			requires std::equality_comparable<value_type>
		[[nodiscard]] constexpr bool
		operator==(Rng const &r) const noexcept {
			return std::ranges::equal(*this, r);
		}

		template <spans<value_type> Rng>
			requires std::equality_comparable<value_type>
		[[nodiscard]] constexpr bool
		operator!=(Rng const &r) const noexcept {
			return !std::ranges::equal(*this, r);
		}

	   private:
		rm_ref<size_reference> *_sz;
	};

	template <class T, of_ienv<T> Env>
		requires pod<rm_const<T>>
	ispan(Env &, id<T> &, usz &) -> ispan<T, Env>;

	template <class T, of_ienv<T> Env>
		requires pod<rm_const<T>>
	ispan(Env const &, id<T> const &, usz const &) -> ispan<T, Env const>;

} // namespace unconstrained

data.sequences

pd::vector: a dynamically-resizable pod array (L10232)

template <class T>
struct vector_ctx {
	using base_type = object<kt<"data", id<T>>, kt<"sz", usz>, kt<"cap", usz>>;

	template <of_ienv<T> Env>
	struct type;

	template <of_ienv<T> Env>
		requires(!std::is_const_v<Env>)
	struct type<Env> : unconstrained::ispan<T, Env> {
		using value_type      = T;
		using size_type       = usz;
		using pointer         = T *;
		using const_pointer   = T const *;
		using reference       = T &;
		using const_reference = T const &;
		using iterator        = T *;
		using const_iterator  = T const *;

		template <of_kt... D>
		[[nodiscard]] constexpr type(base_type &self, Env &e) noexcept
		    : unconstrained::ispan<T, Env>{e, get<"data">(self), get<"sz">(self)},
		      _self{&self} {
		}

		[[nodiscard]] constexpr size_type
		capacity() const noexcept {
			return get<"cap">(*_self);
		}

		[[nodiscard]] constexpr bool
		reserve(usz sz) noexcept {
			auto cap = next_pow2(sz);
			if (cap > capacity()) {
				auto next = this->env().template allocate<T>(cap);
				if (!next)
					return false;

				auto oit = this->env()[next];
				for (usz i = 0; i < this->size(); ++i)
					*oit++ = this->data()[i];
				if (this->base())
					this->env().deallocate(this->base(), capacity());
				this->base()       = next;
				get<"cap">(*_self) = cap;
			}
			return true;
		}

		[[nodiscard]] constexpr bool
		resize(usz sz) noexcept {
			if (sz > this->size()) {
				if (!reserve(sz))
					return false;
				auto p = this->env()[this->base()];
				std::fill(p + this->size(), p + sz, T{});
			}

			this->size() = sz;
			return true;
		}

		[[nodiscard]] constexpr bool
		push_back(T const &v) noexcept {
			if (!reserve(this->size() + 1))
				return false;

			this->data()[this->size()] = v;
			++this->size();

			return true;
		}

		constexpr void
		pop_back() noexcept {
			--this->size();
		}

		constexpr void
		clear() noexcept {
			this->env().deallocate(this->base(), capacity());
			this->base()       = {};
			this->size()       = {};
			get<"cap">(*_self) = 0;
		}

	   private:
		base_type *_self;
	};

	template <of_ienv<T> Env>
		requires(std::is_const_v<Env>)
	struct type<Env> : unconstrained::ispan<T, Env> {
		using value_type      = T;
		using size_type       = usz;
		using pointer         = T const *;
		using const_pointer   = T const *;
		using reference       = T const &;
		using const_reference = T const &;
		using iterator        = T const *;
		using const_iterator  = T const *;

		template <of_kt... D>
		[[nodiscard]] constexpr type(base_type const &self, Env const &e) noexcept
		    : unconstrained::ispan<T, Env>{e, get<"data">(self), get<"sz">(self)},
		      _cap{get<"cap">(self)} {
		}

		[[nodiscard]] constexpr size_type
		capacity() const noexcept {
			return _cap;
		}

	   private:
		size_type _cap;
	};

	template <of_ienv<T> Env, class Self>
	type(Self &, Env &) -> type<Env>;

	template <of_ienv<T> Env, class Self>
	type(Self const &, Env const &) -> type<Env const>;
};

// a dynamically-resizable pod array.
//
// use operator() to access the memory interface, which is a
// non-pod wrapper around a vector/iallocator reference pair.
template <pod T, class Ctx = vector_ctx<T>>
struct vector : dyn<Ctx> {
	using value_type      = T;
	using size_type       = usz;
	using pointer         = T *;
	using const_pointer   = T const *;
	using reference       = T &;
	using const_reference = T const &;
	using iterator        = T *;
	using const_iterator  = T const *;

	[[nodiscard]] constexpr id<T>
	data() const noexcept {
		return get<"data">(*this);
	}

	[[nodiscard]] constexpr size_type
	size() const noexcept {
		return get<"sz">(*this);
	}

	[[nodiscard]] constexpr size_type
	capacity() const noexcept {
		return get<"cap">(*this);
	}
};