diff --git a/makefile b/makefile index 782cf9c32..3204a6e06 100644 --- a/makefile +++ b/makefile @@ -154,6 +154,9 @@ tests/adiar/internal/data_structures/sorter: tests/adiar/internal/data_structures/stack: $(MAKE) $(MAKE_FLAGS) tests TEST_SUBFOLDER=adiar/internal/data_structures/ TEST_NAME=stack +tests/adiar/internal/data_structures/vector: + $(MAKE) $(MAKE_FLAGS) tests TEST_SUBFOLDER=adiar/internal/data_structures/ TEST_NAME=vector + tests/adiar/internal/data_types/arc: $(MAKE) $(MAKE_FLAGS) tests TEST_SUBFOLDER=adiar/internal/data_types/ TEST_NAME=arc diff --git a/src/adiar/CMakeLists.txt b/src/adiar/CMakeLists.txt index d3f8d4852..a761b3163 100644 --- a/src/adiar/CMakeLists.txt +++ b/src/adiar/CMakeLists.txt @@ -66,6 +66,7 @@ set(HEADERS internal/data_structures/priority_queue.h internal/data_structures/sorter.h internal/data_structures/stack.h + internal/data_structures/vector.h # adiar/internal/data_types internal/data_types/arc.h diff --git a/src/adiar/bdd/evaluate.cpp b/src/adiar/bdd/evaluate.cpp index 50d8a66a1..06b0d4129 100644 --- a/src/adiar/bdd/evaluate.cpp +++ b/src/adiar/bdd/evaluate.cpp @@ -16,6 +16,7 @@ namespace adiar { + ////////////////////////////////////////////////////////////////////////////////////////////////// class bdd_eval_func_visitor { const predicate& af; @@ -54,7 +55,7 @@ namespace adiar return v.get_result(); } - ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// class bdd_eval_generator_visitor { const generator>& _generator; @@ -118,7 +119,7 @@ namespace adiar return v.get_result(); } - ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Merge code duplication with Curiously Recurring Template Pattern template @@ -266,7 +267,7 @@ namespace adiar { if (!bdd_iscube(d)) { throw domain_error("BDD 'd' is not a cube"); } - typename internal::level_ifstream_t::template stream_t<> d_levels(d); + internal::level_info_ifstream d_levels(d); const generator d_gen = make_generator__levels(d_levels); const size_t total_levels = std::min(f->levels() + d->levels(), bdd::max_label + 1); @@ -298,7 +299,7 @@ namespace adiar { if (!bdd_iscube(d)) { throw domain_error("BDD 'd' is not a cube"); } - typename internal::level_ifstream_t::template stream_t<> d_levels(d); + internal::level_info_ifstream d_levels(d); const generator d_gen = make_generator__levels(d_levels); const size_t total_levels = std::min(f->levels() + d->levels(), bdd::max_label + 1); diff --git a/src/adiar/internal/algorithms/convert.h b/src/adiar/internal/algorithms/convert.h index 6c7e1afb2..cd4e3884e 100644 --- a/src/adiar/internal/algorithms/convert.h +++ b/src/adiar/internal/algorithms/convert.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -54,9 +55,9 @@ namespace adiar::internal static typename to_policy::dd_type on_terminal_input(const bool terminal_value, const typename from_policy::dd_type& /*dd*/, - const shared_file& dom) + const internal_vector& dom) { - adiar_assert(dom->size() > 0, "Emptiness check is before terminal check"); + adiar_assert(dom.size() > 0, "Emptiness check is before terminal check"); node::uid_type prior_node = node::uid_type(terminal_value); @@ -65,13 +66,9 @@ namespace adiar::internal bool has_output = true; node_ofstream nw(nf); - ifstream ls(dom); - - while (ls.can_pull()) { - const typename to_policy::label_type next_label = ls.pull(); - + for (auto iter = dom.rbegin(); iter != dom.rend(); ++iter) { const tuple children = from_policy::reduction_rule_inv(prior_node); - const node next_node = node(next_label, to_policy::max_id, children[0], children[1]); + const node next_node = node(*iter, to_policy::max_id, children[0], children[1]); const typename to_policy::pointer_type reduction_result = to_policy::reduction_rule(next_node); @@ -79,7 +76,7 @@ namespace adiar::internal prior_node = next_node.uid(); nw.unsafe_push(next_node); - nw.unsafe_push(level_info(next_label, 1u)); + nw.unsafe_push(level_info(*iter, 1u)); } else { // If we kill the resulting node once, then we will also do it for all the other labels we // still are missing. diff --git a/src/adiar/internal/algorithms/intercut.h b/src/adiar/internal/algorithms/intercut.h index a25c3a35c..8a7c8c2de 100644 --- a/src/adiar/internal/algorithms/intercut.h +++ b/src/adiar/internal/algorithms/intercut.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,7 @@ namespace adiar::internal template using intercut_priority_queue_t = - levelized_label_priority_queue; + levelized_node_priority_queue; ////////////////////////////////////////////////////////////////////////////// @@ -194,34 +195,27 @@ namespace adiar::internal // In the general case, we have to use it both in the priority queue // `intercut_pq` below, and for a lookahead of the algorithm. The main issue // is how to design the priority queue such that it can retrieve, merge with - // `dd_levels`, and expose `xs`. - shared_file hit_levels; - { - ofstream lw(hit_levels); - for (auto x = xs(); x; x = xs()) { lw << x.value(); } - - if (lw.size() == 0) { return intercut_policy::on_empty_labels(dd); } - } + // the levels of `dd` and expose `xs`. + internal_vector hit_levels(dd::max_label); + for (auto x = xs(); x; x = xs()) { hit_levels.push_back(x.value()); } + if (hit_levels.empty()) { return intercut_policy::on_empty_labels(dd); } if (n.is_terminal()) { return intercut_policy::on_terminal_input(n.value(), dd, hit_levels); } - ifstream ls(hit_levels); - typename intercut_policy::label_type l = ls.pull(); + typename internal_vector::iterator ls = + hit_levels.begin(); shared_levelized_file out_arcs; arc_ofstream aw(out_arcs); out_arcs->max_1level_cut = 0; - shared_file dd_levels; - { - ofstream lw(dd_levels); - dd_support(dd, [&lw](const typename intercut_policy::label_type x) { lw << x; }); - } - // Add request for root in the queue - pq_t intercut_pq({ dd_levels, hit_levels }, pq_memory, max_pq_size, stats_intercut.lpq); - intercut_pq.push(intercut_req(ptr_uint64::nil(), n.uid(), std::min(l, n.label()))); + pq_t intercut_pq({ dd, make_generator(hit_levels.begin(), hit_levels.end()) }, + pq_memory, + max_pq_size, + stats_intercut.lpq); + intercut_pq.push(intercut_req(ptr_uint64::nil(), n.uid(), std::min(*ls, n.label()))); // Process nodes of the decision diagram in topological order while (!intercut_pq.empty()) { @@ -231,12 +225,13 @@ namespace adiar::internal const typename intercut_policy::label_type out_label = intercut_pq.current_level(); typename intercut_policy::id_type out_id = 0; - const bool hit_level = out_label == l; + const bool hit_level = out_label == *ls; // Forward to next label to cut on after this level - while (ls.can_pull() && l <= out_label) { l = ls.pull(); } + while (ls != hit_levels.end() && *ls <= out_label) { ++ls; } - if (!ls.can_pull() && l <= out_label) { l = intercut_policy::max_label + 1; } + typename intercut_policy::label_type l = + ls == hit_levels.end() ? intercut_policy::max_label + 1 : *ls; // Update max 1-level cut out_arcs->max_1level_cut = std::max(out_arcs->max_1level_cut, intercut_pq.size()); diff --git a/src/adiar/internal/algorithms/reduce.h b/src/adiar/internal/algorithms/reduce.h index c5fe04f64..4d9a4e795 100644 --- a/src/adiar/internal/algorithms/reduce.h +++ b/src/adiar/internal/algorithms/reduce.h @@ -91,17 +91,17 @@ namespace adiar::internal size_t _terminals[2] = { 0u, 0u }; public: - reduce_priority_queue(const shared_levelized_file (&files)[1u], + reduce_priority_queue(std::array&& files, size_t memory_given, size_t max_size, statistics::levelized_priority_queue_t& stats) - : inner_lpq(files, memory_given, max_size, stats) + : inner_lpq(std::move(files), memory_given, max_size, stats) {} - reduce_priority_queue(const shared_levelized_file (&files)[1u], + reduce_priority_queue(std::array&& files, size_t memory_given, size_t max_size) - : reduce_priority_queue(files, memory_given, max_size, stats_reduce.lpq) + : reduce_priority_queue(std::move(files), memory_given, max_size, stats_reduce.lpq) {} //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/adiar/internal/data_structures/level_merger.h b/src/adiar/internal/data_structures/level_merger.h index b3ca1c66d..9f8f43d4e 100644 --- a/src/adiar/internal/data_structures/level_merger.h +++ b/src/adiar/internal/data_structures/level_merger.h @@ -1,94 +1,284 @@ #ifndef ADIAR_INTERNAL_DATA_STRUCTURES_LEVEL_MERGER_H #define ADIAR_INTERNAL_DATA_STRUCTURES_LEVEL_MERGER_H +#include +#include + +#include +#include + #include #include -#include -#include #include -#include #include #include namespace adiar::internal { - // TODO (code clarity): - // Add to 'File' an enum with 'Ascending'/'Descending' to then derive the comparator in - // conjunction with 'Reverse'. - ////////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Merges the levels from one or more files. + /// \brief Merges the levels from several inputs. /// - /// \tparam File Type of the files to read from. + /// \tparam Comp The comparator with which to merge the levels. /// - /// \tparam Comp Comparator with which to merge the levels. + /// \tparam InputCount Number of inputs. /// - /// \tparam FileCount Number of files to read from. + /// \remark Currently, variadic inputs are supported by use of `virtual` functions, i.e. by use of + /// runtime-resolved inheritance. Yet, this is always used in a context where the type of + /// the individual arguments are known. Hence, one should be able to replace `InputCount` + /// with a compile-time known list ``. To this end, a deduction guide + /// might be useful. ////////////////////////////////////////////////////////////////////////////////////////////////// - template + template class level_merger { - static_assert(0 < FileCount, "At least one file should be merged"); - - using stream_type = typename level_ifstream_t::template stream_t; - public: - static size_t - memory_usage() + using value_type = dd::label_type; + using arg_type = std::variant>; + + private: + //////////////////////////////////////////////////////////////////////////////////////////////// + // TODO: Move to ? + class istream { - return FileCount * stream_type::memory_usage(); - } + public: + using value_type = level_merger::value_type; - using level_type = ptr_uint64::label_type; + virtual ~istream() = default; - private: - Comp _comparator = Comp(); + public: + virtual bool + can_pull() = 0; - unique_ptr _level_ifstreams[FileCount]; + virtual value_type + peek() = 0; - public: + virtual value_type + pull() = 0; + }; + + private: //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Attach onto the given list of files. + /// \brief Wrapper for `generator`s to provide the same interface as an `ifstream`, including + /// the ability to `peek()`. //////////////////////////////////////////////////////////////////////////////////////////////// - void - hook(const File (&fs)[FileCount]) + // TODO: Move to or ? + class generator_istream : public istream { - for (size_t idx = 0u; idx < FileCount; idx++) { - _level_ifstreams[idx] = adiar::make_unique(fs[idx]); + private: + const generator _gen; + optional _next; + + public: + generator_istream(const generator& gen) + : _gen(gen) + { + this->_next = this->_gen(); } - } + ~generator_istream() = default; + + public: + bool + can_pull() override + { + return this->_next.has_value(); + } + + value_type + peek() override + { + return this->_next.value(); + } + + value_type + pull() override + { + const value_type ret = this->_next.value(); + this->_next = this->_gen(); + return ret; + } + }; + + private: + //////////////////////////////////////////////////////////////////////////////////////////////// + class level_info_istream : public istream + { + private: + level_info_ifstream<> _ifstream; + + public: + template + level_info_istream(const T& t) + : _ifstream(t) + {} + + ~level_info_istream() = default; + + public: + bool + can_pull() override + { + return this->_ifstream.can_pull(); + } + + value_type + peek() override + { + return this->_ifstream.peek().level(); + } + + value_type + pull() override + { + return this->_ifstream.pull().level(); + } + }; + + private: //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Attach onto the given list of decision diagrams. + /// \deprecated This is merely created to help migrate from the old level_merger. //////////////////////////////////////////////////////////////////////////////////////////////// - void - hook(const dd (&dds)[FileCount]) + class file_istream : public istream { - for (size_t idx = 0u; idx < FileCount; idx++) { - _level_ifstreams[idx] = - adiar::make_unique(dds[idx].file_ptr(), dds[idx].shift()); + private: + ifstream _ifstream; + + public: + [[deprecated("Use a 'generator' instead?")]] + file_istream(const shared_file& f) + : _ifstream(f) + {} + + [[deprecated("Use a 'generator' instead?")]] + file_istream(const file& f) + : _ifstream(f) + {} + + ~file_istream() = default; + + public: + bool + can_pull() override + { + return this->_ifstream.can_pull(); } - } + value_type + peek() override + { + return this->_ifstream.peek(); + } + + value_type + pull() override + { + return this->_ifstream.pull(); + } + }; + + public: //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Attach onto the given list of (unreduced) decision diagrams. + /// \brief Manages initialization and access to an input of levels. + /// + /// \details Since the `internal::ifstream` is not copy-constructable, then we have to create + /// the object on the heap. //////////////////////////////////////////////////////////////////////////////////////////////// - void - hook(const __dd (&dds)[FileCount]) + class istream_ptr { - for (size_t idx = 0u; idx < FileCount; idx++) { - _level_ifstreams[idx] = adiar::make_unique(dds[idx] /*, dds[idx]._shift*/); + public: + using value_type = typename istream::value_type; + + private: + unique_ptr _ptr; + + public: + /// \brief Conversion for reduced diagrams (top-down). + istream_ptr(const dd& diagram) + : _ptr(adiar::make_unique(diagram)) + {} + + /// \brief Conversion for unreduced diagrams (bottom-up). + istream_ptr(const __dd& diagram) + : _ptr(adiar::make_unique(diagram)) + {} + + /// \brief Conversion for files with levels + template + istream_ptr(const levelized_file& f) + : _ptr(adiar::make_unique(f)) + {} + + /// \brief Conversion for files with levels + template + istream_ptr(const shared_ptr>& f) + : _ptr(adiar::make_unique(f)) + {} + + /// \brief Conversion for files. + istream_ptr(const shared_file& f) + : _ptr(adiar::make_unique(f)) + {} + + /// \brief Conversion for files. + istream_ptr(const file& f) + : _ptr(adiar::make_unique(f)) + {} + + /// \brief Conversion for generators. This requires the addition of an intermediate lambda + /// which takes care of converting to `value_type`. + template , Generator>>> + istream_ptr(const Generator& gen) + : _ptr(adiar::make_unique([=]() -> optional { + const auto x = gen(); + if (!x.has_value()) { return {}; } + return x.value(); + })) + {} + + public: + istream* + operator*() + { + return this->_ptr.get(); } + + istream* + operator->() + { + return *(*this); + } + }; + + public: + static size_t + memory_usage() + { + // NOTE: We assume that all `ifstream`s will use the same amount of memory. + return InputCount * level_info_ifstream<>::memory_usage(); } + private: + Comp _comparator = Comp(); + std::array _istream_ptrs; + + public: + level_merger(const level_merger&) = delete; + level_merger(level_merger&&) = delete; + + level_merger(std::array&& args) + : _istream_ptrs(std::move(args)) + {} + + public: //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Whether there are more levels to fetch. //////////////////////////////////////////////////////////////////////////////////////////////// bool can_pull() { - for (size_t idx = 0u; idx < FileCount; idx++) { - if (_level_ifstreams[idx]->can_pull()) { return true; } + for (istream_ptr& p : this->_istream_ptrs) { + if (p->can_pull()) { return true; } } return false; } @@ -98,19 +288,19 @@ namespace adiar::internal /// /// \pre `can_pull() == true` //////////////////////////////////////////////////////////////////////////////////////////////// - level_type + value_type peek() { adiar_assert(can_pull(), "Cannot peek past end of all streams"); bool has_min_level = false; - level_type min_level = 0u; - for (size_t idx = 0u; idx < FileCount; idx++) { - if (_level_ifstreams[idx]->can_pull() - && (!has_min_level - || _comparator(level_of(_level_ifstreams[idx]->peek()), min_level))) { + value_type min_level = 0u; + for (istream_ptr& p : this->_istream_ptrs) { + if (!p->can_pull()) { continue; } + + if (!has_min_level || _comparator(level_of(p->peek()), min_level)) { has_min_level = true; - min_level = level_of(_level_ifstreams[idx]->peek()); + min_level = level_of(p->peek()); } } @@ -122,22 +312,20 @@ namespace adiar::internal /// /// \pre `can_pull() == true` //////////////////////////////////////////////////////////////////////////////////////////////// - level_type + value_type pull() { adiar_assert(can_pull(), "Cannot pull past end of all streams"); - level_type min_level = peek(); + value_type min_level = peek(); // pull from all with min_level - for (const unique_ptr& level_info_ifstream : _level_ifstreams) { - if (level_info_ifstream->can_pull() && level_of(level_info_ifstream->peek()) == min_level) { - level_info_ifstream->pull(); - } + for (istream_ptr& p : this->_istream_ptrs) { + if (p->can_pull() && level_of(p->peek()) == min_level) { p->pull(); } } return min_level; } }; } -#endif // ADIAR_INTERNAL_DATA_STRUCTURES_LEVEL_MERGER_H +#endif // ADIAR_INTERNAL_DATA_STRUCTURES_LEVEL_MERGER_H diff --git a/src/adiar/internal/data_structures/levelized_priority_queue.h b/src/adiar/internal/data_structures/levelized_priority_queue.h index 465b090d9..dbc340430 100644 --- a/src/adiar/internal/data_structures/levelized_priority_queue.h +++ b/src/adiar/internal/data_structures/levelized_priority_queue.h @@ -28,11 +28,11 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Strictly less ' < ' between two levels. //////////////////////////////////////////////////////////////////////////////////////////////// - template + template inline bool level_cmp_lt(const ptr_uint64::label_type l1, const ptr_uint64::label_type l2, - const LevelFileComp& level_comp) + const LevelComp& level_comp) { return level_comp(l1, l2); } @@ -40,11 +40,11 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Less or equal ' <= ' between two levels. //////////////////////////////////////////////////////////////////////////////////////////////// - template + template inline bool level_cmp_le(const ptr_uint64::label_type l1, const ptr_uint64::label_type l2, - const LevelFileComp& level_comp) + const LevelComp& level_comp) { return level_comp(l1, l2) || l1 == l2; } @@ -85,15 +85,11 @@ namespace adiar::internal /// /// \tparam MemoryMode Whether to use 'Internal' or 'External' memory data structures. /// - /// \tparam LevelFile Type of the files to obtain the relevant levels from. + /// \tparam LevelInputs Number of files to obtain the levels from. /// - /// \tparam LevelFileCount Number of files to obtain the levels from. - /// - /// \tparam LevelFileComp Comparator to be used for merging multiple levels from the files + /// \tparam LevelComp Comparator to be used for merging multiple levels from the files /// together (`std::less` is top-down while `std::greater` is bottom-up). /// - /// \tparam LevelReverse Whether to reverse the levelised meta data stream. - /// /// \tparam LevelSkip The index for the first level one can push to. In other words, the /// number of levels to 'skip'. ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -101,10 +97,8 @@ namespace adiar::internal typename Comp = std::less<>, size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, memory_mode MemoryMode = memory_mode::External, - typename LevelFile = shared_file_ptr>, - size_t LevelFileCount = 1u, - typename LevelFileComp = std::less<>, - bool LevelReverse = false, + size_t LevelInputs = 1u, + typename LevelComp = std::less<>, size_t LevelSkip = 1u> class levelized_priority_queue { @@ -141,7 +135,7 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Type of the level merger. //////////////////////////////////////////////////////////////////////////////////////////////// - using level_merger_t = level_merger; + using level_merger_t = level_merger; public: //////////////////////////////////////////////////////////////////////////////////////////////// @@ -250,7 +244,7 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Instantiation of the comparator between levels. //////////////////////////////////////////////////////////////////////////////////////////////// - LevelFileComp _level_comparator = LevelFileComp(); + LevelComp _level_comparator = LevelComp(); //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Instantiation of the comparator between elements. @@ -372,87 +366,45 @@ namespace adiar::internal } } - levelized_priority_queue(size_t memory_given, - size_t max_size, - [[maybe_unused]] statistics::levelized_priority_queue_t& stats) - : _max_size(max_size) - , _memory_given(memory_given) - , _memory_for_buckets(memory_given - _memory_occupied_by_merger - - mem_overflow_queue(memory_given)) - , _memory_occupied_by_overflow(mem_overflow_queue(memory_given)) - , _overflow_queue(mem_overflow_queue(memory_given), max_size) -#ifdef ADIAR_STATS - , _stats(stats) -#endif - { - adiar_assert(_memory_occupied_by_merger + _memory_for_buckets + _memory_occupied_by_overflow - <= _memory_given, - "the amount of memory used should be within the given bounds"); - } - public: //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Instantiate with the given amount of memory. - /// - /// \param files Files to follow the levels of + /// \brief Type of level inputs. /// - /// \param memory_given Total amount of memory to use + /// \remark You only need to use this directly, if you are designing a wrapper/decorator for the + /// televised priority queue, e.g., `reduce` and `nested_sweep`. //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const LevelFile (&files)[LevelFileCount], - size_t memory_given, - size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) - { - _level_merger.hook(files); - init_buckets(); - } + using level_input_type = typename level_merger_t::istream_ptr; + public: //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Instantiate with the given amount of memory. /// - /// \param dds Decision Diagrams to follow the levels of + /// \param level_inputs Inputs that provide the levels. These are implicitly converted to a + /// subclass of `level_merger::istream` as part of construction. /// /// \param memory_given Total amount of memory to use - //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const dd (&dds)[LevelFileCount], - size_t memory_given, - size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) - { - _level_merger.hook(dds); - init_buckets(); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Instantiate with the given amount of memory. - /// - /// \param dds Unreduced Decision Diagrams to follow the levels of /// - /// \param memory_given Total amount of memory to use + /// \param max_size Upper bound on the number of elements placed in the priority queue. //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const __dd (&dds)[LevelFileCount], + levelized_priority_queue(std::array&& level_inputs, size_t memory_given, size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) + [[maybe_unused]] statistics::levelized_priority_queue_t& stats) + : _max_size(max_size) + , _memory_given(memory_given) + , _memory_for_buckets(memory_given - _memory_occupied_by_merger + - mem_overflow_queue(memory_given)) + , _memory_occupied_by_overflow(mem_overflow_queue(memory_given)) + , _level_merger(std::move(level_inputs)) + , _overflow_queue(mem_overflow_queue(memory_given), max_size) +#ifdef ADIAR_STATS + , _stats(stats) +#endif { - _level_merger.hook(dds); - init_buckets(); - } + adiar_assert(_memory_occupied_by_merger + _memory_for_buckets + _memory_occupied_by_overflow + <= _memory_given, + "the amount of memory used should be within the given bounds"); - private: - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Computes final memory usage of internal data structures to then distribute the - /// remaining memory when initialising the buckets. - /// - /// \remark Call this function at the end of the constructor after the level_merger has hooked - /// into the input. - //////////////////////////////////////////////////////////////////////////////////////////////// - void - init_buckets() - { // Initially skip the number of levels for (size_t idx = 0; _level_merger.can_pull() && idx < LevelSkip; idx++) { _level_merger.pull(); @@ -554,7 +506,7 @@ namespace adiar::internal const ptr_uint64::label_type level = e.level(); - adiar_assert(level_cmp_le(next_bucket_level(), level, _level_comparator), + adiar_assert(level_cmp_le(next_bucket_level(), level, _level_comparator), "Can only push element to next bucket or later."); const size_t pushable_buckets = active_buckets() - has_front_bucket(); @@ -611,7 +563,7 @@ namespace adiar::internal !_overflow_queue.empty() ? _overflow_queue.top().level() : stop_level; stop_level = stop_level == no_label - || level_cmp_lt(overflow_level, stop_level, _level_comparator) + || level_cmp_lt(overflow_level, stop_level, _level_comparator) ? overflow_level : stop_level; @@ -621,20 +573,20 @@ namespace adiar::internal adiar_assert( !has_stop_level || !has_front_bucket() - || level_cmp_lt(front_bucket_level(), stop_level, _level_comparator), + || level_cmp_lt(front_bucket_level(), stop_level, _level_comparator), "'stop_level' should be past the current front bucket (if it exists)"); - adiar_assert(!has_front_bucket() - || level_cmp_lt( - front_bucket_level(), back_bucket_level(), _level_comparator), - "Back bucket should be (strictly) ahead of the back bucket"); + adiar_assert( + !has_front_bucket() + || level_cmp_lt(front_bucket_level(), back_bucket_level(), _level_comparator), + "Back bucket should be (strictly) ahead of the back bucket"); // TODO: Add statistics on what case is hit. // Edge Case: ------------------------------------------------------------------------------ : // The given stop_level is prior to the next bucket if (has_stop_level - && level_cmp_lt(stop_level, next_bucket_level(), _level_comparator)) { + && level_cmp_lt(stop_level, next_bucket_level(), _level_comparator)) { return; } @@ -885,12 +837,12 @@ namespace adiar::internal // Is the next bucket past the 'stop_level'? if (has_stop_level - && level_cmp_lt(stop_level, next_bucket_level(), _level_comparator)) { + && level_cmp_lt(stop_level, next_bucket_level(), _level_comparator)) { break; } adiar_assert(!has_front_bucket() - || level_cmp_lt( + || level_cmp_lt( front_bucket_level(), back_bucket_level(), _level_comparator), "Inconsistency in has_next_bucket predicate"); @@ -907,7 +859,7 @@ namespace adiar::internal _front_bucket_idx = (_front_bucket_idx + 1) % buckets; adiar_assert(!has_next_bucket() || !has_front_bucket() - || level_cmp_lt( + || level_cmp_lt( front_bucket_level(), back_bucket_level(), _level_comparator), "Inconsistency in has_next_bucket predicate"); @@ -930,15 +882,14 @@ namespace adiar::internal adiar_assert( (has_stop_level - && (level_cmp_le(stop_level, front_bucket_level(), _level_comparator) + && (level_cmp_le(stop_level, front_bucket_level(), _level_comparator) || (!has_next_bucket() - || level_cmp_lt( - stop_level, next_bucket_level(), _level_comparator)))) + || level_cmp_lt(stop_level, next_bucket_level(), _level_comparator)))) || _has_next_from_bucket, "Either we stopped early or we found a non-bucket"); adiar_assert( - level_cmp_le(front_bucket_level(), back_bucket_level(), _level_comparator), + level_cmp_le(front_bucket_level(), back_bucket_level(), _level_comparator), "Consistent bucket levels"); } @@ -967,9 +918,9 @@ namespace adiar::internal adiar_assert(has_front_bucket(), "After increment the front bucket will 'exist'"); - if (level_cmp_le(front_bucket_level(), stop_level, _level_comparator)) { + if (level_cmp_le(front_bucket_level(), stop_level, _level_comparator)) { _current_level = front_bucket_level(); - } else { // level_cmp_lt(stop_level, front_bucket_level(), _level_comparator) + } else { // level_cmp_lt(stop_level, front_bucket_level(), _level_comparator) new_levels[++_back_bucket_idx] = front_bucket_level(); } } while (_front_bucket_idx != old_back_bucket_idx); @@ -978,7 +929,7 @@ namespace adiar::internal // Add as many levels from the level_merger as we can fit in while (_level_merger.can_pull() - && level_cmp_le(_level_merger.peek(), stop_level, _level_comparator)) { + && level_cmp_le(_level_merger.peek(), stop_level, _level_comparator)) { _current_level = _level_merger.pull(); } @@ -1014,19 +965,15 @@ namespace adiar::internal template class levelized_priority_queue { public: @@ -1041,10 +988,17 @@ namespace adiar::internal using value_comp_type = Comp; //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Number of buckets. + /// \brief Memory mode. //////////////////////////////////////////////////////////////////////////////////////////////// static constexpr memory_mode mem_mode = MemoryMode; + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Type of level inputs. + //////////////////////////////////////////////////////////////////////////////////////////////// + using level_input_type = + typename levelized_priority_queue:: + level_input_type; + public: //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Type of the (overflow) priority queue. @@ -1091,7 +1045,7 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Instantiation of the comparator between levels. //////////////////////////////////////////////////////////////////////////////////////////////// - LevelFileComp _level_comparator = LevelFileComp(); + LevelComp _level_comparator = LevelComp(); //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Instantiation of the comparator between elements. @@ -1126,10 +1080,17 @@ namespace adiar::internal statistics::levelized_priority_queue_t& _stats; #endif - private: - levelized_priority_queue(size_t memory_given, - size_t max_size, - [[maybe_unused]] statistics::levelized_priority_queue_t& stats) + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Instantiate with the given amount of memory. + /// + /// \param memory_given Total amount of memory to use. + //////////////////////////////////////////////////////////////////////////////////////////////// + levelized_priority_queue( + std::array::istream_ptr, LevelInputs>&&, + size_t memory_given, + size_t max_size, + [[maybe_unused]] statistics::levelized_priority_queue_t& stats) : _max_size(max_size) , _memory_given(memory_given) , _priority_queue(memory_given, max_size) @@ -1138,49 +1099,6 @@ namespace adiar::internal #endif {} - public: - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Instantiate with the given amount of memory. - /// - /// \param files Files to follow the levels of - /// - /// \param memory_given Total amount of memory to use - //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const LevelFile (& /*files*/)[LevelFileCount], - size_t memory_given, - size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) - {} - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Instantiate with the given amount of memory. - /// - /// \param dds Decision Diagrams to follow the levels of - /// - /// \param memory_given Total amount of memory to use - //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const dd (& /*dds*/)[LevelFileCount], - size_t memory_given, - size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) - {} - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Instantiate with the given amount of memory. - /// - /// \param dds Decision Diagrams to follow the levels of - /// - /// \param memory_given Total amount of memory to use - //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_priority_queue(const __dd (& /*dds*/)[LevelFileCount], - size_t memory_given, - size_t max_size, - statistics::levelized_priority_queue_t& stats) - : levelized_priority_queue(memory_given, max_size, stats) - {} - public: ~levelized_priority_queue() { @@ -1231,8 +1149,7 @@ namespace adiar::internal if (_priority_queue.empty()) { return false; } ptr_uint64::label_type next_label_from_queue = _priority_queue.top().level(); return (has_current_level() - && level_cmp_lt( - _current_level, next_label_from_queue, _level_comparator)) + && level_cmp_lt(_current_level, next_label_from_queue, _level_comparator)) || (!has_current_level() && !_priority_queue.empty()); } @@ -1294,8 +1211,7 @@ namespace adiar::internal // Edge Case: ------------------------------------------------------------------------------ : // The given stop_level is prior to the next level or there is nothing in the queue - if ((has_stop_level - && level_cmp_lt(stop_level, next_level(), _level_comparator)) + if ((has_stop_level && level_cmp_lt(stop_level, next_level(), _level_comparator)) || _priority_queue.empty()) { _current_level = stop_level; return; @@ -1306,7 +1222,7 @@ namespace adiar::internal adiar_assert(has_next_level(), "There should be a next level to go to"); ptr_uint64::label_type next_level_from_queue = next_level(); if (has_stop_level - && level_cmp_le(stop_level, next_level_from_queue, _level_comparator)) { + && level_cmp_le(stop_level, next_level_from_queue, _level_comparator)) { _current_level = stop_level; return; } @@ -1434,79 +1350,35 @@ namespace adiar::internal /// or `node_raccess`. ////////////////////////////////////////////////////////////////////////////////////////////////// template , - size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, - memory_mode mem_mode = memory_mode::External, - size_t LevelFileCount = 1u, - size_t LevelSkip = 1u> + typename Comp = std::less, + size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, + memory_mode mem_mode = memory_mode::External, + size_t LevelInputs = 1u, + size_t LevelSkip = 1u> using levelized_node_priority_queue = levelized_priority_queue, - LevelFileCount, + LevelInputs, std::less, - false, LevelSkip>; ////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Levelized Priority Queue to be used with `levelized_file` and an `arc_ifstream`. ////////////////////////////////////////////////////////////////////////////////////////////////// template , - size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, - memory_mode mem_mode = memory_mode::External, - size_t LevelFileCount = 1u, - size_t LevelSkip = 1u> + typename Comp = std::less, + size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, + memory_mode mem_mode = memory_mode::External, + size_t LevelInputs = 1u, + size_t LevelSkip = 1u> using levelized_arc_priority_queue = levelized_priority_queue, - LevelFileCount, + LevelInputs, std::greater, - false, LevelSkip>; - - ////////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Levelized Priority Queue to be used with `levelized_file` and a `narc_ifstream` - /// or `narc_raccess`. - ////////////////////////////////////////////////////////////////////////////////////////////////// - template , - size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, - memory_mode mem_mode = memory_mode::External, - size_t LevelFileCount = 1u, - size_t LevelSkip = 1u> - using levelized_narc_priority_queue = levelized_priority_queue, - LevelFileCount, - std::less, - true, - LevelSkip>; - - ////////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Levelized Priority Queue to be used with `shared_file`. - ////////////////////////////////////////////////////////////////////////////////////////////////// - template , - size_t LookAhead = ADIAR_LPQ_LOOKAHEAD, - memory_mode mem_mode = memory_mode::External, - size_t LevelFileCount = 1u, - size_t LevelSkip = 1u> - using levelized_label_priority_queue = - levelized_priority_queue, - LevelFileCount, - std::less, - false, - LevelSkip>; } #endif // ADIAR_INTERNAL_DATA_STRUCTURES_LEVELIZED_PRIORITY_QUEUE_H diff --git a/src/adiar/internal/data_structures/vector.h b/src/adiar/internal/data_structures/vector.h new file mode 100644 index 000000000..862da3a83 --- /dev/null +++ b/src/adiar/internal/data_structures/vector.h @@ -0,0 +1,220 @@ +#ifndef ADIAR_INTERNAL_DATA_STRUCTURES_VECTOR_H +#define ADIAR_INTERNAL_DATA_STRUCTURES_VECTOR_H + +#include + +#include + +namespace adiar::internal +{ + template + class vector; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Wrapper for TPIE's internal memory array to match the interface of `std::vector`. + /// + /// \details TPIE also provides the `internal_vector`. Yet, that does not (yet) include a reverse + /// iterator. Hence, for now it's easier for us to just wrap the `array` directly + /// instead of using the array. This also gives us the freedom to make it more conform to + /// the `std::vector`. + // + // TODO: explicit or implicit `resize` when full? + ////////////////////////////////////////////////////////////////////////////////////////////////// + template + class vector + { + private: + using array_type = tpie::array; + + /// \brief The underlying (internal memory) array to store the information. + array_type _array; + + /// \brief The size of `_array` that was allocated. + size_t _capacity; + + /// \brief The number of elements placed in the vector. + size_t _size = 0; + + public: + using value_type = typename array_type::value_type; + using iterator = typename array_type::iterator; + using reverse_iterator = typename array_type::reverse_iterator; + using const_iterator = typename array_type::const_iterator; + using const_reverse_iterator = typename array_type::const_reverse_iterator; + + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + static size_t + memory_usage(size_t capacity = 0) + { + return array_type::memory_usage(capacity); + } + + static size_t + memory_fits(size_t memory_bytes) + { + const size_t c = array_type::memory_fits(memory_bytes); + + adiar_assert(memory_usage(c) <= memory_bytes, "memory_fits and memory_usage should agree."); + return c; + } + + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + vector(size_t capacity = 0) + : _array(capacity) + , _capacity(capacity) + {} + + //////////////////////////////////////////////////////////////////////////////////////////////// + value_type& + at(size_t i) + { + adiar_assert(i < this->size(), "Use of invalid index!"); + return this->_array[i]; + } + + const value_type& + at(size_t i) const + { + adiar_assert(i < this->size(), "Use of invalid index!"); + return this->_array[i]; + } + + value_type& + operator[](size_t i) + { + return this->at(i); + } + + const value_type& + operator[](size_t i) const + { + return this->at(i); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + value_type& + front() + { + return this->_array[0]; + } + + const value_type& + front() const + { + return this->_array[0]; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + public: + value_type& + back() + { + return this->_array[this->_size - 1]; + } + + const value_type& + back() const + { + return this->_array[this->_size - 1]; + } + + value_type& + push_back(const value_type& x) + { + adiar_assert(this->_size < this->_capacity, "Cannot push at full capacity"); + this->_array[this->_size] = x; + this->_size += 1; + return this->back(); + } + + void + pop_back() + { + this->_size -= 1; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + iterator + begin() + { + return this->_array.begin(); + } + + const_iterator + begin() const + { + return this->_array.begin(); + } + + iterator + end() + { + return this->_array.begin() + this->_size; + } + + const_iterator + end() const + { + return this->_array.begin() + this->_size; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + reverse_iterator + rbegin() + { + return this->_array.rend() - this->_size; + } + + const_reverse_iterator + rbegin() const + { + return this->_array.rend() - this->_size; + } + + reverse_iterator + rend() + { + return this->_array.rend(); + } + + const_reverse_iterator + rend() const + { + return this->_array.rend(); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + size_t + size() const + { + return this->_size; + } + + bool + empty() const + { + return this->_size == 0; + } + + size_t + capacity() const + { + return this->_capacity; + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief Alias for a `vector`. + ////////////////////////////////////////////////////////////////////////////////////////////////// + template + using internal_vector = vector; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + /// \brief An external memory vector in TPIE. + ////////////////////////////////////////////////////////////////////////////////////////////////// + // TODO Implement an external memory (temporary) vector (wrapper on `file`, I suppose). +} + +#endif // ADIAR_INTERNAL_DATA_STRUCTURES_VECTOR_H diff --git a/src/adiar/internal/io/arc_ifstream.h b/src/adiar/internal/io/arc_ifstream.h index b472e3244..32acd5ad4 100644 --- a/src/adiar/internal/io/arc_ifstream.h +++ b/src/adiar/internal/io/arc_ifstream.h @@ -45,10 +45,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////// arc_ifstream() = default; - //////////////////////////////////////////////////////////////////////////// - arc_ifstream(const arc_ifstream&) = delete; - arc_ifstream(arc_ifstream&&) = delete; - //////////////////////////////////////////////////////////////////////////// /// \brief Construct a stream attached to an arc file. //////////////////////////////////////////////////////////////////////////// diff --git a/src/adiar/internal/io/ifstream.h b/src/adiar/internal/io/ifstream.h index 45e875c33..95f272b87 100644 --- a/src/adiar/internal/io/ifstream.h +++ b/src/adiar/internal/io/ifstream.h @@ -67,7 +67,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// ifstream(const ifstream&) = delete; - ifstream(ifstream&&) = delete; //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Construct attached to a given shared `file`. diff --git a/src/adiar/internal/io/iofstream.h b/src/adiar/internal/io/iofstream.h index f91f928c5..caa9e6be0 100644 --- a/src/adiar/internal/io/iofstream.h +++ b/src/adiar/internal/io/iofstream.h @@ -49,7 +49,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// iofstream(const iofstream&) = delete; - iofstream(iofstream&&) = delete; //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Construct attached to a given shared `file`. diff --git a/src/adiar/internal/io/levelized_ifstream.h b/src/adiar/internal/io/levelized_ifstream.h index 22154ac12..a7da7e62c 100644 --- a/src/adiar/internal/io/levelized_ifstream.h +++ b/src/adiar/internal/io/levelized_ifstream.h @@ -52,10 +52,6 @@ namespace adiar::internal levelized_ifstream() {} - //////////////////////////////////////////////////////////////////////////////////////////////// - levelized_ifstream(const levelized_ifstream&) = delete; - levelized_ifstream(levelized_ifstream&&) = delete; - //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Create attached to a levelized file. //////////////////////////////////////////////////////////////////////////////////////////////// @@ -199,10 +195,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// level_info_ifstream() = default; - //////////////////////////////////////////////////////////////////////////////////////////////// - level_info_ifstream(const level_info_ifstream&) = delete; - level_info_ifstream(level_info_ifstream&&) = delete; - //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Construct attached to a file. /// diff --git a/src/adiar/internal/io/narc_ifstream.h b/src/adiar/internal/io/narc_ifstream.h index 2fbb26ca3..c9ac7a418 100644 --- a/src/adiar/internal/io/narc_ifstream.h +++ b/src/adiar/internal/io/narc_ifstream.h @@ -62,10 +62,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// narc_ifstream() = default; - //////////////////////////////////////////////////////////////////////////////////////////////// - narc_ifstream(const narc_ifstream&) = delete; - narc_ifstream(narc_ifstream&&) = delete; - //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Create attached to an arc file. //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/adiar/internal/io/node_ifstream.h b/src/adiar/internal/io/node_ifstream.h index 6b7028fcb..ab597feab 100644 --- a/src/adiar/internal/io/node_ifstream.h +++ b/src/adiar/internal/io/node_ifstream.h @@ -39,10 +39,6 @@ namespace adiar::internal //////////////////////////////////////////////////////////////////////////////////////////////// node_ifstream() = default; - //////////////////////////////////////////////////////////////////////////////////////////////// - node_ifstream(const node_ifstream&) = delete; - node_ifstream(node_ifstream&&) = delete; - //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Create attached to a node file. //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/adiar/internal/io/ofstream.h b/src/adiar/internal/io/ofstream.h index c0c8baa2a..35872b195 100644 --- a/src/adiar/internal/io/ofstream.h +++ b/src/adiar/internal/io/ofstream.h @@ -53,6 +53,9 @@ namespace adiar::internal ofstream() {} + //////////////////////////////////////////////////////////////////////////////////////////////// + ofstream(const ofstream&) = delete; + //////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Construct attached to a given `file`. /// diff --git a/src/adiar/internal/util.h b/src/adiar/internal/util.h index 48e44e5d2..2037237e5 100644 --- a/src/adiar/internal/util.h +++ b/src/adiar/internal/util.h @@ -33,33 +33,6 @@ namespace adiar::internal } } - ////////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief Defines at compile time the type of the file stream to use for reading the levels from - /// some file(s). - // - // TODO: Rename 'stream_t' into 'type' - ////////////////////////////////////////////////////////////////////////////////////////////////// - template - struct level_ifstream_t - { - template - using stream_t = level_info_ifstream; - }; - - template <> - struct level_ifstream_t> - { - template - using stream_t = ifstream; - }; - - template <> - struct level_ifstream_t> - { - template - using stream_t = ifstream; - }; - ////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief Turn a `level_ifstream<...>` into a level-only generator function. /// @@ -82,15 +55,12 @@ namespace adiar::internal /// \brief Obtain whether the levels in two files are disjoint. ////////////////////////////////////////////////////////////////////////////////////////////////// // TODO: Move to dd_func? - template + template bool - disjoint_levels(const A& a, const B& b) + disjoint_levels(const DD& a, const DD& b) { - using stream1_t = typename level_ifstream_t::template stream_t; - stream1_t sa(a); - - using stream2_t = typename level_ifstream_t::template stream_t; - stream2_t sb(b); + level_info_ifstream sa(a); + level_info_ifstream sb(b); while (sa.can_pull() && sb.can_pull()) { if (level_of(sa.peek()) == level_of(sb.peek())) { diff --git a/src/adiar/type_traits.h b/src/adiar/type_traits.h index ba898051c..2fcd8b82c 100644 --- a/src/adiar/type_traits.h +++ b/src/adiar/type_traits.h @@ -17,6 +17,9 @@ namespace adiar template inline constexpr bool needs_conversion = !is_same && is_convertible; + template + inline constexpr bool is_constructible = std::is_constructible::value; + template inline constexpr bool is_const = std::is_const>::value; diff --git a/src/adiar/zdd/change.cpp b/src/adiar/zdd/change.cpp index b6906dcb2..0b33d8fea 100644 --- a/src/adiar/zdd/change.cpp +++ b/src/adiar/zdd/change.cpp @@ -1,8 +1,10 @@ #include +#include #include #include #include +#include #include #include #include @@ -29,12 +31,11 @@ namespace adiar static zdd on_terminal_input(const bool terminal_value, const zdd& dd, - const internal::shared_file& vars) + const internal::internal_vector& vars) { // TODO: simplify with generator function as input if (terminal_value) { - internal::ifstream ls(vars); - return zdd_vars(make_generator(ls)); + return zdd_vars(make_generator(vars.rbegin(), vars.rend())); } else { return dd; } diff --git a/src/adiar/zdd/complement.cpp b/src/adiar/zdd/complement.cpp index c0bb11696..266d83a0e 100644 --- a/src/adiar/zdd/complement.cpp +++ b/src/adiar/zdd/complement.cpp @@ -57,12 +57,11 @@ namespace adiar static zdd on_terminal_input(const bool terminal_value, const zdd& /*dd*/, - const internal::shared_file& universe) + const internal::internal_vector& universe) { // TODO: remove - internal::ifstream ls(universe); - - const generator universe_generator = make_generator(ls); + const generator universe_generator = + make_generator(universe.rbegin(), universe.rend()); using complement_chain_converter = internal::chain_converter>; diff --git a/src/adiar/zdd/expand.cpp b/src/adiar/zdd/expand.cpp index 5b0bbb713..55c55e6d5 100644 --- a/src/adiar/zdd/expand.cpp +++ b/src/adiar/zdd/expand.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -30,12 +31,10 @@ namespace adiar static zdd on_terminal_input(const bool terminal_value, const zdd& dd, - const internal::shared_file& labels) + const internal::internal_vector& vars) { - // TODO: simplify when labels are a generator if (terminal_value) { - internal::ifstream ls(labels); - return zdd_powerset(make_generator(ls)); + return zdd_powerset(make_generator(vars.rbegin(), vars.rend())); } else { return dd; } diff --git a/tests/adiar/internal/data_structures/CMakeLists.txt b/tests/adiar/internal/data_structures/CMakeLists.txt index 63fccba65..82e7af4d2 100644 --- a/tests/adiar/internal/data_structures/CMakeLists.txt +++ b/tests/adiar/internal/data_structures/CMakeLists.txt @@ -3,3 +3,4 @@ add_test(adiar-internal-data_structures-levelized_priority_queue levelized_prior add_test(adiar-internal-data_structures-priority_queue priority_queue.test.cpp) add_test(adiar-internal-data_structures-sorter sorter.test.cpp) add_test(adiar-internal-data_structures-stack stack.test.cpp) +add_test(adiar-internal-data_structures-vector vector.test.cpp) diff --git a/tests/adiar/internal/data_structures/level_merger.test.cpp b/tests/adiar/internal/data_structures/level_merger.test.cpp index 24e965d1b..062ca0adc 100644 --- a/tests/adiar/internal/data_structures/level_merger.test.cpp +++ b/tests/adiar/internal/data_structures/level_merger.test.cpp @@ -4,128 +4,158 @@ go_bandit([]() { describe("adiar/internal/level_merger.h", []() { - describe("level_merger, ...>", []() { - it("can pull from one level_info stream", []() { - shared_levelized_file f; - { // Garbage collect the writer - levelized_ofstream fw(f); + const ptr_uint64 terminal_F = ptr_uint64(false); + const ptr_uint64 terminal_T = ptr_uint64(true); + + shared_levelized_file nf_x0; + /* + // 1 ---- x0 + // / \ + // F T + */ + { + node_ofstream nw(nf_x0); + nw << node(0, node::max_id, terminal_F, terminal_T); + } + + shared_levelized_file nf_x1; + /* + // 1 ---- x1 + // / \ + // F T + */ + { + node_ofstream nw(nf_x1); + nw << node(1, node::max_id, terminal_F, terminal_T); + } + + shared_levelized_file nf_x0_or_x2; + /* + // 1 ---- x0 + // / \ + // | T + // | + // 2 ---- x2 + // / \ + // F T + */ + { + node_ofstream nw(nf_x0_or_x2); + nw << node(2, node::max_id, terminal_F, terminal_T) + << node(0, node::max_id, node::pointer_type(2, node::max_id), terminal_T); + } + + describe("level_merger(dd ...)", [&]() { + it("can pull from a single diagram [x0]", [&]() { + level_merger, 1> merger({ dd(nf_x0) }); - fw.push(level_info(4, 1u)); - fw.push(level_info(3, 2u)); - fw.push(level_info(2, 2u)); - fw.push(level_info(1, 1u)); - } + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - level_merger, std::less<>, 1u> merger; + AssertThat(merger.can_pull(), Is().False()); + }); - merger.hook({ f }); + it("can pull from a single diagram [x1]", [&]() { + level_merger, 1> merger({ dd(nf_x1) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(2u)); + AssertThat(merger.can_pull(), Is().False()); + }); + + it("can pull from a single diagram [x0 | x2]", [&]() { + level_merger, 1> merger({ dd(nf_x0_or_x2) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(3u)); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(4u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can peek from one level_info streams", []() { - shared_levelized_file f; - - { // Garbage collect the writer - levelized_ofstream fw(f); - - fw.push(level_info(4, 1u)); - fw.push(level_info(3, 2u)); - fw.push(level_info(2, 1u)); - fw.push(level_info(1, 1u)); - } - - level_merger, std::less<>, 1> merger; + it("can merge levels of two diagrams [x0, x1] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x0), dd(nf_x1) }); - merger.hook({ f }); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); + AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); - AssertThat(merger.peek(), Is().EqualTo(2u)); - AssertThat(merger.pull(), Is().EqualTo(2u)); - - AssertThat(merger.peek(), Is().EqualTo(3u)); - AssertThat(merger.pull(), Is().EqualTo(3u)); - - AssertThat(merger.pull(), Is().EqualTo(4u)); + AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from merge of two level_info streams, where one is empty [1]", []() { - shared_levelized_file f1; - shared_levelized_file f2; + it("can merge levels of two diagrams [x1, x0] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x1), dd(nf_x0) }); - { // Garbage collect the writer - levelized_ofstream fw1(f1); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - fw1.push(level_info(1, 1u)); - } + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(1u)); - level_merger, std::less<>, 2> merger; + AssertThat(merger.can_pull(), Is().False()); + }); - merger.hook({ f1, f2 }); + it("can merge levels of two diagrams [x1, x0] (std::greater)", [&]() { + level_merger, 2> merger({ dd(nf_x1), dd(nf_x0) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); + AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from merge of two level_info streams, where one is empty [2]", []() { - shared_levelized_file f1; - shared_levelized_file f2; + it("can merge levels of two diagrams [x0, x0 | x2] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x0), dd(nf_x0_or_x2) }); - { // Garbage collect the writer - levelized_ofstream fw1(f1); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - fw1.push(level_info(1, 1u)); - fw1.push(level_info(2, 1u)); - } + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(2u)); - level_merger, std::greater<>, 2> merger; + AssertThat(merger.can_pull(), Is().False()); + }); - merger.hook({ f1, f2 }); + it("can merge levels of two diagrams [x0 | x2, x0] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x0_or_x2), dd(nf_x0) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(2u)); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from merge of two level_info streams [1]", []() { - shared_levelized_file f1; - shared_levelized_file f2; + it("can merge levels of two diagrams [x0 | x2, x1] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x0_or_x2), dd(nf_x1) }); - { // Garbage collect the writers - levelized_ofstream fw1(f1); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - fw1.push(level_info(4, 1u)); - fw1.push(level_info(2, 2u)); - fw1.push(level_info(1, 1u)); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(1u)); - levelized_ofstream fw2(f2); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(2u)); - fw2.push(level_info(4, 1u)); - fw2.push(level_info(3, 1u)); - } + AssertThat(merger.can_pull(), Is().False()); + }); - level_merger, std::less<>, 2> merger; + it("can merge levels of two diagrams [x1, x0 | x2] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x1), dd(nf_x0_or_x2) }); - merger.hook({ f1, f2 }); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); @@ -133,32 +163,41 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(2u)); - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(3u)); + AssertThat(merger.can_pull(), Is().False()); + }); + + it("can shift levels of a single diagram [x0 +1]", [&]() { + level_merger, 1> merger({ dd(nf_x0, false, +1) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(4u)); + AssertThat(merger.pull(), Is().EqualTo(1u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from merge of two level_info streams [2] (std::less)", []() { - shared_levelized_file f1; - shared_levelized_file f2; + it("can shift levels of a single diagram [x1 +2]", [&]() { + level_merger, 1> merger({ dd(nf_x1, false, +2) }); - { // Garbage collect the writers - levelized_ofstream fw1(f1); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(3u)); - fw1.push(level_info(2, 1u)); + AssertThat(merger.can_pull(), Is().False()); + }); - levelized_ofstream fw2(f2); + it("can shift levels of a single diagram [x1 -1]", [&]() { + level_merger, 1> merger({ dd(nf_x1, false, -1) }); - fw2.push(level_info(1, 1u)); - } + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - level_merger, std::less<>, 2> merger; + AssertThat(merger.can_pull(), Is().False()); + }); - merger.hook({ f1, f2 }); + it("can merge shifted levels of two diagrams [x0 +1, x0 | x2] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x0, false, +1), dd(nf_x0_or_x2) }); + + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); @@ -169,138 +208,96 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from merge of two level_info streams [2] (std::greater)", []() { - shared_levelized_file f1; - shared_levelized_file f2; - - { // Garbage collect the writers - levelized_ofstream fw1(f1); - - fw1.push(level_info(2, 1u)); - - levelized_ofstream fw2(f2); - - fw2.push(level_info(1, 1u)); - } - - level_merger, std::greater<>, 2> merger; - - merger.hook({ f1, f2 }); + it("can merge shifted levels of two diagrams [x1 +1, x0 | x2] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x1, false, +1), dd(nf_x0_or_x2) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(2u)); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can peek merge of two level_info stream", []() { - shared_levelized_file f1; - shared_levelized_file f2; - - { // Garbage collect the writers - levelized_ofstream fw1(f1); - - fw1.push(level_info(4, 2u)); - fw1.push(level_info(2, 1u)); + it("can merge shifted levels of two diagrams [x1 -1, x0 | x2] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x1, false, -1), dd(nf_x0_or_x2) }); - levelized_ofstream fw2(f2); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - fw2.push(level_info(4, 3u)); - fw2.push(level_info(3, 2u)); - fw2.push(level_info(1, 1u)); - } + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(2u)); - level_merger, std::less<>, 2> merger; + AssertThat(merger.can_pull(), Is().False()); + }); - merger.hook({ f1, f2 }); + it("can merge shifted levels of two diagrams [x1 +2, x0 | x2 +1] (std::less)", [&]() { + level_merger, 2> merger({ dd(nf_x1, false, +2), dd(nf_x0_or_x2, false, +1) }); - AssertThat(merger.peek(), Is().EqualTo(1u)); + AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); - AssertThat(merger.peek(), Is().EqualTo(2u)); - AssertThat(merger.pull(), Is().EqualTo(2u)); - AssertThat(merger.peek(), Is().EqualTo(3u)); + + AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(3u)); - AssertThat(merger.peek(), Is().EqualTo(4u)); - AssertThat(merger.pull(), Is().EqualTo(4u)); - }); - it("can merge levels in reverse", []() { - shared_levelized_file f1; - shared_levelized_file f2; + AssertThat(merger.can_pull(), Is().False()); + }); + }); - { // Garbage collect the writers - levelized_ofstream fw1(f1); + describe("level_merger(gen ...)", []() { + it("can pull from a single generator []", []() { + const std::vector xs = { }; - fw1.push(level_info(4, 2u)); - fw1.push(level_info(2, 1u)); + level_merger, 1> merger({ make_generator(xs.begin(), xs.end()) }); - levelized_ofstream fw2(f2); + AssertThat(merger.can_pull(), Is().False()); + }); - fw2.push(level_info(4, 3u)); - fw2.push(level_info(3, 2u)); - fw2.push(level_info(1, 1u)); - } + it("can pull from a single generator [0]", []() { + const std::vector xs = { 0 }; - level_merger, std::greater<>, 2, true> merger; + level_merger, 1> merger({ make_generator(xs.begin(), xs.end()) }); - merger.hook({ f1, f2 }); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - AssertThat(merger.peek(), Is().EqualTo(4u)); - AssertThat(merger.pull(), Is().EqualTo(4u)); - AssertThat(merger.peek(), Is().EqualTo(3u)); - AssertThat(merger.pull(), Is().EqualTo(3u)); - AssertThat(merger.peek(), Is().EqualTo(2u)); - AssertThat(merger.pull(), Is().EqualTo(2u)); - AssertThat(merger.peek(), Is().EqualTo(1u)); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.can_pull(), Is().False()); }); - it("can pull, even after the original files have been deleted", []() { - shared_levelized_file f1 = shared_levelized_file(); - shared_levelized_file f2 = shared_levelized_file(); + it("can pull from a single generator [1]", []() { + const std::vector xs = { 1 }; - { // Garbage collect the writers - levelized_ofstream fw1(f1); + level_merger, 1> merger({ make_generator(xs.begin(), xs.end()) }); - fw1.push(level_info(4, 2u)); - fw1.push(level_info(2, 1u)); - - levelized_ofstream fw2(f2); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(1u)); - fw2.push(level_info(4, 1u)); - fw2.push(level_info(3, 2u)); - fw2.push(level_info(1, 1u)); - } + AssertThat(merger.can_pull(), Is().False()); + }); - level_merger, std::less<>, 2> merger; + it("can pull from a single generator [0,2,3]", []() { + const std::vector xs = { 0, 2, 3 }; - merger.hook({ f1, f2 }); + level_merger, 1> merger({ make_generator(xs.begin(), xs.end()) }); - f1.reset(); - f2.reset(); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(0u)); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(2u)); - AssertThat(merger.pull(), Is().EqualTo(3u)); - AssertThat(merger.pull(), Is().EqualTo(4u)); - }); - }); - describe("level_merger, ...>", []() { - it("can use a single label_file", []() { - shared_file f; + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(3u)); - { // Garbage collect the writers - ofstream w(f); - w << 0 << 2 << 3; - } + AssertThat(merger.can_pull(), Is().False()); + }); - level_merger, std::less<>, 1> merger; + it("can merge from a two generators [0,2,3]+[2] (std::less)", []() { + const std::vector xs = { 0, 2, 3 }; + const std::vector ys = { 2 }; - merger.hook({ f }); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -314,21 +311,11 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge two label_files", []() { - shared_file f1; - shared_file f2; - - { // Garbage collect the writers - ofstream w1(f1); - w1 << 0 << 2 << 3; + it("can merge from a two generators [0,2,3]+[1,2] (std::less)", []() { + const std::vector xs = { 0, 2, 3 }; + const std::vector ys = { 1, 2 }; - ofstream w2(f2); - w2 << 0 << 1 << 3; - } - - level_merger, std::less<>, 2> merger; - - merger.hook({ f1, f2 }); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -344,73 +331,27 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - }); - describe("level_merger", []() { - const ptr_uint64 terminal_F = ptr_uint64(false); - const ptr_uint64 terminal_T = ptr_uint64(true); - - shared_levelized_file nf_x0; - /* - // 1 ---- x0 - // / \ - // F T - */ - { - node_ofstream nw(nf_x0); - nw << node(0, node::max_id, terminal_F, terminal_T); - } - - shared_levelized_file nf_x1; - /* - // 1 ---- x1 - // / \ - // F T - */ - { - node_ofstream nw(nf_x1); - nw << node(1, node::max_id, terminal_F, terminal_T); - } - - shared_levelized_file nf_x0_or_x2; - /* - // 1 ---- x0 - // / \ - // | T - // | - // 2 ---- x2 - // / \ - // F T - */ - { - node_ofstream nw(nf_x0_or_x2); - nw << node(2, node::max_id, terminal_F, terminal_T) - << node(0, node::max_id, node::pointer_type(2, node::max_id), terminal_T); - } + it("can merge from a two generators [0,2]+[] (std::less)", []() { + const std::vector xs = { 0, 2 }; + const std::vector ys = { }; - it("can pull from a single diagram [x0]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x0) }); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); - AssertThat(merger.can_pull(), Is().False()); - }); - - it("can pull from a single diagram [x1]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x1) }); - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can pull from a single diagram [x0 | x2]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x0_or_x2) }); + it("can merge from a two generators [2]+[0,2] (std::less)", []() { + const std::vector xs = { 2 }; + const std::vector ys = { 0, 2 }; + + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -421,48 +362,48 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge levels of two diagrams [x0, x1] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x0), dd(nf_x1) }); + it("can merge from a two generators []+[0,2] (std::less)", []() { + const std::vector xs = { }; + const std::vector ys = { 0, 2 }; + + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can merge levels of two diagrams [x1, x0] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x1), dd(nf_x0) }); + it("can merge from a two generators [4,2,0]+[3,2,0] (std::greater)", []() { + const std::vector xs = { 4, 2, 0 }; + const std::vector ys = { 3, 2, 0 }; - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(0u)); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), make_generator(ys.begin(), ys.end()) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); - - AssertThat(merger.can_pull(), Is().False()); - }); + AssertThat(merger.pull(), Is().EqualTo(4u)); - it("can merge levels of two diagrams [x1, x0] (std::greater)", [&]() { - level_merger, std::greater<>, 2> merger; - merger.hook({ dd(nf_x1), dd(nf_x0) }); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(3u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().False()); }); + }); - it("can merge levels of two diagrams [x0, x0 | x2] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x0), dd(nf_x0_or_x2) }); + describe("level_merger(...)", [&]() { + it("can merge a diagram [x0] and a generator [0,2] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; + + level_merger, 2> merger({ dd(nf_x0), make_generator(xs.begin(), xs.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -473,9 +414,10 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge levels of two diagrams [x0 | x2, x0] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x0_or_x2), dd(nf_x0) }); + it("can merge a generator [0,2] and a diagram [x0] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; + + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), dd(nf_x0) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -486,9 +428,10 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge levels of two diagrams [x0 | x2, x1] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x0_or_x2), dd(nf_x1) }); + it("can merge a diagram [x1] and a generator [0,2] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; + + level_merger, 2> merger({ dd(nf_x1), make_generator(xs.begin(), xs.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -502,9 +445,10 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge levels of two diagrams [x1, x0 | x2] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x1), dd(nf_x0_or_x2) }); + it("can merge a generator [0,2] and a diagram [x1] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; + + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), dd(nf_x1) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); @@ -518,45 +462,27 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can shift levels of a single diagram [x0 +1]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x0, false, +1) }); - - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); - - AssertThat(merger.can_pull(), Is().False()); - }); + it("can merge a diagram [x0|x1] and generator [0,2] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; - it("can shift levels of a single diagram [x1 +2]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x1, false, +2) }); + level_merger, 2> merger({ dd(nf_x0_or_x2), make_generator(xs.begin(), xs.end()) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(3u)); - - AssertThat(merger.can_pull(), Is().False()); - }); - - it("can shift levels of a single diagram [x1 -1]", [&]() { - level_merger, std::less<>, 1> merger; - merger.hook({ dd(nf_x1, false, -1) }); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(0u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can merge shifted levels of two diagrams [x0 +1, x0 | x2] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x0, false, +1), dd(nf_x0_or_x2) }); + it("can merge a generator [0,2] and a diagram [x0|x1] (std::less)", [&]() { + const std::vector xs = { 0, 2 }; - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(0u)); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), dd(nf_x0_or_x2) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(2u)); @@ -564,41 +490,36 @@ go_bandit([]() { AssertThat(merger.can_pull(), Is().False()); }); - it("can merge shifted levels of two diagrams [x1 +1, x0 | x2] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x1, false, +1), dd(nf_x0_or_x2) }); + it("can merge a diagram [x0|x1] and generator [1,2] (std::less)", [&]() { + const std::vector xs = { 1, 2 }; + + level_merger, 2> merger({ dd(nf_x0_or_x2), make_generator(xs.begin(), xs.end()) }); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(0u)); + AssertThat(merger.can_pull(), Is().True()); + AssertThat(merger.pull(), Is().EqualTo(1u)); + AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); - it("can merge shifted levels of two diagrams [x1 -1, x0 | x2] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x1, false, -1), dd(nf_x0_or_x2) }); + it("can merge a generator [1,2] and a diagram [x0|x1] (std::less)", [&]() { + const std::vector xs = { 1, 2 }; - AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(0u)); + level_merger, 2> merger({ make_generator(xs.begin(), xs.end()), dd(nf_x0_or_x2) }); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(2u)); - - AssertThat(merger.can_pull(), Is().False()); - }); - - it("can merge shifted levels of two diagrams [x1 +2, x0 | x2 +1] (std::less)", [&]() { - level_merger, std::less<>, 2> merger; - merger.hook({ dd(nf_x1, false, +2), dd(nf_x0_or_x2, false, +1) }); + AssertThat(merger.pull(), Is().EqualTo(0u)); AssertThat(merger.can_pull(), Is().True()); AssertThat(merger.pull(), Is().EqualTo(1u)); AssertThat(merger.can_pull(), Is().True()); - AssertThat(merger.pull(), Is().EqualTo(3u)); + AssertThat(merger.pull(), Is().EqualTo(2u)); AssertThat(merger.can_pull(), Is().False()); }); diff --git a/tests/adiar/internal/data_structures/levelized_priority_queue.test.cpp b/tests/adiar/internal/data_structures/levelized_priority_queue.test.cpp index 3afd116e8..a6cd19b75 100644 --- a/tests/adiar/internal/data_structures/levelized_priority_queue.test.cpp +++ b/tests/adiar/internal/data_structures/levelized_priority_queue.test.cpp @@ -77,10 +77,8 @@ using test_priority_queue = levelized_priority_queue, - false, 1u>; go_bandit([]() { @@ -4114,10 +4112,8 @@ go_bandit([]() { lpq_test_gt, 1u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4155,10 +4151,8 @@ go_bandit([]() { lpq_test_gt, 1u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4183,10 +4177,8 @@ go_bandit([]() { lpq_test_gt, 1u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4244,10 +4236,8 @@ go_bandit([]() { lpq_test_gt, 0u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4285,10 +4275,8 @@ go_bandit([]() { lpq_test_gt, 0u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4313,10 +4301,8 @@ go_bandit([]() { lpq_test_gt, 0u, memory_mode::Internal, - shared_file, 1u, std::greater<>, - false, 1u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4365,10 +4351,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - shared_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4391,10 +4375,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - shared_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4417,10 +4399,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - shared_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4445,10 +4425,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - lpq_test_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4489,10 +4467,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - lpq_test_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4518,225 +4494,6 @@ go_bandit([]() { }); }); - describe("levelized_priority_queue<..., level_reverse=true, ...>", []() { - // TODO: these tests break with a look_ahead of 0. Is this indicating some bug? - - it("can setup buckets in reverse order", []() { - lpq_test_file f; - - { // Garbage collect the writer early - lpq_test_ofstream fw(f); - - fw.push(level_info(4, 2u)); // bucket - fw.push(level_info(3, 3u)); // bucket - fw.push(level_info(2, 2u)); // overflow - fw.push(level_info(1, 1u)); // . - } - - levelized_priority_queue, - true, - 0u> - pq({ f }, memory_available(), 32, stats_lpq_tests); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(4u)); - - pq.setup_next_level(4u); - AssertThat(pq.current_level(), Is().EqualTo(4u)); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(3u)); - - pq.setup_next_level(3u); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(2u)); - - pq.setup_next_level(2u); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(1u)); - - pq.setup_next_level(1u); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().False()); - }); - - it("can push and pull from reversed buckets [1]", []() { - lpq_test_file f; - - { // Garbage collect the writer early - lpq_test_ofstream fw(f); - - fw.push(level_info(4, 2u)); // bucket - fw.push(level_info(3, 3u)); // bucket - fw.push(level_info(2, 2u)); // overflow - fw.push(level_info(1, 1u)); // . - } - - levelized_priority_queue, - true, - 0u> - pq({ f }, memory_available(), 32, stats_lpq_tests); - - AssertThat(pq.size(), Is().EqualTo(0u)); - - pq.push(lpq_test_data{ 4, 1 }); - AssertThat(pq.size(), Is().EqualTo(1u)); - - pq.push(lpq_test_data{ 4, 2 }); - AssertThat(pq.size(), Is().EqualTo(2u)); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(4u)); - - pq.setup_next_level(); // 4 - AssertThat(pq.current_level(), Is().EqualTo(4u)); - - AssertThat(pq.empty_level(), Is().False()); - - pq.push(lpq_test_data{ 3, 3 }); - AssertThat(pq.size(), Is().EqualTo(3u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 4, 2 })); - AssertThat(pq.size(), Is().EqualTo(2u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 4, 1 })); - AssertThat(pq.size(), Is().EqualTo(1u)); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(3u)); - - pq.setup_next_level(); // 3 - AssertThat(pq.current_level(), Is().EqualTo(3u)); - - AssertThat(pq.empty_level(), Is().False()); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 3, 3 })); - AssertThat(pq.size(), Is().EqualTo(0u)); - - AssertThat(pq.empty_level(), Is().True()); - }); - - it("can push and pull from reversed buckets [1]", []() { - lpq_test_file f1; - - { // Garbage collect the writer early - lpq_test_ofstream fw(f1); - - fw.push(level_info(4, 2u)); // bucket - fw.push(level_info(2, 2u)); // overflow - fw.push(level_info(1, 1u)); // . - } - - lpq_test_file f2; - { // Garbage collect the writer early - lpq_test_ofstream fw(f2); - - fw.push(level_info(3, 3u)); // bucket - fw.push(level_info(2, 2u)); // overflow - } - - levelized_priority_queue, - true, - 0u> - pq({ f1, f2 }, memory_available(), 32, stats_lpq_tests); - - AssertThat(pq.size(), Is().EqualTo(0u)); - - pq.push(lpq_test_data{ 4, 1 }); // bucket - AssertThat(pq.size(), Is().EqualTo(1u)); - - pq.push(lpq_test_data{ 4, 2 }); // bucket - AssertThat(pq.size(), Is().EqualTo(2u)); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(4u)); - - pq.setup_next_level(); // 4 - AssertThat(pq.current_level(), Is().EqualTo(4u)); - - AssertThat(pq.empty_level(), Is().False()); - - pq.push(lpq_test_data{ 3, 3 }); // bucket - AssertThat(pq.size(), Is().EqualTo(3u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 4, 2 })); - AssertThat(pq.size(), Is().EqualTo(2u)); - - pq.push(lpq_test_data{ 2, 4 }); // overflow - AssertThat(pq.size(), Is().EqualTo(3u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 4, 1 })); - AssertThat(pq.size(), Is().EqualTo(2u)); - - AssertThat(pq.empty_level(), Is().True()); - - AssertThat(pq.has_next_level(), Is().True()); - AssertThat(pq.next_level(), Is().EqualTo(3u)); - - pq.setup_next_level(); // 3 - AssertThat(pq.current_level(), Is().EqualTo(3u)); - - pq.push(lpq_test_data{ 2, 5 }); // bucket - AssertThat(pq.size(), Is().EqualTo(3u)); - - AssertThat(pq.empty_level(), Is().False()); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 3, 3 })); - AssertThat(pq.size(), Is().EqualTo(2u)); - - AssertThat(pq.empty_level(), Is().True()); - - pq.setup_next_level(); // 2 - AssertThat(pq.current_level(), Is().EqualTo(2u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 2, 5 })); - AssertThat(pq.size(), Is().EqualTo(1u)); - - AssertThat(pq.can_pull(), Is().True()); - AssertThat(pq.pull(), Is().EqualTo(lpq_test_data{ 2, 4 })); - AssertThat(pq.size(), Is().EqualTo(0u)); - - AssertThat(pq.empty_level(), Is().True()); - }); - }); - describe("levelized_priority_queue<..., look_ahead=0, ..., init_level=0>", []() { it("initialises correctly", []() { shared_file f; @@ -4745,10 +4502,8 @@ go_bandit([]() { lpq_test_lt, 0u, memory_mode::Internal, - shared_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4771,10 +4526,8 @@ go_bandit([]() { lpq_test_lt, 0u, memory_mode::Internal, - lpq_test_file, 1u, std::less<>, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4815,10 +4568,8 @@ go_bandit([]() { lpq_test_lt, 0u, memory_mode::Internal, - lpq_test_file, 1u, std::less, - false, 0u> pq({ f }, memory_available(), 32, stats_lpq_tests); @@ -4864,10 +4615,8 @@ go_bandit([]() { lpq_test_lt, 1u, memory_mode::Internal, - lpq_test_file, 2u, std::less<>, - false, 0u> pq({ f1, f2 }, memory_available(), 32, stats_lpq_tests); @@ -4939,10 +4688,8 @@ go_bandit([]() { lpq_test_lt, 0u, memory_mode::Internal, - lpq_test_file, 2u, std::less<>, - false, 0u> pq({ f1, f2 }, memory_available(), 32, stats_lpq_tests); diff --git a/tests/adiar/internal/data_structures/stack.test.cpp b/tests/adiar/internal/data_structures/stack.test.cpp index 117c64abb..3a6480daa 100644 --- a/tests/adiar/internal/data_structures/stack.test.cpp +++ b/tests/adiar/internal/data_structures/stack.test.cpp @@ -3,8 +3,8 @@ #include go_bandit([]() { - describe("adiar/internal/sorter.h", []() { - describe("sorter", []() { + describe("adiar/internal/stack.h", []() { + describe("stack", []() { stack s(16); it("is initially empty", [&s]() { @@ -52,7 +52,7 @@ go_bandit([]() { }); }); - describe("sorter", []() { + describe("stack", []() { // TODO: Add tests when implemented. }); }); diff --git a/tests/adiar/internal/data_structures/vector.test.cpp b/tests/adiar/internal/data_structures/vector.test.cpp new file mode 100644 index 000000000..70c24217d --- /dev/null +++ b/tests/adiar/internal/data_structures/vector.test.cpp @@ -0,0 +1,93 @@ +#include "../../../test.h" + +#include + +go_bandit([]() { + describe("adiar/internal/vector.h", []() { + describe("vector", []() { + vector v(16); + + it("is initially empty", [&v]() { + AssertThat(v.empty(), Is().True()); + AssertThat(v.size(), Is().EqualTo(0u)); + }); + + it("can push elements", [&v]() { + v.push_back(3); + v.push_back(1); + v.push_back(2); + + AssertThat(v.empty(), Is().False()); + AssertThat(v.size(), Is().EqualTo(3u)); + }); + + it("can retrieve the front element", [&v]() { + AssertThat(v.front(), Is().EqualTo(3)); + }); + + it("can retrieve the back element", [&v]() { + AssertThat(v.back(), Is().EqualTo(2)); + }); + + it("can retrieve elements via '.at(i)'", [&v]() { + AssertThat(v.at(0), Is().EqualTo(3)); + AssertThat(v.at(1), Is().EqualTo(1)); + AssertThat(v.at(2), Is().EqualTo(2)); + }); + + it("can pop element", [&v]() { + v.pop_back(); + + AssertThat(v.empty(), Is().False()); + AssertThat(v.size(), Is().EqualTo(2u)); + }); + + it("can retrieve different element from the back", [&v]() { + AssertThat(v.back(), Is().EqualTo(1)); + }); + + it("can new elements", [&v]() { + v.push_back(4); + v.push_back(4); + + AssertThat(v.empty(), Is().False()); + AssertThat(v.size(), Is().EqualTo(4u)); + }); + + it("can retrieve different element from the back", [&v]() { + AssertThat(v.back(), Is().EqualTo(4)); + }); + + it("can retrieve elements via '[i]'", [&v]() { + AssertThat(v[0], Is().EqualTo(3)); + AssertThat(v[1], Is().EqualTo(1)); + AssertThat(v[2], Is().EqualTo(4)); + AssertThat(v[3], Is().EqualTo(4)); + }); + + it("can iterate forwards through the vector", [&v]() { + int i = 0; + const int expected[4] = { 3, 1, 4, 4 }; + + for (auto iter = v.begin(); iter != v.end(); ++iter) { + AssertThat(*iter, Is().EqualTo(expected[i])); + i += 1; + } + }); + + it("can iterate backwards through the vector", [&v]() { + int i = 3; + const int expected[4] = { 3, 1, 4, 4 }; + + for (auto iter = v.rbegin(); iter != v.rend(); ++iter) { + AssertThat(*iter, Is().EqualTo(expected[i])); + i -= 1; + } + }); + }); + + describe("vector", []() { + // TODO: Add tests when implemented. + }); + }); +});