diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh index a6b188fa7..f6193615f 100644 --- a/include/mata/utils/partition.hh +++ b/include/mata/utils/partition.hh @@ -2,7 +2,8 @@ * @brief Definition of a partition * * In this context, we consider a carrier set S which contains all - * natural numbers from 0 to |S|-1. These numbers are called states. + * natural numbers from 0 to |S|-1 and nothing else. These numbers are called + * states. * Then, partition over S is a set of blocks such that: * - each block contains only states * - each state is represented in exactly one block @@ -16,9 +17,12 @@ * - test whether two states share the same block in O(1) * - test whether all states in a vector A share the same block in O(|A|) * - iterate through the block B in O(|B|) + * - iterate through the node N in O(|N|) * - split the whole partition such that each block * is split in two pieces or remains unchanged in O(|S|) * - remember all ancestors of current blocks and access them if necessary + * so we can manipulate multiple generations of a partition (before and + * after it has been split) * * @author Tomáš Kocourek */ @@ -30,6 +34,7 @@ #include #include #include +#include "mata/utils/sparse-set.hh" namespace mata::utils { @@ -47,70 +52,93 @@ using State = unsigned long; using StateBlock = std::vector; using StateBlocks = std::vector; -using BlockItem = struct BlockItem { - State state; - size_t block_idx; - - BlockItem(State s, size_t idx) : state(s), block_idx(idx) {} -}; +/* +* @struct SplitPair +* @brief contains information about block which has been split +* +* The structure SplitPair is created as soon as a block of the partition +* is split. It contains: +* - index of the new block which keeps the identity of the split block +* (first_block_idx) +* - index of the new block which has been created (second_block_idx) +* - index of the node which had represented the former block before +* (node_idx) +* Using first_block_idx and second_block_idx, we can manipulate with the +* current generation of the partition. Using node_idx, we are able to work +* with the older generation of the partition. +*/ +using SplitPair = struct SplitPair { -using Block = struct Block { + size_t first_block_idx; + size_t second_block_idx; size_t node_idx; - Block(size_t idx) : node_idx(idx) {} -}; - -using Node = struct Node { - size_t first; - size_t last; - - Node(size_t fst, size_t lst) : first(fst), last(lst) {} -}; - -using BlockItems = std::vector; -using Blocks = std::vector; -using Nodes = std::vector; - -using SplitPair = struct SplitPair { - size_t former; - size_t created; - size_t old_node_idx; - - SplitPair(size_t former, size_t created, size_t idx) : - former(former), created(created), old_node_idx(idx) {} + /** + * Initialization of the SplitPair + * + * @brief default constructor + * @param[in] first first part of the split block + * @param[in] second second part of the split block + * @param[in] node node corresponding to the split block + */ + SplitPair(size_t first, size_t second, size_t node) : + first_block_idx(first), second_block_idx(second), node_idx(node) {} }; /** - * Partition - * + * @class Partition * @brief Partition of a set of states * * This data structure provides a partition of a set of states S. In this * context, the term 'state' refers to any natural number from the * interval <0, |S|-1>. * + * This representation defines: + * - states - element from a consecutive interval of natural numbers + * - blocks - objects which represent the current generation of the partition. + * Each block refers to several states which belong to the block. + * The block could be possibly split. + * - nodes - objects which represent blocks either from the current generation + * of the partition or the block from the previous generations of + * the partition (block which had been split). Once a node is + * created, it is never changed. When a block is split, two new nodes + * are created. + * - block items + * - objects which serve as intermediate data structure between states + * and blocks. Each block item contains indices of both corresponding + * state and block. Block items are sorted in such way that one could + * iterate through each block B or each node N in the O(B) or O(N) + * time, respectively. + * + * Detailed explanation: + * * STATES: - * This representation works with the vector of indices 'states_' - * with the constant size |S|. Each state is represented by an index - * to that vector so we can refer to a state in constant time using - * states_[state]. + * States are represented by indices of the vector 'states_' with the + * unchangeable size |S|. Each element of the vector represents the state + * by its order in the 'states_' vector. The vector itself contains indices + * of corresponding block items. Index of the block item corresponding + * to the state 's' could be get in constant time using 'states_[s]'. * * BLOCK ITEMS: - * The memory cell states_[state] contains a corresponding index to the - * 'block_items_' vector. The vector 'block_items_' has the constant size |S|. - * Each BlockItem contains an index of the corresponding state which means - * that states and BlockItems are bijectively mapped. In addition, - * each BlockItem includes an index to the corresponding partition class - * (called block). The ordering of BlockItems satisfies the condition that - * the states of the same block should always form a contiguous subvector - * so one could iterate through states in each block efficiently using - * 'block_items_' vector. + * Block items are stored in the 'block_items_' vector of the unchangeable size + * |S|. The block item can by accessed by its index using + * block_items[block_item_idx]. Each block item contains its index (which is + * always the same as its order in the 'block_items_' vector but + * it is stored directly in the object to simplify manipulations with + * the partition). It also contains the index of its corresponding state + * which means that states and block items are bijectively mapped. + * In addition, each BlockItem includes an index of the corresponding partition + * block. + * The ordering of 'block_items_' vector satisfies the condition that the states + * of the same block (or node) should always form a contiguous subvector so one + * could iterate through states in each block (or node) efficiently using + * 'block_items' vector. * * BLOCKS: * The blocks themselves are represented by the vector 'blocks_' with the * size |P|, where P is a partition of states. Each block can be accessed by * its index 0 <= i < |P|. The block can by accessed by its index using - * blocks_[block_idx]. The block contains only an index of its + * blocks_[block_idx]. The block contains an index of itself and an index of its * corresponding node. The total number of blocks can be changed as soon as * one block is split. However, the maximal number of blocks is equal to |S| * (the case when each block contains only one state). When a block 'B' is @@ -127,19 +155,26 @@ using SplitPair = struct SplitPair { * indices. The total number of nodes can be changed as soon as * one block is split. In such situation, two new nodes (which represent both * new blocks) are created and the former node remains unchanged. Therefore, the - * maximal number of nodes is equal to 2 * |P| - 1 since once a node is created, - * it is never changed. Each node contains two indices ('first' and 'last') - * which could be used to access BlockItems corresponding to the first and last - * BlockItems which form a contiguous subvector of BlockItem included in such - * node. When a block is split, the corresponding BlockItems are swapped + * maximal number of nodes is equal to 2 * |S| - 1 since once a node is created, + * it is never changed. Each node contains its own index and two indices of + * block items (namely 'first' and 'last') which could be used to access + * block items corresponding to the first and last block items which form a + * contiguous subvector of 'block_items_' vector included in such node. + * When a block is split, the corresponding block items are swapped * in situ such that the indices 'first' and 'last' still surround the * corresponding node and both new nodes also point to the contiguous subvector - * of its BlockItems. + * of its block items. * * EXAMPLE: - * In the example below, we represent a partition {{0, 2}, {1, 3, 4}}. - * Thus, we have two blocks: 0 ({0, 2}) and 1 ({1, 3, 4}). The block 0 - * corresponds to the node 1 and the block 1 corresponds to the node 2. + * In the example below, we represent a partition {{0, 1, 2, 3, 4}} + * which had been split to the partition {{0, 2}, {1, 3, 4}}. + * Thus, we have two blocks: 0 ({0, 2}) and 1 ({1, 3, 4}) which form the current + * generation of the partition. + * We also have three nodes: 0 ({0, 1, 2, 3, 4}), 1 ({0, 2}) and 2 ({1, 3, 4}) + * which represent both current generation of the partition and also block which + * does not exist anymore since it had been split. + * The block 0 corresponds to the node 1 and the block 1 corresponds to the + * node 2. * The node 1 contains indices 0 (first) and 1 (last) which means that * the blockItems 0 and 1 surround a contiguous subvector of elements in the * node 1 (or in the block 0). @@ -151,6 +186,7 @@ using SplitPair = struct SplitPair { * the blockItems 0 and 4 surround a contiguous subvector of elements in the * node 0. Thus, we know that there had been a block {0, 1, 2, 3, 4} before * it has been split to obtain blocks {0, 2} and {1, 3, 4}. + * In the picture below, indices of vectors are depicted outside of the vectors. * * * 0 1 2 3 4 @@ -183,34 +219,223 @@ using SplitPair = struct SplitPair { * ---------------------------------------------------- * * Using this class, we can: - * - find the block which contains given state in O(1) + * - find the block which contains a given state in O(1) * - find a representative state of the given block in O(1) * - test whether two states share the same block in O(1) * - test whether all states in a vector A share the same block in O(|A|) * - iterate through the block B in O(|B|) + * - iterate through the node N in O(|N|) * - split the whole partition such that each block * is split in two pieces or remains unchanged in O(|S|) - * - remember all ancestors of current blocks and access them + * - remember all ancestors of current blocks and access them if necessary + * so we can manipulate multiple generations of a partition (before and + * after it has been split) * */ class Partition { + private: + + class BlockItem; + class Block; + class Node; + + using States = std::vector; + using BlockItems = std::vector; + using Blocks = std::vector; + using Nodes = std::vector; + + /**< vector of states refering to the block items */ + States states_{}; - /* indices to the block_items_ vector */ - std::vector states_{}; - - /* indices to the states_ and blocks_ vectors */ + /**< vector of block items refering to the states and blocks */ BlockItems block_items_{}; - /* indices to the nodes_ vector */ + /**< vector of blocks refering to the nodes */ Blocks blocks_{}; - /* tuples of indices to the block_items_ vectors */ + /**< vector of nodes refering to the first and last block item + of the node */ Nodes nodes_{}; + + /** + * @class BlockItem + * @brief Intermediate between states and blocks + **/ + class BlockItem { + private: + + /**< index of itself */ + size_t idx_; + + /**< corresponding state */ + State state_; + + /**< index of the corresponding block */ + size_t block_idx_; + + /**< reference to the partition which works + with this block item */ + const Partition& partition_; + + // BlockItem class need to access private members + // of Partition class + friend class Partition; + + public: + + // constructors + BlockItem(size_t idx, size_t state, size_t block_idx, + const Partition& partition) : + + idx_(idx), state_(state), block_idx_(block_idx), + partition_(partition) {} + + // getters + size_t idx(void) const { return idx_; } + size_t state(void) const { return state_; } + + // methods which refer to the partition vectors + const Block& block(void) const { + return partition_.blocks_[block_idx_]; + } + const Node& node(void) const { return block().node(); } + const BlockItem& repr(void) const { return node().repr(); } + }; + + /** + * @class Block + * @brief Contains infromation about block from the current generation + * of the partition. + **/ + class Block { + private: + + /**< index of itself */ + size_t idx_; + + /**< index of the corresponding node */ + size_t node_idx_; + + /**< reference to the partition which works + with this block */ + const Partition& partition_; + + // Blocks need to access private members of Partition class + friend class Partition; + + public: + + // constructors + Block(size_t idx, size_t node_idx, const Partition& partition) : + idx_(idx), node_idx_(node_idx), partition_(partition) {} + + // getters + size_t idx(void) const { return idx_; } + + // methods which refer to the partition vectors + const Node& node(void) const { + return partition_.nodes_[node_idx_]; + } + const BlockItem& repr(void) const { return node().repr(); } + const BlockItem& first(void) const { return node().first(); } + const BlockItem& last(void) const { return node().last(); } + + // iterators + using const_iterator = typename BlockItems::const_iterator; + + const_iterator begin() const { + const_iterator it = partition_.block_items_.begin(); + std::advance(it, static_cast(node().first().idx())); + return it; + } + + const_iterator end() const { + const_iterator it = partition_.block_items_.begin(); + std::advance(it, static_cast(node().last().idx()) +1); + return it; + } + + // information about the block + size_t size(void) const { + return last().idx() - first().idx() + 1; + } + + }; + + /** + * @class Node + * @brief Contains infromation about block from the current generation + * of the partition or from the previous generation of the partition. + **/ + class Node { + private: + + /**< index of itself */ + size_t idx_; + + /**< index of the first block item in the node */ + size_t first_; + + /**< index of the last block item in the node */ + size_t last_; + + /**< reference to the partition which works + with this block */ + const Partition& partition_; + + // Blocks need to access private members of Partition class + friend class Partition; + + public: + + // constructors + Node(size_t idx, size_t first, size_t last, + const Partition& partition) : + + idx_(idx), first_(first), last_(last), partition_(partition) + {} + + // getters + size_t idx(void) const { return idx_; } + + // methods which refer to the partition vectors + const BlockItem& first(void) const { + return partition_.block_items_[first_]; + } + const BlockItem& last(void) const { + return partition_.block_items_[last_]; + } + const BlockItem& repr(void) const { + return partition_.block_items_[first_]; + } + + // iterators + using const_iterator = + typename BlockItems::const_iterator; + + const_iterator begin() const { + const_iterator it = partition_.block_items_.begin(); + std::advance(it, static_cast(first().idx())); + return it; + } + + const_iterator end() const { + const_iterator it = partition_.block_items_.begin(); + std::advance(it, static_cast(last().idx()) + 1); + return it; + } + + // information about the node + size_t size(void) const { + return last().idx() - first().idx() + 1; + } + }; public: // constructors + Partition() = default; Partition(size_t num_of_states, const StateBlocks& partition = StateBlocks()); Partition(const Partition& other); @@ -222,37 +447,28 @@ class Partition { size_t num_of_nodes(void) const { return nodes_.size(); } // blocks splitting - std::vector split_blocks(const std::vector& marked); + std::vector split_blocks(const SparseSet& marked); // basic information about the partition + size_t get_block_idx(State state) const; bool in_same_block(State first, State second) const; bool in_same_block(const std::vector& states) const; std::vector states_in_same_block(State state) const; - // accessing blockItems, blocks, nodes through indices + // accessing block items, blocks, nodes through indices const BlockItem& get_block_item(size_t block_item_idx) const; const Block& get_block(size_t block_idx) const; - const Node& get_node(size_t node_idx) const; - - // refering between blockItems, blocks, nodes using indices - size_t get_block_idx_from_state(State state) const; - size_t get_node_idx_from_state(State state) const; - size_t get_block_item_idx_from_state(State state) const; - size_t get_node_idx_from_block_item_idx(size_t block_item_idx) const; - size_t get_node_idx_from_block_idx(size_t block_idx) const; - size_t get_repr_idx_from_block_idx(size_t block_idx) const; - size_t get_repr_idx_from_node_idx(size_t node_idx) const; + const Node& get_node(size_t node_idx) const; // converts the partition to the vector of vectors of states - StateBlocks partition(void); + StateBlocks partition(void) const; // operators Partition& operator=(const Partition& other); friend std::ostream& operator<<(std::ostream& os, const Partition& p); - - + const BlockItem& operator[](State state) const; }; // Partition diff --git a/src/partition.cc b/src/partition.cc index 9ab9278ed..38ac2ade2 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -2,7 +2,8 @@ * @brief Definition of a partition * * In this context, we consider a carrier set S which contains all - * natural numbers from 0 to |S|-1. These numbers are called states. + * natural numbers from 0 to |S|-1 and nothing else. These numbers are called + * states. * Then, partition over S is a set of blocks such that: * - each block contains only states * - each state is represented in exactly one block @@ -16,9 +17,12 @@ * - test whether two states share the same block in O(1) * - test whether all states in a vector A share the same block in O(|A|) * - iterate through the block B in O(|B|) + * - iterate through the node N in O(|N|) * - split the whole partition such that each block * is split in two pieces or remains unchanged in O(|S|) * - remember all ancestors of current blocks and access them if necessary + * so we can manipulate multiple generations of a partition (before and + * after it has been split) * * @author Tomáš Kocourek */ @@ -26,20 +30,22 @@ #include #include #include "mata/utils/partition.hh" +#include "mata/utils/sparse-set.hh" namespace mata::utils { /** Constructor of the partition object. This method reserves memory space * for the vectors used to represent partition to ensure us that they won't -* ever be moved in the memory when extended. +* ever be moved in the memory when extended. The maximal sizes of these vectors +* can be computed using the num_of_states parameter. * The partition can be initialized in linear time (in respect to the carrier * set of the partition) using initial partition represented as a vector of * vectors of states. * The constructor works as follows: -* - if there is any nonexisting state in the initial partition, the function -* fails -* - if there are duplicates in the initial partition, the function fails -* - if there is an empty partition class, the function fails +* - if there is any nonexisting state in the initial partition, the construction +* fails (state >= num_of_states) +* - if there are duplicates in the initial partition, the construction fails +* - if there is an empty partition class, the construction fails * - if there are states which are not represented in the initial partition, * they will be all part of the one additional block * If there is no initial partition, all states will be assigned @@ -50,22 +56,26 @@ namespace mata::utils { * vectors of states */ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { + // reserving memory space to avoid moving extended vectors - states_.reserve(num_of_states); - block_items_.reserve(num_of_states); - blocks_.reserve(num_of_states); - nodes_.reserve(2 * num_of_states - 1); - + if(num_of_states) [[likely]] { + states_.reserve(num_of_states); + block_items_.reserve(num_of_states); + blocks_.reserve(num_of_states); + nodes_.reserve(2 * num_of_states - 1); + } + // this vector says whether the given state has been already seen // in the given initial partition to detect duplicates // and to detect unused states std::vector used; + if(num_of_states) [[likely]] { used.reserve(num_of_states); } used.insert(used.end(), num_of_states, false); // initialization of the states_ vector states_.insert(states_.end(), num_of_states, 0); - // creating partition using given initial vector of vectors + // creating the partition using given initial vector of vectors size_t num_of_blocks = partition.size(); // iterating through initial partition blocks for(size_t block_idx = 0; block_idx < num_of_blocks; ++block_idx) { @@ -73,46 +83,51 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { "Partition class cannot be empty."); // iterating through one partition block - for(auto state : partition[block_idx]) { - assert(state < num_of_states && + for(auto state_idx : partition[block_idx]) { + assert(state_idx < num_of_states && "Invalid state name detected while creating" "a partition relation pair."); - assert(!used[state] && + assert(!used[state_idx] && "Partition could not be created." "Duplicate occurence of a state"); - used[state] = true; + used[state_idx] = true; // creating a corresponding BlockItem - states_[state] = block_items_.size(); - block_items_.emplace_back(state, block_idx); + size_t block_item_idx = block_items_.size(); + states_[state_idx] = block_item_idx; + block_items_.emplace_back(block_item_idx, state_idx, + block_idx, *this); } // first and last states of the block will be used to create // a corresponding node - State first = partition[block_idx].front(); - State last = partition[block_idx].back(); + size_t first = partition[block_idx].front(); + size_t last = partition[block_idx].back(); // creating a corresponding block and node - nodes_.emplace_back(states_[first], states_[last]); - blocks_.emplace_back(block_idx); + size_t node_idx = nodes_.size(); + nodes_.emplace_back(node_idx, states_[first], states_[last], *this); + blocks_.emplace_back(block_idx, block_idx, *this); } - // we need to detect whether there is a state which has not be used + // we need to detect whether there is a state which has not been used // to create an additional partition block bool all_states_used = true; // first and last unused states will surround a contiguous subvector // of BlockItems - State first = 0; - State last = 0; + size_t first = 0; + size_t last = 0; - // iterating through the vector of flags saying which states has been seen - for(State state = 0; state < num_of_states; ++state) { + // iterating through the vector of flags saying which states have been seen. + // We need to create an additional block which will contain all states which + // have not been represented in the input partition if such states exist + for(size_t state_idx = 0; state_idx < num_of_states; ++state_idx) { // if a state has been already seen and processed, // there is no need to add it to the additional block - if(used[state]) { + if(used[state_idx]) { continue; } @@ -122,20 +137,23 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // and then it will be never executed again if(all_states_used) [[unlikely]] { all_states_used = false; - first = state; + first = state_idx; ++num_of_blocks; } // creating the new BlockItem - last = state; - states_[state] = block_items_.size(); - block_items_.emplace_back(state, num_of_blocks-1); + size_t block_item_idx = block_items_.size(); + last = state_idx; + states_[state_idx] = block_item_idx; + block_items_.emplace_back(block_item_idx, state_idx, num_of_blocks-1, + *this); } // creating a new block and node if there was an unused state if(!all_states_used) { - nodes_.emplace_back(states_[first], states_[last]); - blocks_.emplace_back(num_of_blocks-1); + nodes_.emplace_back(nodes_.size(), states_[first], states_[last], + *this); + blocks_.emplace_back(num_of_blocks-1, num_of_blocks-1, *this); } } @@ -152,13 +170,14 @@ Partition::Partition(const Partition& other) { *this = other; } - /** * @brief returns a BlockItem corresponding to the given index * @param[in] block_item_idx index of the BlockItem * @return corresponding BlockItem */ -const BlockItem& Partition::get_block_item(size_t block_item_idx) const { +const Partition::BlockItem& Partition::get_block_item( + size_t block_item_idx) const { + assert(block_item_idx < num_of_block_items() && "Nonexisting block item index used."); return block_items_[block_item_idx]; @@ -169,7 +188,7 @@ const BlockItem& Partition::get_block_item(size_t block_item_idx) const { * @param[in] block_idx index of the block * @return corresponding block */ -const Block& Partition::get_block(size_t block_idx) const { +const Partition::Block& Partition::get_block(size_t block_idx) const { assert(block_idx < num_of_blocks() && "Nonexisting block index used."); return blocks_[block_idx]; } @@ -179,7 +198,7 @@ const Block& Partition::get_block(size_t block_idx) const { * @param[in] node_idx index of the node * @return corresponding node */ -const Node& Partition::get_node(size_t node_idx) const { +const Partition::Node& Partition::get_node(size_t node_idx) const { assert(node_idx < num_of_nodes() && "Nonexisting node index used."); return nodes_[node_idx]; } @@ -189,73 +208,9 @@ const Node& Partition::get_node(size_t node_idx) const { * @param[in] state given state * @return corresponding block index */ -size_t Partition::get_block_idx_from_state(State state) const { - assert(state < num_of_states() && "Nonexisting state name used."); - return block_items_[states_[state]].block_idx; -} - -/** -* @brief returns a node index corresponding to the given state -* @param[in] state given state -* @return corresponding node index -*/ -size_t Partition::get_node_idx_from_state(State state) const { - assert(state < num_of_states() && "Nonexisting state name used."); - return blocks_[block_items_[states_[state]].block_idx].node_idx; -} - -/** -* @brief returns a BlockItem index corresponding to the given state -* @param[in] state given state -* @return corresponding BlockItem index -*/ -size_t Partition::get_block_item_idx_from_state(State state) const { - assert(state < num_of_states() && "Nonexisting state name used."); - return states_[state]; -} - -/** -* @brief returns a Node index corresponding to the given BlockItem index -* @param[in] block_item_idx BlockItem index -* @return corresponding node index -*/ -size_t Partition::get_node_idx_from_block_item_idx( - size_t block_item_idx) const { - - assert(block_item_idx < num_of_block_items() && - "Nonexisting BlockItem index used."); - return blocks_[block_items_[block_item_idx].block_idx].node_idx; -} - -/** -* @brief returns a node index corresponding to the given block index -* @param[in] block_idx given block index -* @return corresponding node index -*/ -size_t Partition::get_node_idx_from_block_idx(size_t block_idx) const { - assert(block_idx < num_of_blocks() && "Nonexisting block index used."); - return blocks_[block_idx].node_idx; -} - -/** Get a representant from the block index -* @brief returns the first blockItem index corresponding to the given -* block index -* @param[in] block_idx given block index -* @return first blockItem index corresponding to the given block index -*/ -size_t Partition::get_repr_idx_from_block_idx(size_t block_idx) const { - assert(block_idx < num_of_blocks() && "Nonexisting block index used."); - return nodes_[blocks_[block_idx].node_idx].first; -} - -/** Get a representant from the node index -* @brief returns the first blockItem index corresponding to the given node index -* @param[in] node_idx given node index -* @return first blockItem index corresponding to the given node index -*/ -size_t Partition::get_repr_idx_from_node_idx(size_t node_idx) const { - assert(node_idx < num_of_nodes() && "Nonexisting node index used."); - return nodes_[node_idx].first; +size_t Partition::get_block_idx(State state) const { + assert(state < num_of_states() && "Nonexisting state udsed"); + return block_items_[states_[state]].block_idx_; } /** @@ -268,7 +223,7 @@ size_t Partition::get_repr_idx_from_node_idx(size_t node_idx) const { bool Partition::in_same_block(State first, State second) const { assert(first < states_.size() && "The given state does not exist"); assert(second < states_.size() && "The given state does not exist"); - return get_block_idx_from_state(first) == get_block_idx_from_state(second); + return get_block_idx(first) == get_block_idx(second); } /** @@ -276,12 +231,12 @@ bool Partition::in_same_block(State first, State second) const { * @param[in] states vector of states to be checked * @return true iff all of the given states belong to the same partition block */ -bool Partition::in_same_block(const std::vector& states) const { - if(states.empty()) [[unlikely]] { return true; } - size_t block_idx = get_block_idx_from_state(states.front()); - for(size_t state : states) { - assert(state < states_.size() && "The given state does not exist."); - if(get_block_idx_from_state(state) != block_idx) { return false; } +bool Partition::in_same_block(const std::vector& state_idxs) const { + if(state_idxs.empty()) [[unlikely]] { return true; } + size_t block_idx = get_block_idx(state_idxs.front()); + for(size_t state_idx : state_idxs) { + assert(state_idx < states_.size() && "The given state does not exist."); + if(get_block_idx(state_idx) != block_idx) { return false; } } return true; } @@ -291,18 +246,13 @@ bool Partition::in_same_block(const std::vector& states) const { * @param[in] state input state * @return vector of all the states in the corresponding block */ -std::vector Partition::states_in_same_block(State state) const { - assert(state < num_of_states() && "The given state does not exist."); - std::vector result{}; - - // first and last states in the block stored in the vector - // of BlockItems - size_t first = get_node(get_node_idx_from_state(state)).first; - size_t last = get_node(get_node_idx_from_state(state)).last; +std::vector Partition::states_in_same_block(size_t state_idx) const { + assert(state_idx < num_of_states() && "The given state does not exist."); + std::vector result{}; - // iterating through BlockItems - for(size_t i = first; i <= last; ++i) { - result.push_back(get_block_item(i).state); + // iterating through the corresponding block + for(const BlockItem& block_item : (*this)[state_idx].block()) { + result.push_back(block_item.idx()); } return result; @@ -313,17 +263,19 @@ std::vector Partition::states_in_same_block(State state) const { * vectors of states * @return vector of vectors of states */ -StateBlocks Partition::partition(void) { +StateBlocks Partition::partition(void) const { StateBlocks result{}; - result.insert(result.end(), blocks_.size(), std::vector()); - for(auto block_item : block_items_) { - result[block_item.block_idx].push_back(block_item.state); + for(const Block& block : blocks_) { + result.emplace_back(); + for(const BlockItem& block_item : block) { + result.back().push_back(block_item.state()); + } } return result; } /** Splitting the blocks of existing partition. According to the input -* vector of states 'marked', there will be two types of states - marked +* sparse set of states 'marked', there will be two types of states - marked * and unmarked ones. The partition will be split as follows: * - if there is a block whose all elements are marked, the block remains * unchanged @@ -344,11 +296,7 @@ StateBlocks Partition::partition(void) { * changes its position. * Moreover, the node corresponding to the ancestor of the two split blocks * will still describe a valid contiguous interval of natural numbers . -* -* There are several troubles which can possiby occur: * - if an nonexisting state is used, the function detects it and fails -* - if there is a state which is marked multiple times, the function detects it -* and fails * * If a block is split, the function creates a structure SplitPair which * contains: @@ -359,52 +307,44 @@ StateBlocks Partition::partition(void) { * * @brief splits blocks of the partition * @param[in] marked marked states which influence splitting -* @return vector of SplitPair which contains information about split blocks +* @return vector of SplitPairs which contain information about split blocks */ std::vector Partition::split_blocks( - const std::vector& marked) { + const SparseSet& marked) { // the vector which will be returned as the result std::vector split{}; // if there is no marked state, no block could be split if(marked.empty()) [[unlikely]] { return split; } - - // this vector contains information about states which has been marked - // and helps to detect states which has been marked multiple times - std::vector used_states{}; - used_states.insert(used_states.end(), states_.size(), false); // this vector contains information about blocks whose states has been // marked and keeps number of states of each block which has been marked // to ease detecting whether the whole block has been marked std::vector used_blocks{}; + if(blocks_.size()) [[likely]] { used_blocks.reserve(blocks_.size()); } used_blocks.insert(used_blocks.end(), blocks_.size(), 0); - // iterating through the marked states to fill used_states and - // used_blocks vectors + // iterating through the marked states to fill used_blocks vector for(size_t i : marked) { assert(i < states_.size() && "The given state does not exist."); - assert(!used_states[i] && "The given state was marked multiple times"); - used_states[i] = true; - ++used_blocks[get_block_idx_from_state(i)]; + ++used_blocks[get_block_idx(i)]; } size_t old_blocks_size, new_block_idx; old_blocks_size = new_block_idx = blocks_.size(); - // iterating through existing blocks + // iterating through the existing blocks for(size_t i = 0; i < old_blocks_size; ++i) { - // if no state of the given block has been marked, it - // won't be split + // if no state of the given block has been marked, it won't be split if(!used_blocks[i]) { continue; } // looking for the subvector of BlockItems which forms processed // block and computing its size - size_t node_idx = get_node_idx_from_block_idx(i); - size_t iter_first = get_node(node_idx).first; - size_t iter_last = get_node(node_idx).last; - size_t block_size = iter_last - iter_first + 1; + Node node = get_block(i).node(); + size_t iter_first = node.first().idx(); + size_t iter_last = node.last().idx(); + size_t block_size = node.size(); // if all states of the processed block have been marked, the block // won't be split @@ -412,8 +352,7 @@ std::vector Partition::split_blocks( // choosing the strategy of swapping BlocksItems such that // the representant of split block keeps its position - bool repr_marked = used_states[get_block_item( - get_repr_idx_from_node_idx(node_idx)).state]; + bool repr_marked = marked[node.repr().state()]; // We access the first and last element of the subvector of BlockItems // which forms processed block. We look for the first unmarked element @@ -423,15 +362,17 @@ std::vector Partition::split_blocks( // are swapped. This procedure continues until these two indices used // to iterate through the BlockItems meet somewhere in the middle do { + // we choose the swapping strategy using XOR operation - while((repr_marked - ^ (!used_states[get_block_item(iter_first).state]))) { + while(repr_marked ^ (!marked[get_block_item(iter_first).state()])) { + // this visited state will be part of the former block ++iter_first; } - while((repr_marked ^ used_states[get_block_item(iter_last).state])) { + while(repr_marked ^ marked[get_block_item(iter_last).state()]) { + // this visited state will be part of the new block - block_items_[iter_last].block_idx = new_block_idx; + block_items_[iter_last].block_idx_ = new_block_idx; --iter_last; } @@ -441,18 +382,18 @@ std::vector Partition::split_blocks( } // swapping BlockItems - BlockItem swapped_block_item = get_block_item(iter_first); - block_items_[iter_first] = get_block_item(iter_last); - block_items_[iter_last] = swapped_block_item; + State swapped_state = block_items_[iter_first].state(); + block_items_[iter_first].state_ = block_items_[iter_last].state_; + block_items_[iter_last].state_ = swapped_state; // since states_ and block_items_ vectors should be bijectively // mapped, we need to update states_ after swapping two BlockItems - states_[block_items_[iter_first].state] = iter_first; - states_[block_items_[iter_last].state] = iter_last; + states_[block_items_[iter_first].state_] = iter_first; + states_[block_items_[iter_last].state_] = iter_last; // after the blockItems are swapped, one of them should // be assigned to the new block - block_items_[iter_last].block_idx = new_block_idx; + block_items_[iter_last].block_idx_ = new_block_idx; // after the blockItems are swapped, we continue to the // next blockItems @@ -461,17 +402,20 @@ std::vector Partition::split_blocks( } while(iter_first <= iter_last); // creating new nodes - nodes_.emplace_back(nodes_[node_idx].first, iter_last); - nodes_.emplace_back(iter_first, nodes_[node_idx].last); + size_t first_idx = node.first().idx(); + size_t last_idx = node.last().idx(); + nodes_.emplace_back(nodes_.size(), first_idx, iter_last, *this); + nodes_.emplace_back(nodes_.size(), iter_first, last_idx, *this); // split blocks has to refer to the new nodes - blocks_[i].node_idx = nodes_.size() - 2; - blocks_.emplace_back(nodes_.size() - 1); + blocks_[i].node_idx_ = nodes_.size() - 2; + blocks_.emplace_back(new_block_idx, nodes_.size() - 1, *this); + blocks_.back().idx_ = new_block_idx; // since a block has been split, we need to return information about // indices of components of split block and about the node which // correspond to the block which has been split - split.emplace_back(i, new_block_idx, node_idx); + split.emplace_back(i, new_block_idx, node.idx()); // index of the following block which could be created ++new_block_idx; @@ -481,7 +425,8 @@ std::vector Partition::split_blocks( /** * Custom assignment operator which preserves reserved capacities for the -* partition vectors +* partition vectors and assign a proper partition reference to the newly +* created block items, blocks and nodes * @brief assignment of the partition * @param[in] other partition which will be copied * @return modified partition @@ -491,28 +436,51 @@ Partition& Partition::operator=(const Partition& other) { // reserved capacity, we need to reserve it manually and // then insert elements of the other partition to the reserved space // if we want to keep the former capacity - states_.reserve(other.num_of_states()); - block_items_.reserve(other.num_of_states()); - blocks_.reserve(other.num_of_states()); - nodes_.reserve(2 * other.num_of_states() - 1); + states_.clear(); + block_items_.clear(); + blocks_.clear(); + nodes_.clear(); - // copying vectors without losing information about reserved capacity size_t states_num = other.num_of_states(); + if(states_num) [[likely]] { + states_.reserve(states_num); + block_items_.reserve(states_num); + blocks_.reserve(states_num); + nodes_.reserve(2 * states_num - 1); + } + + // copying vectors without losing information about reserved capacity + // and storing reference to the assigned partition when creating block + // items, blocks and nodes for(size_t i = 0; i < states_num; ++i) { - states_.push_back(other.get_block_item_idx_from_state(i)); - block_items_.push_back(other.get_block_item(i)); + states_.push_back(other.states_[i]); + const BlockItem& b = other.get_block_item(i); + block_items_.emplace_back( + BlockItem(b.idx_, b.state_, b.block_idx_, *this)); } size_t blocks_num = other.num_of_blocks(); for(size_t i = 0; i < blocks_num; ++i) { - blocks_.push_back(other.get_block(i)); + const Block& b = other.get_block(i); + blocks_.emplace_back(Block(b.idx_, b.node_idx_, *this)); } size_t nodes_num = other.num_of_nodes(); for(size_t i = 0; i < nodes_num; ++i) { - nodes_.push_back(other.get_node(i)); + const Node& n = other.get_node(i); + nodes_.emplace_back(Node(n.idx_, n.first_, n.last_, *this)); } return *this; } +/** +* @brief finding a block item corresponding to the given state +* @param[in] state state whose block item will be found +* @return corresponding block item +*/ +const Partition::BlockItem& Partition::operator[](State state) const { + assert(state < states_.size() && "The given state does not exist."); + return block_items_[states_[state]]; +} + /** * @brief debugging function which allows us to print text representation of * the partition @@ -527,36 +495,39 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) { result += "NUM OF NODES: " + std::to_string(p.num_of_nodes()) + "\n"; result += "\n"; + result += "BLOCK ITEMS:\n"; + result += "idx -> state block_idx\n"; + for(const Partition::BlockItem& block_item : p.block_items_) { + result += std::to_string(block_item.idx()) + " -> " + + std::to_string(block_item.state()) + " " + + std::to_string(block_item.block().idx()) + "\n"; + + } + result += "\n"; result += "BLOCKS:\n"; - size_t num_of_blocks = p.num_of_blocks(); - for(size_t block_idx = 0; block_idx < num_of_blocks; ++block_idx) { - result += std::to_string(block_idx) + ": "; - Node node = p.nodes_[p.get_node_idx_from_block_idx(block_idx)]; - for(size_t block_item_idx = node.first; - block_item_idx <= node.last; ++block_item_idx) { - result += std::to_string(p.block_items_[block_item_idx].state) - + " "; + result += "idx: states -> node_idx\n"; + for(const Partition::Block& block : p.blocks_) { + result += std::to_string(block.idx()) + ": "; + for(const Partition::BlockItem& block_item : block) { + result += std::to_string(block_item.state()) + " "; } - result += "\n"; + result += "-> " + std::to_string(block.node().idx()) + "\n"; } result += "\n"; - + result += "NODES:\n"; - size_t num_of_nodes = p.num_of_nodes(); - for(size_t node_idx = 0; node_idx < num_of_nodes; ++node_idx) { - result += std::to_string(node_idx) + ": "; - Node node = p.nodes_[node_idx]; - for(size_t block_item_idx = node.first; - block_item_idx <= node.last; ++block_item_idx) { - result += std::to_string(p.block_items_[block_item_idx].state) + - " "; + result += "idx: states -> first_idx last_idx\n"; + for(const Partition::Node& node : p.nodes_) { + result += std::to_string(node.idx()) + ": "; + for(const Partition::BlockItem& block_item : node) { + result += std::to_string(block_item.state()) + " "; } - result += "\n"; + result += "-> " + std::to_string(node.first().idx()) + " " + + std::to_string(node.last().idx()) + "\n"; } result += "\n"; return os << result; - } } diff --git a/tests/partition.cc b/tests/partition.cc index 36b7b7efb..df63be8f2 100644 --- a/tests/partition.cc +++ b/tests/partition.cc @@ -5,8 +5,7 @@ using namespace mata::utils; TEST_CASE("mata::utils::Partition") { - - SECTION("Create a simple partition with 1 block") { + SECTION("Create simple partition with 1 block") { Partition p{10}; CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); @@ -17,28 +16,35 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.in_same_block(0, 1)); CHECK(p.in_same_block(1, 8)); CHECK(p.in_same_block({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - for(size_t i = 0; i < 10; ++i) { - CHECK(p.get_block_item_idx_from_state(i) == i); - CHECK(p.get_block_idx_from_state(i) == 0); - CHECK(p.get_node_idx_from_state(i) == 0); - CHECK(p.get_block_item(i).state == i); - CHECK(p.get_block_item(i).block_idx == 0); - CHECK(p.get_node_idx_from_block_item_idx(i) == 0); + for(size_t i = 0; i < 10; ++i) { + CHECK(p.get_block_item(i).state() == i); + CHECK(p.get_block_item(i).idx() == i); + CHECK(p.get_block_item(i).block().idx() == 0); + CHECK(p.get_block_idx(i) == 0); + CHECK(p.get_block_item(i).node().idx() == 0); + CHECK(p.get_block_item(i).repr().idx() == 0); + CHECK(p.get_block_item(i).node().first().idx() == 0); + CHECK(p.get_block_item(i).node().last().idx() == 9); + CHECK(p[i].idx() == i); + } + CHECK(p.get_block(0).idx() == 0); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p.get_block(0).repr().idx() == 0); + CHECK(p.get_block(0).size() == 10); + CHECK(p.get_node(0).idx() == 0); + CHECK(p.get_node(0).first().idx() == 0); + CHECK(p.get_node(0).last().idx() == 9); + CHECK(p.get_node(0).size() == 10); + for(auto& block_item : p.get_block(0)) { + CHECK(block_item.block().idx() == 0); + } + for(auto& block_item : p.get_node(0)) { + CHECK(block_item.node().idx() == 0); } - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(0)).state == 0); - CHECK(p.get_block_item( - p.get_repr_idx_from_block_idx(0)).block_idx == 0); - CHECK(p.get_block_item( - p.get_repr_idx_from_node_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(0)).block_idx == 0); - CHECK(p.get_node(0).first == 0); - CHECK(p.get_node(0).last == 9); - CHECK(p.get_block(0).node_idx == 0); - CHECK(p.states_in_same_block(0).size() == 10); CHECK(p.partition().size() == 1); } - + SECTION("Create another simple partition with 1 block") { Partition p = Partition(10, {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}); CHECK(p.num_of_states() == 10); @@ -50,24 +56,30 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.in_same_block(0, 1)); CHECK(p.in_same_block(1, 8)); CHECK(p.in_same_block({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - for(size_t i = 0; i < 10; ++i) { - CHECK(p.get_block_item_idx_from_state(i) == i); - CHECK(p.get_block_idx_from_state(i) == 0); - CHECK(p.get_node_idx_from_state(i) == 0); - CHECK(p.get_block_item(i).state == i); - CHECK(p.get_block_item(i).block_idx == 0); - CHECK(p.get_node_idx_from_block_item_idx(i) == 0); + for(size_t i = 0; i < 10; ++i) { + CHECK(p.get_block_item(i).state() == i); + CHECK(p.get_block_item(i).idx() == i); + CHECK(p.get_block_item(i).block().idx() == 0); + CHECK(p.get_block_item(i).node().idx() == 0); + CHECK(p.get_block_item(i).repr().idx() == 0); + CHECK(p.get_block_item(i).node().first().idx() == 0); + CHECK(p.get_block_item(i).node().last().idx() == 9); + CHECK(p[i].idx() == i); + } + CHECK(p.get_block(0).idx() == 0); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p.get_block(0).repr().idx() == 0); + CHECK(p.get_block(0).size() == 10); + CHECK(p.get_node(0).idx() == 0); + CHECK(p.get_node(0).first().idx() == 0); + CHECK(p.get_node(0).last().idx() == 9); + CHECK(p.get_node(0).size() == 10); + for(auto& block_item : p.get_block(0)) { + CHECK(block_item.block().idx() == 0); + } + for(auto& block_item : p.get_node(0)) { + CHECK(block_item.node().idx() == 0); } - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(0)).state == 0); - CHECK(p.get_block_item( - p.get_repr_idx_from_block_idx(0)).block_idx == 0); - CHECK(p.get_block_item( - p.get_repr_idx_from_node_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(0)).block_idx == 0); - CHECK(p.get_node(0).first == 0); - CHECK(p.get_node(0).last == 9); - CHECK(p.get_block(0).node_idx == 0); - CHECK(p.states_in_same_block(0).size() == 10); CHECK(p.partition().size() == 1); } @@ -86,44 +98,38 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.in_same_block({0, 5, 8})); CHECK(p.in_same_block({1, 2, 3, 4, 6, 7, 9})); CHECK(!p.in_same_block({1, 2, 3, 4, 5, 7, 9})); - - CHECK(p.get_block_item_idx_from_state(0) == 0); - CHECK(p.get_block_item(0).state == 0); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_node_idx_from_state(0) == 0); - CHECK(p.get_block_item(0).block_idx == 0); - CHECK(p.get_node_idx_from_block_item_idx(0) == 0); - - CHECK(p.get_block_item_idx_from_state(1) == 3); - CHECK(p.get_block_item(3).state == 1); - CHECK(p.get_block_idx_from_state(1) == 1); - CHECK(p.get_node_idx_from_state(1) == 1); - CHECK(p.get_block_item(3).block_idx == 1); - CHECK(p.get_node_idx_from_block_item_idx(3) == 1); - - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(1)).state == 1); - CHECK(p.get_block_item( - p.get_repr_idx_from_block_idx(0)).block_idx == 0); - CHECK(p.get_block_item( - p.get_repr_idx_from_block_idx(1)).block_idx == 1); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(0)).block_idx == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(1)).state == 1); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(1)).block_idx == 1); - CHECK(p.get_node(0).first == 0); - CHECK(p.get_node(0).last == 2); - CHECK(p.get_node(1).first == 3); - CHECK(p.get_node(1).last == 9); - CHECK(p.get_block(0).node_idx == 0); - CHECK(p.get_block(1).node_idx == 1); - + CHECK(p[0].idx() == 0); + CHECK(p[0].state() == 0); + CHECK(p[0].block().idx() == 0); + CHECK(p[0].node().idx() == 0); + CHECK(p.get_block_item(0).node().idx() == 0); + CHECK(p[1].idx() == 3); + CHECK(p.get_block_item(3).state() == 1); + CHECK(p[1].block().idx() == 1); + CHECK(p[1].node().idx() == 1); + CHECK(p.get_block_item(3).block().idx() == 1); + CHECK(p.get_block_item(3).node().idx() == 1); + CHECK(p.get_block(0).repr().state() == 0); + CHECK(p.get_block(1).repr().state() == 1); + CHECK(p.get_block(0).repr().block().idx() == 0); + CHECK(p.get_block(1).repr().block().idx() == 1); + CHECK(p.get_block(0).size() == 3); + CHECK(p.get_block(1).size() == 7); + CHECK(p.get_node(0).repr().state() == 0); + CHECK(p.get_node(0).repr().block().idx() == 0); + CHECK(p.get_node(1).repr().state() == 1); + CHECK(p.get_node(1).repr().block().idx() == 1); + CHECK(p.get_node(0).first().idx() == 0); + CHECK(p.get_node(0).last().idx() == 2); + CHECK(p.get_node(1).first().idx() == 3); + CHECK(p.get_node(1).last().idx() == 9); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p.get_block(1).node().idx() == 1); CHECK(p.states_in_same_block(0).size() == 3); CHECK(p.states_in_same_block(1).size() == 7); CHECK(p.partition().size() == 2); } - - + SECTION("Create a simple partition with 3 blocks") { Partition p{6, {{0}, {1, 2}}}; CHECK(p.num_of_states() == 6); @@ -138,33 +144,35 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.in_same_block({3, 4, 5})); CHECK(!p.in_same_block({2, 3, 4, 5})); for(size_t i = 0; i <= 5; ++i) { - CHECK(p.get_block_item_idx_from_state(i) == i); - CHECK(p.get_block_item(i).state == i); + CHECK(p[i].idx() == i); + CHECK(p[i].state() == i); } - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_node_idx_from_state(0) == 0); - CHECK(p.get_block_item(0).block_idx == 0); - CHECK(p.get_node_idx_from_block_item_idx(0) == 0); - CHECK(p.get_block_idx_from_state(1) == 1); - CHECK(p.get_node_idx_from_state(1) == 1); - CHECK(p.get_block_item(1).block_idx == 1); - CHECK(p.get_node_idx_from_block_item_idx(1) == 1); - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(1)).state == 1); - CHECK(p.get_block_item(p.get_repr_idx_from_block_idx(2)).state == 3); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(0)).state == 0); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(1)).state == 1); - CHECK(p.get_block_item(p.get_repr_idx_from_node_idx(2)).state == 3); - CHECK(p.get_node(0).first == 0); - CHECK(p.get_node(0).last == 0); - CHECK(p.get_node(1).first == 1); - CHECK(p.get_node(1).last == 2); - CHECK(p.get_node(2).first == 3); - CHECK(p.get_node(2).last == 5); - CHECK(p.get_block(0).node_idx == 0); - CHECK(p.get_block(1).node_idx == 1); - CHECK(p.get_block(2).node_idx == 2); - + CHECK(p[0].block().idx() == 0); + CHECK(p[0].node().idx() == 0); + CHECK(p.get_block_item(0).block().idx() == 0); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p[1].block().idx() == 1); + CHECK(p[1].node().idx() == 1); + CHECK(p.get_block_item(1).block().idx() == 1); + CHECK(p.get_block_item(1).node().idx() == 1); + CHECK(p.get_block(0).repr().state() == 0); + CHECK(p.get_block(1).repr().state() == 1); + CHECK(p.get_block(2).repr().state() == 3); + CHECK(p.get_node(0).repr().state() == 0); + CHECK(p.get_node(1).repr().state() == 1); + CHECK(p.get_node(2).repr().state() == 3); + CHECK(p.get_node(0).first().idx() == 0); + CHECK(p.get_node(0).last().idx() == 0); + CHECK(p.get_node(1).first().idx() == 1); + CHECK(p.get_node(1).last().idx() == 2); + CHECK(p.get_node(2).first().idx() == 3); + CHECK(p.get_node(2).last().idx() == 5); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p.get_block(1).node().idx() == 1); + CHECK(p.get_block(2).node().idx() == 2); + CHECK(p.get_block(0).size() == 1); + CHECK(p.get_block(1).size() == 2); + CHECK(p.get_block(2).size() == 3); CHECK(p.states_in_same_block(0).size() == 1); CHECK(p.states_in_same_block(1).size() == 2); CHECK(p.states_in_same_block(3).size() == 3); @@ -177,30 +185,33 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 1); CHECK(p.num_of_nodes() == 1); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 0); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 0); CHECK(p.in_same_block({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); CHECK(p.states_in_same_block(0).size() == 10); CHECK(p.partition().size() == 1); + CHECK(p.get_block(0).size() == 10); p.split_blocks({0, 1, 2, 3, 4}); CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 2); CHECK(p.num_of_nodes() == 3); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 1); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 1); CHECK(p.in_same_block({0, 1, 2, 3, 4})); CHECK(p.in_same_block({5, 6, 7, 8, 9})); CHECK(p.states_in_same_block(0).size() == 5); CHECK(p.states_in_same_block(5).size() == 5); CHECK(p.partition().size() == 2); + CHECK(p.get_block(0).size() == 5); + CHECK(p.get_block(1).size() == 5); p.split_blocks({0, 1, 2, 5, 6, 7}); CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 4); CHECK(p.num_of_nodes() == 7); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 3); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 3); CHECK(p.in_same_block({0, 1, 2})); CHECK(p.in_same_block({3, 4})); CHECK(p.in_same_block({5, 6, 7})); @@ -215,8 +226,8 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 8); CHECK(p.num_of_nodes() == 15); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 7); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); CHECK(p.in_same_block({0})); CHECK(p.in_same_block({1, 2})); CHECK(p.in_same_block({3})); @@ -239,8 +250,8 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 10); CHECK(p.num_of_nodes() == 19); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 7); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); CHECK(p.states_in_same_block(0).size() == 1); CHECK(p.states_in_same_block(1).size() == 1); CHECK(p.states_in_same_block(2).size() == 1); @@ -257,8 +268,8 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.num_of_block_items() == 10); CHECK(p.num_of_blocks() == 10); CHECK(p.num_of_nodes() == 19); - CHECK(p.get_block_idx_from_state(0) == 0); - CHECK(p.get_block_idx_from_state(9) == 7); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); CHECK(p.states_in_same_block(0).size() == 1); CHECK(p.states_in_same_block(1).size() == 1); CHECK(p.states_in_same_block(2).size() == 1); @@ -290,9 +301,8 @@ TEST_CASE("mata::utils::Partition") { CHECK(!p.in_same_block(1, 5)); CHECK(!p.in_same_block(1, 7)); } - - SECTION("Custom copying and assigning") - { + + SECTION("Custom copying and assigning with splitting") { Partition p = Partition(5, {{2, 3}}); p.split_blocks({0}); @@ -313,35 +323,261 @@ TEST_CASE("mata::utils::Partition") { size_t nodesNum = p.num_of_nodes(); for(size_t i = 0; i < statesNum; ++i) { - CHECK(p.get_block_item_idx_from_state(i) == - q.get_block_item_idx_from_state(i)); - CHECK(p.get_block_item_idx_from_state(i) == - r.get_block_item_idx_from_state(i)); - CHECK(p.get_block_item(i).state == q.get_block_item(i).state); - CHECK(p.get_block_item(i).state == r.get_block_item(i).state); - CHECK(p.get_block_item(i).block_idx == - q.get_block_item(i).block_idx); - CHECK(p.get_block_item(i).block_idx == - r.get_block_item(i).block_idx); + CHECK(p[i].idx() == q[i].idx()); + CHECK(p[i].idx() == r[i].idx()); + CHECK(p[i].state() == q[i].state()); + CHECK(p[i].state() == r[i].state()); + CHECK(p[i].block().idx() == q[i].block().idx()); + CHECK(p[i].block().idx() == r[i].block().idx()); } for(size_t i = 0; i < blocksNum; ++i) { - CHECK(p.get_block(i).node_idx == q.get_block(i).node_idx); - CHECK(p.get_block(i).node_idx == r.get_block(i).node_idx); + CHECK(p[i].node().idx() == q[i].node().idx()); + CHECK(p[i].node().idx() == r[i].node().idx()); } for(size_t i = 0; i < nodesNum; ++i) { - CHECK(p.get_node(i).first == q.get_node(i).first); - CHECK(p.get_node(i).first == r.get_node(i).first); - CHECK(p.get_node(i).last == q.get_node(i).last); - CHECK(p.get_node(i).last == r.get_node(i).last); + CHECK(p[i].node().first().idx() == q[i].node().first().idx()); + CHECK(p[i].node().first().idx() == r[i].node().first().idx()); + CHECK(p[i].node().last().idx() == q[i].node().last().idx()); + CHECK(p[i].node().last().idx() == r[i].node().last().idx()); } - - q.split_blocks({1, 2}); - r.split_blocks({1, 2}); - std::cout << q; + std::cout << q; + r.split_blocks({1, 2}); + r.split_blocks({1, 2}); std::cout << r; - } + } + + SECTION("Custom copying and assigning without splitting") { + Partition q{6, {{0}, {1, 2}}}; + Partition p = q; + CHECK(p.num_of_states() == 6); + CHECK(p.num_of_block_items() == 6); + CHECK(p.num_of_blocks() == 3); + CHECK(p.num_of_nodes() == 3); + CHECK(p.in_same_block({})); + CHECK(p.in_same_block({0})); + CHECK(p.in_same_block(3, 5)); + CHECK(p.in_same_block(1, 2)); + CHECK(!p.in_same_block(1, 4)); + CHECK(p.in_same_block({3, 4, 5})); + CHECK(!p.in_same_block({2, 3, 4, 5})); + for(size_t i = 0; i <= 5; ++i) { + CHECK(p[i].idx() == i); + CHECK(p[i].state() == i); + } + CHECK(p[0].block().idx() == 0); + CHECK(p[0].node().idx() == 0); + CHECK(p.get_block_item(0).block().idx() == 0); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p[1].block().idx() == 1); + CHECK(p[1].node().idx() == 1); + CHECK(p.get_block_item(1).block().idx() == 1); + CHECK(p.get_block_item(1).node().idx() == 1); + CHECK(p.get_block(0).repr().state() == 0); + CHECK(p.get_block(1).repr().state() == 1); + CHECK(p.get_block(2).repr().state() == 3); + CHECK(p.get_node(0).repr().state() == 0); + CHECK(p.get_node(1).repr().state() == 1); + CHECK(p.get_node(2).repr().state() == 3); + CHECK(p.get_node(0).first().idx() == 0); + CHECK(p.get_node(0).last().idx() == 0); + CHECK(p.get_node(1).first().idx() == 1); + CHECK(p.get_node(1).last().idx() == 2); + CHECK(p.get_node(2).first().idx() == 3); + CHECK(p.get_node(2).last().idx() == 5); + CHECK(p.get_block(0).node().idx() == 0); + CHECK(p.get_block(1).node().idx() == 1); + CHECK(p.get_block(2).node().idx() == 2); + CHECK(p.states_in_same_block(0).size() == 1); + CHECK(p.states_in_same_block(1).size() == 2); + CHECK(p.states_in_same_block(3).size() == 3); + CHECK(p.partition().size() == 3); + } + + SECTION("Another splitting blocks with partition copying") { + Partition q{10}; + Partition p = q; + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 1); + CHECK(p.num_of_nodes() == 1); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 0); + CHECK(p.in_same_block({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + CHECK(p.states_in_same_block(0).size() == 10); + CHECK(p.partition().size() == 1); + p.split_blocks({0, 1, 2, 3, 4}); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 2); + CHECK(p.num_of_nodes() == 3); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 1); + CHECK(p.in_same_block({0, 1, 2, 3, 4})); + CHECK(p.in_same_block({5, 6, 7, 8, 9})); + CHECK(p.states_in_same_block(0).size() == 5); + CHECK(p.states_in_same_block(5).size() == 5); + CHECK(p.partition().size() == 2); + p.split_blocks({0, 1, 2, 5, 6, 7}); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 4); + CHECK(p.num_of_nodes() == 7); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 3); + CHECK(p.in_same_block({0, 1, 2})); + CHECK(p.in_same_block({3, 4})); + CHECK(p.in_same_block({5, 6, 7})); + CHECK(p.in_same_block({8, 9})); + CHECK(p.states_in_same_block(0).size() == 3); + CHECK(p.states_in_same_block(3).size() == 2); + CHECK(p.states_in_same_block(5).size() == 3); + CHECK(p.states_in_same_block(8).size() == 2); + CHECK(p.partition().size() == 4); + p.split_blocks({0, 3, 5, 8}); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 8); + CHECK(p.num_of_nodes() == 15); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); + CHECK(p.in_same_block({0})); + CHECK(p.in_same_block({1, 2})); + CHECK(p.in_same_block({3})); + CHECK(p.in_same_block({4})); + CHECK(p.in_same_block({5})); + CHECK(p.in_same_block({6, 7})); + CHECK(p.in_same_block({8})); + CHECK(p.in_same_block({9})); + CHECK(p.states_in_same_block(0).size() == 1); + CHECK(p.states_in_same_block(1).size() == 2); + CHECK(p.states_in_same_block(3).size() == 1); + CHECK(p.states_in_same_block(4).size() == 1); + CHECK(p.states_in_same_block(5).size() == 1); + CHECK(p.states_in_same_block(6).size() == 2); + CHECK(p.states_in_same_block(8).size() == 1); + CHECK(p.states_in_same_block(9).size() == 1); + CHECK(p.partition().size() == 8); + p.split_blocks({1, 6}); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 10); + CHECK(p.num_of_nodes() == 19); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); + CHECK(p.states_in_same_block(0).size() == 1); + CHECK(p.states_in_same_block(1).size() == 1); + CHECK(p.states_in_same_block(2).size() == 1); + CHECK(p.states_in_same_block(3).size() == 1); + CHECK(p.states_in_same_block(4).size() == 1); + CHECK(p.states_in_same_block(5).size() == 1); + CHECK(p.states_in_same_block(6).size() == 1); + CHECK(p.states_in_same_block(7).size() == 1); + CHECK(p.states_in_same_block(8).size() == 1); + CHECK(p.states_in_same_block(9).size() == 1); + CHECK(p.partition().size() == 10); + p.split_blocks({0, 2, 4, 6, 8}); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 10); + CHECK(p.num_of_nodes() == 19); + CHECK(p[0].block().idx() == 0); + CHECK(p[9].block().idx() == 7); + CHECK(p.states_in_same_block(0).size() == 1); + CHECK(p.states_in_same_block(1).size() == 1); + CHECK(p.states_in_same_block(2).size() == 1); + CHECK(p.states_in_same_block(3).size() == 1); + CHECK(p.states_in_same_block(4).size() == 1); + CHECK(p.states_in_same_block(5).size() == 1); + CHECK(p.states_in_same_block(6).size() == 1); + CHECK(p.states_in_same_block(7).size() == 1); + CHECK(p.states_in_same_block(8).size() == 1); + CHECK(p.states_in_same_block(9).size() == 1); + CHECK(p.partition().size() == 10); + std::cout << p; + } + + SECTION("Another complicated blocks splitting with swapping and copying") { + Partition q{10}; + Partition p = q; + p.split_blocks({0, 2, 4, 6, 8}); + CHECK(p.in_same_block(0, 2)); + CHECK(p.in_same_block(0, 4)); + CHECK(p.in_same_block(0, 6)); + CHECK(p.in_same_block(0, 8)); + CHECK(!p.in_same_block(0, 1)); + CHECK(!p.in_same_block(0, 3)); + CHECK(!p.in_same_block(0, 5)); + CHECK(!p.in_same_block(0, 7)); + CHECK(!p.in_same_block(0, 9)); + p.split_blocks({1, 9}); + CHECK(p.in_same_block(1, 9)); + CHECK(!p.in_same_block(1, 3)); + CHECK(!p.in_same_block(1, 5)); + CHECK(!p.in_same_block(1, 7)); + std::cout << p; + } + + SECTION("Partition over an empty set") { + Partition q{0}; + Partition p = q; + p.split_blocks({}); + CHECK(!p.num_of_states()); + CHECK(!p.num_of_block_items()); + CHECK(!p.num_of_blocks()); + CHECK(!p.num_of_nodes()); + CHECK(!p.partition().size()); + std::cout << p; + } + + SECTION("Partition iterators") { + Partition p = Partition(8, {{0, 1}, {2, 3, 4, 5}}); + size_t index = 0; + for(auto& block_item : p.get_block(0)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 0); + CHECK(block_item.node().idx() == 0); + CHECK(block_item.state() == index); + ++index; + } + for(auto& block_item : p.get_block(1)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 1); + CHECK(block_item.node().idx() == 1); + CHECK(block_item.state() == index); + ++index; + } + for(auto& block_item : p.get_block(2)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 2); + CHECK(block_item.node().idx() == 2); + CHECK(block_item.state() == index); + ++index; + } + index = 0; + for(auto& block_item : p.get_node(0)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 0); + CHECK(block_item.node().idx() == 0); + CHECK(block_item.state() == index); + ++index; + } + for(auto& block_item : p.get_node(1)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 1); + CHECK(block_item.node().idx() == 1); + CHECK(block_item.state() == index); + ++index; + } + for(auto& block_item : p.get_node(2)) { + CHECK(block_item.idx() == index); + CHECK(block_item.block().idx() == 2); + CHECK(block_item.node().idx() == 2); + CHECK(block_item.state() == index); + ++index; + } + } }