pyurd 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. pyurd/__init__.py +95 -0
  2. pyurd/__main__.py +51 -0
  3. pyurd/include/urd/bits.hpp +36 -0
  4. pyurd/include/urd/config.hpp +47 -0
  5. pyurd/include/urd/cost.hpp +313 -0
  6. pyurd/include/urd/elements/builder.hpp +12 -0
  7. pyurd/include/urd/elements/children.hpp +120 -0
  8. pyurd/include/urd/elements/concepts.hpp +189 -0
  9. pyurd/include/urd/elements/data.hpp +33 -0
  10. pyurd/include/urd/elements/equal_to.hpp +183 -0
  11. pyurd/include/urd/elements/flat.hpp +151 -0
  12. pyurd/include/urd/elements/hash.hpp +249 -0
  13. pyurd/include/urd/elements/hash_dynamic_bitset.hpp +26 -0
  14. pyurd/include/urd/elements/index.hpp +98 -0
  15. pyurd/include/urd/elements/less.hpp +272 -0
  16. pyurd/include/urd/elements/prehashed.hpp +68 -0
  17. pyurd/include/urd/elements/requirements.hpp +148 -0
  18. pyurd/include/urd/elements/subset_view.hpp +57 -0
  19. pyurd/include/urd/elements/view.hpp +499 -0
  20. pyurd/include/urd/format.hpp +174 -0
  21. pyurd/include/urd/kinds.hpp +467 -0
  22. pyurd/include/urd/memory.hpp +105 -0
  23. pyurd/include/urd/property_set.hpp +367 -0
  24. pyurd/include/urd/python/bindings.hpp +89 -0
  25. pyurd/include/urd/python/caster.hpp +67 -0
  26. pyurd/include/urd/python/flat.hpp +136 -0
  27. pyurd/include/urd/python/maps/flat_map.hpp +14 -0
  28. pyurd/include/urd/python/maps/index_map.hpp +14 -0
  29. pyurd/include/urd/python/maps/ordered_map.hpp +14 -0
  30. pyurd/include/urd/python/maps/parallel_hash_map.hpp +16 -0
  31. pyurd/include/urd/python/maps/std_map.hpp +16 -0
  32. pyurd/include/urd/python/maps/unordered_flat_map.hpp +14 -0
  33. pyurd/include/urd/python/owner.hpp +67 -0
  34. pyurd/include/urd/python/ranges.hpp +79 -0
  35. pyurd/include/urd/python/sequences/segmented.hpp +14 -0
  36. pyurd/include/urd/python/sequences/sequence.hpp +14 -0
  37. pyurd/include/urd/python/values/dense_set.hpp +14 -0
  38. pyurd/include/urd/python/values/flat_set.hpp +14 -0
  39. pyurd/include/urd/python/values/indexed.hpp +14 -0
  40. pyurd/include/urd/python/values/parallel_hash_set.hpp +16 -0
  41. pyurd/include/urd/python/values/sparse_set.hpp +14 -0
  42. pyurd/include/urd/python/values/std_set.hpp +16 -0
  43. pyurd/include/urd/python/values/unordered_flat_set.hpp +14 -0
  44. pyurd/include/urd/registry/declaration.hpp +148 -0
  45. pyurd/include/urd/registry/declarations.hpp +179 -0
  46. pyurd/include/urd/registry/derived.hpp +65 -0
  47. pyurd/include/urd/registry/facts.hpp +213 -0
  48. pyurd/include/urd/registry/scan.hpp +87 -0
  49. pyurd/include/urd/registry/select.hpp +133 -0
  50. pyurd/include/urd/registry/table.hpp +24 -0
  51. pyurd/include/urd/repr/containers/bit.hpp +304 -0
  52. pyurd/include/urd/repr/containers/bit_rows.hpp +196 -0
  53. pyurd/include/urd/repr/containers/bitset_span.hpp +631 -0
  54. pyurd/include/urd/repr/containers/byte_arena.hpp +128 -0
  55. pyurd/include/urd/repr/containers/detail/geometric_segment_layout.hpp +59 -0
  56. pyurd/include/urd/repr/containers/detail/segmented_rows.hpp +96 -0
  57. pyurd/include/urd/repr/containers/detail/threading.hpp +114 -0
  58. pyurd/include/urd/repr/containers/detail/tracking.hpp +59 -0
  59. pyurd/include/urd/repr/containers/schema_rows.hpp +324 -0
  60. pyurd/include/urd/repr/containers/segment_sizes.hpp +40 -0
  61. pyurd/include/urd/repr/containers/segmented_vector.hpp +384 -0
  62. pyurd/include/urd/repr/containers/spin_mutex.hpp +143 -0
  63. pyurd/include/urd/repr/containers/uniform_rows.hpp +210 -0
  64. pyurd/include/urd/repr/core/boost_memory.hpp +44 -0
  65. pyurd/include/urd/repr/core/gtl_memory.hpp +61 -0
  66. pyurd/include/urd/repr/core/gtl_spin_lock.hpp +28 -0
  67. pyurd/include/urd/repr/core/numbered.hpp +58 -0
  68. pyurd/include/urd/repr/core/position_iterator.hpp +124 -0
  69. pyurd/include/urd/repr/core/std_memory.hpp +120 -0
  70. pyurd/include/urd/repr/maps/bidirectional_csr.hpp +90 -0
  71. pyurd/include/urd/repr/maps/csr_multimap.hpp +24 -0
  72. pyurd/include/urd/repr/maps/dense_groups.hpp +117 -0
  73. pyurd/include/urd/repr/maps/dense_map_fwd.hpp +164 -0
  74. pyurd/include/urd/repr/maps/dense_multimap.hpp +24 -0
  75. pyurd/include/urd/repr/maps/flat_map.hpp +31 -0
  76. pyurd/include/urd/repr/maps/flat_map_fwd.hpp +87 -0
  77. pyurd/include/urd/repr/maps/grouped_fwd.hpp +183 -0
  78. pyurd/include/urd/repr/maps/grouped_multimap.hpp +255 -0
  79. pyurd/include/urd/repr/maps/index_groups.hpp +115 -0
  80. pyurd/include/urd/repr/maps/index_map.hpp +506 -0
  81. pyurd/include/urd/repr/maps/ordered_map.hpp +209 -0
  82. pyurd/include/urd/repr/maps/ordered_map_fwd.hpp +89 -0
  83. pyurd/include/urd/repr/maps/parallel_hash_map.hpp +135 -0
  84. pyurd/include/urd/repr/maps/parallel_hash_map_fwd.hpp +98 -0
  85. pyurd/include/urd/repr/maps/postings.hpp +403 -0
  86. pyurd/include/urd/repr/maps/std_map.hpp +178 -0
  87. pyurd/include/urd/repr/maps/unordered_flat_map.hpp +25 -0
  88. pyurd/include/urd/repr/maps/unordered_flat_map_fwd.hpp +49 -0
  89. pyurd/include/urd/repr/partitions/disjoint_sets.hpp +115 -0
  90. pyurd/include/urd/repr/partitions/disjoint_sets_fwd.hpp +48 -0
  91. pyurd/include/urd/repr/pools/pools.hpp +460 -0
  92. pyurd/include/urd/repr/pools/pools_fwd.hpp +181 -0
  93. pyurd/include/urd/repr/pools/tree_bitset.hpp +616 -0
  94. pyurd/include/urd/repr/pools/tree_vector.hpp +309 -0
  95. pyurd/include/urd/repr/queues/addressable_heap.hpp +182 -0
  96. pyurd/include/urd/repr/queues/bucket_queue.hpp +114 -0
  97. pyurd/include/urd/repr/queues/queue.hpp +129 -0
  98. pyurd/include/urd/repr/queues/queues_fwd.hpp +203 -0
  99. pyurd/include/urd/repr/queues/ring_fifo.hpp +156 -0
  100. pyurd/include/urd/repr/records/arena.hpp +129 -0
  101. pyurd/include/urd/repr/records/records.hpp +132 -0
  102. pyurd/include/urd/repr/records/records_fwd.hpp +163 -0
  103. pyurd/include/urd/repr/sequences/jagged.hpp +323 -0
  104. pyurd/include/urd/repr/sequences/row_pool.hpp +173 -0
  105. pyurd/include/urd/repr/sequences/rows_fwd.hpp +180 -0
  106. pyurd/include/urd/repr/sequences/segmented.hpp +29 -0
  107. pyurd/include/urd/repr/sequences/segmented_fwd.hpp +81 -0
  108. pyurd/include/urd/repr/sequences/sequence.hpp +55 -0
  109. pyurd/include/urd/repr/slots/slots.hpp +249 -0
  110. pyurd/include/urd/repr/slots/slots_fwd.hpp +86 -0
  111. pyurd/include/urd/repr/values/dense_set.hpp +362 -0
  112. pyurd/include/urd/repr/values/dense_set_fwd.hpp +157 -0
  113. pyurd/include/urd/repr/values/flat_set.hpp +41 -0
  114. pyurd/include/urd/repr/values/flat_set_fwd.hpp +94 -0
  115. pyurd/include/urd/repr/values/indexed.hpp +663 -0
  116. pyurd/include/urd/repr/values/indexed_fwd.hpp +145 -0
  117. pyurd/include/urd/repr/values/parallel_hash_set.hpp +32 -0
  118. pyurd/include/urd/repr/values/parallel_hash_set_fwd.hpp +103 -0
  119. pyurd/include/urd/repr/values/sparse_set.hpp +198 -0
  120. pyurd/include/urd/repr/values/std_set.hpp +109 -0
  121. pyurd/include/urd/repr/values/unordered_flat_set.hpp +25 -0
  122. pyurd/include/urd/repr/values/unordered_flat_set_fwd.hpp +49 -0
  123. pyurd/include/urd/repr/wrappers/layer.hpp +275 -0
  124. pyurd/include/urd/repr/wrappers/layered_fwd.hpp +70 -0
  125. pyurd/include/urd/repr/wrappers/layered_pool.hpp +311 -0
  126. pyurd/include/urd/repr/wrappers/recycling.hpp +281 -0
  127. pyurd/include/urd/repr/wrappers/recycling_fwd.hpp +152 -0
  128. pyurd/include/urd/repr/wrappers/scoped.hpp +227 -0
  129. pyurd/include/urd/repr/wrappers/scoped_fwd.hpp +122 -0
  130. pyurd/include/urd/spec.hpp +231 -0
  131. pyurd/include/urd/store/appending.hpp +85 -0
  132. pyurd/include/urd/store/assigning.hpp +41 -0
  133. pyurd/include/urd/store/capacity.hpp +35 -0
  134. pyurd/include/urd/store/contiguous_data.hpp +40 -0
  135. pyurd/include/urd/store/ends.hpp +66 -0
  136. pyurd/include/urd/store/entries.hpp +39 -0
  137. pyurd/include/urd/store/equality.hpp +40 -0
  138. pyurd/include/urd/store/grouping.hpp +47 -0
  139. pyurd/include/urd/store/insertion.hpp +67 -0
  140. pyurd/include/urd/store/inverse.hpp +30 -0
  141. pyurd/include/urd/store/iteration.hpp +89 -0
  142. pyurd/include/urd/store/layers.hpp +97 -0
  143. pyurd/include/urd/store/lookup.hpp +77 -0
  144. pyurd/include/urd/store/multi_values.hpp +39 -0
  145. pyurd/include/urd/store/ordered_lookup.hpp +43 -0
  146. pyurd/include/urd/store/ordering.hpp +43 -0
  147. pyurd/include/urd/store/pool.hpp +225 -0
  148. pyurd/include/urd/store/positions.hpp +137 -0
  149. pyurd/include/urd/store/queue_access.hpp +101 -0
  150. pyurd/include/urd/store/records.hpp +126 -0
  151. pyurd/include/urd/store/removal.hpp +85 -0
  152. pyurd/include/urd/store/scopes.hpp +53 -0
  153. pyurd/include/urd/store/set_algebra.hpp +89 -0
  154. pyurd/include/urd/store/shape.hpp +59 -0
  155. pyurd/include/urd/store/sizing.hpp +57 -0
  156. pyurd/include/urd/store/slots.hpp +180 -0
  157. pyurd/include/urd/store/store_facts.hpp +51 -0
  158. pyurd/include/urd/store/types.hpp +170 -0
  159. pyurd/include/urd/store/values_by_key.hpp +110 -0
  160. pyurd/include/urd/store.hpp +266 -0
  161. pyurd/include/urd/tags.hpp +1226 -0
  162. pyurd/include/urd/testing/allocations.cpp +51 -0
  163. pyurd/include/urd/testing/allocations.hpp +91 -0
  164. pyurd/include/urd/traits.hpp +70 -0
  165. pyurd/include/urd/type_traits_of.hpp +200 -0
  166. pyurd/lib/cmake/urd/urdConfig.cmake +54 -0
  167. pyurd/lib/cmake/urd/urdConfigVersion.cmake +54 -0
  168. pyurd/lib/cmake/urd/urdTargets.cmake +118 -0
  169. pyurd/py.typed +0 -0
  170. pyurd/repair.py +95 -0
  171. pyurd/share/urd/complexity.json +85 -0
  172. pyurd/share/urd/vocabulary.json +78 -0
  173. pyurd-0.1.0.dist-info/METADATA +122 -0
  174. pyurd-0.1.0.dist-info/RECORD +176 -0
  175. pyurd-0.1.0.dist-info/WHEEL +5 -0
  176. pyurd-0.1.0.dist-info/licenses/LICENSE +674 -0
pyurd/__init__.py ADDED
@@ -0,0 +1,95 @@
1
+ # Copyright (C) 2026 Dominik Drexler
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+
4
+ """urd's headers and CMake package: the paths of the bundled prefix. The libraries urd's headers include are
5
+ pyurd-native's (``pyurd_native.native_prefix()``)."""
6
+
7
+ import tomllib as _tomllib
8
+ from importlib.metadata import PackageNotFoundError, version
9
+ from pathlib import Path
10
+
11
+
12
+ def _has_urd_headers(path: Path) -> bool:
13
+ return (path / "include" / "urd").is_dir()
14
+
15
+
16
+ def source_version() -> str:
17
+ for parent in Path(__file__).resolve().parents:
18
+ pyproject = parent / "pyproject.toml"
19
+ if not pyproject.exists():
20
+ continue
21
+
22
+ project_version = _tomllib.loads(pyproject.read_text(encoding="utf-8")).get("project", {}).get("version")
23
+ if project_version is not None:
24
+ return project_version
25
+
26
+ return "0.0.0"
27
+
28
+
29
+ try:
30
+ __version__ = version("pyurd")
31
+ except PackageNotFoundError:
32
+ __version__ = source_version()
33
+
34
+
35
+ def native_prefix() -> Path:
36
+ """Return the directory containing urd's headers and CMake package."""
37
+ package_dir = Path(__file__).resolve().parent
38
+ if _has_urd_headers(package_dir):
39
+ return package_dir
40
+ for parent in package_dir.parents:
41
+ if (parent / "pyproject.toml").exists() and _has_urd_headers(parent):
42
+ return parent
43
+
44
+ return package_dir
45
+
46
+
47
+ def include_dir() -> Path:
48
+ """Return the bundled C++ header include directory."""
49
+ return native_prefix() / "include"
50
+
51
+
52
+ def library_dirs() -> tuple[Path, ...]:
53
+ """Return existing bundled native library directories."""
54
+ prefix = native_prefix()
55
+ return tuple(path for path in (prefix / "lib", prefix / "lib64") if path.is_dir())
56
+
57
+
58
+ def cmake_dirs() -> tuple[Path, ...]:
59
+ """Return existing bundled CMake package directories."""
60
+ return tuple(path / "cmake" for path in library_dirs() if (path / "cmake").is_dir())
61
+
62
+
63
+ def cmake_prefix() -> Path:
64
+ """Return the prefix to put on CMAKE_PREFIX_PATH.
65
+
66
+ find_package then finds urd there; its dependencies are under pyurd-native's prefix, which goes on
67
+ CMAKE_PREFIX_PATH too.
68
+ """
69
+ return native_prefix()
70
+
71
+
72
+ def cmake_dir() -> Path:
73
+ """Return the directory containing urdConfig.cmake."""
74
+ for cmake_packages_dir in cmake_dirs():
75
+ candidate = cmake_packages_dir / "urd"
76
+ if (candidate / "urdConfig.cmake").is_file():
77
+ return candidate
78
+
79
+ msg = (
80
+ "urdConfig.cmake not found under "
81
+ f"{[str(path) for path in cmake_dirs()] or native_prefix()}; "
82
+ "the installed pyurd is too old or incomplete."
83
+ )
84
+ raise FileNotFoundError(msg)
85
+
86
+
87
+ __all__ = [
88
+ "__version__",
89
+ "cmake_dir",
90
+ "cmake_dirs",
91
+ "cmake_prefix",
92
+ "include_dir",
93
+ "library_dirs",
94
+ "native_prefix",
95
+ ]
pyurd/__main__.py ADDED
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2026 Dominik Drexler
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+
4
+ """``python -m pyurd``: print where urd's headers and CMake package are."""
5
+
6
+ import argparse
7
+
8
+ import pyurd
9
+
10
+
11
+ def main() -> None:
12
+ """Print the requested path or version."""
13
+ parser = argparse.ArgumentParser(
14
+ prog="python -m pyurd",
15
+ description="Print discovery paths for urd's headers and CMake package.",
16
+ )
17
+ group = parser.add_mutually_exclusive_group(required=True)
18
+ group.add_argument(
19
+ "--prefix",
20
+ action="store_true",
21
+ help="Print the prefix to put on CMAKE_PREFIX_PATH.",
22
+ )
23
+ group.add_argument(
24
+ "--include-dir",
25
+ action="store_true",
26
+ help="Print the bundled C++ header include directory.",
27
+ )
28
+ group.add_argument(
29
+ "--cmake-dir",
30
+ action="store_true",
31
+ help="Print the directory containing urdConfig.cmake.",
32
+ )
33
+ group.add_argument(
34
+ "--version",
35
+ action="store_true",
36
+ help="Print the pyurd version.",
37
+ )
38
+ args = parser.parse_args()
39
+
40
+ if args.prefix:
41
+ print(pyurd.cmake_prefix())
42
+ elif args.include_dir:
43
+ print(pyurd.include_dir())
44
+ elif args.cmake_dir:
45
+ print(pyurd.cmake_dir())
46
+ elif args.version:
47
+ print(pyurd.__version__)
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()
@@ -0,0 +1,36 @@
1
+ #pragma once
2
+
3
+ // urd's bit vocabulary, for code outside urd: a span over the words of a bitset, its set bits as a range, and the
4
+ // helpers that read and write integers packed into words. These are the names to use; urd::repr is urd's own. (The
5
+ // tag urd::bit is a request's; the helpers are urd::bits.)
6
+
7
+ #include "urd/repr/containers/bit.hpp"
8
+ #include "urd/repr/containers/bitset_span.hpp"
9
+
10
+ namespace urd
11
+ {
12
+
13
+ /// A span over the words of a bitset, a bit per index below its length.
14
+ template<typename Block>
15
+ using bitset_span = repr::BitsetSpan<Block>;
16
+ /// The set bits of a bitset_span, as a range of their indices: what set_bit_indices gives.
17
+ template<typename Block>
18
+ using set_bit_indices_view = repr::SetBitIndices<Block>;
19
+ using repr::for_each_bit;
20
+ using repr::set_bit_indices;
21
+
22
+ namespace bits
23
+ {
24
+
25
+ using repr::bit::bits_needed;
26
+ using repr::bit::bits_per_block_v;
27
+ using repr::bit::ceil_div;
28
+ using repr::bit::for_each_set_bit;
29
+ using repr::bit::int_reference;
30
+ using repr::bit::lo_set;
31
+ using repr::bit::read_int;
32
+ using repr::bit::write_int;
33
+
34
+ } // namespace bits
35
+
36
+ } // namespace urd
@@ -0,0 +1,47 @@
1
+ #pragma once
2
+
3
+ #include <cassert>
4
+ #include <cstddef>
5
+ #include <cstdint>
6
+ #include <limits>
7
+
8
+ namespace urd
9
+ {
10
+
11
+ /// urd's word: the unsigned integer that bit-packed storage is made of (rows of a bit width or of column widths, sets
12
+ /// kept as bits), and that a packed value is read and written as.
13
+ using word_t = std::uint64_t;
14
+
15
+ /// Its bits, derived from it: the most a packed value takes. No code spells them as a number.
16
+ inline constexpr std::size_t word_bits = std::numeric_limits<word_t>::digits;
17
+
18
+ } // namespace urd
19
+
20
+ // NOLINTBEGIN(cppcoreguidelines-macro-usage): each must expand at its call site
21
+ /// States an invariant of side-effect free `condition`: asserted in Debug, assumed in Release (undefined
22
+ /// behavior if false, at no cost), and visible to static analysis in both, unlike a plain assert under NDEBUG
23
+ /// (a function would hide the condition from the analysis of optionals).
24
+ #if defined(_MSC_VER)
25
+ #define URD_ASSUME(condition) (assert(condition), (condition) ? void(0) : __assume(0))
26
+ #else
27
+ #define URD_ASSUME(condition) (assert(condition), (condition) ? void(0) : __builtin_unreachable())
28
+ #endif
29
+
30
+ /// Keeps a rarely taken helper out of line, so its size does not count towards the inlined size of its caller.
31
+ #if defined(_MSC_VER)
32
+ #define URD_NOINLINE __declspec(noinline)
33
+ #else
34
+ #define URD_NOINLINE [[gnu::noinline]]
35
+ #endif
36
+
37
+ /// Marks a parameter that the function's result refers to, so the compiler can warn when the result outlives a
38
+ /// temporary argument (Clang); empty where the attribute is unknown.
39
+ #if defined(__has_cpp_attribute)
40
+ #if __has_cpp_attribute(clang::lifetimebound)
41
+ #define URD_LIFETIMEBOUND [[clang::lifetimebound]]
42
+ #endif
43
+ #endif
44
+ #ifndef URD_LIFETIMEBOUND
45
+ #define URD_LIFETIMEBOUND
46
+ #endif
47
+ // NOLINTEND(cppcoreguidelines-macro-usage)
@@ -0,0 +1,313 @@
1
+ #pragma once
2
+
3
+ // What each operation costs a representation, and the memory it takes: how a cost grows, in which quantity, urd's
4
+ // operations, and a container's table of costs, one entry per operation; its memory in words (urd/memory.hpp tells
5
+ // what it holds). Stated in its declaration (operation_costs, words; urd/registry/declaration.hpp), from which
6
+ // urd/registry/derived.hpp gives container_costs<R>, without defining R. Reading a representation's costs therefore
7
+ // completes nothing and includes no implementation: the registry's own headers suffice. The docs' complexity tables
8
+ // and the benchmarks' judgment of the registry (benchmarks/record.py) read these.
9
+
10
+ #include "urd/traits.hpp"
11
+
12
+ #include <concepts>
13
+ #include <cstdint>
14
+ #include <optional>
15
+ #include <string_view>
16
+
17
+ namespace urd
18
+ {
19
+
20
+ /// How a cost grows, in which quantity.
21
+ enum class growth : std::uint8_t
22
+ {
23
+ one,
24
+ log,
25
+ linear,
26
+ n_log_n,
27
+ n_times, ///< n times the quantity: O(n changes)
28
+ };
29
+
30
+ /// A growth's name: one, log, linear, n_log_n, n_times.
31
+ constexpr std::string_view name(growth of)
32
+ {
33
+ switch (of)
34
+ {
35
+ case growth::one:
36
+ return "one";
37
+ case growth::log:
38
+ return "log";
39
+ case growth::linear:
40
+ return "linear";
41
+ case growth::n_log_n:
42
+ return "n_log_n";
43
+ case growth::n_times:
44
+ return "n_times";
45
+ }
46
+ return {};
47
+ }
48
+
49
+ /// What a cost grows in.
50
+ enum class quantity : std::uint8_t
51
+ {
52
+ n, ///< the elements
53
+ keys, ///< U, the key range (dense keys fill it: about n)
54
+ capacity, ///< the slots held (about n over the load factor)
55
+ row, ///< r, a row's length
56
+ delta, ///< k, a delta's changes
57
+ changes, ///< the changes since a scope began, or since the log was cleared
58
+ };
59
+
60
+ /// A quantity's name: n, keys, capacity, row, delta, changes.
61
+ constexpr std::string_view name(quantity of)
62
+ {
63
+ switch (of)
64
+ {
65
+ case quantity::n:
66
+ return "n";
67
+ case quantity::keys:
68
+ return "keys";
69
+ case quantity::capacity:
70
+ return "capacity";
71
+ case quantity::row:
72
+ return "row";
73
+ case quantity::delta:
74
+ return "delta";
75
+ case quantity::changes:
76
+ return "changes";
77
+ }
78
+ return {};
79
+ }
80
+
81
+ /// A quantity as a cost writes it: n, U, capacity, r, k, changes.
82
+ constexpr std::string_view symbol(quantity of)
83
+ {
84
+ switch (of)
85
+ {
86
+ case quantity::n:
87
+ return "n";
88
+ case quantity::keys:
89
+ return "U";
90
+ case quantity::capacity:
91
+ return "capacity";
92
+ case quantity::row:
93
+ return "r";
94
+ case quantity::delta:
95
+ return "k";
96
+ case quantity::changes:
97
+ return "changes";
98
+ }
99
+ return {};
100
+ }
101
+
102
+ /// Which quantities grow together: the elements, their key range and the capacity are one; a row's length, a delta's
103
+ /// changes and the changes since a scope began each grow on their own.
104
+ enum class scale : std::uint8_t
105
+ {
106
+ n,
107
+ row,
108
+ delta,
109
+ changes,
110
+ };
111
+ /// A scale's name.
112
+ constexpr std::string_view name(scale of)
113
+ {
114
+ switch (of)
115
+ {
116
+ case scale::n:
117
+ return "n";
118
+ case scale::row:
119
+ return "row";
120
+ case scale::delta:
121
+ return "delta";
122
+ case scale::changes:
123
+ return "changes";
124
+ }
125
+ return {};
126
+ }
127
+ constexpr scale scale_of(quantity of)
128
+ {
129
+ switch (of)
130
+ {
131
+ case quantity::n:
132
+ case quantity::keys:
133
+ case quantity::capacity:
134
+ return scale::n;
135
+ case quantity::row:
136
+ return scale::row;
137
+ case quantity::delta:
138
+ return scale::delta;
139
+ case quantity::changes:
140
+ return scale::changes;
141
+ }
142
+ return scale::n;
143
+ }
144
+
145
+ /// One operation's cost: its growth in a quantity, and whether it is amortized or expected.
146
+ struct cost
147
+ {
148
+ growth grows = growth::one;
149
+ quantity of = quantity::n;
150
+ bool amortized = false;
151
+ bool expected = false;
152
+ };
153
+
154
+ /// An operation's words (urd/property_set.hpp, words), and the operation Op (a struct with its name, its description,
155
+ /// and how costs name it). A kind added outside urd adds its own, with costs its containers declare as members named
156
+ /// for it.
157
+ using operation_info = words<struct operation_words>;
158
+ template<typename Op>
159
+ inline constexpr const operation_info* operation_v = &words_of<struct operation_words, Op>;
160
+
161
+ /// A list of operations: what a facet offers.
162
+ template<typename... Ops>
163
+ struct operation_list
164
+ {
165
+ };
166
+ /// urd's operations: each its words, and the key of its entry in a container's costs (cost_table).
167
+ namespace operation
168
+ {
169
+
170
+ struct insert
171
+ {
172
+ static constexpr std::string_view name = "insert";
173
+ static constexpr std::string_view description = "a set's or map's insert, a sequence's push_back";
174
+ };
175
+ struct lookup
176
+ {
177
+ static constexpr std::string_view name = "lookup";
178
+ static constexpr std::string_view description = "find, contains, a map's value by key";
179
+ };
180
+ struct ordered_lookup
181
+ {
182
+ static constexpr std::string_view name = "ordered_lookup";
183
+ static constexpr std::string_view description = "lower_bound";
184
+ };
185
+ struct erase
186
+ {
187
+ static constexpr std::string_view name = "erase";
188
+ static constexpr std::string_view description = "by key, or by position";
189
+ };
190
+ struct clear
191
+ {
192
+ static constexpr std::string_view name = "clear";
193
+ static constexpr std::string_view description = "every element at once";
194
+ };
195
+ struct iterate
196
+ {
197
+ static constexpr std::string_view name = "iterate";
198
+ static constexpr std::string_view description = "every element in turn";
199
+ };
200
+ struct access
201
+ {
202
+ static constexpr std::string_view name = "access";
203
+ static constexpr std::string_view description = "by position or handle";
204
+ };
205
+ struct push
206
+ {
207
+ static constexpr std::string_view name = "push";
208
+ static constexpr std::string_view description = "a queue's";
209
+ };
210
+ struct pop
211
+ {
212
+ static constexpr std::string_view name = "pop";
213
+ static constexpr std::string_view description = "a queue's";
214
+ };
215
+ struct top
216
+ {
217
+ static constexpr std::string_view name = "top";
218
+ static constexpr std::string_view description = "a queue's";
219
+ };
220
+ struct merge
221
+ {
222
+ static constexpr std::string_view name = "merge";
223
+ static constexpr std::string_view description = "a partition's";
224
+ };
225
+ struct insert_row
226
+ {
227
+ static constexpr std::string_view name = "insert_row";
228
+ static constexpr std::string_view description = "a pool's insert";
229
+ };
230
+ struct find_row
231
+ {
232
+ static constexpr std::string_view name = "find_row";
233
+ static constexpr std::string_view description = "a pool's find";
234
+ };
235
+ struct scope
236
+ {
237
+ static constexpr std::string_view name = "scope";
238
+ static constexpr std::string_view description = "push_scope, changes, pop_scope";
239
+ };
240
+
241
+ } // namespace operation
242
+
243
+ /// What operation Op costs in a container: one entry of its costs.
244
+ template<typename Op, cost Cost>
245
+ struct cost_entry
246
+ {
247
+ using operation = Op;
248
+ static constexpr cost value = Cost;
249
+ };
250
+
251
+ /// A container's costs: one entry per operation it offers. `of<Op>()` is Op's cost, or none where it has no entry.
252
+ template<typename... Entries>
253
+ struct cost_table
254
+ {
255
+ template<typename Op>
256
+ static constexpr std::optional<cost> of()
257
+ {
258
+ auto found = std::optional<cost>();
259
+ ((std::same_as<Op, typename Entries::operation> ? void(found = Entries::value) : void()), ...);
260
+ return found;
261
+ }
262
+ template<typename Op>
263
+ static constexpr bool passed_on()
264
+ { return false; }
265
+ };
266
+
267
+ /// A wrapper's costs: its own entries, and for every other operation its part's (Part, a declaration). An operation it
268
+ /// has no entry for is passed on to its part.
269
+ template<typename Part, typename... Entries>
270
+ struct costs_over
271
+ {
272
+ using part = Part;
273
+ template<typename Op>
274
+ static constexpr std::optional<cost> of()
275
+ {
276
+ const auto own = cost_table<Entries...>::template of<Op>();
277
+ return own ? own : Part::operation_costs::template of<Op>();
278
+ }
279
+ template<typename Op>
280
+ static constexpr bool passed_on()
281
+ { return !cost_table<Entries...>::template of<Op>() && Part::operation_costs::template of<Op>(); }
282
+ };
283
+
284
+ /// The costs of the operations of representation R: its declaration's operation_costs, a cost_table with an entry per
285
+ /// operation R offers (a wrapper's, costs_over its part's declaration). Its memory is the declaration's `words`: at
286
+ /// most so many words per element, or per unit of its `words_per` (a key of its range, a slot of its capacity), an
287
+ /// upper bound the benchmarks judge (benchmarks/record.py); what that memory holds is in words in its synopsis
288
+ /// (urd/registry/derived.hpp, container_synopsis). Declared for every registered representation, derived from its
289
+ /// declaration (urd/registry/derived.hpp); none here.
290
+ template<typename R>
291
+ struct container_costs
292
+ {
293
+ };
294
+
295
+ /// The costs, named: growth, then a for amortized, e for expected.
296
+ namespace costs
297
+ {
298
+ inline constexpr cost one {};
299
+ inline constexpr cost one_a { .amortized = true };
300
+ inline constexpr cost one_e { .expected = true };
301
+ inline constexpr cost one_ae { .amortized = true, .expected = true };
302
+ inline constexpr cost log_n { .grows = growth::log };
303
+ inline constexpr cost log_na { .grows = growth::log, .amortized = true };
304
+ inline constexpr cost lin_n { .grows = growth::linear };
305
+ inline constexpr cost lin_keys { .grows = growth::linear, .of = quantity::keys };
306
+ inline constexpr cost lin_capacity { .grows = growth::linear, .of = quantity::capacity };
307
+ inline constexpr cost lin_row { .grows = growth::linear, .of = quantity::row };
308
+ inline constexpr cost lin_row_e { .grows = growth::linear, .of = quantity::row, .expected = true };
309
+ inline constexpr cost lin_changes { .grows = growth::linear, .of = quantity::changes };
310
+ inline constexpr cost nlog_changes { .grows = growth::n_log_n, .of = quantity::changes };
311
+ } // namespace costs
312
+
313
+ } // namespace urd
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+
3
+ namespace urd
4
+ {
5
+
6
+ /// The mutable form of a T, filled in place before T is stored or read (a state while it is expanded, a denotation
7
+ /// while it is computed). Specialize it per type; views read it as their record, urd::borrowed_view<T, C, builder<T>>
8
+ /// (or owning_view, shared_view), with the accessors of view_mixin<builder<T>, V>.
9
+ template<typename T>
10
+ struct builder;
11
+
12
+ } // namespace urd
@@ -0,0 +1,120 @@
1
+ #pragma once
2
+
3
+ #include "urd/elements/concepts.hpp"
4
+ #include "urd/elements/flat.hpp"
5
+ #include "urd/elements/index.hpp"
6
+
7
+ #include <boost/mp11/algorithm.hpp>
8
+ #include <concepts>
9
+ #include <tuple>
10
+ #include <type_traits>
11
+ #include <utility>
12
+
13
+ /// The children of a record of element type T: every index<U> with U in the type list Types (a boost::mp11 list),
14
+ /// found anywhere in the record's cista members: directly, in (nested) flat vectors, as a variant's alternative, in an
15
+ /// optional or a pair (std's or cista's, as urd::resolution reads them), or inside a member that names cista members
16
+ /// itself (urd::Serializable). U may be T: an index<T> nested in any of these shapes is a child (an (and (and ...))).
17
+ /// Only a direct member of type index<T> is not: that is the record's own handle. A direct index<T> member that links
18
+ /// to another record of type T (a parent) is therefore not a child either; nest it (an optional, say) to make it one.
19
+ /// They are handed out as the views their context makes, context.view(index) (urd::resolves), so callers never handle
20
+ /// raw indices. urd::map_variant_children instead hands out each variant slot whole (urd::resolve of it), so that a
21
+ /// callback may replace it by another alternative.
22
+ namespace urd
23
+ {
24
+ namespace detail
25
+ {
26
+ /// Calls f with each member of a record whose cista_members() is a Members list.
27
+ template<typename R, typename F>
28
+ void for_each_cista_member(R& record, F f)
29
+ {
30
+ [&]<auto... Ms>(Members<Ms...>) { (f(record.*Ms), ...); }(std::remove_const_t<R>::cista_members());
31
+ }
32
+
33
+ /// A slot of type S that is a child: an index of a type in the list Types.
34
+ template<typename S, typename Types>
35
+ concept child_of = is_index_type<std::remove_const_t<S>> && boost::mp11::mp_contains<Types, typename std::remove_const_t<S>::element_type>::value;
36
+ template<typename Types, typename S>
37
+ inline constexpr bool is_child = child_of<S, Types>;
38
+
39
+ /// Walks member (const or not, as member is) in member order, calling f with each slot f takes (std::invocable) and
40
+ /// not descending into it; any other slot is descended into where it is one of the flat containers a stored record
41
+ /// holds (flat_vector, flat_variant, flat_optional, flat_pair) or a nested record, else skipped. An index f does not
42
+ /// take is skipped too (its own cista member, value, is no slot).
43
+ template<typename M, typename F>
44
+ void walk(M& member, F& f)
45
+ {
46
+ using V = std::remove_const_t<M>;
47
+ if constexpr (std::invocable<F&, M&>)
48
+ f(member);
49
+ else if constexpr (is_index_type<V>)
50
+ return;
51
+ else if constexpr (is_flat_vector<V>)
52
+ for (auto& element : member)
53
+ walk(element, f);
54
+ else if constexpr (is_flat_variant<V>)
55
+ member.apply([&](auto& alternative) { walk(alternative, f); });
56
+ else if constexpr (is_flat_optional<V>)
57
+ {
58
+ if (member.has_value())
59
+ walk(*member, f);
60
+ }
61
+ else if constexpr (is_flat_pair<V>)
62
+ {
63
+ walk(member.first, f);
64
+ walk(member.second, f);
65
+ }
66
+ else if constexpr (Serializable<V>)
67
+ for_each_cista_member(member, [&](auto& nested) { walk(nested, f); });
68
+ }
69
+
70
+ /// walk over record's members, but for its own handle: a direct member of type index<T>.
71
+ template<typename T, typename R, typename F>
72
+ void walk_members(R& record, F f)
73
+ {
74
+ for_each_cista_member(record,
75
+ [&]<typename M>(M& member)
76
+ {
77
+ if constexpr (!std::same_as<std::remove_const_t<M>, index<T>>)
78
+ walk(member, f);
79
+ });
80
+ }
81
+ } // namespace detail
82
+
83
+ /// Calls f(context.view(child)) for every child of record, in member order.
84
+ template<typename Types, typename T, typename R, typename C, typename F>
85
+ void for_each_child(const R& record, const C& context, F f)
86
+ {
87
+ detail::walk_members<T>(record,
88
+ [&]<typename S>(S& child)
89
+ requires detail::is_child<Types, S> { f(context.view(child)); });
90
+ }
91
+
92
+ /// record with every child c replaced by urd::to_stored(f(context.view(c))), in member order: f returns an index of
93
+ /// c's type, or a view of one.
94
+ template<typename Types, typename T, typename R, typename C, typename F>
95
+ R map_children(R record, const C& context, F f)
96
+ {
97
+ detail::walk_members<T>(record,
98
+ [&]<typename S>(S& child)
99
+ requires detail::is_child<Types, S> { child = urd::to_stored(f(context.view(child))); });
100
+ return record;
101
+ }
102
+
103
+ /// map_children, but every flat_variant slot (one not inside another variant; any alternatives, children or not) is
104
+ /// replaced whole: by urd::to_stored(f(urd::resolve(slot, context))), f called with the std::variant of the slot's
105
+ /// alternatives resolved (an empty slot throws std::bad_variant_access). f returns that variant, another of its
106
+ /// alternatives (a view or index of another alternative type), or anything else to_stored makes into the slot's type or
107
+ /// one of its alternatives. The walk does not descend into a slot: the children (and nested variants) inside it are
108
+ /// f's to map. Children outside variants are replaced as map_children replaces them, so f is an overload set (or a
109
+ /// generic callback) taking both the views and the resolved variants.
110
+ template<typename Types, typename T, typename R, typename C, typename F>
111
+ R map_variant_children(R record, const C& context, F f)
112
+ {
113
+ detail::walk_members<T>(record,
114
+ [&]<typename S>(S& slot)
115
+ requires(detail::is_child<Types, S> || is_flat_variant<S>)
116
+ { slot = urd::to_stored(f(urd::resolve(std::as_const(slot), context))); });
117
+ return record;
118
+ }
119
+
120
+ } // namespace urd