From 6249a30e0791d0b44cce673aa2b6ca443b6a6ddb Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 16 Mar 2024 06:43:14 +0100 Subject: [PATCH 01/34] Data structures for partition and extendable square matrix added. Corresponding tests created. --- include/mata/utils/partition-relation-pair.hh | 1235 +++++++++++++++++ tests/CMakeLists.txt | 1 + tests/partition-relation-pair.cc | 296 ++++ 3 files changed, 1532 insertions(+) create mode 100644 include/mata/utils/partition-relation-pair.hh create mode 100644 tests/partition-relation-pair.cc diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh new file mode 100644 index 000000000..24ebb3d1b --- /dev/null +++ b/include/mata/utils/partition-relation-pair.hh @@ -0,0 +1,1235 @@ +/** @file partition_relation_pair.hh + * @brief Definition of a partition, extendable square matrix + * and partition-relation pair. + * + * This file contains definitions of partition, extendable square + * matrix and operations which allow us to manipulate with them. + * + * Description: + * + * A partition-relation pair is a tuple (P, Rel). It is an efficient + * representation of a preorder/quasiorder R, which is a reflexive and + * transitive binary relation. + * In this context, we consider a carrier set S which contains interval + * of contiguous natural numbers from 0 to |S|-1. + * These numbers are called states. + * P is a partition of S which corresponds to an equivalence relation + * induced by the preorder R. + * Thus, (P, Rel) corresponds to a preorder relation R over states S. + * + * This file provides implementation of a partition P and defines the + * ExtendableSquareMatrix structure which can be used to represent + * the binary relation Rel. It can be combined to represent the preorder R. + * + * @author Tomáš Kocourek + */ + +#ifndef _PARTITION_RELATION_PAIR_HH_ +#define _PARTITION_RELATION_PAIR_HH_ + +#include +#include +#include +#include + +namespace mata::utils { + +/************************************************************************ +* +* +* +* PARTITION +* +* +* +*************************************************************************/ + + +using State = unsigned long; +using StateBlock = std::vector; +using StateBlocks = std::vector; + +using BlockItem = struct BlockItem {State state; size_t blockIdx;}; +using Block = struct Block {size_t nodeIdx;}; +using Node = struct Node {size_t first; size_t last;}; + +using BlockItems = std::vector; +using Blocks = std::vector; +using Nodes = std::vector; + +using SplitPair = struct SplitPair + {size_t former; size_t created; size_t oldNodeIdx;}; + +/** + * 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 a natural number from the contiguous + * interval <0, |S|-1>. + * + * STATES: + * This representation works with the vector of indices 'm_states' + * with the constant size |S|. Each state is represented by the index + * of that vector so we can refer to a state in constant time using + * m_states[state]. + * + * BLOCK ITEMS: + * The memory cell m_states[state] contains an index to the corresponding + * 'm_blockItems' vector. The vector 'm_blockItems' has the constant size |S|. + * Each BlockItem contains an index of the corresponding state which means + * that states and block items 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 + * 'm_blockItems' vector. + * + * BLOCKS: + * The blocks themselves are represented by the vector 'm_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 contains only 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 + * split, we create a new block 'C' and modify the former block 'B'. + * The former block is thus no more represented in the 'm_blocks' vector. + * + * NODES: + * Each node represents a current block or a block which has been split before. + * The node can by accessed by its index using m_nodes[nodeIdx]. If the given + * node represents an existing block, such block contains an index of that node. + * In context of nodes which represent former blocks, no block contains their + * 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. Thus, 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 states included in such + * node. When a block is split, the corresponding BlockItems are swapped + * in situ such that the indices 'first' and 'last' still surround the + * corresponding node. + * + * EXAMPLE: + * In the example below, we represent a partition {{0, 1}, {2, 3}}. + * Thus, we have two blocks 0 ({0, 1}) and 1 ({2, 3}). 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). + * Likewise, the node 2 contains indices 2 (first) and 3 (last) which means that + * the blockItems 2 and 3 surround a contiguous subvector of elements in the + * node 2 (or in the block 1). + * Moreover, we also represent the former block which does not exist anymore + * by the node 0 which contains indices 0 (first) and 3 (last) which means that + * the blockItems 0 and 3 surround a contiguous subvector of elements in the + * node 0. Thus, we know that there had been a block {0, 1, 2, 3} before it has + * been split to obtain blocks {0, 1} and {2, 3}. + * + * + * 0 1 2 3 + * ------- ------- ------- ------- + * | 0 | 1 | 3 | 2 | m_states + * ------- ------- ------- ------- + * ↑ ↑ ↑ ↑ + * | | \ / + * | | X + * | | / \ + * 0 ↓ 1 ↓ 2 ↓ ↓ 3 + * ------- ------- ------- ------- + * | 0 | 1 | 3 | 2 | m_blockItems + * ----→|-------|-------|-------|-------|←------------- + * | | 0 | 0 | 1 | 1 | | + * | ------- ------- ------- ------- | + * | | | | | | + * | 0 ↓ ↓ 1 ↓ ↓ | + * | ------------------------------- | + * | | 1 | 2 | m_blocks | + * | ------------------------------- | + * | | | | + * | 0 1 ↓ 2 ↓ | + * | ------- ------- ------- | + * -----| 0 | 0 | 2 | m_nodes | + * |-------|-------|-------| | + * | 3 | 1 | 3 | | + * ------- ------- ------- | + * | | + * ------------------------------------------- + * + * Using this structure, we can: + * - find the block which contains 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 a vector of states A share the same block in O(|A|) + * - iterate through the block P in O(|P|) + * - 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 + * + */ +typedef struct Partition +{ + private: + + /* indices to the m_blockItems vector */ + std::vector m_states{}; + + BlockItems m_blockItems{}; + Blocks m_blocks{}; + Nodes m_nodes{}; + + public: + + // constructors + Partition(size_t numOfStates, + StateBlocks partition = StateBlocks()); + + // sizes of the used vectors + inline size_t numOfStates(void) const { return m_states.size(); } + inline size_t numOfBlockItems(void) const { return m_blockItems.size();} + inline size_t numOfBlocks(void) const { return m_blocks.size(); } + inline size_t numOfNodes(void) const { return m_nodes.size(); } + + // blocks splitting + std::vector splitBlocks(std::vector marked); + + // basic information about partition + inline bool inSameBlock(State first, State second) const; + bool inSameBlock(std::vector states) const; + std::vector statesInSameBlock(State state) const; + + // accessing states, blockItems, blocks, nodes + + /** + * @brief returns a BlockItem corresponding to the given index + * @param blockItemIdx index of the BlockItem + * @return corresponding BlockItem + */ + inline BlockItem getBlockItem(size_t blockItemIdx) const + { + assert(blockItemIdx < numOfBlockItems() && + "Nonexisting block item index used."); + return m_blockItems[blockItemIdx]; + } + + /** + * @brief returns a block corresponding to the given index + * @param blockIdx index of the block + * @return corresponding block + */ + inline Block getBlock(size_t blockIdx) const + { + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_blocks[blockIdx]; + } + + /** + * @brief returns a node corresponding to the given index + * @param nodeIdx index of the node + * @return corresponding node + */ + inline Node getNode(size_t nodeIdx) const + { + assert(nodeIdx < numOfNodes() && + "Nonexisting node index used."); + return m_nodes[nodeIdx]; + } + + /** + * @brief returns a block index corresponding to the given state + * @param state given state + * @return corresponding block index + */ + inline size_t getBlockIdxFromState(State state) const + { + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_blockItems[m_states[state]].blockIdx; + } + + /** + * @brief returns a node index corresponding to the given state + * @param state given state + * @return corresponding node index + */ + inline size_t getNodeIdxFromState(State state) const + { + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_blocks[m_blockItems[m_states[state]].blockIdx].nodeIdx; + } + + /** + * @brief returns a BlockItem index corresponding to the given state + * @param state given state + * @return corresponding BlockItem index + */ + inline size_t getBlockItemIdxFromState(State state) const + { + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_states[state]; + } + + /** + * @brief returns a Node index corresponding to the given BlockItem index + * @param blockItemIdx BlockItem index + * @return corresponding Node index + */ + inline size_t getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const + { + assert(blockItemIdx < numOfBlockItems() && + "Nonexisting BlockItem index used."); + return m_blocks[m_blockItems[blockItemIdx].blockIdx].nodeIdx; + } + + /** + * @brief returns a node index corresponding to the given block index + * @param blockIdx given block index + * @return corresponding node index + */ + inline size_t getNodeIdxFromBlockIdx(size_t blockIdx) const + { + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_blocks[blockIdx].nodeIdx; + } + + /** Get a representant from the block index + * @brief returns a blockItem corresponding to the given block index + * @param blockIdx given block index + * @return first blockItem corresponding to the given block index + */ + inline BlockItem getReprFromBlockIdx(size_t blockIdx) const + { + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_blockItems[m_nodes[m_blocks[blockIdx].nodeIdx].first]; + } + + /** Get a representant from the node index + * @brief returns a blockItem corresponding to the given node index + * @param nodeIdx given node index + * @return first blockItem corresponding to the given node index + */ + inline BlockItem getReprFromNodeIdx(size_t nodeIdx) const + { + assert(nodeIdx < numOfNodes() && + "Nonexisting node index used."); + return m_blockItems[m_nodes[nodeIdx].first]; + } + + /** + * @brief converts the partition to the vector of vectors of states + * @return vector of vectors of states + */ + StateBlocks partition(void); + + // debug + + friend std::ostream& operator<<(std::ostream& os, + const Partition& p); + + +} Partition; + +/** Constructor of the partition structure. 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. +* The partition can be initialized in linear time 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 are states which are not represented in the initial partition, +* they will be all part of the one additional block +* @brief constructs the partition +* @param numOfStates cardinality of the carrier set +* @param partition optional initial partition in the form of vectors of +* vectors of states +*/ +Partition::Partition(size_t numOfStates, StateBlocks partition) +{ + // reserving memory space to avoid moving extended vector + m_states.reserve(numOfStates); + m_blockItems.reserve(numOfStates); + m_blocks.reserve(numOfStates); + m_nodes.reserve(2 * numOfStates - 1); + + // this vector says whether the given state has been already seen + // to detect duplicates and to detect unused states + std::vector used; + used.insert(used.end(), numOfStates, false); + m_states.insert(m_states.end(), numOfStates, 0); + + // creating partition using given initial vector of vectors + size_t numOfBlocks = partition.size(); + // iterating through initial partition blocks + for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) + { + assert(!partition[blockIdx].empty() && + "Partition class cannot be empty."); + + // iterating through one partition block + for(auto state : partition[blockIdx]) + { + assert(state < numOfStates && + "Invalid state name detected while creating" + "a partition relation pair."); + assert(!used[state] && + "Partition could not be created." + "Duplicate occurence of a state"); + + used[state] = true; + + // creating a corresponding BlockItem + m_states[state] = m_blockItems.size(); + m_blockItems.push_back({.state = state, .blockIdx = blockIdx}); + + } + + // first and last states of the block will be used to create + // a corresponding node + State first = partition[blockIdx].front(); + State last = partition[blockIdx].back(); + + // creating a corresponding block and node + m_nodes.push_back({.first = m_states[first], .last = m_states[last]}); + m_blocks.push_back({.nodeIdx = blockIdx}); + } + + // we need to detect whether there is a state which has not be used + // to create a additional partition block + bool allStatesUsed = true; + State first = 0; + State last = 0; + + // iterating through the vector of flags saying which states has been seen + for(State state = 0; state < numOfStates; ++state) + { + if(used[state]) + { + continue; + } + + // if there is an unused state, we need to create an additional block + if(allStatesUsed) + { + allStatesUsed = false; + first = state; + ++numOfBlocks; + } + + // creating the new BlockItem + last = state; + m_states[state] = m_blockItems.size(); + m_blockItems.push_back({.state = state, .blockIdx = numOfBlocks-1}); + } + + // creating a new block and node if there was an unused state + if(!allStatesUsed) + { + m_nodes.push_back({.first = m_states[first], .last = m_states[last]}); + m_blocks.push_back({.nodeIdx = numOfBlocks-1}); + } +} + +/** +* @brief tests whether thw two given states correspond +* to the same partition block +* @param first first state to be checked +* @param second second state to be checked +* @param true iff both given states belong to the same partition block +*/ +inline bool Partition::inSameBlock(State first, State second) const +{ + assert(first < m_states.size() && "The given state does not exist"); + assert(second < m_states.size() && "The given state does not exist"); + return getBlockIdxFromState(first) == getBlockIdxFromState(second); +} + +/** +* @brief tests whether the given n states correspond to the same partition block +* @param states vector of states to be checked +* @return true iff all of the given states belong to the same partition block +*/ +bool Partition::inSameBlock(std::vector states) const +{ + if(states.empty()) { return true; } + size_t blockIdx = getBlockIdxFromState(states.front()); + for(size_t state : states) + { + assert(state < m_states.size() && "The given state does not exist."); + if(getBlockIdxFromState(state) != blockIdx) { return false; } + } + return true; +} + +/** +* @brief find all of the states which share the block with the input state +* @param state input state +* @return vector of all the states in the corresponding block +*/ +std::vector Partition::statesInSameBlock(State state) const +{ + assert(state < numOfStates() && "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 = getNode(getNodeIdxFromState(state)).first; + size_t last = getNode(getNodeIdxFromState(state)).last; + + // iterating through BlockItems + for(size_t i = first; i <= last; ++i) + { + result.push_back(getBlockItem(i).state); + } + + return result; +} + +/** +* @brief transforms inner representation of the partition to the vector of +* vectors of states +* @return vector of vectors of states +*/ +StateBlocks Partition::partition(void) +{ + StateBlocks result{}; + result.insert(result.end(), m_blocks.size(), std::vector()); + for(auto blockItem : m_blockItems) + { + result[blockItem.blockIdx].push_back(blockItem.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 +* and unmarked. The partition will be split as follows: +* - if there is a block whose all elements are marked, the block remains +* unchanged +* - if there is a block whose all elements are unmarked, the block remains +* unchanged +* - if there is a block which contains both marked and unmarked states, it +* will be split in two blocks such that the first one contains marked states +* and the second one contains unmarked states of the original block +* - it means that each block is either unchanged or split in two parts +* - if a block contains states such that corresponding BlockItems form +* contiguous subvector on the interval of natural numbers , the split +* nodes will correspond to such BlockItems that they form contiguous subvectors +* on the intervals of natural numbers and , where a <= k < b. +* The BlockItems on the interval will be swapped such that the property +* above holds. The representant (first BlockItem on the interval) will always +* keep its position (the strategy of swapping will adapt to the fact whether +* the representant is marked or not). Thus, a representant of any node never +* 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 unexisting 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: +* - index of the new block which keeps identity of the former block +* - index of the new block which is newly constructed +* - index of the node which is an ancestor of these two blocks +* The function returns a vector of such SplitPairs. +* +* @brief splits blocks of the partition +* @param marked marked states which influence splitting +* @return vector of SplitPair which contain information about split blocks +*/ +std::vector Partition::splitBlocks(std::vector marked) +{ + // the vector which will be returned as a result + std::vector split{}; + + // if there is no marked state, no block could be split + if(marked.empty()) { 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 usedStates{}; + usedStates.insert(usedStates.end(), m_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 usedBlocks{}; + usedBlocks.insert(usedBlocks.end(), m_blocks.size(), 0); + + // iterating through the marked states to fill usedStates and + // usedBlocks vectors + for(size_t i : marked) + { + assert(i < m_states.size() && "The given state does not exist."); + assert(!usedStates[i] && "The given state was marked multiple times"); + usedStates[i] = true; + ++usedBlocks[getBlockIdxFromState(i)]; + } + + size_t oldBlocksSize, newBlockIdx; + oldBlocksSize = newBlockIdx = m_blocks.size(); + + // iterating through existing blocks + for(size_t i = 0; i < oldBlocksSize; ++i) + { + // if no state of the given block has been marked, it + // won't be split + if(!usedBlocks[i]) { continue; } + + // looking for the subvector of BlockItems which forms processed + // block and computing its size + size_t nodeIdx = getNodeIdxFromBlockIdx(i); + size_t iterFirst = getNode(nodeIdx).first; + size_t iterLast = getNode(nodeIdx).last; + size_t blockSize = iterLast - iterFirst + 1; + + // if all states of the given block has been marked, it + // won't be split + if(usedBlocks[i] >= blockSize) { continue; } + + // choosing the strategy of swapping BlocksItems such that + // the representant of split block keeps its position + bool reprMarked = usedStates[getReprFromNodeIdx(nodeIdx).state]; + + // We access the first and last element of the subvector of BlockItems + // which forms processed block. We look for the first unmarked element + // from left and first marked element from right (or vice versa since + // the exact strategy is chosen according to the fact whether the first + // element is marked or not). As soon as such elements are found, they + // are swapped. This procedure continues until these two indices used + // to iterate through the BlockItems meet somewhere in the middle + while(iterFirst <= iterLast) + { + // we choose the swapping strategy using XOR operation + while(reprMarked ^ !usedStates[getBlockItem(iterFirst).state]) + { + // this visited state will be part of the former block + ++iterFirst; + } + while(reprMarked ^ usedStates[getBlockItem(iterLast).state]) + { + // this visited state will be part of the new block + m_blockItems[iterLast].blockIdx = newBlockIdx; + --iterLast; + } + + // if the used indexes meet, we finish swapping + if(iterFirst > iterLast) + { + break; + } + + // swapping BlockItems + BlockItem tmp = getBlockItem(iterFirst); + m_blockItems[iterFirst] = getBlockItem(iterLast); + m_blockItems[iterLast] = tmp; + + // since m_states and m_blockItems vectors should be bijectively + // mapped, we need to update m_states after swapping two BlockItems + m_states[m_blockItems[iterFirst].state] = iterFirst; + m_states[m_blockItems[iterLast].state] = iterLast; + + // after the blockItems are swapped, one of them should + // be assigned to the new block + m_blockItems[iterLast].blockIdx = newBlockIdx; + + // after the blockItems are swapped, we continue to the + // next blockItems + ++iterFirst; + --iterLast; + } + + // creating new nodes + m_nodes.push_back({.first = m_nodes[nodeIdx].first, .last = iterLast}); + m_nodes.push_back({.first = iterFirst, .last = m_nodes[nodeIdx].last}); + + // split blocks has to refer to the new nodes + m_blocks[i].nodeIdx = m_nodes.size() - 2; + m_blocks.push_back({.nodeIdx = m_nodes.size() - 1}); + + // 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.push_back({.former = i, .created = newBlockIdx, + .oldNodeIdx = nodeIdx}); + ++newBlockIdx; + } + return split; +} + +// debugging function which allows us to print text representation of +// the partition +std::ostream& operator<<(std::ostream& os, const Partition& p) +{ + std::string result = std::string(); + result += "NUM OF STATES: " + std::to_string(p.numOfStates()) + "\n"; + result += "NUM OF BLOCKS: " + std::to_string(p.numOfBlocks()) + "\n"; + result += "NUM OF NODES: " + std::to_string(p.numOfNodes()) + "\n"; + result += "\n"; + + result += "BLOCKS:\n"; + size_t numOfBlocks = p.numOfBlocks(); + for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) + { + result += std::to_string(blockIdx) + ": "; + Node node = p.m_nodes[p.getNodeIdxFromBlockIdx(blockIdx)]; + for(size_t blockItemIdx = node.first; + blockItemIdx <= node.last; ++blockItemIdx) + { + result += std::to_string(p.m_blockItems[blockItemIdx].state) + + " "; + } + result += "\n"; + } + result += "\n"; + + result += "NODES:\n"; + size_t numOfNodes = p.numOfNodes(); + for(size_t nodeIdx = 0; nodeIdx < numOfNodes; ++nodeIdx) + { + result += std::to_string(nodeIdx) + ": "; + Node node = p.m_nodes[nodeIdx]; + for(size_t blockItemIdx = node.first; + blockItemIdx <= node.last; ++blockItemIdx) + { + result += std::to_string(p.m_blockItems[blockItemIdx].state) + + " "; + } + result += "\n"; + } + result += "\n"; + + return os << result; + +} + + +/************************************************************************ +* +* +* EXTENDABLE SQUARE MATRIX +* +* +* (RELATIONS AND COUNTERS) +* +* +*************************************************************************/ + +/** + * ExtendableSquareMatrix + * + * @brief interface for extendable square matrix implementations + * + * Square matrix "n x n" which can be extended to "(n+1) x (n+1)" matrix + * if n is less than the maximal capacity. Such structure allows us to + * represent binary relations over carrier set with n elements and adjust + * it to n+1 elements whenever a new element of the carrier set is created + * (for example when we represent relation over partition and a block of + * partition is split in two) or matrices of counters etc. + * + * This abstract structure declares methods for accessing elements of matrix, + * assigning to the elements of matrix and extending the matrix by one row + * and one column (changing the size). + * It defines attributes for maximal capacity (which cannot be changed) and + * current size. + * It does not define the data structure for storing data. Each subclass + * which inherits from this abstract structure should: + * - contain the storage for data of datatype T which represents n x n matrix + * - implement methods set, get and extend + * Then, the ExtendableSquareMatrix can be used independently of the inner + * representation of the matrix. Therefore, one can dynamically choose from + * various of implementations depending on the situation. + * + * Note that in context of an n x n matrix, this implementation uses the word + * 'size' to refer to the number n (number of rows or columns). The word + * 'capacity' corresponds to the maximal allowed size (maximal number + * of rows or columns). + * +**/ + + +template +struct ExtendableSquareMatrix +{ + protected: + + // number of rows (or columns) of the current square matrix + size_t m_size{0}; + + // maximal allowed number of rows (or columns) of the square matrix + size_t m_capacity{0}; + + public: + + // getters + inline size_t size(void) const { return m_size; } + inline size_t capacity(void) const { return m_capacity; } + + // virtual functions which will be implemented in the subclasses + // according to the concrete implementation of the matrix + virtual inline void set(size_t i, size_t j, T value = T()) = 0; + virtual inline T get(size_t i, size_t j) const = 0; + virtual inline void extend(T placeholder = T()) = 0; + + virtual ~ExtendableSquareMatrix() = default; + +}; + +/** + * CascadeSquareMatrix + * + * @brief Linearized square matrix implemented using single vector of + * elements which stores data in some kind of "cascading" way + * + * This implementation tries to avoid + * - moving the whole matrix when it is extended + * - allocation of unneccessary data cells + * - violation of data locality + * + * The matrix is represented as a single vector of a type T. Initially, + * the maximal possible capacity is given to the constructor. It reserves + * 'capacity * capacity' data cells for the vector (in the constant time O(1)) + * without need to allocate anything. + * When the matrix is extended, additional (size * 2) + 1 elements of the + * vectors are allocated. We matrix is referred in some kind of "cascading" way + * as follows: + * + * Each number in the matrix corresponds to the order of accessing that element + * using this "cascading traversal". + * + * MATRIX: + * ----------------- + * | 0 | 3 | 8 | 15| + * ----------------- + * | 1 | 2 | 7 | 14| + * ----------------- + * | 4 | 5 | 6 | 13| + * ----------------- + * | 9 | 10| 11| 12| + * ----------------- + * + * VECTOR: + * ----------------------------------------------------------------------- + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + * ----------------------------------------------------------------------- + * + * The data cell matrix[i][j] could be accessed using the formula + * vector[i >= j ? i * i + j : j * j + 2 * j - i]. + * + * Using this approach, there is no need to allocate unnecessary data cells + * when extending n x n matrix to the (n+1) x (n+1) matrix (in contrast with + * using row or column traversal). + * Since 'capacity * capacity' data cells were reserved, the vector won't ever + * be moved in the memory due to the extending. However, it requieres to + * reserve a lot of memory space when the capacity is a huge number. + * The data locality won't be violated since all of the elements will be stored + * as a contiguous vector. + * +**/ +template +struct CascadeSquareMatrix : public ExtendableSquareMatrix +{ + private: + + // data are stored in a single vector + std::vector data{}; + + public: + + // constructors + CascadeSquareMatrix(size_t maxRows, size_t initRows = 0); + + // implemented virtual functions + inline void set(size_t i, size_t j, T value) override; + inline T get(size_t i, size_t j) const override; + inline void extend(T placeholder = T()) override; + +}; + +/** +* @brief creates a Cascade square matrix +* @param maxRows capacity of the square matrix +* @param initRows initial size of the square matrix +*/ +template +CascadeSquareMatrix::CascadeSquareMatrix + (size_t maxRows, size_t initRows) +{ + assert(initRows <= maxRows && + "Initial size of the matrix cannot be bigger than the capacity"); + + this->m_capacity = maxRows; + data.reserve(this->m_capacity * this->m_capacity); + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < initRows; ++i) {extend();} +} + +/** +* @brief assings a value to the Cascade square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @param value value to be assigned to the square matrix data cell +*/ +template +inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + // accessing the matrix in the cascading way + data[i >= j ? i * i + j : j * j + 2 * j - i] = value; +} + +/** +* @brief returns a value of the Cascade square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @return value found in the square matrix data cell +*/ +template +inline T CascadeSquareMatrix::get(size_t i, size_t j) const +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + // accessing the matrix in the cascading way + return data[i >= j ? i * i + j : j * j + 2 * j - i]; +} + +/** +* @brief extends the Cascade square matrix by a new row and column +* @param placeholder a value which will be assigned to all the new data cells +*/ +template +inline void CascadeSquareMatrix::extend(T placeholder) +{ + assert(this->m_size < this->m_capacity + && "Matrix cannot be extened anymore"); + + // allocation of 2 * size + 1 new data cells + data.insert(data.end(), 2 * this->m_size + 1, placeholder); + + // the size inceases + ++this->m_size; +} + +/** + * DynamicSquareMatrix + * + * @brief Dynamic square matrix implemented using vector of vectors + * of the type T + * + * This implementation tries to avoid + * - allocation or reservation of data cells which won't ever be used + * + * The matrix is represented as a vector of vectors of the type T. It is + * extended dynamically without any need to allocate or reserve any unnecessary + * memory space before it is used. + * However, the data locality is not ensured. Moreover, if the matrix is + * extended, it could possibly be moved in the memory. +**/ +template +struct DynamicSquareMatrix : public ExtendableSquareMatrix +{ + private: + + // data are stored in a single vector + std::vector> data{}; + + public: + + // constructors + DynamicSquareMatrix(size_t maxRows, size_t initRows = 0); + + // implemented virtual functions + inline void set(size_t i, size_t j, T value) override; + inline T get(size_t i, size_t j) const override; + void extend(T placeholder = T()) override; + +}; + +/** +* @brief creates a Dynamic square matrix +* @param maxRows capacity of the square matrix +* @param initRows initial size of the square matrix +*/ +template +DynamicSquareMatrix::DynamicSquareMatrix + (size_t maxRows, size_t initRows) +{ + assert(initRows <= maxRows && + "Initial size of the matrix cannot be bigger than the capacity"); + + this->m_capacity = maxRows; + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < initRows; ++i) {extend();} +} + +/** +* @brief assings a value to the Dynamic square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @param value value to be assigned to the square matrix data cell +*/ +template +inline T DynamicSquareMatrix::get(size_t i, size_t j) const +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + return data[i][j]; +} + +/** +* @brief returns a value of the Dynamic square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @return value found in the square matrix data cell +*/ +template +inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + data[i][j] = value; +} + +/** +* @brief extends the Dynamic square matrix by a new row and column +* @param placeholder a value which will be assigned to all the new data cells +*/ +template +void DynamicSquareMatrix::extend(T placeholder) +{ + assert(this->m_size < this->m_capacity + && "Matrix cannot be extened anymore"); + + // creating a new column + for(size_t i = 0; i < this->m_size; ++i) + { + data[i].push_back(placeholder); + } + + // creating a new row + data.push_back(std::vector()); + ++this->m_size; + data.back().insert(data.back().end(), this->m_size, placeholder); +} + +/** + * HashedSquareMatrix + * + * @brief Hashed square matrix implemented using unordered hash map + * + * The matrix is represented as a unordered hash map of the type T indexed + * by the size_t type. It is referred as in context of row-traversal of the + * matrix. To access matrix[i][j], we use map[i * capacity + j]. +**/ +template +struct HashedSquareMatrix : public ExtendableSquareMatrix +{ + private: + + // data are stored in a hashmap + mutable std::unordered_map data + {std::unordered_map()}; + + public: + + // constructors + HashedSquareMatrix(size_t maxRows, size_t initRows = 0); + + // implemented virtual functions + inline void set(size_t i, size_t j, T value) override; + inline T get(size_t i, size_t j) const override; + inline void extend(T placeholder = T()) override; + +}; + +/** +* @brief creates a Hashed square matrix +* @param maxRows capacity of the square matrix +* @param initRows initial size of the square matrix +*/ +template +HashedSquareMatrix::HashedSquareMatrix + (size_t maxRows, size_t initRows) +{ + assert(initRows <= maxRows && + "Initial size of the matrix cannot be bigger than the capacity"); + + this->m_capacity = maxRows; + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < initRows; ++i) {extend();} +} + +/** +* @brief assings a value to the Hashed square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @param value value to be assigned to the square matrix data cell +*/ +template +inline void HashedSquareMatrix::set(size_t i, size_t j, T value) +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + // accessing the hashmap using row matrix traversal + data[i * this->m_capacity + j] = value; +} + +/** +* @brief returns a value of the Hashed square matrix +* @param i row of the square matrix +* @param j column of the square matrix +* @return value found in the square matrix data cell +*/ +template +inline T HashedSquareMatrix::get(size_t i, size_t j) const +{ + assert(i < this->m_size && "Nonexisting row cannot be accessed"); + assert(j < this->m_size && "Nonexisting column cannot be accessed"); + + // accessing the hashmap using row matrix traversal + return data[i * this->m_capacity + j]; +} + +/** +* @brief extends the Hashed square matrix by a new row and column +* @param placeholder a value which will be assigned to all the new data cells +*/ +template +inline void HashedSquareMatrix::extend(T placeholder) +{ + assert(this->m_size < this->m_capacity + && "Matrix cannot be extened anymore"); + + // creating a new row and column + for(size_t i = 0; i < this->m_size; ++i) + { + data[this->m_size * this->m_capacity + i] = placeholder; + data[i * this->m_capacity + this->m_size] = placeholder; + } + data[this->m_size * this->m_capacity + this->m_size] = placeholder; + + // increasing size + ++this->m_size; +} + +// debugging function which allows us to print text representation of +// the Extendable square matrix +template +std::ostream& operator<<(std::ostream& os, + const ExtendableSquareMatrix& matrix) +{ + size_t size = matrix.size(); + size_t capacity = matrix.capacity(); + std::string result = "\nSIZE: " + std::to_string(size) + "\n"; + result += "CAPACITY: " + std::to_string(capacity) + "\n"; + result += "MATRIX:\n"; + for(size_t i = 0; i < size; ++i) + { + for(size_t j = 0; j < size; ++j) + { + result += std::to_string(matrix.get(i, j)) + " "; + } + result += "\n"; + } + return os << result; +} + +/** This function checks whether the given matrix is reflexive. In this +* context, the matrix is reflexive iff none of the elements od the main +* diagonal is the zero element of the type T +* @brief checks whether the Extendable square matrix is reflexive +* @param matrix input matrix to be checked +* @return true iff the input matrix is reflexive +*/ +template +bool isReflexive(ExtendableSquareMatrix *matrix) +{ + size_t size = matrix->size(); + for(size_t i = 0; i < size; ++i) + { + if(!matrix->get(i, i)) { return false; } + } + return true; +} + +/** This function checks whether the given matrix is antisymetric. In this +* context, the matrix is antisymetric iff there are no indices i, j +* where i != j and both matrix[i][j], matrix[j][i] contain nonzero elementes +* of the type T +* @brief checks whether the Extendable square matrix is antisymetric +* @param matrix input matrix to be checked +* @return true iff the input matrix is antisymetric +*/ +template +bool isAntisymetric(ExtendableSquareMatrix *matrix) +{ + size_t size = matrix->size(); + for(size_t i = 0; i < size; ++i) + { + for(size_t j = 0; j < size; ++j) + { + if(i == j) { continue; } + if(matrix->get(i, j) && matrix->get(j, i)) { return false; } + } + } + return true; +} + +/** This function checks whether the given matrix is transitive. In this +* context, the matrix is transitive iff it holds that the input matrix +* casted to the matrix of booleans (false for zero values of type T, otherwise +* true) remains the same if it is multiplied by itself. +* @brief checks whether the Extendable square matrix is transitive +* @param matrix input matrix to be checked +* @return true iff the input matrix is transitive +*/ +template +bool isTransitive(ExtendableSquareMatrix *matrix) +{ + size_t size = matrix->size(); + for(size_t i = 0; i < size; ++i) + { + for(size_t j = 0; j < size; ++j) + { + bool found = false; + for(size_t k = 0; k < size; ++k) + { + if(matrix->get(i, k) && matrix->get(k, j)) + { + found = true; + break; + } + } + if(!found == static_cast(matrix->get(i, j))) { return false; } + } + } + return true; +} + +} + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c7a0a17e6..838116b4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(tests ord-vector.cc sparse-set.cc + partition-relation-pair.cc synchronized-iterator.cc main.cc alphabet.cc diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc new file mode 100644 index 000000000..b29a9853c --- /dev/null +++ b/tests/partition-relation-pair.cc @@ -0,0 +1,296 @@ +#include + +#include "mata/utils/partition-relation-pair.hh" + +using namespace mata::utils; + +TEST_CASE("mata::utils::Partition") { + + SECTION("Create a simple partition with 1 block") { + Partition p{10}; + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 1); + CHECK(p.numOfNodes() == 1); + CHECK(p.inSameBlock({})); + CHECK(p.inSameBlock({0})); + CHECK(p.inSameBlock(0, 1)); + CHECK(p.inSameBlock(1, 8)); + CHECK(p.inSameBlock({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + for(size_t i = 0; i < 10; ++i) + { + CHECK(p.getBlockItemIdxFromState(i) == i); + CHECK(p.getBlockIdxFromState(i) == 0); + CHECK(p.getNodeIdxFromState(i) == 0); + CHECK(p.getBlockItem(i).state == i); + CHECK(p.getBlockItem(i).blockIdx == 0); + CHECK(p.getNodeIdxFromBlockItemIdx(i) == 0); + } + CHECK(p.getReprFromBlockIdx(0).state == 0); + CHECK(p.getReprFromBlockIdx(0).blockIdx == 0); + CHECK(p.getReprFromNodeIdx(0).state == 0); + CHECK(p.getReprFromNodeIdx(0).blockIdx == 0); + CHECK(p.getNode(0).first == 0); + CHECK(p.getNode(0).last == 9); + CHECK(p.getBlock(0).nodeIdx == 0); + } + + SECTION("Create a simple partition with 2 blocks") { + Partition p{10, {{0, 5, 8}}}; + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 2); + CHECK(p.numOfNodes() == 2); + CHECK(p.inSameBlock({})); + CHECK(p.inSameBlock({0})); + CHECK(p.inSameBlock(0, 5)); + CHECK(p.inSameBlock(5, 8)); + CHECK(!p.inSameBlock(6, 5)); + CHECK(p.inSameBlock({0, 5, 8})); + CHECK(p.inSameBlock({1, 2, 3, 4, 6, 7, 9})); + CHECK(!p.inSameBlock({1, 2, 3, 4, 5, 7, 9})); + + CHECK(p.getBlockItemIdxFromState(0) == 0); + CHECK(p.getBlockItem(0).state == 0); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getNodeIdxFromState(0) == 0); + CHECK(p.getBlockItem(0).blockIdx == 0); + CHECK(p.getNodeIdxFromBlockItemIdx(0) == 0); + + CHECK(p.getBlockItemIdxFromState(1) == 3); + CHECK(p.getBlockItem(3).state == 1); + CHECK(p.getBlockIdxFromState(1) == 1); + CHECK(p.getNodeIdxFromState(1) == 1); + CHECK(p.getBlockItem(3).blockIdx == 1); + CHECK(p.getNodeIdxFromBlockItemIdx(3) == 1); + + CHECK(p.getReprFromBlockIdx(0).state == 0); + CHECK(p.getReprFromBlockIdx(1).state == 1); + CHECK(p.getReprFromBlockIdx(0).blockIdx == 0); + CHECK(p.getReprFromBlockIdx(1).blockIdx == 1); + CHECK(p.getReprFromNodeIdx(0).state == 0); + CHECK(p.getReprFromNodeIdx(0).blockIdx == 0); + CHECK(p.getReprFromNodeIdx(1).state == 1); + CHECK(p.getReprFromNodeIdx(1).blockIdx == 1); + CHECK(p.getNode(0).first == 0); + CHECK(p.getNode(0).last == 2); + CHECK(p.getNode(1).first == 3); + CHECK(p.getNode(1).last == 9); + CHECK(p.getBlock(0).nodeIdx == 0); + CHECK(p.getBlock(1).nodeIdx == 1); + } + + + SECTION("Create a simple partition with 3 blocks") { + Partition p{6, {{0}, {1, 2}}}; + CHECK(p.numOfStates() == 6); + CHECK(p.numOfBlockItems() == 6); + CHECK(p.numOfBlocks() == 3); + CHECK(p.numOfNodes() == 3); + CHECK(p.inSameBlock({})); + CHECK(p.inSameBlock({0})); + CHECK(p.inSameBlock(3, 5)); + CHECK(p.inSameBlock(1, 2)); + CHECK(!p.inSameBlock(1, 4)); + CHECK(p.inSameBlock({3, 4, 5})); + CHECK(!p.inSameBlock({2, 3, 4, 5})); + for(size_t i = 0; i <= 5; ++i) + { + CHECK(p.getBlockItemIdxFromState(i) == i); + CHECK(p.getBlockItem(i).state == i); + } + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getNodeIdxFromState(0) == 0); + CHECK(p.getBlockItem(0).blockIdx == 0); + CHECK(p.getNodeIdxFromBlockItemIdx(0) == 0); + CHECK(p.getBlockIdxFromState(1) == 1); + CHECK(p.getNodeIdxFromState(1) == 1); + CHECK(p.getBlockItem(1).blockIdx == 1); + CHECK(p.getNodeIdxFromBlockItemIdx(1) == 1); + CHECK(p.getReprFromBlockIdx(0).state == 0); + CHECK(p.getReprFromBlockIdx(1).state == 1); + CHECK(p.getReprFromBlockIdx(2).state == 3); + CHECK(p.getReprFromNodeIdx(0).state == 0); + CHECK(p.getReprFromNodeIdx(1).state == 1); + CHECK(p.getReprFromNodeIdx(2).state == 3); + CHECK(p.getNode(0).first == 0); + CHECK(p.getNode(0).last == 0); + CHECK(p.getNode(1).first == 1); + CHECK(p.getNode(1).last == 2); + CHECK(p.getNode(2).first == 3); + CHECK(p.getNode(2).last == 5); + CHECK(p.getBlock(0).nodeIdx == 0); + CHECK(p.getBlock(1).nodeIdx == 1); + CHECK(p.getBlock(2).nodeIdx == 2); + } + + SECTION("Splitting blocks") { + Partition p{10}; + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 1); + CHECK(p.numOfNodes() == 1); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 0); + CHECK(p.inSameBlock({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + p.splitBlocks({0, 1, 2, 3, 4}); + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 2); + CHECK(p.numOfNodes() == 3); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 1); + CHECK(p.inSameBlock({0, 1, 2, 3, 4})); + CHECK(p.inSameBlock({5, 6, 7, 8, 9})); + p.splitBlocks({0, 1, 2, 5, 6, 7}); + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 4); + CHECK(p.numOfNodes() == 7); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 3); + CHECK(p.inSameBlock({0, 1, 2})); + CHECK(p.inSameBlock({3, 4})); + CHECK(p.inSameBlock({5, 6, 7})); + CHECK(p.inSameBlock({8, 9})); + p.splitBlocks({0, 3, 5, 8}); + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 8); + CHECK(p.numOfNodes() == 15); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 7); + CHECK(p.inSameBlock({0})); + CHECK(p.inSameBlock({1, 2})); + CHECK(p.inSameBlock({3})); + CHECK(p.inSameBlock({4})); + CHECK(p.inSameBlock({5})); + CHECK(p.inSameBlock({6, 7})); + CHECK(p.inSameBlock({8})); + CHECK(p.inSameBlock({9})); + p.splitBlocks({1, 6}); + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 10); + CHECK(p.numOfNodes() == 19); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 7); + p.splitBlocks({0, 2, 4, 6, 8}); + CHECK(p.numOfStates() == 10); + CHECK(p.numOfBlockItems() == 10); + CHECK(p.numOfBlocks() == 10); + CHECK(p.numOfNodes() == 19); + CHECK(p.getBlockIdxFromState(0) == 0); + CHECK(p.getBlockIdxFromState(9) == 7); + } + +} + + +TEST_CASE("mata::utils::ExtendableSquareMatrix") { + + SECTION("CascadeSquareMatrix") { + + ExtendableSquareMatrix *e = + new CascadeSquareMatrix(5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(0, 0, 1); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(3, 1, 1); + e->set(1, 2, 1); + std::cout << (*e); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(!isTransitive(e)); + delete e; + } + + SECTION("DynamicSquareMatrix") { + + ExtendableSquareMatrix *e = + new DynamicSquareMatrix(5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(0, 0, 1); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(3, 1, 1); + e->set(1, 2, 1); + std::cout << (*e); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(!isTransitive(e)); + delete e; + } + + SECTION("HashedSquareMatrix") { + + ExtendableSquareMatrix *e = + new HashedSquareMatrix(5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(0, 0, 1); + CHECK(!isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(isTransitive(e)); + e->set(3, 1, 1); + e->set(1, 2, 1); + std::cout << (*e); + CHECK(isReflexive(e)); + CHECK(isAntisymetric(e)); + CHECK(!isTransitive(e)); + delete e; + } +} From e2f18a95f9faefbb684d35aee8f1c8356c520c67 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sun, 17 Mar 2024 06:02:41 +0100 Subject: [PATCH 02/34] comments modified, mistyped characters fixed --- include/mata/utils/partition-relation-pair.hh | 465 ++++++++++-------- tests/partition-relation-pair.cc | 36 +- 2 files changed, 268 insertions(+), 233 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 24ebb3d1b..c5506c7c0 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -3,23 +3,25 @@ * and partition-relation pair. * * This file contains definitions of partition, extendable square - * matrix and operations which allow us to manipulate with them. + * matrix, partition-relation pair and operations which allow us + * to manipulate with them. * * Description: * * A partition-relation pair is a tuple (P, Rel). It is an efficient * representation of a preorder/quasiorder R, which is a reflexive and * transitive binary relation. - * In this context, we consider a carrier set S which contains interval - * of contiguous natural numbers from 0 to |S|-1. - * These numbers are called states. + * In this context, we consider a carrier set S which contains all + * natural numbers from 0 to |S|-1. These numbers are called states. * P is a partition of S which corresponds to an equivalence relation * induced by the preorder R. + * Rel is a partial order over P. * Thus, (P, Rel) corresponds to a preorder relation R over states S. * * This file provides implementation of a partition P and defines the * ExtendableSquareMatrix structure which can be used to represent - * the binary relation Rel. It can be combined to represent the preorder R. + * the binary relation Rel. + * These structures can be combined to represent the preorder R. * * @author Tomáš Kocourek */ @@ -66,20 +68,20 @@ using SplitPair = struct SplitPair * @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 a natural number from the contiguous + * context, the term 'state' refers to any natural number from the * interval <0, |S|-1>. * * STATES: * This representation works with the vector of indices 'm_states' - * with the constant size |S|. Each state is represented by the index - * of that vector so we can refer to a state in constant time using + * 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 * m_states[state]. * * BLOCK ITEMS: - * The memory cell m_states[state] contains an index to the corresponding + * The memory cell m_states[state] contains a corresponding index to the * 'm_blockItems' vector. The vector 'm_blockItems' has the constant size |S|. * Each BlockItem contains an index of the corresponding state which means - * that states and block items are bijectively mapped. In addition, + * 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 @@ -89,12 +91,15 @@ using SplitPair = struct SplitPair * BLOCKS: * The blocks themselves are represented by the vector 'm_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 contains only an index of its + * its index 0 <= i < |P|. The block can by accessed by its index using + * m_blocks[blockIdx]. The block contains only 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 - * split, we create a new block 'C' and modify the former block 'B'. - * The former block is thus no more represented in the 'm_blocks' vector. + * split in two pieces 'B1' and 'B2', we create a brand new block 'B2' + * and modify the former block 'B' such that it will correspond to its + * subblock 'B1'. The former block 'B' is thus not represented + * in the 'm_blocks' vector anymore since 'B1' takes over its identity. * * NODES: * Each node represents a current block or a block which has been split before. @@ -103,67 +108,68 @@ using SplitPair = struct SplitPair * In context of nodes which represent former blocks, no block contains their * 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. Thus, the + * 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 states included in such + * BlockItems which form a contiguous subvector of BlockItem included in such * node. When a block is split, the corresponding BlockItems are swapped * in situ such that the indices 'first' and 'last' still surround the - * corresponding node. + * corresponding node and both new nodes also point to the contiguous subvector + * of its BlockItems. * * EXAMPLE: - * In the example below, we represent a partition {{0, 1}, {2, 3}}. - * Thus, we have two blocks 0 ({0, 1}) and 1 ({2, 3}). 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, 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. * 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). - * Likewise, the node 2 contains indices 2 (first) and 3 (last) which means that - * the blockItems 2 and 3 surround a contiguous subvector of elements in the + * Likewise, the node 2 contains indices 2 (first) and 4 (last) which means that + * the blockItems 2 and 4 surround a contiguous subvector of elements in the * node 2 (or in the block 1). * Moreover, we also represent the former block which does not exist anymore - * by the node 0 which contains indices 0 (first) and 3 (last) which means that - * the blockItems 0 and 3 surround a contiguous subvector of elements in the - * node 0. Thus, we know that there had been a block {0, 1, 2, 3} before it has - * been split to obtain blocks {0, 1} and {2, 3}. + * by the node 0 which contains indices 0 (first) and 4 (last) which means that + * 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}. * * - * 0 1 2 3 - * ------- ------- ------- ------- - * | 0 | 1 | 3 | 2 | m_states - * ------- ------- ------- ------- - * ↑ ↑ ↑ ↑ - * | | \ / - * | | X - * | | / \ - * 0 ↓ 1 ↓ 2 ↓ ↓ 3 - * ------- ------- ------- ------- - * | 0 | 1 | 3 | 2 | m_blockItems - * ----→|-------|-------|-------|-------|←------------- - * | | 0 | 0 | 1 | 1 | | - * | ------- ------- ------- ------- | - * | | | | | | - * | 0 ↓ ↓ 1 ↓ ↓ | - * | ------------------------------- | - * | | 1 | 2 | m_blocks | - * | ------------------------------- | - * | | | | - * | 0 1 ↓ 2 ↓ | - * | ------- ------- ------- | - * -----| 0 | 0 | 2 | m_nodes | - * |-------|-------|-------| | - * | 3 | 1 | 3 | | - * ------- ------- ------- | - * | | - * ------------------------------------------- + * 0 1 2 3 4 + * ------- ------- ------- ------- ------- + * | 0 | 2 | 1 | 4 | 3 | m_states + * ------- ------- ------- ------- ------- + * ↑ ↑ ↑ ↑ ↑ + * | \ / \ / + * | X X + * | / \ / \ + * 0 ↓ 1 ↓ ↓ 2 3 ↓ ↓ 4 + * ------- ------- ------- ------- ------- + * | 0 | 2 | 1 | 4 | 3 | m_blockItems + * --→→→|-------|-------|-------|-------|--------←←←------------ + * | | 0 | 0 | 1 | 1 | 1 | | + * | ------- ------- ------- ------- ------- | + * | | | | | | | + * | 0 ↓ ↓ 1 ↓ ↓ ↓ | + * | ---------------------------------------- | + * | | 1 | 2 | m_blocks | + * | ---------------------------------------- | + * | | | | + * | 0 1 ↓ 2 ↓ | + * | ------- ------- ------- | + * -----| 0 | 0 | 2 | m_nodes | + * |-------|-------|-------| | + * | 4 | 1 | 4 | | + * ------- ------- ------- | + * | | + * ---------------------------------------------------- * * Using this structure, we can: * - find the block which contains 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 a vector of states A share the same block in O(|A|) - * - iterate through the block P in O(|P|) + * - test whether all states in a vector A share the same block in O(|A|) + * - iterate through the block B in O(|B|) * - 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 @@ -176,15 +182,19 @@ typedef struct Partition /* indices to the m_blockItems vector */ std::vector m_states{}; + /* indices to the m_states and m_blocks vectors */ BlockItems m_blockItems{}; + + /* indices to the m_nodes vector */ Blocks m_blocks{}; + + /* tuples of indices to the m_blockItems vectors */ Nodes m_nodes{}; public: // constructors - Partition(size_t numOfStates, - StateBlocks partition = StateBlocks()); + Partition(size_t numOfStates, StateBlocks partition = StateBlocks()); // sizes of the used vectors inline size_t numOfStates(void) const { return m_states.size(); } @@ -195,152 +205,41 @@ typedef struct Partition // blocks splitting std::vector splitBlocks(std::vector marked); - // basic information about partition + // basic information about the partition inline bool inSameBlock(State first, State second) const; bool inSameBlock(std::vector states) const; std::vector statesInSameBlock(State state) const; - // accessing states, blockItems, blocks, nodes + // accessing blockItems, blocks, nodes through indices + inline BlockItem getBlockItem(size_t blockItemIdx) const; + inline Block getBlock(size_t blockIdx) const; + inline Node getNode(size_t nodeIdx) const; - /** - * @brief returns a BlockItem corresponding to the given index - * @param blockItemIdx index of the BlockItem - * @return corresponding BlockItem - */ - inline BlockItem getBlockItem(size_t blockItemIdx) const - { - assert(blockItemIdx < numOfBlockItems() && - "Nonexisting block item index used."); - return m_blockItems[blockItemIdx]; - } - - /** - * @brief returns a block corresponding to the given index - * @param blockIdx index of the block - * @return corresponding block - */ - inline Block getBlock(size_t blockIdx) const - { - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); - return m_blocks[blockIdx]; - } - - /** - * @brief returns a node corresponding to the given index - * @param nodeIdx index of the node - * @return corresponding node - */ - inline Node getNode(size_t nodeIdx) const - { - assert(nodeIdx < numOfNodes() && - "Nonexisting node index used."); - return m_nodes[nodeIdx]; - } - - /** - * @brief returns a block index corresponding to the given state - * @param state given state - * @return corresponding block index - */ - inline size_t getBlockIdxFromState(State state) const - { - assert(state < numOfStates() && - "Nonexisting state name used."); - return m_blockItems[m_states[state]].blockIdx; - } - - /** - * @brief returns a node index corresponding to the given state - * @param state given state - * @return corresponding node index - */ - inline size_t getNodeIdxFromState(State state) const - { - assert(state < numOfStates() && - "Nonexisting state name used."); - return m_blocks[m_blockItems[m_states[state]].blockIdx].nodeIdx; - } - - /** - * @brief returns a BlockItem index corresponding to the given state - * @param state given state - * @return corresponding BlockItem index - */ - inline size_t getBlockItemIdxFromState(State state) const - { - assert(state < numOfStates() && - "Nonexisting state name used."); - return m_states[state]; - } - - /** - * @brief returns a Node index corresponding to the given BlockItem index - * @param blockItemIdx BlockItem index - * @return corresponding Node index - */ - inline size_t getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const - { - assert(blockItemIdx < numOfBlockItems() && - "Nonexisting BlockItem index used."); - return m_blocks[m_blockItems[blockItemIdx].blockIdx].nodeIdx; - } - - /** - * @brief returns a node index corresponding to the given block index - * @param blockIdx given block index - * @return corresponding node index - */ - inline size_t getNodeIdxFromBlockIdx(size_t blockIdx) const - { - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); - return m_blocks[blockIdx].nodeIdx; - } - - /** Get a representant from the block index - * @brief returns a blockItem corresponding to the given block index - * @param blockIdx given block index - * @return first blockItem corresponding to the given block index - */ - inline BlockItem getReprFromBlockIdx(size_t blockIdx) const - { - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); - return m_blockItems[m_nodes[m_blocks[blockIdx].nodeIdx].first]; - } - - /** Get a representant from the node index - * @brief returns a blockItem corresponding to the given node index - * @param nodeIdx given node index - * @return first blockItem corresponding to the given node index - */ - inline BlockItem getReprFromNodeIdx(size_t nodeIdx) const - { - assert(nodeIdx < numOfNodes() && - "Nonexisting node index used."); - return m_blockItems[m_nodes[nodeIdx].first]; - } - - /** - * @brief converts the partition to the vector of vectors of states - * @return vector of vectors of states - */ - StateBlocks partition(void); + // refering between blockItems, blocks, nodes using indices + inline size_t getBlockIdxFromState(State state) const; + inline size_t getNodeIdxFromState(State state) const; + inline size_t getBlockItemIdxFromState(State state) const; + inline size_t getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const; + inline size_t getNodeIdxFromBlockIdx(size_t blockIdx) const; + inline size_t getReprIdxFromBlockIdx(size_t blockIdx) const; + inline size_t getReprIdxFromNodeIdx(size_t nodeIdx) const; - // debug + // converts the partition to the vector of vectors of states + StateBlocks partition(void); + // debug friend std::ostream& operator<<(std::ostream& os, const Partition& p); -} Partition; +} Partition; // Partition /** Constructor of the partition structure. 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. -* The partition can be initialized in linear time using initial partition -* represented as a vector of vectors of states. +* 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 @@ -348,6 +247,8 @@ typedef struct Partition * - if there is an empty partition class, the function 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 +* to the same block * @brief constructs the partition * @param numOfStates cardinality of the carrier set * @param partition optional initial partition in the form of vectors of @@ -355,16 +256,19 @@ typedef struct Partition */ Partition::Partition(size_t numOfStates, StateBlocks partition) { - // reserving memory space to avoid moving extended vector + // reserving memory space to avoid moving extended vectors m_states.reserve(numOfStates); m_blockItems.reserve(numOfStates); m_blocks.reserve(numOfStates); m_nodes.reserve(2 * numOfStates - 1); // this vector says whether the given state has been already seen - // to detect duplicates and to detect unused states + // in the given initial partition to detect duplicates + // and to detect unused states std::vector used; used.insert(used.end(), numOfStates, false); + + // initialization of the m_states vector m_states.insert(m_states.end(), numOfStates, 0); // creating partition using given initial vector of vectors @@ -404,20 +308,26 @@ Partition::Partition(size_t numOfStates, StateBlocks partition) } // we need to detect whether there is a state which has not be used - // to create a additional partition block + // to create an additional partition block bool allStatesUsed = true; + + // first and last unused states will surround a contiguous subvector + // of BlockItems State first = 0; State last = 0; // iterating through the vector of flags saying which states has been seen for(State state = 0; state < numOfStates; ++state) { + // if a state has been already seen and processed, + // there is no need to add it to the additional block if(used[state]) { continue; } - // if there is an unused state, we need to create an additional block + // if there is at least one unused state, we need to + // create an additional block if(allStatesUsed) { allStatesUsed = false; @@ -439,12 +349,134 @@ Partition::Partition(size_t numOfStates, StateBlocks partition) } } + /** -* @brief tests whether thw two given states correspond +* @brief returns a BlockItem corresponding to the given index +* @param blockItemIdx index of the BlockItem +* @return corresponding BlockItem +*/ +inline BlockItem Partition::getBlockItem(size_t blockItemIdx) const +{ + assert(blockItemIdx < numOfBlockItems() && + "Nonexisting block item index used."); + return m_blockItems[blockItemIdx]; +} + +/** +* @brief returns a block corresponding to the given index +* @param blockIdx index of the block +* @return corresponding block +*/ +inline Block Partition::getBlock(size_t blockIdx) const +{ + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_blocks[blockIdx]; +} + +/** +* @brief returns a node corresponding to the given index +* @param nodeIdx index of the node +* @return corresponding node +*/ +inline Node Partition::getNode(size_t nodeIdx) const +{ + assert(nodeIdx < numOfNodes() && + "Nonexisting node index used."); + return m_nodes[nodeIdx]; +} + +/** +* @brief returns a block index corresponding to the given state +* @param state given state +* @return corresponding block index +*/ +inline size_t Partition::getBlockIdxFromState(State state) const +{ + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_blockItems[m_states[state]].blockIdx; +} + +/** +* @brief returns a node index corresponding to the given state +* @param state given state +* @return corresponding node index +*/ +inline size_t Partition::getNodeIdxFromState(State state) const +{ + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_blocks[m_blockItems[m_states[state]].blockIdx].nodeIdx; +} + +/** +* @brief returns a BlockItem index corresponding to the given state +* @param state given state +* @return corresponding BlockItem index +*/ +inline size_t Partition::getBlockItemIdxFromState(State state) const +{ + assert(state < numOfStates() && + "Nonexisting state name used."); + return m_states[state]; +} + +/** +* @brief returns a Node index corresponding to the given BlockItem index +* @param blockItemIdx BlockItem index +* @return corresponding node index +*/ +inline size_t Partition::getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const +{ + assert(blockItemIdx < numOfBlockItems() && + "Nonexisting BlockItem index used."); + return m_blocks[m_blockItems[blockItemIdx].blockIdx].nodeIdx; +} + +/** +* @brief returns a node index corresponding to the given block index +* @param blockIdx given block index +* @return corresponding node index +*/ +inline size_t Partition::getNodeIdxFromBlockIdx(size_t blockIdx) const +{ + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_blocks[blockIdx].nodeIdx; +} + +/** Get a representant from the block index +* @brief returns the first blockItem index corresponding to the given +* block index +* @param blockIdx given block index +* @return first blockItem index corresponding to the given block index +*/ +inline size_t Partition::getReprIdxFromBlockIdx(size_t blockIdx) const +{ + assert(blockIdx < numOfBlocks() && + "Nonexisting block index used."); + return m_nodes[m_blocks[blockIdx].nodeIdx].first; +} + +/** Get a representant from the node index +* @brief returns the first blockItem index corresponding to the given node index +* @param nodeIdx given node index +* @return first blockItem index corresponding to the given node index +*/ +inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const +{ + assert(nodeIdx < numOfNodes() && + "Nonexisting node index used."); + return m_nodes[nodeIdx].first; +} + +/** +* @brief tests whether the two given states correspond * to the same partition block * @param first first state to be checked * @param second second state to be checked -* @param true iff both given states belong to the same partition block +* @return true iff both given states belong to the same partition block */ inline bool Partition::inSameBlock(State first, State second) const { @@ -512,7 +544,7 @@ StateBlocks Partition::partition(void) /** Splitting the blocks of existing partition. According to the input * vector of states 'marked', there will be two types of states - marked -* and unmarked. The partition will be split as follows: +* and unmarked ones. The partition will be split as follows: * - if there is a block whose all elements are marked, the block remains * unchanged * - if there is a block whose all elements are unmarked, the block remains @@ -534,7 +566,7 @@ StateBlocks Partition::partition(void) * will still describe a valid contiguous interval of natural numbers . * * There are several troubles which can possiby occur: -* - if an unexisting state is used, the function detects it and fails +* - 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 * @@ -547,11 +579,11 @@ StateBlocks Partition::partition(void) * * @brief splits blocks of the partition * @param marked marked states which influence splitting -* @return vector of SplitPair which contain information about split blocks +* @return vector of SplitPair which contains information about split blocks */ std::vector Partition::splitBlocks(std::vector marked) { - // the vector which will be returned as a result + // the vector which will be returned as the result std::vector split{}; // if there is no marked state, no block could be split @@ -595,13 +627,14 @@ std::vector Partition::splitBlocks(std::vector marked) size_t iterLast = getNode(nodeIdx).last; size_t blockSize = iterLast - iterFirst + 1; - // if all states of the given block has been marked, it + // if all states of the processed block have been marked, the block // won't be split if(usedBlocks[i] >= blockSize) { continue; } // choosing the strategy of swapping BlocksItems such that // the representant of split block keeps its position - bool reprMarked = usedStates[getReprFromNodeIdx(nodeIdx).state]; + bool reprMarked = usedStates + [getBlockItem(getReprIdxFromNodeIdx(nodeIdx)).state]; // We access the first and last element of the subvector of BlockItems // which forms processed block. We look for the first unmarked element @@ -625,16 +658,16 @@ std::vector Partition::splitBlocks(std::vector marked) --iterLast; } - // if the used indexes meet, we finish swapping + // if the used indices meet, we finish swapping if(iterFirst > iterLast) { break; } // swapping BlockItems - BlockItem tmp = getBlockItem(iterFirst); + BlockItem swappedBlockItem = getBlockItem(iterFirst); m_blockItems[iterFirst] = getBlockItem(iterLast); - m_blockItems[iterLast] = tmp; + m_blockItems[iterLast] = swappedBlockItem; // since m_states and m_blockItems vectors should be bijectively // mapped, we need to update m_states after swapping two BlockItems @@ -664,6 +697,8 @@ std::vector Partition::splitBlocks(std::vector marked) // correspond to the block which has been split split.push_back({.former = i, .created = newBlockIdx, .oldNodeIdx = nodeIdx}); + + // index of the following block which could be created ++newBlockIdx; } return split; @@ -739,12 +774,12 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) * (for example when we represent relation over partition and a block of * partition is split in two) or matrices of counters etc. * - * This abstract structure declares methods for accessing elements of matrix, - * assigning to the elements of matrix and extending the matrix by one row + * This abstract structure declares methods for accessing elements of the + * matrix, assigning to the cells of matrix and extending the matrix by one row * and one column (changing the size). * It defines attributes for maximal capacity (which cannot be changed) and * current size. - * It does not define the data structure for storing data. Each subclass + * It does not define the data structure for storing data. Each substructure * which inherits from this abstract structure should: * - contain the storage for data of datatype T which represents n x n matrix * - implement methods set, get and extend @@ -777,8 +812,8 @@ struct ExtendableSquareMatrix inline size_t size(void) const { return m_size; } inline size_t capacity(void) const { return m_capacity; } - // virtual functions which will be implemented in the subclasses - // according to the concrete implementation of the matrix + // virtual functions which will be implemented in the substructures + // according to the concrete representation of the matrix virtual inline void set(size_t i, size_t j, T value = T()) = 0; virtual inline T get(size_t i, size_t j) const = 0; virtual inline void extend(T placeholder = T()) = 0; @@ -803,7 +838,7 @@ struct ExtendableSquareMatrix * 'capacity * capacity' data cells for the vector (in the constant time O(1)) * without need to allocate anything. * When the matrix is extended, additional (size * 2) + 1 elements of the - * vectors are allocated. We matrix is referred in some kind of "cascading" way + * vector are allocated. The matrix is traversed in some kind of "cascading" way * as follows: * * Each number in the matrix corresponds to the order of accessing that element @@ -913,17 +948,18 @@ inline T CascadeSquareMatrix::get(size_t i, size_t j) const /** * @brief extends the Cascade square matrix by a new row and column * @param placeholder a value which will be assigned to all the new data cells +* (optional) */ template inline void CascadeSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "Matrix cannot be extened anymore"); + && "The matrix cannot be extened anymore"); // allocation of 2 * size + 1 new data cells data.insert(data.end(), 2 * this->m_size + 1, placeholder); - // the size inceases + // the size increases ++this->m_size; } @@ -1019,7 +1055,7 @@ template void DynamicSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "Matrix cannot be extened anymore"); + && "The matrix cannot be extened anymore"); // creating a new column for(size_t i = 0; i < this->m_size; ++i) @@ -1048,8 +1084,7 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix private: // data are stored in a hashmap - mutable std::unordered_map data - {std::unordered_map()}; + mutable std::unordered_map data{}; public: @@ -1159,7 +1194,7 @@ std::ostream& operator<<(std::ostream& os, } /** This function checks whether the given matrix is reflexive. In this -* context, the matrix is reflexive iff none of the elements od the main +* context, the matrix is reflexive iff none of the elements on the main * diagonal is the zero element of the type T * @brief checks whether the Extendable square matrix is reflexive * @param matrix input matrix to be checked diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index b29a9853c..aa1e36a94 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -26,10 +26,10 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.getBlockItem(i).blockIdx == 0); CHECK(p.getNodeIdxFromBlockItemIdx(i) == 0); } - CHECK(p.getReprFromBlockIdx(0).state == 0); - CHECK(p.getReprFromBlockIdx(0).blockIdx == 0); - CHECK(p.getReprFromNodeIdx(0).state == 0); - CHECK(p.getReprFromNodeIdx(0).blockIdx == 0); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).blockIdx == 0); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).blockIdx == 0); CHECK(p.getNode(0).first == 0); CHECK(p.getNode(0).last == 9); CHECK(p.getBlock(0).nodeIdx == 0); @@ -64,14 +64,14 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.getBlockItem(3).blockIdx == 1); CHECK(p.getNodeIdxFromBlockItemIdx(3) == 1); - CHECK(p.getReprFromBlockIdx(0).state == 0); - CHECK(p.getReprFromBlockIdx(1).state == 1); - CHECK(p.getReprFromBlockIdx(0).blockIdx == 0); - CHECK(p.getReprFromBlockIdx(1).blockIdx == 1); - CHECK(p.getReprFromNodeIdx(0).state == 0); - CHECK(p.getReprFromNodeIdx(0).blockIdx == 0); - CHECK(p.getReprFromNodeIdx(1).state == 1); - CHECK(p.getReprFromNodeIdx(1).blockIdx == 1); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).state == 1); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).blockIdx == 0); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).blockIdx == 1); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).blockIdx == 0); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).state == 1); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).blockIdx == 1); CHECK(p.getNode(0).first == 0); CHECK(p.getNode(0).last == 2); CHECK(p.getNode(1).first == 3); @@ -107,12 +107,12 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.getNodeIdxFromState(1) == 1); CHECK(p.getBlockItem(1).blockIdx == 1); CHECK(p.getNodeIdxFromBlockItemIdx(1) == 1); - CHECK(p.getReprFromBlockIdx(0).state == 0); - CHECK(p.getReprFromBlockIdx(1).state == 1); - CHECK(p.getReprFromBlockIdx(2).state == 3); - CHECK(p.getReprFromNodeIdx(0).state == 0); - CHECK(p.getReprFromNodeIdx(1).state == 1); - CHECK(p.getReprFromNodeIdx(2).state == 3); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).state == 1); + CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(2)).state == 3); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).state == 1); + CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(2)).state == 3); CHECK(p.getNode(0).first == 0); CHECK(p.getNode(0).last == 0); CHECK(p.getNode(1).first == 1); From ce9734e0f5d3d11d80140658b352e9ed8b35ddf7 Mon Sep 17 00:00:00 2001 From: kocotom Date: Tue, 19 Mar 2024 01:15:25 +0100 Subject: [PATCH 03/34] Factory function and copy function for the ExtendableSquaredMatrix added. --- include/mata/utils/partition-relation-pair.hh | 150 +++++++++++++++--- tests/partition-relation-pair.cc | 132 +++++++++------ 2 files changed, 216 insertions(+), 66 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index c5506c7c0..812274343 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -785,7 +785,9 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) * - implement methods set, get and extend * Then, the ExtendableSquareMatrix can be used independently of the inner * representation of the matrix. Therefore, one can dynamically choose from - * various of implementations depending on the situation. + * various of implementations depending on the situation. If any new + * substructure is implemented, one should also modify 'create' and 'copy' + * functions and extend 'MatrixType' enumerator. * * Note that in context of an n x n matrix, this implementation uses the word * 'size' to refer to the number n (number of rows or columns). The word @@ -794,6 +796,7 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) * **/ +using MatrixType = enum MatrixType { None, Cascade, Dynamic, Hashed }; template struct ExtendableSquareMatrix @@ -804,13 +807,18 @@ struct ExtendableSquareMatrix size_t m_size{0}; // maximal allowed number of rows (or columns) of the square matrix - size_t m_capacity{0}; + size_t m_capacity{0}; + + // type of the matrix which will be chosen as soon as the + // child structure will be created + MatrixType m_type{MatrixType::None}; public: // getters inline size_t size(void) const { return m_size; } inline size_t capacity(void) const { return m_capacity; } + inline size_t type(void) const { return m_type; } // virtual functions which will be implemented in the substructures // according to the concrete representation of the matrix @@ -819,9 +827,20 @@ struct ExtendableSquareMatrix virtual inline void extend(T placeholder = T()) = 0; virtual ~ExtendableSquareMatrix() = default; + + // matrix properties + bool isReflexive(void); + bool isAntisymetric(void); + bool isTransitive(void); }; +/************************************* +* +* CASCADE SQUARE MATRIX +* +**************************************/ + /** * CascadeSquareMatrix * @@ -905,6 +924,7 @@ CascadeSquareMatrix::CascadeSquareMatrix assert(initRows <= maxRows && "Initial size of the matrix cannot be bigger than the capacity"); + this->m_type = MatrixType::Cascade; this->m_capacity = maxRows; data.reserve(this->m_capacity * this->m_capacity); @@ -963,6 +983,12 @@ inline void CascadeSquareMatrix::extend(T placeholder) ++this->m_size; } +/************************************* +* +* DYNAMIC SQUARE MATRIX +* +**************************************/ + /** * DynamicSquareMatrix * @@ -1010,6 +1036,7 @@ DynamicSquareMatrix::DynamicSquareMatrix assert(initRows <= maxRows && "Initial size of the matrix cannot be bigger than the capacity"); + this->m_type = MatrixType::Dynamic; this->m_capacity = maxRows; // creating the initial size and filling the data cells with @@ -1069,6 +1096,12 @@ void DynamicSquareMatrix::extend(T placeholder) data.back().insert(data.back().end(), this->m_size, placeholder); } +/************************************* +* +* HASHED SQUARE MATRIX +* +**************************************/ + /** * HashedSquareMatrix * @@ -1110,6 +1143,7 @@ HashedSquareMatrix::HashedSquareMatrix assert(initRows <= maxRows && "Initial size of the matrix cannot be bigger than the capacity"); + this->m_type = MatrixType::Hashed; this->m_capacity = maxRows; // creating the initial size and filling the data cells with @@ -1171,6 +1205,83 @@ inline void HashedSquareMatrix::extend(T placeholder) ++this->m_size; } +/************************************* +* +* ADDITIONAL FUNCTIONS +* +**************************************/ + +/** +* @brief factory function which creates an ExtendableSquareMatrix of given type +* @param type type of the new matrix +* @param capacity maximal matrix capacity +* @param size initial matrix size +* @return pointer to the newly created matrix +*/ +template +ExtendableSquareMatrix *create(MatrixType type, + size_t capacity, size_t size = 0) +{ + switch(type) + { + case MatrixType::Cascade: + return new CascadeSquareMatrix(capacity, size); + case MatrixType::Dynamic: + return new DynamicSquareMatrix(capacity, size); + case MatrixType::Hashed: + return new HashedSquareMatrix(capacity, size); + default: + return nullptr; + } +} + +/** +* @brief creates a deep copy of the given ExtendableSquareMatrix +* @param matrix input matrix which should be copied +* @return pointer to the newly created matrix +*/ +template +ExtendableSquareMatrix *copy(ExtendableSquareMatrix *matrix) +{ + ExtendableSquareMatrix *newMatrix = nullptr; + size_t size = matrix->size(); + + switch(matrix->type()) + { + case MatrixType::Cascade: + + // since a default copy of a vector does not preserve its reserved + // capacity, we need to explicitly create new matrix + // with the former capacity + newMatrix = create(Cascade, matrix->capacity(), matrix->size()); + + // copying of the data + for(size_t i = 0; i < size; ++i) + { + for(size_t j = 0; j < size; ++j) + { + newMatrix->set(i, j, matrix->get(i, j)); + } + } + break; + + case MatrixType::Dynamic: + newMatrix = new DynamicSquareMatrix + (*dynamic_cast*>(matrix)); + break; + + case MatrixType::Hashed: + newMatrix = new HashedSquareMatrix + (*dynamic_cast*>(matrix)); + break; + + default: + newMatrix = nullptr; + break; + } + return newMatrix; +} + // debugging function which allows us to print text representation of // the Extendable square matrix template @@ -1193,59 +1304,56 @@ std::ostream& operator<<(std::ostream& os, return os << result; } -/** This function checks whether the given matrix is reflexive. In this +/** This function checks whether the matrix is reflexive. In this * context, the matrix is reflexive iff none of the elements on the main * diagonal is the zero element of the type T * @brief checks whether the Extendable square matrix is reflexive -* @param matrix input matrix to be checked -* @return true iff the input matrix is reflexive +* @return true iff the matrix is reflexive */ template -bool isReflexive(ExtendableSquareMatrix *matrix) +bool ExtendableSquareMatrix::isReflexive(void) { - size_t size = matrix->size(); + size_t size = this->size(); for(size_t i = 0; i < size; ++i) { - if(!matrix->get(i, i)) { return false; } + if(!get(i, i)) { return false; } } return true; } -/** This function checks whether the given matrix is antisymetric. In this +/** This function checks whether the matrix is antisymetric. In this * context, the matrix is antisymetric iff there are no indices i, j * where i != j and both matrix[i][j], matrix[j][i] contain nonzero elementes * of the type T * @brief checks whether the Extendable square matrix is antisymetric -* @param matrix input matrix to be checked -* @return true iff the input matrix is antisymetric +* @return true iff the matrix is antisymetric */ template -bool isAntisymetric(ExtendableSquareMatrix *matrix) +bool ExtendableSquareMatrix::isAntisymetric(void) { - size_t size = matrix->size(); + size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { if(i == j) { continue; } - if(matrix->get(i, j) && matrix->get(j, i)) { return false; } + if(get(i, j) && get(j, i)) { return false; } } } return true; } -/** This function checks whether the given matrix is transitive. In this +/** This function checks whether the matrix is transitive. In this * context, the matrix is transitive iff it holds that the input matrix * casted to the matrix of booleans (false for zero values of type T, otherwise * true) remains the same if it is multiplied by itself. * @brief checks whether the Extendable square matrix is transitive -* @param matrix input matrix to be checked -* @return true iff the input matrix is transitive +* @return true iff the matrix is transitive */ template -bool isTransitive(ExtendableSquareMatrix *matrix) +bool ExtendableSquareMatrix::isTransitive(void) { - size_t size = matrix->size(); + size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) @@ -1253,13 +1361,13 @@ bool isTransitive(ExtendableSquareMatrix *matrix) bool found = false; for(size_t k = 0; k < size; ++k) { - if(matrix->get(i, k) && matrix->get(k, j)) + if(get(i, k) && get(k, j)) { found = true; break; } } - if(!found == static_cast(matrix->get(i, j))) { return false; } + if(!found == static_cast(get(i, j))) { return false; } } } return true; diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index aa1e36a94..497210a34 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -191,8 +191,8 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { SECTION("CascadeSquareMatrix") { - ExtendableSquareMatrix *e = - new CascadeSquareMatrix(5, 2); + ExtendableSquareMatrix *e = create + (Cascade, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -202,32 +202,31 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(0, 0, 1); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(3, 1, 1); e->set(1, 2, 1); - std::cout << (*e); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(!isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(!e->isTransitive()); delete e; } SECTION("DynamicSquareMatrix") { - ExtendableSquareMatrix *e = - new DynamicSquareMatrix(5, 2); + ExtendableSquareMatrix *e = create + (Dynamic, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -237,32 +236,31 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(0, 0, 1); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(3, 1, 1); e->set(1, 2, 1); - std::cout << (*e); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(!isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(!e->isTransitive()); delete e; } SECTION("HashedSquareMatrix") { - ExtendableSquareMatrix *e = - new HashedSquareMatrix(5, 2); + ExtendableSquareMatrix *e = create + (Hashed, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -272,25 +270,69 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(0, 0, 1); - CHECK(!isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(!e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(e->isTransitive()); e->set(3, 1, 1); e->set(1, 2, 1); - std::cout << (*e); - CHECK(isReflexive(e)); - CHECK(isAntisymetric(e)); - CHECK(!isTransitive(e)); + CHECK(e->isReflexive()); + CHECK(e->isAntisymetric()); + CHECK(!e->isTransitive()); delete e; } + + + SECTION("Copying matrices") { + + ExtendableSquareMatrix *m1 = create(Cascade, 1000, 2); + ExtendableSquareMatrix *m2 = create(Dynamic, 5, 2); + ExtendableSquareMatrix *m3 = create(Hashed, 5, 2); + + ExtendableSquareMatrix *c1 = m1; + ExtendableSquareMatrix *c2 = m2; + ExtendableSquareMatrix *c3 = m3; + + m1->set(1, 1, true); + m2->set(1, 1, true); + m3->set(1, 1, true); + + CHECK(m1->get(1, 1) == c1->get(1, 1)); + CHECK(m2->get(1, 1) == c2->get(1, 1)); + CHECK(m3->get(1, 1) == c3->get(1, 1)); + + c1 = copy(m1); + c2 = copy(m2); + c3 = copy(m3); + + m1->set(0, 1, true); + m2->set(0, 1, true); + m3->set(0, 1, true); + + CHECK(m1->get(0, 1) != c1->get(0, 1)); + CHECK(m2->get(0, 1) != c2->get(0, 1)); + CHECK(m3->get(0, 1) != c3->get(0, 1)); + + CHECK(!c1->get(0, 1)); + CHECK(!c2->get(0, 1)); + CHECK(!c3->get(0, 1)); + + delete m1; + delete m2; + delete m3; + delete c1; + delete c2; + delete c3; + + } + } From 9119b199b89811dbfc83cc38158b4f1c99a30abf Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 20 Mar 2024 21:51:08 +0100 Subject: [PATCH 04/34] Custom copy constructor and operator= added to the Partition structure --- include/mata/utils/partition-relation-pair.hh | 60 ++++++++++++++++++- tests/partition-relation-pair.cc | 53 +++++++++++++++- 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 812274343..c0ed5f574 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -195,6 +195,7 @@ typedef struct Partition // constructors Partition(size_t numOfStates, StateBlocks partition = StateBlocks()); + Partition(const Partition& other); // sizes of the used vectors inline size_t numOfStates(void) const { return m_states.size(); } @@ -227,9 +228,12 @@ typedef struct Partition // converts the partition to the vector of vectors of states StateBlocks partition(void); - // debug + // operators + Partition& operator=(const Partition& other); friend std::ostream& operator<<(std::ostream& os, const Partition& p); + + } Partition; // Partition @@ -349,6 +353,20 @@ Partition::Partition(size_t numOfStates, StateBlocks partition) } } +/** +* Custom copy constructor which preserves reserved memory for the +* partition vectors. This method has to be implemented since the custom +* assignment operator is also implemented. The preservation of the reserved +* memory is provided by the custom assignment operator=. +* @brief copy constructor of the Partition +* @param other partition which will be copied +*/ +Partition::Partition(const Partition& other) +{ + // using the custom assignment operator + *this = other; +} + /** * @brief returns a BlockItem corresponding to the given index @@ -704,6 +722,44 @@ std::vector Partition::splitBlocks(std::vector marked) return split; } +/** +* Custom assignment operator which preserves reserved capacities for the +* partition vectors +* @brief assignment of the partition +* @param other partition which will be copied +* @return modified partition +*/ +Partition& Partition::operator=(const Partition& other) +{ + // since the default copying of the vectors do not preserve + // 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 + m_states.reserve(other.numOfStates()); + m_blockItems.reserve(other.numOfStates()); + m_blocks.reserve(other.numOfStates()); + m_nodes.reserve(2 * other.numOfStates() - 1); + + // copying vectors without losing information about reserved capacity + size_t statesNum = other.numOfStates(); + for(size_t i = 0; i < statesNum; ++i) + { + m_states.push_back(other.getBlockItemIdxFromState(i)); + m_blockItems.push_back(other.getBlockItem(i)); + } + size_t blocksNum = other.numOfBlocks(); + for(size_t i = 0; i < blocksNum; ++i) + { + m_blocks.push_back(other.getBlock(i)); + } + size_t nodesNum = other.numOfNodes(); + for(size_t i = 0; i < nodesNum; ++i) + { + m_nodes.push_back(other.getNode(i)); + } + return *this; +} + // debugging function which allows us to print text representation of // the partition std::ostream& operator<<(std::ostream& os, const Partition& p) @@ -974,7 +1030,7 @@ template inline void CascadeSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "The matrix cannot be extened anymore"); + && "The matrix cannot be extended anymore"); // allocation of 2 * size + 1 new data cells data.insert(data.end(), 2 * this->m_size + 1, placeholder); diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 497210a34..a9bf0aa5e 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -182,7 +182,58 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.numOfNodes() == 19); CHECK(p.getBlockIdxFromState(0) == 0); CHECK(p.getBlockIdxFromState(9) == 7); - } + } + + SECTION("Custom copying and assigning") + { + Partition p = Partition(5, {{2, 3}}); + p.splitBlocks({0}); + + Partition q = p; + Partition r = Partition(p); + + CHECK(p.numOfStates() == q.numOfStates()); + CHECK(p.numOfStates() == r.numOfStates()); + CHECK(p.numOfBlockItems() == q.numOfBlockItems()); + CHECK(p.numOfBlockItems() == r.numOfBlockItems()); + CHECK(p.numOfBlocks() == q.numOfBlocks()); + CHECK(p.numOfBlocks() == r.numOfBlocks()); + CHECK(p.numOfNodes() == q.numOfNodes()); + CHECK(p.numOfNodes() == r.numOfNodes()); + + size_t statesNum = p.numOfStates(); + size_t blocksNum = p.numOfBlocks(); + size_t nodesNum = p.numOfNodes(); + + for(size_t i = 0; i < statesNum; ++i) + { + CHECK(p.getBlockItemIdxFromState(i) == + q.getBlockItemIdxFromState(i)); + CHECK(p.getBlockItemIdxFromState(i) == + r.getBlockItemIdxFromState(i)); + CHECK(p.getBlockItem(i).state == q.getBlockItem(i).state); + CHECK(p.getBlockItem(i).state == r.getBlockItem(i).state); + CHECK(p.getBlockItem(i).blockIdx == q.getBlockItem(i).blockIdx); + CHECK(p.getBlockItem(i).blockIdx == r.getBlockItem(i).blockIdx); + } + + for(size_t i = 0; i < blocksNum; ++i) + { + CHECK(p.getBlock(i).nodeIdx == q.getBlock(i).nodeIdx); + CHECK(p.getBlock(i).nodeIdx == r.getBlock(i).nodeIdx); + } + + for(size_t i = 0; i < nodesNum; ++i) + { + CHECK(p.getNode(i).first == q.getNode(i).first); + CHECK(p.getNode(i).first == r.getNode(i).first); + CHECK(p.getNode(i).last == q.getNode(i).last); + CHECK(p.getNode(i).last == r.getNode(i).last); + } + + q.splitBlocks({1, 2}); + r.splitBlocks({1, 2}); + } } From 7e56b8851b31b0b763dee9d9134253810c44e87a Mon Sep 17 00:00:00 2001 From: kocotom Date: Thu, 21 Mar 2024 18:28:02 +0100 Subject: [PATCH 05/34] Vectors are now passed to functions using const references --- include/mata/utils/partition-relation-pair.hh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index c0ed5f574..d131495c8 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -194,7 +194,8 @@ typedef struct Partition public: // constructors - Partition(size_t numOfStates, StateBlocks partition = StateBlocks()); + Partition(size_t numOfStates, + const StateBlocks& partition = StateBlocks()); Partition(const Partition& other); // sizes of the used vectors @@ -204,11 +205,11 @@ typedef struct Partition inline size_t numOfNodes(void) const { return m_nodes.size(); } // blocks splitting - std::vector splitBlocks(std::vector marked); + std::vector splitBlocks(const std::vector& marked); // basic information about the partition inline bool inSameBlock(State first, State second) const; - bool inSameBlock(std::vector states) const; + bool inSameBlock(const std::vector& states) const; std::vector statesInSameBlock(State state) const; // accessing blockItems, blocks, nodes through indices @@ -258,7 +259,7 @@ typedef struct Partition * @param partition optional initial partition in the form of vectors of * vectors of states */ -Partition::Partition(size_t numOfStates, StateBlocks partition) +Partition::Partition(size_t numOfStates, const StateBlocks& partition) { // reserving memory space to avoid moving extended vectors m_states.reserve(numOfStates); @@ -508,7 +509,7 @@ inline bool Partition::inSameBlock(State first, State second) const * @param states vector of states to be checked * @return true iff all of the given states belong to the same partition block */ -bool Partition::inSameBlock(std::vector states) const +bool Partition::inSameBlock(const std::vector& states) const { if(states.empty()) { return true; } size_t blockIdx = getBlockIdxFromState(states.front()); @@ -599,7 +600,7 @@ StateBlocks Partition::partition(void) * @param marked marked states which influence splitting * @return vector of SplitPair which contains information about split blocks */ -std::vector Partition::splitBlocks(std::vector marked) +std::vector Partition::splitBlocks(const std::vector& marked) { // the vector which will be returned as the result std::vector split{}; From ee330abe8652c3f0e2db2ac4e1eb1b02d45909ed Mon Sep 17 00:00:00 2001 From: kocotom Date: Tue, 26 Mar 2024 08:05:50 +0100 Subject: [PATCH 06/34] cloning method added to the ExtendableSquaredMatrix structure and its children --- include/mata/utils/partition-relation-pair.hh | 110 ++++++++++-------- tests/partition-relation-pair.cc | 6 +- 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index d131495c8..70ed99fb1 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -840,11 +840,12 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) * which inherits from this abstract structure should: * - contain the storage for data of datatype T which represents n x n matrix * - implement methods set, get and extend + * - implement a method clone which creates a deep copy of a matrix * Then, the ExtendableSquareMatrix can be used independently of the inner * representation of the matrix. Therefore, one can dynamically choose from * various of implementations depending on the situation. If any new - * substructure is implemented, one should also modify 'create' and 'copy' - * functions and extend 'MatrixType' enumerator. + * substructure is implemented, one should also modify the 'create' + * function and extend 'MatrixType' enumerator. * * Note that in context of an n x n matrix, this implementation uses the word * 'size' to refer to the number n (number of rows or columns). The word @@ -872,6 +873,7 @@ struct ExtendableSquareMatrix public: + // getters inline size_t size(void) const { return m_size; } inline size_t capacity(void) const { return m_capacity; } @@ -882,6 +884,9 @@ struct ExtendableSquareMatrix virtual inline void set(size_t i, size_t j, T value = T()) = 0; virtual inline T get(size_t i, size_t j) const = 0; virtual inline void extend(T placeholder = T()) = 0; + + // cloning + virtual ExtendableSquareMatrix *clone(void) const = 0; virtual ~ExtendableSquareMatrix() = default; @@ -961,12 +966,20 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix // constructors CascadeSquareMatrix(size_t maxRows, size_t initRows = 0); + CascadeSquareMatrix(const CascadeSquareMatrix& other); // implemented virtual functions inline void set(size_t i, size_t j, T value) override; inline T get(size_t i, size_t j) const override; inline void extend(T placeholder = T()) override; + // cloning + CascadeSquareMatrix *clone(void) const + { return new CascadeSquareMatrix(*this); } + + // operators + CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other); + }; /** @@ -990,6 +1003,18 @@ CascadeSquareMatrix::CascadeSquareMatrix for(size_t i = 0; i < initRows; ++i) {extend();} } +/** This method provides a way to create a copy of a given CascadeSquareMatrix +* and preserves the reserved capacity of the vector 'data'. This goal is +* achieved using the custom assignment operator. +* @brief copy constructor of a CascadeSquareMatrix +* @param other matrix which should be copied +*/ +template +CascadeSquareMatrix::CascadeSquareMatrix(const CascadeSquareMatrix& other) +{ + *this = other; +} + /** * @brief assings a value to the Cascade square matrix * @param i row of the square matrix @@ -1040,6 +1065,33 @@ inline void CascadeSquareMatrix::extend(T placeholder) ++this->m_size; } +/** This method provides a way to assign a CascadeSquareMatrix to the variable. * The method ensure us to keep the reserved capacity of the vector 'data' since * the default vector assignment do not preserve it. +* @brief assignment operator for the CascadeSquareMatrix structure +* @param other matrix which should be copied assigned +*/ +template +CascadeSquareMatrix& CascadeSquareMatrix::operator= + (const CascadeSquareMatrix& other) +{ + // initialization of the matrix + this->m_capacity = other.capacity(); + this->m_size = 0; + this->data = std::vector(); + this->data.reserve(this->m_capacity * this->m_capacity); + size_t otherSize = other.size(); + for(size_t i = 0; i < otherSize; ++i) {this->extend();} + + // copying memory cells + for(size_t i = 0; i < this->m_size; ++i) + { + for(size_t j = 0; j < this->m_size; ++j) + { + this->set(i, j, other.get(i, j)); + } + } + return *this; +} + /************************************* * * DYNAMIC SQUARE MATRIX @@ -1079,6 +1131,9 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix inline T get(size_t i, size_t j) const override; void extend(T placeholder = T()) override; + // cloning + DynamicSquareMatrix *clone(void) const + { return new DynamicSquareMatrix(*this); } }; /** @@ -1185,6 +1240,10 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix inline void set(size_t i, size_t j, T value) override; inline T get(size_t i, size_t j) const override; inline void extend(T placeholder = T()) override; + + // cloning + HashedSquareMatrix *clone(void) const + { return new HashedSquareMatrix(*this); } }; @@ -1292,53 +1351,6 @@ ExtendableSquareMatrix *create(MatrixType type, } } -/** -* @brief creates a deep copy of the given ExtendableSquareMatrix -* @param matrix input matrix which should be copied -* @return pointer to the newly created matrix -*/ -template -ExtendableSquareMatrix *copy(ExtendableSquareMatrix *matrix) -{ - ExtendableSquareMatrix *newMatrix = nullptr; - size_t size = matrix->size(); - - switch(matrix->type()) - { - case MatrixType::Cascade: - - // since a default copy of a vector does not preserve its reserved - // capacity, we need to explicitly create new matrix - // with the former capacity - newMatrix = create(Cascade, matrix->capacity(), matrix->size()); - - // copying of the data - for(size_t i = 0; i < size; ++i) - { - for(size_t j = 0; j < size; ++j) - { - newMatrix->set(i, j, matrix->get(i, j)); - } - } - break; - - case MatrixType::Dynamic: - newMatrix = new DynamicSquareMatrix - (*dynamic_cast*>(matrix)); - break; - - case MatrixType::Hashed: - newMatrix = new HashedSquareMatrix - (*dynamic_cast*>(matrix)); - break; - - default: - newMatrix = nullptr; - break; - } - return newMatrix; -} - // debugging function which allows us to print text representation of // the Extendable square matrix template diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index a9bf0aa5e..daa80a707 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -361,9 +361,9 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(m2->get(1, 1) == c2->get(1, 1)); CHECK(m3->get(1, 1) == c3->get(1, 1)); - c1 = copy(m1); - c2 = copy(m2); - c3 = copy(m3); + c1 = m1->clone(); + c2 = m2->clone(); + c3 = m3->clone(); m1->set(0, 1, true); m2->set(0, 1, true); From b43d2573d3ecd522b6b4718205a707b0725e813e Mon Sep 17 00:00:00 2001 From: kocotom Date: Tue, 26 Mar 2024 08:10:48 +0100 Subject: [PATCH 07/34] missing EOL added --- include/mata/utils/partition-relation-pair.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 70ed99fb1..0cd332140 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -1065,7 +1065,8 @@ inline void CascadeSquareMatrix::extend(T placeholder) ++this->m_size; } -/** This method provides a way to assign a CascadeSquareMatrix to the variable. * The method ensure us to keep the reserved capacity of the vector 'data' since * the default vector assignment do not preserve it. +/** This method provides a way to assign a CascadeSquareMatrix to the variable. +* The method ensure us to keep the reserved capacity of the vector 'data' since * the default vector assignment do not preserve it. * @brief assignment operator for the CascadeSquareMatrix structure * @param other matrix which should be copied assigned */ From fb4c67cf8841b109fe3eb2e0ff2b106ccfc1c81d Mon Sep 17 00:00:00 2001 From: kocotom Date: Tue, 26 Mar 2024 08:14:29 +0100 Subject: [PATCH 08/34] Mistyped character --- include/mata/utils/partition-relation-pair.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 0cd332140..ba155a918 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -1066,7 +1066,8 @@ inline void CascadeSquareMatrix::extend(T placeholder) } /** This method provides a way to assign a CascadeSquareMatrix to the variable. -* The method ensure us to keep the reserved capacity of the vector 'data' since * the default vector assignment do not preserve it. +* The method ensures us to keep the reserved capacity of the vector 'data' since +* the default vector assignment does not preserve it. * @brief assignment operator for the CascadeSquareMatrix structure * @param other matrix which should be copied assigned */ From 50b0ede8463fd8bfa7e8da759869a5deb23d43a8 Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 02:57:01 +0100 Subject: [PATCH 09/34] Newlines fixed, left curly braces are not on separate lines now --- include/mata/utils/partition-relation-pair.hh | 344 +++++++----------- tests/partition-relation-pair.cc | 27 +- 2 files changed, 150 insertions(+), 221 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index ba155a918..7c9781799 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -51,16 +51,29 @@ using State = unsigned long; using StateBlock = std::vector; using StateBlocks = std::vector; -using BlockItem = struct BlockItem {State state; size_t blockIdx;}; -using Block = struct Block {size_t nodeIdx;}; -using Node = struct Node {size_t first; size_t last;}; +using BlockItem = struct BlockItem { + State state; + size_t blockIdx; +}; + +using Block = struct Block { + size_t nodeIdx; +}; + +using Node = struct Node { + size_t first; + size_t last; +}; using BlockItems = std::vector; using Blocks = std::vector; using Nodes = std::vector; -using SplitPair = struct SplitPair - {size_t former; size_t created; size_t oldNodeIdx;}; +using SplitPair = struct SplitPair { + size_t former; + size_t created; + size_t oldNodeIdx; +}; /** * Partition @@ -175,8 +188,7 @@ using SplitPair = struct SplitPair * - remember all ancestors of current blocks and access them * */ -typedef struct Partition -{ +typedef struct Partition { private: /* indices to the m_blockItems vector */ @@ -259,8 +271,7 @@ typedef struct Partition * @param partition optional initial partition in the form of vectors of * vectors of states */ -Partition::Partition(size_t numOfStates, const StateBlocks& partition) -{ +Partition::Partition(size_t numOfStates, const StateBlocks& partition) { // reserving memory space to avoid moving extended vectors m_states.reserve(numOfStates); m_blockItems.reserve(numOfStates); @@ -279,20 +290,18 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) // creating partition using given initial vector of vectors size_t numOfBlocks = partition.size(); // iterating through initial partition blocks - for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) - { + for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) { assert(!partition[blockIdx].empty() && - "Partition class cannot be empty."); + "Partition class cannot be empty."); // iterating through one partition block - for(auto state : partition[blockIdx]) - { + for(auto state : partition[blockIdx]) { assert(state < numOfStates && - "Invalid state name detected while creating" - "a partition relation pair."); + "Invalid state name detected while creating" + "a partition relation pair."); assert(!used[state] && - "Partition could not be created." - "Duplicate occurence of a state"); + "Partition could not be created." + "Duplicate occurence of a state"); used[state] = true; @@ -322,19 +331,16 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) State last = 0; // iterating through the vector of flags saying which states has been seen - for(State state = 0; state < numOfStates; ++state) - { + for(State state = 0; state < numOfStates; ++state) { // 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]) { continue; } // if there is at least one unused state, we need to // create an additional block - if(allStatesUsed) - { + if(allStatesUsed) { allStatesUsed = false; first = state; ++numOfBlocks; @@ -347,8 +353,7 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) } // creating a new block and node if there was an unused state - if(!allStatesUsed) - { + if(!allStatesUsed) { m_nodes.push_back({.first = m_states[first], .last = m_states[last]}); m_blocks.push_back({.nodeIdx = numOfBlocks-1}); } @@ -362,8 +367,7 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) * @brief copy constructor of the Partition * @param other partition which will be copied */ -Partition::Partition(const Partition& other) -{ +Partition::Partition(const Partition& other) { // using the custom assignment operator *this = other; } @@ -374,10 +378,9 @@ Partition::Partition(const Partition& other) * @param blockItemIdx index of the BlockItem * @return corresponding BlockItem */ -inline BlockItem Partition::getBlockItem(size_t blockItemIdx) const -{ +inline BlockItem Partition::getBlockItem(size_t blockItemIdx) const { assert(blockItemIdx < numOfBlockItems() && - "Nonexisting block item index used."); + "Nonexisting block item index used."); return m_blockItems[blockItemIdx]; } @@ -386,10 +389,8 @@ inline BlockItem Partition::getBlockItem(size_t blockItemIdx) const * @param blockIdx index of the block * @return corresponding block */ -inline Block Partition::getBlock(size_t blockIdx) const -{ - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); +inline Block Partition::getBlock(size_t blockIdx) const { + assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); return m_blocks[blockIdx]; } @@ -398,10 +399,8 @@ inline Block Partition::getBlock(size_t blockIdx) const * @param nodeIdx index of the node * @return corresponding node */ -inline Node Partition::getNode(size_t nodeIdx) const -{ - assert(nodeIdx < numOfNodes() && - "Nonexisting node index used."); +inline Node Partition::getNode(size_t nodeIdx) const { + assert(nodeIdx < numOfNodes() && "Nonexisting node index used."); return m_nodes[nodeIdx]; } @@ -410,10 +409,8 @@ inline Node Partition::getNode(size_t nodeIdx) const * @param state given state * @return corresponding block index */ -inline size_t Partition::getBlockIdxFromState(State state) const -{ - assert(state < numOfStates() && - "Nonexisting state name used."); +inline size_t Partition::getBlockIdxFromState(State state) const { + assert(state < numOfStates() && "Nonexisting state name used."); return m_blockItems[m_states[state]].blockIdx; } @@ -422,10 +419,8 @@ inline size_t Partition::getBlockIdxFromState(State state) const * @param state given state * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromState(State state) const -{ - assert(state < numOfStates() && - "Nonexisting state name used."); +inline size_t Partition::getNodeIdxFromState(State state) const { + assert(state < numOfStates() && "Nonexisting state name used."); return m_blocks[m_blockItems[m_states[state]].blockIdx].nodeIdx; } @@ -434,10 +429,8 @@ inline size_t Partition::getNodeIdxFromState(State state) const * @param state given state * @return corresponding BlockItem index */ -inline size_t Partition::getBlockItemIdxFromState(State state) const -{ - assert(state < numOfStates() && - "Nonexisting state name used."); +inline size_t Partition::getBlockItemIdxFromState(State state) const { + assert(state < numOfStates() && "Nonexisting state name used."); return m_states[state]; } @@ -446,10 +439,9 @@ inline size_t Partition::getBlockItemIdxFromState(State state) const * @param blockItemIdx BlockItem index * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const -{ +inline size_t Partition::getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const { assert(blockItemIdx < numOfBlockItems() && - "Nonexisting BlockItem index used."); + "Nonexisting BlockItem index used."); return m_blocks[m_blockItems[blockItemIdx].blockIdx].nodeIdx; } @@ -458,10 +450,8 @@ inline size_t Partition::getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const * @param blockIdx given block index * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromBlockIdx(size_t blockIdx) const -{ - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); +inline size_t Partition::getNodeIdxFromBlockIdx(size_t blockIdx) const { + assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); return m_blocks[blockIdx].nodeIdx; } @@ -471,10 +461,8 @@ inline size_t Partition::getNodeIdxFromBlockIdx(size_t blockIdx) const * @param blockIdx given block index * @return first blockItem index corresponding to the given block index */ -inline size_t Partition::getReprIdxFromBlockIdx(size_t blockIdx) const -{ - assert(blockIdx < numOfBlocks() && - "Nonexisting block index used."); +inline size_t Partition::getReprIdxFromBlockIdx(size_t blockIdx) const { + assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); return m_nodes[m_blocks[blockIdx].nodeIdx].first; } @@ -483,10 +471,8 @@ inline size_t Partition::getReprIdxFromBlockIdx(size_t blockIdx) const * @param nodeIdx given node index * @return first blockItem index corresponding to the given node index */ -inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const -{ - assert(nodeIdx < numOfNodes() && - "Nonexisting node index used."); +inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const { + assert(nodeIdx < numOfNodes() && "Nonexisting node index used."); return m_nodes[nodeIdx].first; } @@ -497,8 +483,7 @@ inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const * @param second second state to be checked * @return true iff both given states belong to the same partition block */ -inline bool Partition::inSameBlock(State first, State second) const -{ +inline bool Partition::inSameBlock(State first, State second) const { assert(first < m_states.size() && "The given state does not exist"); assert(second < m_states.size() && "The given state does not exist"); return getBlockIdxFromState(first) == getBlockIdxFromState(second); @@ -509,12 +494,10 @@ inline bool Partition::inSameBlock(State first, State second) const * @param states vector of states to be checked * @return true iff all of the given states belong to the same partition block */ -bool Partition::inSameBlock(const std::vector& states) const -{ +bool Partition::inSameBlock(const std::vector& states) const { if(states.empty()) { return true; } size_t blockIdx = getBlockIdxFromState(states.front()); - for(size_t state : states) - { + for(size_t state : states) { assert(state < m_states.size() && "The given state does not exist."); if(getBlockIdxFromState(state) != blockIdx) { return false; } } @@ -526,8 +509,7 @@ bool Partition::inSameBlock(const std::vector& states) const * @param state input state * @return vector of all the states in the corresponding block */ -std::vector Partition::statesInSameBlock(State state) const -{ +std::vector Partition::statesInSameBlock(State state) const { assert(state < numOfStates() && "The given state does not exist."); std::vector result{}; @@ -537,8 +519,7 @@ std::vector Partition::statesInSameBlock(State state) const size_t last = getNode(getNodeIdxFromState(state)).last; // iterating through BlockItems - for(size_t i = first; i <= last; ++i) - { + for(size_t i = first; i <= last; ++i) { result.push_back(getBlockItem(i).state); } @@ -550,12 +531,10 @@ std::vector Partition::statesInSameBlock(State state) const * vectors of states * @return vector of vectors of states */ -StateBlocks Partition::partition(void) -{ +StateBlocks Partition::partition(void) { StateBlocks result{}; result.insert(result.end(), m_blocks.size(), std::vector()); - for(auto blockItem : m_blockItems) - { + for(auto blockItem : m_blockItems) { result[blockItem.blockIdx].push_back(blockItem.state); } return result; @@ -600,8 +579,9 @@ StateBlocks Partition::partition(void) * @param marked marked states which influence splitting * @return vector of SplitPair which contains information about split blocks */ -std::vector Partition::splitBlocks(const std::vector& marked) -{ +std::vector Partition::splitBlocks( + const std::vector& marked) { + // the vector which will be returned as the result std::vector split{}; @@ -621,8 +601,7 @@ std::vector Partition::splitBlocks(const std::vector& marked) // iterating through the marked states to fill usedStates and // usedBlocks vectors - for(size_t i : marked) - { + for(size_t i : marked) { assert(i < m_states.size() && "The given state does not exist."); assert(!usedStates[i] && "The given state was marked multiple times"); usedStates[i] = true; @@ -633,8 +612,7 @@ std::vector Partition::splitBlocks(const std::vector& marked) oldBlocksSize = newBlockIdx = m_blocks.size(); // iterating through existing blocks - for(size_t i = 0; i < oldBlocksSize; ++i) - { + for(size_t i = 0; i < oldBlocksSize; ++i) { // if no state of the given block has been marked, it // won't be split if(!usedBlocks[i]) { continue; } @@ -662,24 +640,20 @@ std::vector Partition::splitBlocks(const std::vector& marked) // element is marked or not). As soon as such elements are found, they // are swapped. This procedure continues until these two indices used // to iterate through the BlockItems meet somewhere in the middle - while(iterFirst <= iterLast) - { + while(iterFirst <= iterLast) { // we choose the swapping strategy using XOR operation - while(reprMarked ^ !usedStates[getBlockItem(iterFirst).state]) - { + while(reprMarked ^ !usedStates[getBlockItem(iterFirst).state]) { // this visited state will be part of the former block ++iterFirst; } - while(reprMarked ^ usedStates[getBlockItem(iterLast).state]) - { + while(reprMarked ^ usedStates[getBlockItem(iterLast).state]) { // this visited state will be part of the new block m_blockItems[iterLast].blockIdx = newBlockIdx; --iterLast; } // if the used indices meet, we finish swapping - if(iterFirst > iterLast) - { + if(iterFirst > iterLast) { break; } @@ -730,8 +704,7 @@ std::vector Partition::splitBlocks(const std::vector& marked) * @param other partition which will be copied * @return modified partition */ -Partition& Partition::operator=(const Partition& other) -{ +Partition& Partition::operator=(const Partition& other) { // since the default copying of the vectors do not preserve // reserved capacity, we need to reserve it manually and // then insert elements of the other partition to the reserved space @@ -743,19 +716,16 @@ Partition& Partition::operator=(const Partition& other) // copying vectors without losing information about reserved capacity size_t statesNum = other.numOfStates(); - for(size_t i = 0; i < statesNum; ++i) - { + for(size_t i = 0; i < statesNum; ++i) { m_states.push_back(other.getBlockItemIdxFromState(i)); m_blockItems.push_back(other.getBlockItem(i)); } size_t blocksNum = other.numOfBlocks(); - for(size_t i = 0; i < blocksNum; ++i) - { + for(size_t i = 0; i < blocksNum; ++i) { m_blocks.push_back(other.getBlock(i)); } size_t nodesNum = other.numOfNodes(); - for(size_t i = 0; i < nodesNum; ++i) - { + for(size_t i = 0; i < nodesNum; ++i) { m_nodes.push_back(other.getNode(i)); } return *this; @@ -763,8 +733,7 @@ Partition& Partition::operator=(const Partition& other) // debugging function which allows us to print text representation of // the partition -std::ostream& operator<<(std::ostream& os, const Partition& p) -{ +std::ostream& operator<<(std::ostream& os, const Partition& p) { std::string result = std::string(); result += "NUM OF STATES: " + std::to_string(p.numOfStates()) + "\n"; result += "NUM OF BLOCKS: " + std::to_string(p.numOfBlocks()) + "\n"; @@ -773,13 +742,11 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) result += "BLOCKS:\n"; size_t numOfBlocks = p.numOfBlocks(); - for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) - { + for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) { result += std::to_string(blockIdx) + ": "; Node node = p.m_nodes[p.getNodeIdxFromBlockIdx(blockIdx)]; for(size_t blockItemIdx = node.first; - blockItemIdx <= node.last; ++blockItemIdx) - { + blockItemIdx <= node.last; ++blockItemIdx) { result += std::to_string(p.m_blockItems[blockItemIdx].state) + " "; } @@ -789,15 +756,12 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) result += "NODES:\n"; size_t numOfNodes = p.numOfNodes(); - for(size_t nodeIdx = 0; nodeIdx < numOfNodes; ++nodeIdx) - { + for(size_t nodeIdx = 0; nodeIdx < numOfNodes; ++nodeIdx) { result += std::to_string(nodeIdx) + ": "; Node node = p.m_nodes[nodeIdx]; for(size_t blockItemIdx = node.first; - blockItemIdx <= node.last; ++blockItemIdx) - { - result += std::to_string(p.m_blockItems[blockItemIdx].state) - + " "; + blockItemIdx <= node.last; ++blockItemIdx) { + result += std::to_string(p.m_blockItems[blockItemIdx].state) + " "; } result += "\n"; } @@ -857,8 +821,7 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) using MatrixType = enum MatrixType { None, Cascade, Dynamic, Hashed }; template -struct ExtendableSquareMatrix -{ +struct ExtendableSquareMatrix { protected: // number of rows (or columns) of the current square matrix @@ -873,7 +836,6 @@ struct ExtendableSquareMatrix public: - // getters inline size_t size(void) const { return m_size; } inline size_t capacity(void) const { return m_capacity; } @@ -955,8 +917,7 @@ struct ExtendableSquareMatrix * **/ template -struct CascadeSquareMatrix : public ExtendableSquareMatrix -{ +struct CascadeSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector @@ -974,8 +935,8 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix inline void extend(T placeholder = T()) override; // cloning - CascadeSquareMatrix *clone(void) const - { return new CascadeSquareMatrix(*this); } + CascadeSquareMatrix *clone(void) const { + return new CascadeSquareMatrix(*this); } // operators CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other); @@ -988,11 +949,11 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix * @param initRows initial size of the square matrix */ template -CascadeSquareMatrix::CascadeSquareMatrix - (size_t maxRows, size_t initRows) -{ +CascadeSquareMatrix::CascadeSquareMatrix( + size_t maxRows, size_t initRows) { + assert(initRows <= maxRows && - "Initial size of the matrix cannot be bigger than the capacity"); + "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Cascade; this->m_capacity = maxRows; @@ -1010,8 +971,9 @@ CascadeSquareMatrix::CascadeSquareMatrix * @param other matrix which should be copied */ template -CascadeSquareMatrix::CascadeSquareMatrix(const CascadeSquareMatrix& other) -{ +CascadeSquareMatrix::CascadeSquareMatrix( + const CascadeSquareMatrix& other) { + *this = other; } @@ -1022,8 +984,7 @@ CascadeSquareMatrix::CascadeSquareMatrix(const CascadeSquareMatrix& other) * @param value value to be assigned to the square matrix data cell */ template -inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) -{ +inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1038,8 +999,7 @@ inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) * @return value found in the square matrix data cell */ template -inline T CascadeSquareMatrix::get(size_t i, size_t j) const -{ +inline T CascadeSquareMatrix::get(size_t i, size_t j) const { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1053,10 +1013,9 @@ inline T CascadeSquareMatrix::get(size_t i, size_t j) const * (optional) */ template -inline void CascadeSquareMatrix::extend(T placeholder) -{ +inline void CascadeSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "The matrix cannot be extended anymore"); + && "The matrix cannot be extended anymore"); // allocation of 2 * size + 1 new data cells data.insert(data.end(), 2 * this->m_size + 1, placeholder); @@ -1072,9 +1031,9 @@ inline void CascadeSquareMatrix::extend(T placeholder) * @param other matrix which should be copied assigned */ template -CascadeSquareMatrix& CascadeSquareMatrix::operator= - (const CascadeSquareMatrix& other) -{ +CascadeSquareMatrix& CascadeSquareMatrix::operator=( + const CascadeSquareMatrix& other) { + // initialization of the matrix this->m_capacity = other.capacity(); this->m_size = 0; @@ -1084,10 +1043,8 @@ CascadeSquareMatrix& CascadeSquareMatrix::operator= for(size_t i = 0; i < otherSize; ++i) {this->extend();} // copying memory cells - for(size_t i = 0; i < this->m_size; ++i) - { - for(size_t j = 0; j < this->m_size; ++j) - { + for(size_t i = 0; i < this->m_size; ++i) { + for(size_t j = 0; j < this->m_size; ++j) { this->set(i, j, other.get(i, j)); } } @@ -1116,8 +1073,7 @@ CascadeSquareMatrix& CascadeSquareMatrix::operator= * extended, it could possibly be moved in the memory. **/ template -struct DynamicSquareMatrix : public ExtendableSquareMatrix -{ +struct DynamicSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector @@ -1134,8 +1090,8 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix void extend(T placeholder = T()) override; // cloning - DynamicSquareMatrix *clone(void) const - { return new DynamicSquareMatrix(*this); } + DynamicSquareMatrix *clone(void) const { + return new DynamicSquareMatrix(*this); } }; /** @@ -1144,11 +1100,11 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix * @param initRows initial size of the square matrix */ template -DynamicSquareMatrix::DynamicSquareMatrix - (size_t maxRows, size_t initRows) -{ +DynamicSquareMatrix::DynamicSquareMatrix( + size_t maxRows, size_t initRows) { + assert(initRows <= maxRows && - "Initial size of the matrix cannot be bigger than the capacity"); + "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Dynamic; this->m_capacity = maxRows; @@ -1165,8 +1121,7 @@ DynamicSquareMatrix::DynamicSquareMatrix * @param value value to be assigned to the square matrix data cell */ template -inline T DynamicSquareMatrix::get(size_t i, size_t j) const -{ +inline T DynamicSquareMatrix::get(size_t i, size_t j) const { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1180,8 +1135,7 @@ inline T DynamicSquareMatrix::get(size_t i, size_t j) const * @return value found in the square matrix data cell */ template -inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) -{ +inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1193,14 +1147,12 @@ inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) * @param placeholder a value which will be assigned to all the new data cells */ template -void DynamicSquareMatrix::extend(T placeholder) -{ +void DynamicSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "The matrix cannot be extened anymore"); + && "The matrix cannot be extened anymore"); // creating a new column - for(size_t i = 0; i < this->m_size; ++i) - { + for(size_t i = 0; i < this->m_size; ++i) { data[i].push_back(placeholder); } @@ -1226,8 +1178,7 @@ void DynamicSquareMatrix::extend(T placeholder) * matrix. To access matrix[i][j], we use map[i * capacity + j]. **/ template -struct HashedSquareMatrix : public ExtendableSquareMatrix -{ +struct HashedSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a hashmap @@ -1244,8 +1195,8 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix inline void extend(T placeholder = T()) override; // cloning - HashedSquareMatrix *clone(void) const - { return new HashedSquareMatrix(*this); } + HashedSquareMatrix *clone(void) const { + return new HashedSquareMatrix(*this); } }; @@ -1255,11 +1206,11 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix * @param initRows initial size of the square matrix */ template -HashedSquareMatrix::HashedSquareMatrix - (size_t maxRows, size_t initRows) -{ +HashedSquareMatrix::HashedSquareMatrix( + size_t maxRows, size_t initRows) { + assert(initRows <= maxRows && - "Initial size of the matrix cannot be bigger than the capacity"); + "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Hashed; this->m_capacity = maxRows; @@ -1276,8 +1227,7 @@ HashedSquareMatrix::HashedSquareMatrix * @param value value to be assigned to the square matrix data cell */ template -inline void HashedSquareMatrix::set(size_t i, size_t j, T value) -{ +inline void HashedSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1292,8 +1242,7 @@ inline void HashedSquareMatrix::set(size_t i, size_t j, T value) * @return value found in the square matrix data cell */ template -inline T HashedSquareMatrix::get(size_t i, size_t j) const -{ +inline T HashedSquareMatrix::get(size_t i, size_t j) const { assert(i < this->m_size && "Nonexisting row cannot be accessed"); assert(j < this->m_size && "Nonexisting column cannot be accessed"); @@ -1306,14 +1255,12 @@ inline T HashedSquareMatrix::get(size_t i, size_t j) const * @param placeholder a value which will be assigned to all the new data cells */ template -inline void HashedSquareMatrix::extend(T placeholder) -{ +inline void HashedSquareMatrix::extend(T placeholder) { assert(this->m_size < this->m_capacity - && "Matrix cannot be extened anymore"); + && "Matrix cannot be extened anymore"); // creating a new row and column - for(size_t i = 0; i < this->m_size; ++i) - { + for(size_t i = 0; i < this->m_size; ++i) { data[this->m_size * this->m_capacity + i] = placeholder; data[i * this->m_capacity + this->m_size] = placeholder; } @@ -1338,10 +1285,9 @@ inline void HashedSquareMatrix::extend(T placeholder) */ template ExtendableSquareMatrix *create(MatrixType type, - size_t capacity, size_t size = 0) -{ - switch(type) - { + size_t capacity, size_t size = 0) { + + switch(type) { case MatrixType::Cascade: return new CascadeSquareMatrix(capacity, size); case MatrixType::Dynamic: @@ -1357,17 +1303,15 @@ ExtendableSquareMatrix *create(MatrixType type, // the Extendable square matrix template std::ostream& operator<<(std::ostream& os, - const ExtendableSquareMatrix& matrix) -{ + const ExtendableSquareMatrix& matrix) { + size_t size = matrix.size(); size_t capacity = matrix.capacity(); std::string result = "\nSIZE: " + std::to_string(size) + "\n"; result += "CAPACITY: " + std::to_string(capacity) + "\n"; result += "MATRIX:\n"; - for(size_t i = 0; i < size; ++i) - { - for(size_t j = 0; j < size; ++j) - { + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { result += std::to_string(matrix.get(i, j)) + " "; } result += "\n"; @@ -1382,11 +1326,9 @@ std::ostream& operator<<(std::ostream& os, * @return true iff the matrix is reflexive */ template -bool ExtendableSquareMatrix::isReflexive(void) -{ +bool ExtendableSquareMatrix::isReflexive(void) { size_t size = this->size(); - for(size_t i = 0; i < size; ++i) - { + for(size_t i = 0; i < size; ++i) { if(!get(i, i)) { return false; } } return true; @@ -1400,13 +1342,10 @@ bool ExtendableSquareMatrix::isReflexive(void) * @return true iff the matrix is antisymetric */ template -bool ExtendableSquareMatrix::isAntisymetric(void) -{ +bool ExtendableSquareMatrix::isAntisymetric(void) { size_t size = this->size(); - for(size_t i = 0; i < size; ++i) - { - for(size_t j = 0; j < size; ++j) - { + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { if(i == j) { continue; } if(get(i, j) && get(j, i)) { return false; } } @@ -1422,18 +1361,13 @@ bool ExtendableSquareMatrix::isAntisymetric(void) * @return true iff the matrix is transitive */ template -bool ExtendableSquareMatrix::isTransitive(void) -{ +bool ExtendableSquareMatrix::isTransitive(void) { size_t size = this->size(); - for(size_t i = 0; i < size; ++i) - { - for(size_t j = 0; j < size; ++j) - { + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { bool found = false; - for(size_t k = 0; k < size; ++k) - { - if(get(i, k) && get(k, j)) - { + for(size_t k = 0; k < size; ++k) { + if(get(i, k) && get(k, j)) { found = true; break; } diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index daa80a707..2e6eadbca 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -17,8 +17,7 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.inSameBlock(0, 1)); CHECK(p.inSameBlock(1, 8)); CHECK(p.inSameBlock({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - for(size_t i = 0; i < 10; ++i) - { + for(size_t i = 0; i < 10; ++i) { CHECK(p.getBlockItemIdxFromState(i) == i); CHECK(p.getBlockIdxFromState(i) == 0); CHECK(p.getNodeIdxFromState(i) == 0); @@ -94,8 +93,7 @@ TEST_CASE("mata::utils::Partition") { CHECK(!p.inSameBlock(1, 4)); CHECK(p.inSameBlock({3, 4, 5})); CHECK(!p.inSameBlock({2, 3, 4, 5})); - for(size_t i = 0; i <= 5; ++i) - { + for(size_t i = 0; i <= 5; ++i) { CHECK(p.getBlockItemIdxFromState(i) == i); CHECK(p.getBlockItem(i).state == i); } @@ -205,8 +203,7 @@ TEST_CASE("mata::utils::Partition") { size_t blocksNum = p.numOfBlocks(); size_t nodesNum = p.numOfNodes(); - for(size_t i = 0; i < statesNum; ++i) - { + for(size_t i = 0; i < statesNum; ++i) { CHECK(p.getBlockItemIdxFromState(i) == q.getBlockItemIdxFromState(i)); CHECK(p.getBlockItemIdxFromState(i) == @@ -217,14 +214,12 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.getBlockItem(i).blockIdx == r.getBlockItem(i).blockIdx); } - for(size_t i = 0; i < blocksNum; ++i) - { + for(size_t i = 0; i < blocksNum; ++i) { CHECK(p.getBlock(i).nodeIdx == q.getBlock(i).nodeIdx); CHECK(p.getBlock(i).nodeIdx == r.getBlock(i).nodeIdx); } - for(size_t i = 0; i < nodesNum; ++i) - { + for(size_t i = 0; i < nodesNum; ++i) { CHECK(p.getNode(i).first == q.getNode(i).first); CHECK(p.getNode(i).first == r.getNode(i).first); CHECK(p.getNode(i).last == q.getNode(i).last); @@ -242,8 +237,8 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { SECTION("CascadeSquareMatrix") { - ExtendableSquareMatrix *e = create - (Cascade, 5, 2); + ExtendableSquareMatrix *e = create( + Cascade, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -276,8 +271,8 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { SECTION("DynamicSquareMatrix") { - ExtendableSquareMatrix *e = create - (Dynamic, 5, 2); + ExtendableSquareMatrix *e = create( + Dynamic, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -310,8 +305,8 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { SECTION("HashedSquareMatrix") { - ExtendableSquareMatrix *e = create - (Hashed, 5, 2); + ExtendableSquareMatrix *e = create( + Hashed, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); From 0c49f4b73043c706d924c4d02332d3e00b7348d0 Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 04:14:39 +0100 Subject: [PATCH 10/34] lowerCamelCase converted to snake_case. Member variables converted from m_varName to var_name_ --- include/mata/utils/partition-relation-pair.hh | 569 +++++++++--------- tests/partition-relation-pair.cc | 438 +++++++------- 2 files changed, 509 insertions(+), 498 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 7c9781799..75eba0ff4 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -53,11 +53,11 @@ using StateBlocks = std::vector; using BlockItem = struct BlockItem { State state; - size_t blockIdx; + size_t block_idx; }; using Block = struct Block { - size_t nodeIdx; + size_t node_idx; }; using Node = struct Node { @@ -72,7 +72,7 @@ using Nodes = std::vector; using SplitPair = struct SplitPair { size_t former; size_t created; - size_t oldNodeIdx; + size_t old_node_idx; }; /** @@ -85,38 +85,38 @@ using SplitPair = struct SplitPair { * interval <0, |S|-1>. * * STATES: - * This representation works with the vector of indices 'm_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 - * m_states[state]. + * states_[state]. * * BLOCK ITEMS: - * The memory cell m_states[state] contains a corresponding index to the - * 'm_blockItems' vector. The vector 'm_blockItems' has the constant size |S|. + * 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 - * 'm_blockItems' vector. + * 'block_items_' vector. * * BLOCKS: - * The blocks themselves are represented by the vector 'm_blocks' with the + * 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 - * m_blocks[blockIdx]. The block contains only an index of its + * blocks_[block_idx]. The block contains only 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 * split in two pieces 'B1' and 'B2', we create a brand new block 'B2' * and modify the former block 'B' such that it will correspond to its * subblock 'B1'. The former block 'B' is thus not represented - * in the 'm_blocks' vector anymore since 'B1' takes over its identity. + * in the 'blocks_' vector anymore since 'B1' takes over its identity. * * NODES: * Each node represents a current block or a block which has been split before. - * The node can by accessed by its index using m_nodes[nodeIdx]. If the given + * The node can by accessed by its index using nodes_[node_idx]. If the given * node represents an existing block, such block contains an index of that node. * In context of nodes which represent former blocks, no block contains their * indices. The total number of nodes can be changed as soon as @@ -150,7 +150,7 @@ using SplitPair = struct SplitPair { * * 0 1 2 3 4 * ------- ------- ------- ------- ------- - * | 0 | 2 | 1 | 4 | 3 | m_states + * | 0 | 2 | 1 | 4 | 3 | states_ * ------- ------- ------- ------- ------- * ↑ ↑ ↑ ↑ ↑ * | \ / \ / @@ -158,19 +158,19 @@ using SplitPair = struct SplitPair { * | / \ / \ * 0 ↓ 1 ↓ ↓ 2 3 ↓ ↓ 4 * ------- ------- ------- ------- ------- - * | 0 | 2 | 1 | 4 | 3 | m_blockItems + * | 0 | 2 | 1 | 4 | 3 | block_items_ * --→→→|-------|-------|-------|-------|--------←←←------------ * | | 0 | 0 | 1 | 1 | 1 | | * | ------- ------- ------- ------- ------- | * | | | | | | | * | 0 ↓ ↓ 1 ↓ ↓ ↓ | * | ---------------------------------------- | - * | | 1 | 2 | m_blocks | + * | | 1 | 2 | blocks_ | * | ---------------------------------------- | * | | | | * | 0 1 ↓ 2 ↓ | * | ------- ------- ------- | - * -----| 0 | 0 | 2 | m_nodes | + * -----| 0 | 0 | 2 | nodes_ | * |-------|-------|-------| | * | 4 | 1 | 4 | | * ------- ------- ------- | @@ -191,52 +191,54 @@ using SplitPair = struct SplitPair { typedef struct Partition { private: - /* indices to the m_blockItems vector */ - std::vector m_states{}; + /* indices to the block_items_ vector */ + std::vector states_{}; - /* indices to the m_states and m_blocks vectors */ - BlockItems m_blockItems{}; + /* indices to the states_ and blocks_ vectors */ + BlockItems block_items_{}; - /* indices to the m_nodes vector */ - Blocks m_blocks{}; + /* indices to the nodes_ vector */ + Blocks blocks_{}; - /* tuples of indices to the m_blockItems vectors */ - Nodes m_nodes{}; + /* tuples of indices to the block_items_ vectors */ + Nodes nodes_{}; public: // constructors - Partition(size_t numOfStates, + Partition(size_t num_of_states, const StateBlocks& partition = StateBlocks()); Partition(const Partition& other); // sizes of the used vectors - inline size_t numOfStates(void) const { return m_states.size(); } - inline size_t numOfBlockItems(void) const { return m_blockItems.size();} - inline size_t numOfBlocks(void) const { return m_blocks.size(); } - inline size_t numOfNodes(void) const { return m_nodes.size(); } + inline size_t num_of_states(void) const { return states_.size(); } + inline size_t num_of_block_items(void) const { + return block_items_.size();} + inline size_t num_of_blocks(void) const { return blocks_.size(); } + inline size_t num_of_nodes(void) const { return nodes_.size(); } // blocks splitting - std::vector splitBlocks(const std::vector& marked); + std::vector split_blocks(const std::vector& marked); // basic information about the partition - inline bool inSameBlock(State first, State second) const; - bool inSameBlock(const std::vector& states) const; - std::vector statesInSameBlock(State state) const; + inline 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 - inline BlockItem getBlockItem(size_t blockItemIdx) const; - inline Block getBlock(size_t blockIdx) const; - inline Node getNode(size_t nodeIdx) const; + inline BlockItem get_block_item(size_t block_item_idx) const; + inline Block get_block(size_t block_idx) const; + inline Node get_node(size_t node_idx) const; // refering between blockItems, blocks, nodes using indices - inline size_t getBlockIdxFromState(State state) const; - inline size_t getNodeIdxFromState(State state) const; - inline size_t getBlockItemIdxFromState(State state) const; - inline size_t getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const; - inline size_t getNodeIdxFromBlockIdx(size_t blockIdx) const; - inline size_t getReprIdxFromBlockIdx(size_t blockIdx) const; - inline size_t getReprIdxFromNodeIdx(size_t nodeIdx) const; + inline size_t get_block_idx_from_state(State state) const; + inline size_t get_node_idx_from_state(State state) const; + inline size_t get_block_item_idx_from_state(State state) const; + inline size_t get_node_idx_from_block_item_idx( + size_t block_item_idx) const; + inline size_t get_node_idx_from_block_idx(size_t block_idx) const; + inline size_t get_repr_idx_from_block_idx(size_t block_idx) const; + inline size_t get_repr_idx_from_node_idx(size_t node_idx) const; // converts the partition to the vector of vectors of states StateBlocks partition(void); @@ -267,36 +269,36 @@ typedef struct Partition { * If there is no initial partition, all states will be assigned * to the same block * @brief constructs the partition -* @param numOfStates cardinality of the carrier set +* @param num_of_states cardinality of the carrier set * @param partition optional initial partition in the form of vectors of * vectors of states */ -Partition::Partition(size_t numOfStates, const StateBlocks& partition) { +Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // reserving memory space to avoid moving extended vectors - m_states.reserve(numOfStates); - m_blockItems.reserve(numOfStates); - m_blocks.reserve(numOfStates); - m_nodes.reserve(2 * numOfStates - 1); + 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; - used.insert(used.end(), numOfStates, false); + used.insert(used.end(), num_of_states, false); - // initialization of the m_states vector - m_states.insert(m_states.end(), numOfStates, 0); + // initialization of the states_ vector + states_.insert(states_.end(), num_of_states, 0); // creating partition using given initial vector of vectors - size_t numOfBlocks = partition.size(); + size_t num_of_blocks = partition.size(); // iterating through initial partition blocks - for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) { - assert(!partition[blockIdx].empty() && + for(size_t block_idx = 0; block_idx < num_of_blocks; ++block_idx) { + assert(!partition[block_idx].empty() && "Partition class cannot be empty."); // iterating through one partition block - for(auto state : partition[blockIdx]) { - assert(state < numOfStates && + for(auto state : partition[block_idx]) { + assert(state < num_of_states && "Invalid state name detected while creating" "a partition relation pair."); assert(!used[state] && @@ -306,24 +308,24 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) { used[state] = true; // creating a corresponding BlockItem - m_states[state] = m_blockItems.size(); - m_blockItems.push_back({.state = state, .blockIdx = blockIdx}); + states_[state] = block_items_.size(); + block_items_.push_back({.state = state, .block_idx = block_idx}); } // first and last states of the block will be used to create // a corresponding node - State first = partition[blockIdx].front(); - State last = partition[blockIdx].back(); + State first = partition[block_idx].front(); + State last = partition[block_idx].back(); // creating a corresponding block and node - m_nodes.push_back({.first = m_states[first], .last = m_states[last]}); - m_blocks.push_back({.nodeIdx = blockIdx}); + nodes_.push_back({.first = states_[first], .last = states_[last]}); + blocks_.push_back({.node_idx = block_idx}); } // we need to detect whether there is a state which has not be used // to create an additional partition block - bool allStatesUsed = true; + bool all_states_used = true; // first and last unused states will surround a contiguous subvector // of BlockItems @@ -331,7 +333,7 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) { State last = 0; // iterating through the vector of flags saying which states has been seen - for(State state = 0; state < numOfStates; ++state) { + for(State state = 0; state < num_of_states; ++state) { // if a state has been already seen and processed, // there is no need to add it to the additional block if(used[state]) { @@ -340,22 +342,22 @@ Partition::Partition(size_t numOfStates, const StateBlocks& partition) { // if there is at least one unused state, we need to // create an additional block - if(allStatesUsed) { - allStatesUsed = false; + if(all_states_used) { + all_states_used = false; first = state; - ++numOfBlocks; + ++num_of_blocks; } // creating the new BlockItem last = state; - m_states[state] = m_blockItems.size(); - m_blockItems.push_back({.state = state, .blockIdx = numOfBlocks-1}); + states_[state] = block_items_.size(); + block_items_.push_back({.state = state, .block_idx = num_of_blocks-1}); } // creating a new block and node if there was an unused state - if(!allStatesUsed) { - m_nodes.push_back({.first = m_states[first], .last = m_states[last]}); - m_blocks.push_back({.nodeIdx = numOfBlocks-1}); + if(!all_states_used) { + nodes_.push_back({.first = states_[first], .last = states_[last]}); + blocks_.push_back({.node_idx = num_of_blocks-1}); } } @@ -375,33 +377,33 @@ Partition::Partition(const Partition& other) { /** * @brief returns a BlockItem corresponding to the given index -* @param blockItemIdx index of the BlockItem +* @param block_item_idx index of the BlockItem * @return corresponding BlockItem */ -inline BlockItem Partition::getBlockItem(size_t blockItemIdx) const { - assert(blockItemIdx < numOfBlockItems() && +inline 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 m_blockItems[blockItemIdx]; + return block_items_[block_item_idx]; } /** * @brief returns a block corresponding to the given index -* @param blockIdx index of the block +* @param block_idx index of the block * @return corresponding block */ -inline Block Partition::getBlock(size_t blockIdx) const { - assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); - return m_blocks[blockIdx]; +inline Block Partition::get_block(size_t block_idx) const { + assert(block_idx < num_of_blocks() && "Nonexisting block index used."); + return blocks_[block_idx]; } /** * @brief returns a node corresponding to the given index -* @param nodeIdx index of the node +* @param node_idx index of the node * @return corresponding node */ -inline Node Partition::getNode(size_t nodeIdx) const { - assert(nodeIdx < numOfNodes() && "Nonexisting node index used."); - return m_nodes[nodeIdx]; +inline Node Partition::get_node(size_t node_idx) const { + assert(node_idx < num_of_nodes() && "Nonexisting node index used."); + return nodes_[node_idx]; } /** @@ -409,9 +411,9 @@ inline Node Partition::getNode(size_t nodeIdx) const { * @param state given state * @return corresponding block index */ -inline size_t Partition::getBlockIdxFromState(State state) const { - assert(state < numOfStates() && "Nonexisting state name used."); - return m_blockItems[m_states[state]].blockIdx; +inline 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; } /** @@ -419,9 +421,9 @@ inline size_t Partition::getBlockIdxFromState(State state) const { * @param state given state * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromState(State state) const { - assert(state < numOfStates() && "Nonexisting state name used."); - return m_blocks[m_blockItems[m_states[state]].blockIdx].nodeIdx; +inline 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; } /** @@ -429,51 +431,53 @@ inline size_t Partition::getNodeIdxFromState(State state) const { * @param state given state * @return corresponding BlockItem index */ -inline size_t Partition::getBlockItemIdxFromState(State state) const { - assert(state < numOfStates() && "Nonexisting state name used."); - return m_states[state]; +inline 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 blockItemIdx BlockItem index +* @param block_item_idx BlockItem index * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromBlockItemIdx(size_t blockItemIdx) const { - assert(blockItemIdx < numOfBlockItems() && +inline 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 m_blocks[m_blockItems[blockItemIdx].blockIdx].nodeIdx; + return blocks_[block_items_[block_item_idx].block_idx].node_idx; } /** * @brief returns a node index corresponding to the given block index -* @param blockIdx given block index +* @param block_idx given block index * @return corresponding node index */ -inline size_t Partition::getNodeIdxFromBlockIdx(size_t blockIdx) const { - assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); - return m_blocks[blockIdx].nodeIdx; +inline 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 blockIdx given block index +* @param block_idx given block index * @return first blockItem index corresponding to the given block index */ -inline size_t Partition::getReprIdxFromBlockIdx(size_t blockIdx) const { - assert(blockIdx < numOfBlocks() && "Nonexisting block index used."); - return m_nodes[m_blocks[blockIdx].nodeIdx].first; +inline 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 nodeIdx given node index +* @param node_idx given node index * @return first blockItem index corresponding to the given node index */ -inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const { - assert(nodeIdx < numOfNodes() && "Nonexisting node index used."); - return m_nodes[nodeIdx].first; +inline 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; } /** @@ -483,10 +487,10 @@ inline size_t Partition::getReprIdxFromNodeIdx(size_t nodeIdx) const { * @param second second state to be checked * @return true iff both given states belong to the same partition block */ -inline bool Partition::inSameBlock(State first, State second) const { - assert(first < m_states.size() && "The given state does not exist"); - assert(second < m_states.size() && "The given state does not exist"); - return getBlockIdxFromState(first) == getBlockIdxFromState(second); +inline 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); } /** @@ -494,12 +498,12 @@ inline bool Partition::inSameBlock(State first, State second) const { * @param states vector of states to be checked * @return true iff all of the given states belong to the same partition block */ -bool Partition::inSameBlock(const std::vector& states) const { +bool Partition::in_same_block(const std::vector& states) const { if(states.empty()) { return true; } - size_t blockIdx = getBlockIdxFromState(states.front()); + size_t block_idx = get_block_idx_from_state(states.front()); for(size_t state : states) { - assert(state < m_states.size() && "The given state does not exist."); - if(getBlockIdxFromState(state) != blockIdx) { return false; } + assert(state < states_.size() && "The given state does not exist."); + if(get_block_idx_from_state(state) != block_idx) { return false; } } return true; } @@ -509,18 +513,18 @@ bool Partition::inSameBlock(const std::vector& states) const { * @param state input state * @return vector of all the states in the corresponding block */ -std::vector Partition::statesInSameBlock(State state) const { - assert(state < numOfStates() && "The given state does not exist."); +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 = getNode(getNodeIdxFromState(state)).first; - size_t last = getNode(getNodeIdxFromState(state)).last; + size_t first = get_node(get_node_idx_from_state(state)).first; + size_t last = get_node(get_node_idx_from_state(state)).last; // iterating through BlockItems for(size_t i = first; i <= last; ++i) { - result.push_back(getBlockItem(i).state); + result.push_back(get_block_item(i).state); } return result; @@ -533,9 +537,9 @@ std::vector Partition::statesInSameBlock(State state) const { */ StateBlocks Partition::partition(void) { StateBlocks result{}; - result.insert(result.end(), m_blocks.size(), std::vector()); - for(auto blockItem : m_blockItems) { - result[blockItem.blockIdx].push_back(blockItem.state); + result.insert(result.end(), blocks_.size(), std::vector()); + for(auto block_item : block_items_) { + result[block_item.block_idx].push_back(block_item.state); } return result; } @@ -579,7 +583,7 @@ StateBlocks Partition::partition(void) { * @param marked marked states which influence splitting * @return vector of SplitPair which contains information about split blocks */ -std::vector Partition::splitBlocks( +std::vector Partition::split_blocks( const std::vector& marked) { // the vector which will be returned as the result @@ -590,48 +594,48 @@ std::vector Partition::splitBlocks( // this vector contains information about states which has been marked // and helps to detect states which has been marked multiple times - std::vector usedStates{}; - usedStates.insert(usedStates.end(), m_states.size(), false); + 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 usedBlocks{}; - usedBlocks.insert(usedBlocks.end(), m_blocks.size(), 0); + std::vector used_blocks{}; + used_blocks.insert(used_blocks.end(), blocks_.size(), 0); - // iterating through the marked states to fill usedStates and - // usedBlocks vectors + // iterating through the marked states to fill used_states and + // used_blocks vectors for(size_t i : marked) { - assert(i < m_states.size() && "The given state does not exist."); - assert(!usedStates[i] && "The given state was marked multiple times"); - usedStates[i] = true; - ++usedBlocks[getBlockIdxFromState(i)]; + 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)]; } - size_t oldBlocksSize, newBlockIdx; - oldBlocksSize = newBlockIdx = m_blocks.size(); + size_t old_blocks_size, new_block_idx; + old_blocks_size = new_block_idx = blocks_.size(); // iterating through existing blocks - for(size_t i = 0; i < oldBlocksSize; ++i) { + 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(!usedBlocks[i]) { continue; } + if(!used_blocks[i]) { continue; } // looking for the subvector of BlockItems which forms processed // block and computing its size - size_t nodeIdx = getNodeIdxFromBlockIdx(i); - size_t iterFirst = getNode(nodeIdx).first; - size_t iterLast = getNode(nodeIdx).last; - size_t blockSize = iterLast - iterFirst + 1; + 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; // if all states of the processed block have been marked, the block // won't be split - if(usedBlocks[i] >= blockSize) { continue; } + if(used_blocks[i] >= block_size) { continue; } // choosing the strategy of swapping BlocksItems such that // the representant of split block keeps its position - bool reprMarked = usedStates - [getBlockItem(getReprIdxFromNodeIdx(nodeIdx)).state]; + bool reprMarked = used_states[get_block_item( + get_repr_idx_from_node_idx(node_idx)).state]; // We access the first and last element of the subvector of BlockItems // which forms processed block. We look for the first unmarked element @@ -640,59 +644,59 @@ std::vector Partition::splitBlocks( // element is marked or not). As soon as such elements are found, they // are swapped. This procedure continues until these two indices used // to iterate through the BlockItems meet somewhere in the middle - while(iterFirst <= iterLast) { + while(iter_first <= iter_last) { // we choose the swapping strategy using XOR operation - while(reprMarked ^ !usedStates[getBlockItem(iterFirst).state]) { + while(reprMarked ^ !used_states[get_block_item(iter_first).state]) { // this visited state will be part of the former block - ++iterFirst; + ++iter_first; } - while(reprMarked ^ usedStates[getBlockItem(iterLast).state]) { + while(reprMarked ^ used_states[get_block_item(iter_last).state]) { // this visited state will be part of the new block - m_blockItems[iterLast].blockIdx = newBlockIdx; - --iterLast; + block_items_[iter_last].block_idx = new_block_idx; + --iter_last; } // if the used indices meet, we finish swapping - if(iterFirst > iterLast) { + if(iter_first > iter_last) { break; } // swapping BlockItems - BlockItem swappedBlockItem = getBlockItem(iterFirst); - m_blockItems[iterFirst] = getBlockItem(iterLast); - m_blockItems[iterLast] = swappedBlockItem; + 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; - // since m_states and m_blockItems vectors should be bijectively - // mapped, we need to update m_states after swapping two BlockItems - m_states[m_blockItems[iterFirst].state] = iterFirst; - m_states[m_blockItems[iterLast].state] = iterLast; + // 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; // after the blockItems are swapped, one of them should // be assigned to the new block - m_blockItems[iterLast].blockIdx = newBlockIdx; + block_items_[iter_last].block_idx = new_block_idx; // after the blockItems are swapped, we continue to the // next blockItems - ++iterFirst; - --iterLast; + ++iter_first; + --iter_last; } // creating new nodes - m_nodes.push_back({.first = m_nodes[nodeIdx].first, .last = iterLast}); - m_nodes.push_back({.first = iterFirst, .last = m_nodes[nodeIdx].last}); + nodes_.push_back({.first = nodes_[node_idx].first, .last = iter_last}); + nodes_.push_back({.first = iter_first, .last = nodes_[node_idx].last}); // split blocks has to refer to the new nodes - m_blocks[i].nodeIdx = m_nodes.size() - 2; - m_blocks.push_back({.nodeIdx = m_nodes.size() - 1}); + blocks_[i].node_idx = nodes_.size() - 2; + blocks_.push_back({.node_idx = nodes_.size() - 1}); // 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.push_back({.former = i, .created = newBlockIdx, - .oldNodeIdx = nodeIdx}); + split.push_back({.former = i, .created = new_block_idx, + .old_node_idx = node_idx}); // index of the following block which could be created - ++newBlockIdx; + ++new_block_idx; } return split; } @@ -709,24 +713,24 @@ 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 - m_states.reserve(other.numOfStates()); - m_blockItems.reserve(other.numOfStates()); - m_blocks.reserve(other.numOfStates()); - m_nodes.reserve(2 * other.numOfStates() - 1); + 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); // copying vectors without losing information about reserved capacity - size_t statesNum = other.numOfStates(); - for(size_t i = 0; i < statesNum; ++i) { - m_states.push_back(other.getBlockItemIdxFromState(i)); - m_blockItems.push_back(other.getBlockItem(i)); + size_t states_num = other.num_of_states(); + 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)); } - size_t blocksNum = other.numOfBlocks(); - for(size_t i = 0; i < blocksNum; ++i) { - m_blocks.push_back(other.getBlock(i)); + size_t blocks_num = other.num_of_blocks(); + for(size_t i = 0; i < blocks_num; ++i) { + blocks_.push_back(other.get_block(i)); } - size_t nodesNum = other.numOfNodes(); - for(size_t i = 0; i < nodesNum; ++i) { - m_nodes.push_back(other.getNode(i)); + size_t nodes_num = other.num_of_nodes(); + for(size_t i = 0; i < nodes_num; ++i) { + nodes_.push_back(other.get_node(i)); } return *this; } @@ -735,19 +739,19 @@ Partition& Partition::operator=(const Partition& other) { // the partition std::ostream& operator<<(std::ostream& os, const Partition& p) { std::string result = std::string(); - result += "NUM OF STATES: " + std::to_string(p.numOfStates()) + "\n"; - result += "NUM OF BLOCKS: " + std::to_string(p.numOfBlocks()) + "\n"; - result += "NUM OF NODES: " + std::to_string(p.numOfNodes()) + "\n"; + result += "NUM OF STATES: " + std::to_string(p.num_of_states()) + "\n"; + result += "NUM OF BLOCKS: " + std::to_string(p.num_of_blocks()) + "\n"; + result += "NUM OF NODES: " + std::to_string(p.num_of_nodes()) + "\n"; result += "\n"; result += "BLOCKS:\n"; - size_t numOfBlocks = p.numOfBlocks(); - for(size_t blockIdx = 0; blockIdx < numOfBlocks; ++blockIdx) { - result += std::to_string(blockIdx) + ": "; - Node node = p.m_nodes[p.getNodeIdxFromBlockIdx(blockIdx)]; - for(size_t blockItemIdx = node.first; - blockItemIdx <= node.last; ++blockItemIdx) { - result += std::to_string(p.m_blockItems[blockItemIdx].state) + 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 += "\n"; @@ -755,13 +759,14 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) { result += "\n"; result += "NODES:\n"; - size_t numOfNodes = p.numOfNodes(); - for(size_t nodeIdx = 0; nodeIdx < numOfNodes; ++nodeIdx) { - result += std::to_string(nodeIdx) + ": "; - Node node = p.m_nodes[nodeIdx]; - for(size_t blockItemIdx = node.first; - blockItemIdx <= node.last; ++blockItemIdx) { - result += std::to_string(p.m_blockItems[blockItemIdx].state) + " "; + 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 += "\n"; } @@ -825,10 +830,10 @@ struct ExtendableSquareMatrix { protected: // number of rows (or columns) of the current square matrix - size_t m_size{0}; + size_t size_{0}; // maximal allowed number of rows (or columns) of the square matrix - size_t m_capacity{0}; + size_t capacity_{0}; // type of the matrix which will be chosen as soon as the // child structure will be created @@ -837,8 +842,8 @@ struct ExtendableSquareMatrix { public: // getters - inline size_t size(void) const { return m_size; } - inline size_t capacity(void) const { return m_capacity; } + inline size_t size(void) const { return size_; } + inline size_t capacity(void) const { return capacity_; } inline size_t type(void) const { return m_type; } // virtual functions which will be implemented in the substructures @@ -853,9 +858,9 @@ struct ExtendableSquareMatrix { virtual ~ExtendableSquareMatrix() = default; // matrix properties - bool isReflexive(void); - bool isAntisymetric(void); - bool isTransitive(void); + bool is_reflexive(void); + bool is_antisymetric(void); + bool is_transitive(void); }; @@ -921,12 +926,12 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector - std::vector data{}; + std::vector data_{}; public: // constructors - CascadeSquareMatrix(size_t maxRows, size_t initRows = 0); + CascadeSquareMatrix(size_t max_rows, size_t init_rows = 0); CascadeSquareMatrix(const CascadeSquareMatrix& other); // implemented virtual functions @@ -945,27 +950,27 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix { /** * @brief creates a Cascade square matrix -* @param maxRows capacity of the square matrix -* @param initRows initial size of the square matrix +* @param max_rows capacity of the square matrix +* @param init_rows initial size of the square matrix */ template CascadeSquareMatrix::CascadeSquareMatrix( - size_t maxRows, size_t initRows) { + size_t max_rows, size_t init_rows) { - assert(initRows <= maxRows && + assert(init_rows <= max_rows && "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Cascade; - this->m_capacity = maxRows; - data.reserve(this->m_capacity * this->m_capacity); + this->capacity_ = max_rows; + data_.reserve(this->capacity_ * this->capacity_); // creating the initial size and filling the data cells with // default values - for(size_t i = 0; i < initRows; ++i) {extend();} + for(size_t i = 0; i < init_rows; ++i) {extend();} } /** This method provides a way to create a copy of a given CascadeSquareMatrix -* and preserves the reserved capacity of the vector 'data'. This goal is +* and preserves the reserved capacity of the vector 'data_'. This goal is * achieved using the custom assignment operator. * @brief copy constructor of a CascadeSquareMatrix * @param other matrix which should be copied @@ -985,11 +990,11 @@ CascadeSquareMatrix::CascadeSquareMatrix( */ template inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); // accessing the matrix in the cascading way - data[i >= j ? i * i + j : j * j + 2 * j - i] = value; + data_[i >= j ? i * i + j : j * j + 2 * j - i] = value; } /** @@ -1000,11 +1005,11 @@ inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) { */ template inline T CascadeSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); // accessing the matrix in the cascading way - return data[i >= j ? i * i + j : j * j + 2 * j - i]; + return data_[i >= j ? i * i + j : j * j + 2 * j - i]; } /** @@ -1014,19 +1019,19 @@ inline T CascadeSquareMatrix::get(size_t i, size_t j) const { */ template inline void CascadeSquareMatrix::extend(T placeholder) { - assert(this->m_size < this->m_capacity + assert(this->size_ < this->capacity_ && "The matrix cannot be extended anymore"); // allocation of 2 * size + 1 new data cells - data.insert(data.end(), 2 * this->m_size + 1, placeholder); + data_.insert(data_.end(), 2 * this->size_ + 1, placeholder); // the size increases - ++this->m_size; + ++this->size_; } /** This method provides a way to assign a CascadeSquareMatrix to the variable. -* The method ensures us to keep the reserved capacity of the vector 'data' since -* the default vector assignment does not preserve it. +* The method ensures us to keep the reserved capacity of the vector 'data_' +* since the default vector assignment does not preserve it. * @brief assignment operator for the CascadeSquareMatrix structure * @param other matrix which should be copied assigned */ @@ -1035,16 +1040,16 @@ CascadeSquareMatrix& CascadeSquareMatrix::operator=( const CascadeSquareMatrix& other) { // initialization of the matrix - this->m_capacity = other.capacity(); - this->m_size = 0; - this->data = std::vector(); - this->data.reserve(this->m_capacity * this->m_capacity); - size_t otherSize = other.size(); - for(size_t i = 0; i < otherSize; ++i) {this->extend();} + this->capacity_ = other.capacity(); + this->size_ = 0; + this->data_ = std::vector(); + this->data_.reserve(this->capacity_ * this->capacity_); + size_t other_size = other.size(); + for(size_t i = 0; i < other_size; ++i) {this->extend();} // copying memory cells - for(size_t i = 0; i < this->m_size; ++i) { - for(size_t j = 0; j < this->m_size; ++j) { + for(size_t i = 0; i < this->size_; ++i) { + for(size_t j = 0; j < this->size_; ++j) { this->set(i, j, other.get(i, j)); } } @@ -1077,12 +1082,12 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector - std::vector> data{}; + std::vector> data_{}; public: // constructors - DynamicSquareMatrix(size_t maxRows, size_t initRows = 0); + DynamicSquareMatrix(size_t max_rows, size_t init_rows = 0); // implemented virtual functions inline void set(size_t i, size_t j, T value) override; @@ -1096,22 +1101,22 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix { /** * @brief creates a Dynamic square matrix -* @param maxRows capacity of the square matrix -* @param initRows initial size of the square matrix +* @param max_rows capacity of the square matrix +* @param init_rows initial size of the square matrix */ template DynamicSquareMatrix::DynamicSquareMatrix( - size_t maxRows, size_t initRows) { + size_t max_rows, size_t init_rows) { - assert(initRows <= maxRows && + assert(init_rows <= max_rows && "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Dynamic; - this->m_capacity = maxRows; + this->capacity_ = max_rows; // creating the initial size and filling the data cells with // default values - for(size_t i = 0; i < initRows; ++i) {extend();} + for(size_t i = 0; i < init_rows; ++i) {extend();} } /** @@ -1122,10 +1127,10 @@ DynamicSquareMatrix::DynamicSquareMatrix( */ template inline T DynamicSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); - return data[i][j]; + return data_[i][j]; } /** @@ -1136,10 +1141,10 @@ inline T DynamicSquareMatrix::get(size_t i, size_t j) const { */ template inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); - data[i][j] = value; + data_[i][j] = value; } /** @@ -1148,18 +1153,18 @@ inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) { */ template void DynamicSquareMatrix::extend(T placeholder) { - assert(this->m_size < this->m_capacity + assert(this->size_ < this->capacity_ && "The matrix cannot be extened anymore"); // creating a new column - for(size_t i = 0; i < this->m_size; ++i) { - data[i].push_back(placeholder); + for(size_t i = 0; i < this->size_; ++i) { + data_[i].push_back(placeholder); } // creating a new row - data.push_back(std::vector()); - ++this->m_size; - data.back().insert(data.back().end(), this->m_size, placeholder); + data_.push_back(std::vector()); + ++this->size_; + data_.back().insert(data_.back().end(), this->size_, placeholder); } /************************************* @@ -1182,12 +1187,12 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a hashmap - mutable std::unordered_map data{}; + mutable std::unordered_map data_{}; public: // constructors - HashedSquareMatrix(size_t maxRows, size_t initRows = 0); + HashedSquareMatrix(size_t max_rows, size_t init_rows = 0); // implemented virtual functions inline void set(size_t i, size_t j, T value) override; @@ -1202,22 +1207,22 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix { /** * @brief creates a Hashed square matrix -* @param maxRows capacity of the square matrix -* @param initRows initial size of the square matrix +* @param max_rows capacity of the square matrix +* @param init_rows initial size of the square matrix */ template HashedSquareMatrix::HashedSquareMatrix( - size_t maxRows, size_t initRows) { + size_t max_rows, size_t init_rows) { - assert(initRows <= maxRows && + assert(init_rows <= max_rows && "Initial size of the matrix cannot be bigger than the capacity"); this->m_type = MatrixType::Hashed; - this->m_capacity = maxRows; + this->capacity_ = max_rows; // creating the initial size and filling the data cells with // default values - for(size_t i = 0; i < initRows; ++i) {extend();} + for(size_t i = 0; i < init_rows; ++i) {extend();} } /** @@ -1228,11 +1233,11 @@ HashedSquareMatrix::HashedSquareMatrix( */ template inline void HashedSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); // accessing the hashmap using row matrix traversal - data[i * this->m_capacity + j] = value; + data_[i * this->capacity_ + j] = value; } /** @@ -1243,11 +1248,11 @@ inline void HashedSquareMatrix::set(size_t i, size_t j, T value) { */ template inline T HashedSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->m_size && "Nonexisting row cannot be accessed"); - assert(j < this->m_size && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); // accessing the hashmap using row matrix traversal - return data[i * this->m_capacity + j]; + return data_[i * this->capacity_ + j]; } /** @@ -1256,18 +1261,18 @@ inline T HashedSquareMatrix::get(size_t i, size_t j) const { */ template inline void HashedSquareMatrix::extend(T placeholder) { - assert(this->m_size < this->m_capacity + assert(this->size_ < this->capacity_ && "Matrix cannot be extened anymore"); // creating a new row and column - for(size_t i = 0; i < this->m_size; ++i) { - data[this->m_size * this->m_capacity + i] = placeholder; - data[i * this->m_capacity + this->m_size] = placeholder; + for(size_t i = 0; i < this->size_; ++i) { + data_[this->size_ * this->capacity_ + i] = placeholder; + data_[i * this->capacity_ + this->size_] = placeholder; } - data[this->m_size * this->m_capacity + this->m_size] = placeholder; + data_[this->size_ * this->capacity_ + this->size_] = placeholder; // increasing size - ++this->m_size; + ++this->size_; } /************************************* @@ -1326,7 +1331,7 @@ std::ostream& operator<<(std::ostream& os, * @return true iff the matrix is reflexive */ template -bool ExtendableSquareMatrix::isReflexive(void) { +bool ExtendableSquareMatrix::is_reflexive(void) { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { if(!get(i, i)) { return false; } @@ -1342,7 +1347,7 @@ bool ExtendableSquareMatrix::isReflexive(void) { * @return true iff the matrix is antisymetric */ template -bool ExtendableSquareMatrix::isAntisymetric(void) { +bool ExtendableSquareMatrix::is_antisymetric(void) { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { @@ -1361,7 +1366,7 @@ bool ExtendableSquareMatrix::isAntisymetric(void) { * @return true iff the matrix is transitive */ template -bool ExtendableSquareMatrix::isTransitive(void) { +bool ExtendableSquareMatrix::is_transitive(void) { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 2e6eadbca..317ea4e8a 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -8,226 +8,232 @@ TEST_CASE("mata::utils::Partition") { SECTION("Create a simple partition with 1 block") { Partition p{10}; - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 1); - CHECK(p.numOfNodes() == 1); - CHECK(p.inSameBlock({})); - CHECK(p.inSameBlock({0})); - CHECK(p.inSameBlock(0, 1)); - CHECK(p.inSameBlock(1, 8)); - CHECK(p.inSameBlock({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + 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.in_same_block({})); + CHECK(p.in_same_block({0})); + 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.getBlockItemIdxFromState(i) == i); - CHECK(p.getBlockIdxFromState(i) == 0); - CHECK(p.getNodeIdxFromState(i) == 0); - CHECK(p.getBlockItem(i).state == i); - CHECK(p.getBlockItem(i).blockIdx == 0); - CHECK(p.getNodeIdxFromBlockItemIdx(i) == 0); + 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); } - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).blockIdx == 0); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).blockIdx == 0); - CHECK(p.getNode(0).first == 0); - CHECK(p.getNode(0).last == 9); - CHECK(p.getBlock(0).nodeIdx == 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); } SECTION("Create a simple partition with 2 blocks") { Partition p{10, {{0, 5, 8}}}; - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 2); - CHECK(p.numOfNodes() == 2); - CHECK(p.inSameBlock({})); - CHECK(p.inSameBlock({0})); - CHECK(p.inSameBlock(0, 5)); - CHECK(p.inSameBlock(5, 8)); - CHECK(!p.inSameBlock(6, 5)); - CHECK(p.inSameBlock({0, 5, 8})); - CHECK(p.inSameBlock({1, 2, 3, 4, 6, 7, 9})); - CHECK(!p.inSameBlock({1, 2, 3, 4, 5, 7, 9})); + CHECK(p.num_of_states() == 10); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 2); + CHECK(p.num_of_nodes() == 2); + CHECK(p.in_same_block({})); + CHECK(p.in_same_block({0})); + CHECK(p.in_same_block(0, 5)); + CHECK(p.in_same_block(5, 8)); + CHECK(!p.in_same_block(6, 5)); + 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.getBlockItemIdxFromState(0) == 0); - CHECK(p.getBlockItem(0).state == 0); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getNodeIdxFromState(0) == 0); - CHECK(p.getBlockItem(0).blockIdx == 0); - CHECK(p.getNodeIdxFromBlockItemIdx(0) == 0); + 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.getBlockItemIdxFromState(1) == 3); - CHECK(p.getBlockItem(3).state == 1); - CHECK(p.getBlockIdxFromState(1) == 1); - CHECK(p.getNodeIdxFromState(1) == 1); - CHECK(p.getBlockItem(3).blockIdx == 1); - CHECK(p.getNodeIdxFromBlockItemIdx(3) == 1); + 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.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).state == 1); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).blockIdx == 0); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).blockIdx == 1); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).blockIdx == 0); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).state == 1); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).blockIdx == 1); - CHECK(p.getNode(0).first == 0); - CHECK(p.getNode(0).last == 2); - CHECK(p.getNode(1).first == 3); - CHECK(p.getNode(1).last == 9); - CHECK(p.getBlock(0).nodeIdx == 0); - CHECK(p.getBlock(1).nodeIdx == 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); } SECTION("Create a simple partition with 3 blocks") { Partition p{6, {{0}, {1, 2}}}; - CHECK(p.numOfStates() == 6); - CHECK(p.numOfBlockItems() == 6); - CHECK(p.numOfBlocks() == 3); - CHECK(p.numOfNodes() == 3); - CHECK(p.inSameBlock({})); - CHECK(p.inSameBlock({0})); - CHECK(p.inSameBlock(3, 5)); - CHECK(p.inSameBlock(1, 2)); - CHECK(!p.inSameBlock(1, 4)); - CHECK(p.inSameBlock({3, 4, 5})); - CHECK(!p.inSameBlock({2, 3, 4, 5})); + 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.getBlockItemIdxFromState(i) == i); - CHECK(p.getBlockItem(i).state == i); + CHECK(p.get_block_item_idx_from_state(i) == i); + CHECK(p.get_block_item(i).state == i); } - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getNodeIdxFromState(0) == 0); - CHECK(p.getBlockItem(0).blockIdx == 0); - CHECK(p.getNodeIdxFromBlockItemIdx(0) == 0); - CHECK(p.getBlockIdxFromState(1) == 1); - CHECK(p.getNodeIdxFromState(1) == 1); - CHECK(p.getBlockItem(1).blockIdx == 1); - CHECK(p.getNodeIdxFromBlockItemIdx(1) == 1); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(1)).state == 1); - CHECK(p.getBlockItem(p.getReprIdxFromBlockIdx(2)).state == 3); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(0)).state == 0); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(1)).state == 1); - CHECK(p.getBlockItem(p.getReprIdxFromNodeIdx(2)).state == 3); - CHECK(p.getNode(0).first == 0); - CHECK(p.getNode(0).last == 0); - CHECK(p.getNode(1).first == 1); - CHECK(p.getNode(1).last == 2); - CHECK(p.getNode(2).first == 3); - CHECK(p.getNode(2).last == 5); - CHECK(p.getBlock(0).nodeIdx == 0); - CHECK(p.getBlock(1).nodeIdx == 1); - CHECK(p.getBlock(2).nodeIdx == 2); + 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); } SECTION("Splitting blocks") { Partition p{10}; - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 1); - CHECK(p.numOfNodes() == 1); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 0); - CHECK(p.inSameBlock({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - p.splitBlocks({0, 1, 2, 3, 4}); - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 2); - CHECK(p.numOfNodes() == 3); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 1); - CHECK(p.inSameBlock({0, 1, 2, 3, 4})); - CHECK(p.inSameBlock({5, 6, 7, 8, 9})); - p.splitBlocks({0, 1, 2, 5, 6, 7}); - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 4); - CHECK(p.numOfNodes() == 7); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 3); - CHECK(p.inSameBlock({0, 1, 2})); - CHECK(p.inSameBlock({3, 4})); - CHECK(p.inSameBlock({5, 6, 7})); - CHECK(p.inSameBlock({8, 9})); - p.splitBlocks({0, 3, 5, 8}); - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 8); - CHECK(p.numOfNodes() == 15); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 7); - CHECK(p.inSameBlock({0})); - CHECK(p.inSameBlock({1, 2})); - CHECK(p.inSameBlock({3})); - CHECK(p.inSameBlock({4})); - CHECK(p.inSameBlock({5})); - CHECK(p.inSameBlock({6, 7})); - CHECK(p.inSameBlock({8})); - CHECK(p.inSameBlock({9})); - p.splitBlocks({1, 6}); - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 10); - CHECK(p.numOfNodes() == 19); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 7); - p.splitBlocks({0, 2, 4, 6, 8}); - CHECK(p.numOfStates() == 10); - CHECK(p.numOfBlockItems() == 10); - CHECK(p.numOfBlocks() == 10); - CHECK(p.numOfNodes() == 19); - CHECK(p.getBlockIdxFromState(0) == 0); - CHECK(p.getBlockIdxFromState(9) == 7); + 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.get_block_idx_from_state(0) == 0); + CHECK(p.get_block_idx_from_state(9) == 0); + CHECK(p.in_same_block({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + 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.in_same_block({0, 1, 2, 3, 4})); + CHECK(p.in_same_block({5, 6, 7, 8, 9})); + 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.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})); + 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.get_block_idx_from_state(0) == 0); + CHECK(p.get_block_idx_from_state(9) == 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})); + 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.get_block_idx_from_state(0) == 0); + CHECK(p.get_block_idx_from_state(9) == 7); + 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.get_block_idx_from_state(0) == 0); + CHECK(p.get_block_idx_from_state(9) == 7); } SECTION("Custom copying and assigning") { Partition p = Partition(5, {{2, 3}}); - p.splitBlocks({0}); + p.split_blocks({0}); Partition q = p; Partition r = Partition(p); - CHECK(p.numOfStates() == q.numOfStates()); - CHECK(p.numOfStates() == r.numOfStates()); - CHECK(p.numOfBlockItems() == q.numOfBlockItems()); - CHECK(p.numOfBlockItems() == r.numOfBlockItems()); - CHECK(p.numOfBlocks() == q.numOfBlocks()); - CHECK(p.numOfBlocks() == r.numOfBlocks()); - CHECK(p.numOfNodes() == q.numOfNodes()); - CHECK(p.numOfNodes() == r.numOfNodes()); + CHECK(p.num_of_states() == q.num_of_states()); + CHECK(p.num_of_states() == r.num_of_states()); + CHECK(p.num_of_block_items() == q.num_of_block_items()); + CHECK(p.num_of_block_items() == r.num_of_block_items()); + CHECK(p.num_of_blocks() == q.num_of_blocks()); + CHECK(p.num_of_blocks() == r.num_of_blocks()); + CHECK(p.num_of_nodes() == q.num_of_nodes()); + CHECK(p.num_of_nodes() == r.num_of_nodes()); - size_t statesNum = p.numOfStates(); - size_t blocksNum = p.numOfBlocks(); - size_t nodesNum = p.numOfNodes(); + size_t statesNum = p.num_of_states(); + size_t blocksNum = p.num_of_blocks(); + size_t nodesNum = p.num_of_nodes(); for(size_t i = 0; i < statesNum; ++i) { - CHECK(p.getBlockItemIdxFromState(i) == - q.getBlockItemIdxFromState(i)); - CHECK(p.getBlockItemIdxFromState(i) == - r.getBlockItemIdxFromState(i)); - CHECK(p.getBlockItem(i).state == q.getBlockItem(i).state); - CHECK(p.getBlockItem(i).state == r.getBlockItem(i).state); - CHECK(p.getBlockItem(i).blockIdx == q.getBlockItem(i).blockIdx); - CHECK(p.getBlockItem(i).blockIdx == r.getBlockItem(i).blockIdx); + 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); } for(size_t i = 0; i < blocksNum; ++i) { - CHECK(p.getBlock(i).nodeIdx == q.getBlock(i).nodeIdx); - CHECK(p.getBlock(i).nodeIdx == r.getBlock(i).nodeIdx); + 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); } for(size_t i = 0; i < nodesNum; ++i) { - CHECK(p.getNode(i).first == q.getNode(i).first); - CHECK(p.getNode(i).first == r.getNode(i).first); - CHECK(p.getNode(i).last == q.getNode(i).last); - CHECK(p.getNode(i).last == r.getNode(i).last); + 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); } - q.splitBlocks({1, 2}); - r.splitBlocks({1, 2}); + q.split_blocks({1, 2}); + r.split_blocks({1, 2}); } } @@ -248,24 +254,24 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(0, 0, 1); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(!e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); delete e; } @@ -282,24 +288,24 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(0, 0, 1); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(!e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); delete e; } @@ -316,24 +322,24 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->size() == 4); CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(0, 0, 1); - CHECK(!e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); - CHECK(e->isReflexive()); - CHECK(e->isAntisymetric()); - CHECK(!e->isTransitive()); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); delete e; } From 9a8deed7afb416abd8ad459d9e9be3bbd86ba4f0 Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 04:19:50 +0100 Subject: [PATCH 11/34] lowerCamelCase converted to snake_case. Member variables converted from m_varName to var_name_ --- include/mata/utils/partition-relation-pair.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 75eba0ff4..c65d1ad55 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -634,7 +634,7 @@ std::vector Partition::split_blocks( // choosing the strategy of swapping BlocksItems such that // the representant of split block keeps its position - bool reprMarked = used_states[get_block_item( + bool repr_marked = used_states[get_block_item( get_repr_idx_from_node_idx(node_idx)).state]; // We access the first and last element of the subvector of BlockItems @@ -646,11 +646,11 @@ std::vector Partition::split_blocks( // to iterate through the BlockItems meet somewhere in the middle while(iter_first <= iter_last) { // we choose the swapping strategy using XOR operation - while(reprMarked ^ !used_states[get_block_item(iter_first).state]) { + while(repr_marked ^ !used_states[get_block_item(iter_first).state]) { // this visited state will be part of the former block ++iter_first; } - while(reprMarked ^ used_states[get_block_item(iter_last).state]) { + while(repr_marked ^ used_states[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; --iter_last; From b5d176796745d74960ebd589017e6093190af59a Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 04:55:19 +0100 Subject: [PATCH 12/34] Partition, ExtendableSquareMatrix and its children are now classes (not structures) --- include/mata/utils/partition-relation-pair.hh | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index c65d1ad55..0ee6e93bf 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -19,9 +19,9 @@ * Thus, (P, Rel) corresponds to a preorder relation R over states S. * * This file provides implementation of a partition P and defines the - * ExtendableSquareMatrix structure which can be used to represent + * ExtendableSquareMatrix class which can be used to represent * the binary relation Rel. - * These structures can be combined to represent the preorder R. + * These classes can be combined to represent the preorder R. * * @author Tomáš Kocourek */ @@ -177,7 +177,7 @@ using SplitPair = struct SplitPair { * | | * ---------------------------------------------------- * - * Using this structure, we can: + * Using this class, we can: * - find the block which contains 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) @@ -188,7 +188,7 @@ using SplitPair = struct SplitPair { * - remember all ancestors of current blocks and access them * */ -typedef struct Partition { +class Partition { private: /* indices to the block_items_ vector */ @@ -251,9 +251,9 @@ typedef struct Partition { -} Partition; // Partition +}; // Partition -/** Constructor of the partition structure. This method reserves memory space +/** 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. * The partition can be initialized in linear time (in respect to the carrier @@ -794,26 +794,26 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) { * @brief interface for extendable square matrix implementations * * Square matrix "n x n" which can be extended to "(n+1) x (n+1)" matrix - * if n is less than the maximal capacity. Such structure allows us to + * if n is less than the maximal capacity. Such a class allows us to * represent binary relations over carrier set with n elements and adjust * it to n+1 elements whenever a new element of the carrier set is created * (for example when we represent relation over partition and a block of * partition is split in two) or matrices of counters etc. * - * This abstract structure declares methods for accessing elements of the + * This abstract class declares methods for accessing elements of the * matrix, assigning to the cells of matrix and extending the matrix by one row * and one column (changing the size). * It defines attributes for maximal capacity (which cannot be changed) and * current size. - * It does not define the data structure for storing data. Each substructure - * which inherits from this abstract structure should: + * It does not define the data structure for storing data. Each subclass + * which inherits from this abstract class should: * - contain the storage for data of datatype T which represents n x n matrix * - implement methods set, get and extend * - implement a method clone which creates a deep copy of a matrix * Then, the ExtendableSquareMatrix can be used independently of the inner * representation of the matrix. Therefore, one can dynamically choose from * various of implementations depending on the situation. If any new - * substructure is implemented, one should also modify the 'create' + * subclass is implemented, one should also modify the 'create' * function and extend 'MatrixType' enumerator. * * Note that in context of an n x n matrix, this implementation uses the word @@ -826,7 +826,7 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) { using MatrixType = enum MatrixType { None, Cascade, Dynamic, Hashed }; template -struct ExtendableSquareMatrix { +class ExtendableSquareMatrix { protected: // number of rows (or columns) of the current square matrix @@ -836,7 +836,7 @@ struct ExtendableSquareMatrix { size_t capacity_{0}; // type of the matrix which will be chosen as soon as the - // child structure will be created + // child class will be created MatrixType m_type{MatrixType::None}; public: @@ -846,7 +846,7 @@ struct ExtendableSquareMatrix { inline size_t capacity(void) const { return capacity_; } inline size_t type(void) const { return m_type; } - // virtual functions which will be implemented in the substructures + // virtual functions which will be implemented in the subclasses // according to the concrete representation of the matrix virtual inline void set(size_t i, size_t j, T value = T()) = 0; virtual inline T get(size_t i, size_t j) const = 0; @@ -862,7 +862,7 @@ struct ExtendableSquareMatrix { bool is_antisymetric(void); bool is_transitive(void); -}; +}; // ExtendableSquareMatrix /************************************* * @@ -922,7 +922,7 @@ struct ExtendableSquareMatrix { * **/ template -struct CascadeSquareMatrix : public ExtendableSquareMatrix { +class CascadeSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector @@ -946,7 +946,7 @@ struct CascadeSquareMatrix : public ExtendableSquareMatrix { // operators CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other); -}; +}; // CascadeSquareMatrix /** * @brief creates a Cascade square matrix @@ -1032,7 +1032,7 @@ inline void CascadeSquareMatrix::extend(T placeholder) { /** This method provides a way to assign a CascadeSquareMatrix to the variable. * The method ensures us to keep the reserved capacity of the vector 'data_' * since the default vector assignment does not preserve it. -* @brief assignment operator for the CascadeSquareMatrix structure +* @brief assignment operator for the CascadeSquareMatrix class * @param other matrix which should be copied assigned */ template @@ -1078,7 +1078,7 @@ CascadeSquareMatrix& CascadeSquareMatrix::operator=( * extended, it could possibly be moved in the memory. **/ template -struct DynamicSquareMatrix : public ExtendableSquareMatrix { +class DynamicSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a single vector @@ -1097,7 +1097,8 @@ struct DynamicSquareMatrix : public ExtendableSquareMatrix { // cloning DynamicSquareMatrix *clone(void) const { return new DynamicSquareMatrix(*this); } -}; + +}; // DynamicSquareMatrix /** * @brief creates a Dynamic square matrix @@ -1183,7 +1184,7 @@ void DynamicSquareMatrix::extend(T placeholder) { * matrix. To access matrix[i][j], we use map[i * capacity + j]. **/ template -struct HashedSquareMatrix : public ExtendableSquareMatrix { +class HashedSquareMatrix : public ExtendableSquareMatrix { private: // data are stored in a hashmap @@ -1203,7 +1204,7 @@ struct HashedSquareMatrix : public ExtendableSquareMatrix { HashedSquareMatrix *clone(void) const { return new HashedSquareMatrix(*this); } -}; +}; // HashedSquareMatrix /** * @brief creates a Hashed square matrix From b0f4bb666c1281473bf1a2decc3c3845bf89343b Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 05:16:14 +0100 Subject: [PATCH 13/34] Class methods are no longer markes as 'inline' --- include/mata/utils/partition-relation-pair.hh | 101 +++++++++--------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 0ee6e93bf..09dbc8bde 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -211,34 +211,32 @@ class Partition { Partition(const Partition& other); // sizes of the used vectors - inline size_t num_of_states(void) const { return states_.size(); } - inline size_t num_of_block_items(void) const { - return block_items_.size();} - inline size_t num_of_blocks(void) const { return blocks_.size(); } - inline size_t num_of_nodes(void) const { return nodes_.size(); } + size_t num_of_states(void) const { return states_.size(); } + size_t num_of_block_items(void) const { return block_items_.size(); } + size_t num_of_blocks(void) const { return blocks_.size(); } + size_t num_of_nodes(void) const { return nodes_.size(); } // blocks splitting std::vector split_blocks(const std::vector& marked); // basic information about the partition - inline bool in_same_block(State first, State second) 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 - inline BlockItem get_block_item(size_t block_item_idx) const; - inline Block get_block(size_t block_idx) const; - inline Node get_node(size_t node_idx) const; + BlockItem get_block_item(size_t block_item_idx) const; + Block get_block(size_t block_idx) const; + Node get_node(size_t node_idx) const; // refering between blockItems, blocks, nodes using indices - inline size_t get_block_idx_from_state(State state) const; - inline size_t get_node_idx_from_state(State state) const; - inline size_t get_block_item_idx_from_state(State state) const; - inline size_t get_node_idx_from_block_item_idx( - size_t block_item_idx) const; - inline size_t get_node_idx_from_block_idx(size_t block_idx) const; - inline size_t get_repr_idx_from_block_idx(size_t block_idx) const; - inline size_t get_repr_idx_from_node_idx(size_t node_idx) const; + 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; // converts the partition to the vector of vectors of states StateBlocks partition(void); @@ -380,7 +378,7 @@ Partition::Partition(const Partition& other) { * @param block_item_idx index of the BlockItem * @return corresponding BlockItem */ -inline BlockItem Partition::get_block_item(size_t block_item_idx) const { +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]; @@ -391,7 +389,7 @@ inline BlockItem Partition::get_block_item(size_t block_item_idx) const { * @param block_idx index of the block * @return corresponding block */ -inline Block Partition::get_block(size_t block_idx) const { +Block Partition::get_block(size_t block_idx) const { assert(block_idx < num_of_blocks() && "Nonexisting block index used."); return blocks_[block_idx]; } @@ -401,7 +399,7 @@ inline Block Partition::get_block(size_t block_idx) const { * @param node_idx index of the node * @return corresponding node */ -inline Node Partition::get_node(size_t node_idx) const { +Node Partition::get_node(size_t node_idx) const { assert(node_idx < num_of_nodes() && "Nonexisting node index used."); return nodes_[node_idx]; } @@ -411,7 +409,7 @@ inline Node Partition::get_node(size_t node_idx) const { * @param state given state * @return corresponding block index */ -inline size_t Partition::get_block_idx_from_state(State state) const { +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; } @@ -421,7 +419,7 @@ inline size_t Partition::get_block_idx_from_state(State state) const { * @param state given state * @return corresponding node index */ -inline size_t Partition::get_node_idx_from_state(State state) const { +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; } @@ -431,7 +429,7 @@ inline size_t Partition::get_node_idx_from_state(State state) const { * @param state given state * @return corresponding BlockItem index */ -inline size_t Partition::get_block_item_idx_from_state(State state) const { +size_t Partition::get_block_item_idx_from_state(State state) const { assert(state < num_of_states() && "Nonexisting state name used."); return states_[state]; } @@ -441,7 +439,7 @@ inline size_t Partition::get_block_item_idx_from_state(State state) const { * @param block_item_idx BlockItem index * @return corresponding node index */ -inline size_t Partition::get_node_idx_from_block_item_idx( +size_t Partition::get_node_idx_from_block_item_idx( size_t block_item_idx) const { assert(block_item_idx < num_of_block_items() && @@ -454,7 +452,7 @@ inline size_t Partition::get_node_idx_from_block_item_idx( * @param block_idx given block index * @return corresponding node index */ -inline size_t Partition::get_node_idx_from_block_idx(size_t block_idx) const { +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; } @@ -465,7 +463,7 @@ inline size_t Partition::get_node_idx_from_block_idx(size_t block_idx) const { * @param block_idx given block index * @return first blockItem index corresponding to the given block index */ -inline size_t Partition::get_repr_idx_from_block_idx(size_t block_idx) const { +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; } @@ -475,7 +473,7 @@ inline size_t Partition::get_repr_idx_from_block_idx(size_t block_idx) const { * @param node_idx given node index * @return first blockItem index corresponding to the given node index */ -inline size_t Partition::get_repr_idx_from_node_idx(size_t node_idx) const { +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; } @@ -487,7 +485,7 @@ inline size_t Partition::get_repr_idx_from_node_idx(size_t node_idx) const { * @param second second state to be checked * @return true iff both given states belong to the same partition block */ -inline bool Partition::in_same_block(State first, State second) 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); @@ -646,7 +644,8 @@ std::vector Partition::split_blocks( // to iterate through the BlockItems meet somewhere in the middle while(iter_first <= iter_last) { // we choose the swapping strategy using XOR operation - while(repr_marked ^ !used_states[get_block_item(iter_first).state]) { + while(repr_marked + ^ !used_states[get_block_item(iter_first).state]) { // this visited state will be part of the former block ++iter_first; } @@ -842,15 +841,15 @@ class ExtendableSquareMatrix { public: // getters - inline size_t size(void) const { return size_; } - inline size_t capacity(void) const { return capacity_; } - inline size_t type(void) const { return m_type; } + size_t size(void) const { return size_; } + size_t capacity(void) const { return capacity_; } + size_t type(void) const { return m_type; } // virtual functions which will be implemented in the subclasses // according to the concrete representation of the matrix - virtual inline void set(size_t i, size_t j, T value = T()) = 0; - virtual inline T get(size_t i, size_t j) const = 0; - virtual inline void extend(T placeholder = T()) = 0; + virtual void set(size_t i, size_t j, T value = T()) = 0; + virtual T get(size_t i, size_t j) const = 0; + virtual void extend(T placeholder = T()) = 0; // cloning virtual ExtendableSquareMatrix *clone(void) const = 0; @@ -935,9 +934,9 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { CascadeSquareMatrix(const CascadeSquareMatrix& other); // implemented virtual functions - inline void set(size_t i, size_t j, T value) override; - inline T get(size_t i, size_t j) const override; - inline void extend(T placeholder = T()) override; + void set(size_t i, size_t j, T value) override; + T get(size_t i, size_t j) const override; + void extend(T placeholder = T()) override; // cloning CascadeSquareMatrix *clone(void) const { @@ -989,7 +988,7 @@ CascadeSquareMatrix::CascadeSquareMatrix( * @param value value to be assigned to the square matrix data cell */ template -inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) { +void CascadeSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1004,7 +1003,7 @@ inline void CascadeSquareMatrix::set(size_t i, size_t j, T value) { * @return value found in the square matrix data cell */ template -inline T CascadeSquareMatrix::get(size_t i, size_t j) const { +T CascadeSquareMatrix::get(size_t i, size_t j) const { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1018,7 +1017,7 @@ inline T CascadeSquareMatrix::get(size_t i, size_t j) const { * (optional) */ template -inline void CascadeSquareMatrix::extend(T placeholder) { +void CascadeSquareMatrix::extend(T placeholder) { assert(this->size_ < this->capacity_ && "The matrix cannot be extended anymore"); @@ -1090,8 +1089,8 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { DynamicSquareMatrix(size_t max_rows, size_t init_rows = 0); // implemented virtual functions - inline void set(size_t i, size_t j, T value) override; - inline T get(size_t i, size_t j) const override; + void set(size_t i, size_t j, T value) override; + T get(size_t i, size_t j) const override; void extend(T placeholder = T()) override; // cloning @@ -1127,7 +1126,7 @@ DynamicSquareMatrix::DynamicSquareMatrix( * @param value value to be assigned to the square matrix data cell */ template -inline T DynamicSquareMatrix::get(size_t i, size_t j) const { +T DynamicSquareMatrix::get(size_t i, size_t j) const { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1141,7 +1140,7 @@ inline T DynamicSquareMatrix::get(size_t i, size_t j) const { * @return value found in the square matrix data cell */ template -inline void DynamicSquareMatrix::set(size_t i, size_t j, T value) { +void DynamicSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1196,9 +1195,9 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { HashedSquareMatrix(size_t max_rows, size_t init_rows = 0); // implemented virtual functions - inline void set(size_t i, size_t j, T value) override; - inline T get(size_t i, size_t j) const override; - inline void extend(T placeholder = T()) override; + void set(size_t i, size_t j, T value) override; + T get(size_t i, size_t j) const override; + void extend(T placeholder = T()) override; // cloning HashedSquareMatrix *clone(void) const { @@ -1233,7 +1232,7 @@ HashedSquareMatrix::HashedSquareMatrix( * @param value value to be assigned to the square matrix data cell */ template -inline void HashedSquareMatrix::set(size_t i, size_t j, T value) { +void HashedSquareMatrix::set(size_t i, size_t j, T value) { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1248,7 +1247,7 @@ inline void HashedSquareMatrix::set(size_t i, size_t j, T value) { * @return value found in the square matrix data cell */ template -inline T HashedSquareMatrix::get(size_t i, size_t j) const { +T HashedSquareMatrix::get(size_t i, size_t j) const { assert(i < this->size_ && "Nonexisting row cannot be accessed"); assert(j < this->size_ && "Nonexisting column cannot be accessed"); @@ -1261,7 +1260,7 @@ inline T HashedSquareMatrix::get(size_t i, size_t j) const { * @param placeholder a value which will be assigned to all the new data cells */ template -inline void HashedSquareMatrix::extend(T placeholder) { +void HashedSquareMatrix::extend(T placeholder) { assert(this->size_ < this->capacity_ && "Matrix cannot be extened anymore"); From 1fe0be0b582176640c75b58bf7414053950814e5 Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 05:56:30 +0100 Subject: [PATCH 14/34] push_back replaced by emplace_back when creating object/structure --- include/mata/utils/partition-relation-pair.hh | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 09dbc8bde..09b41f67b 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -307,7 +307,7 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // creating a corresponding BlockItem states_[state] = block_items_.size(); - block_items_.push_back({.state = state, .block_idx = block_idx}); + block_items_.emplace_back(state, block_idx); } @@ -317,8 +317,8 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { State last = partition[block_idx].back(); // creating a corresponding block and node - nodes_.push_back({.first = states_[first], .last = states_[last]}); - blocks_.push_back({.node_idx = block_idx}); + nodes_.emplace_back(states_[first], states_[last]); + blocks_.emplace_back(block_idx); } // we need to detect whether there is a state which has not be used @@ -349,13 +349,13 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // creating the new BlockItem last = state; states_[state] = block_items_.size(); - block_items_.push_back({.state = state, .block_idx = num_of_blocks-1}); + block_items_.emplace_back(state, num_of_blocks-1); } // creating a new block and node if there was an unused state if(!all_states_used) { - nodes_.push_back({.first = states_[first], .last = states_[last]}); - blocks_.push_back({.node_idx = num_of_blocks-1}); + nodes_.emplace_back(states_[first], states_[last]); + blocks_.emplace_back(num_of_blocks-1); } } @@ -681,18 +681,17 @@ std::vector Partition::split_blocks( } // creating new nodes - nodes_.push_back({.first = nodes_[node_idx].first, .last = iter_last}); - nodes_.push_back({.first = iter_first, .last = nodes_[node_idx].last}); + nodes_.emplace_back(nodes_[node_idx].first, iter_last); + nodes_.emplace_back(iter_first, nodes_[node_idx].last); // split blocks has to refer to the new nodes blocks_[i].node_idx = nodes_.size() - 2; - blocks_.push_back({.node_idx = nodes_.size() - 1}); + blocks_.emplace_back(nodes_.size() - 1); // 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.push_back({.former = i, .created = new_block_idx, - .old_node_idx = node_idx}); + split.emplace_back(i, new_block_idx, node_idx); // index of the following block which could be created ++new_block_idx; @@ -1162,7 +1161,7 @@ void DynamicSquareMatrix::extend(T placeholder) { } // creating a new row - data_.push_back(std::vector()); + data_.emplace_back(); ++this->size_; data_.back().insert(data_.back().end(), this->size_, placeholder); } From ca925ac822a03bf04cdb5352234a215147cce93e Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 27 Mar 2024 21:07:33 +0100 Subject: [PATCH 15/34] constructors added to the structures --- include/mata/utils/partition-relation-pair.hh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 09b41f67b..9f14019a2 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -54,15 +54,21 @@ 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) {} }; using Block = struct Block { 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; @@ -73,6 +79,9 @@ 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) {} }; /** From 4a9d255f907772a34e708c0b2b6b138d941d80fd Mon Sep 17 00:00:00 2001 From: kocotom Date: Thu, 28 Mar 2024 05:27:31 +0100 Subject: [PATCH 16/34] methods get_block_item, get_block, get_node return const reference to the desired element by now --- include/mata/utils/partition-relation-pair.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 9f14019a2..3f8c5b13b 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -234,9 +234,9 @@ class Partition { std::vector states_in_same_block(State state) const; // accessing blockItems, blocks, nodes through indices - BlockItem get_block_item(size_t block_item_idx) const; - Block get_block(size_t block_idx) const; - Node get_node(size_t node_idx) const; + 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; @@ -387,7 +387,7 @@ Partition::Partition(const Partition& other) { * @param block_item_idx index of the BlockItem * @return corresponding BlockItem */ -BlockItem Partition::get_block_item(size_t block_item_idx) const { +const 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]; @@ -398,7 +398,7 @@ BlockItem Partition::get_block_item(size_t block_item_idx) const { * @param block_idx index of the block * @return corresponding block */ -Block Partition::get_block(size_t block_idx) const { +const Block& Partition::get_block(size_t block_idx) const { assert(block_idx < num_of_blocks() && "Nonexisting block index used."); return blocks_[block_idx]; } @@ -408,7 +408,7 @@ Block Partition::get_block(size_t block_idx) const { * @param node_idx index of the node * @return corresponding node */ -Node Partition::get_node(size_t node_idx) const { +const Node& Partition::get_node(size_t node_idx) const { assert(node_idx < num_of_nodes() && "Nonexisting node index used."); return nodes_[node_idx]; } From ded88c5ade91abc187cc2210e8e35f705094f38d Mon Sep 17 00:00:00 2001 From: kocotom Date: Thu, 28 Mar 2024 20:21:25 +0100 Subject: [PATCH 17/34] Ordinary functions marked as inline, definitions of the methods of the ExtendableSquareMatrix placed after the definition of the class --- include/mata/utils/partition-relation-pair.hh | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 3f8c5b13b..680330f25 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -871,6 +871,65 @@ class ExtendableSquareMatrix { }; // ExtendableSquareMatrix +/** This function checks whether the matrix is reflexive. In this +* context, the matrix is reflexive iff none of the elements on the main +* diagonal is the zero element of the type T +* @brief checks whether the Extendable square matrix is reflexive +* @return true iff the matrix is reflexive +*/ +template +bool ExtendableSquareMatrix::is_reflexive(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + if(!get(i, i)) { return false; } + } + return true; +} + +/** This function checks whether the matrix is antisymetric. In this +* context, the matrix is antisymetric iff there are no indices i, j +* where i != j and both matrix[i][j], matrix[j][i] contain nonzero elementes +* of the type T +* @brief checks whether the Extendable square matrix is antisymetric +* @return true iff the matrix is antisymetric +*/ +template +bool ExtendableSquareMatrix::is_antisymetric(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { + if(i == j) { continue; } + if(get(i, j) && get(j, i)) { return false; } + } + } + return true; +} + +/** This function checks whether the matrix is transitive. In this +* context, the matrix is transitive iff it holds that the input matrix +* casted to the matrix of booleans (false for zero values of type T, otherwise +* true) remains the same if it is multiplied by itself. +* @brief checks whether the Extendable square matrix is transitive +* @return true iff the matrix is transitive +*/ +template +bool ExtendableSquareMatrix::is_transitive(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { + bool found = false; + for(size_t k = 0; k < size; ++k) { + if(get(i, k) && get(k, j)) { + found = true; + break; + } + } + if(!found == static_cast(get(i, j))) { return false; } + } + } + return true; +} + /************************************* * * CASCADE SQUARE MATRIX @@ -1297,7 +1356,7 @@ void HashedSquareMatrix::extend(T placeholder) { * @return pointer to the newly created matrix */ template -ExtendableSquareMatrix *create(MatrixType type, +inline ExtendableSquareMatrix *create(MatrixType type, size_t capacity, size_t size = 0) { switch(type) { @@ -1315,7 +1374,7 @@ ExtendableSquareMatrix *create(MatrixType type, // debugging function which allows us to print text representation of // the Extendable square matrix template -std::ostream& operator<<(std::ostream& os, +inline std::ostream& operator<<(std::ostream& os, const ExtendableSquareMatrix& matrix) { size_t size = matrix.size(); @@ -1332,65 +1391,6 @@ std::ostream& operator<<(std::ostream& os, return os << result; } -/** This function checks whether the matrix is reflexive. In this -* context, the matrix is reflexive iff none of the elements on the main -* diagonal is the zero element of the type T -* @brief checks whether the Extendable square matrix is reflexive -* @return true iff the matrix is reflexive -*/ -template -bool ExtendableSquareMatrix::is_reflexive(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - if(!get(i, i)) { return false; } - } - return true; -} - -/** This function checks whether the matrix is antisymetric. In this -* context, the matrix is antisymetric iff there are no indices i, j -* where i != j and both matrix[i][j], matrix[j][i] contain nonzero elementes -* of the type T -* @brief checks whether the Extendable square matrix is antisymetric -* @return true iff the matrix is antisymetric -*/ -template -bool ExtendableSquareMatrix::is_antisymetric(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - for(size_t j = 0; j < size; ++j) { - if(i == j) { continue; } - if(get(i, j) && get(j, i)) { return false; } - } - } - return true; -} - -/** This function checks whether the matrix is transitive. In this -* context, the matrix is transitive iff it holds that the input matrix -* casted to the matrix of booleans (false for zero values of type T, otherwise -* true) remains the same if it is multiplied by itself. -* @brief checks whether the Extendable square matrix is transitive -* @return true iff the matrix is transitive -*/ -template -bool ExtendableSquareMatrix::is_transitive(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - for(size_t j = 0; j < size; ++j) { - bool found = false; - for(size_t k = 0; k < size; ++k) { - if(get(i, k) && get(k, j)) { - found = true; - break; - } - } - if(!found == static_cast(get(i, j))) { return false; } - } - } - return true; -} - } #endif From 686bd5d57ba07de5f5de30a8e6956da415252cbe Mon Sep 17 00:00:00 2001 From: kocotom Date: Fri, 29 Mar 2024 06:16:32 +0100 Subject: [PATCH 18/34] Several tests added to increase the test coverage --- tests/partition-relation-pair.cc | 86 +++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 317ea4e8a..8cd5734d3 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -34,6 +34,9 @@ TEST_CASE("mata::utils::Partition") { 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 a simple partition with 2 blocks") { @@ -81,6 +84,10 @@ TEST_CASE("mata::utils::Partition") { 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.states_in_same_block(0).size() == 3); + CHECK(p.states_in_same_block(1).size() == 7); + CHECK(p.partition().size() == 2); } @@ -124,6 +131,11 @@ TEST_CASE("mata::utils::Partition") { 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("Splitting blocks") { @@ -135,6 +147,8 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.get_block_idx_from_state(0) == 0); CHECK(p.get_block_idx_from_state(9) == 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); @@ -144,6 +158,9 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.get_block_idx_from_state(9) == 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); @@ -155,6 +172,11 @@ TEST_CASE("mata::utils::Partition") { 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); @@ -170,6 +192,15 @@ TEST_CASE("mata::utils::Partition") { 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); @@ -177,6 +208,17 @@ TEST_CASE("mata::utils::Partition") { 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.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); @@ -184,6 +226,36 @@ TEST_CASE("mata::utils::Partition") { 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.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); + } + + SECTION("Complicated blocks splitting with swapping") { + Partition p{10}; + 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)); } SECTION("Custom copying and assigning") @@ -233,7 +305,10 @@ TEST_CASE("mata::utils::Partition") { } q.split_blocks({1, 2}); - r.split_blocks({1, 2}); + r.split_blocks({1, 2}); + + std::cout << q; + std::cout << r; } } @@ -342,7 +417,14 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(!e->is_transitive()); delete e; } - + + SECTION("Matrix of the None type") { + + ExtendableSquareMatrix *e = create( + None, 5, 2); + CHECK(e == nullptr); + + } SECTION("Copying matrices") { From ca7c0d5d0f90125d58db2e9db9c392e46fac1a48 Mon Sep 17 00:00:00 2001 From: kocotom Date: Fri, 29 Mar 2024 06:34:12 +0100 Subject: [PATCH 19/34] while cycle replaced by do-while to increase test coverage --- include/mata/utils/partition-relation-pair.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh index 680330f25..248a537c1 100644 --- a/include/mata/utils/partition-relation-pair.hh +++ b/include/mata/utils/partition-relation-pair.hh @@ -651,7 +651,7 @@ std::vector Partition::split_blocks( // element is marked or not). As soon as such elements are found, they // are swapped. This procedure continues until these two indices used // to iterate through the BlockItems meet somewhere in the middle - while(iter_first <= iter_last) { + do { // we choose the swapping strategy using XOR operation while(repr_marked ^ !used_states[get_block_item(iter_first).state]) { @@ -687,7 +687,7 @@ std::vector Partition::split_blocks( // next blockItems ++iter_first; --iter_last; - } + } while(iter_first <= iter_last); // creating new nodes nodes_.emplace_back(nodes_[node_idx].first, iter_last); From bc8f7457bc9f326d7d89c5a35ab598850c5ff903 Mon Sep 17 00:00:00 2001 From: kocotom Date: Fri, 29 Mar 2024 06:41:15 +0100 Subject: [PATCH 20/34] Several tests added to increase the test coverage --- tests/partition-relation-pair.cc | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 8cd5734d3..49bf9054c 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -38,6 +38,39 @@ TEST_CASE("mata::utils::Partition") { 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); + CHECK(p.num_of_block_items() == 10); + CHECK(p.num_of_blocks() == 1); + CHECK(p.num_of_nodes() == 1); + CHECK(p.in_same_block({})); + CHECK(p.in_same_block({0})); + 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); + } + 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 a simple partition with 2 blocks") { Partition p{10, {{0, 5, 8}}}; @@ -423,8 +456,44 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { ExtendableSquareMatrix *e = create( None, 5, 2); CHECK(e == nullptr); + + delete e; } + + SECTION("Empty matrices") { + + ExtendableSquareMatrix *e1 = create( + Cascade, 5); + ExtendableSquareMatrix *e2 = create( + Dynamic, 5); + ExtendableSquareMatrix *e3 = create( + Hashed, 5); + + CHECK(!e1->size()); + CHECK(!e2->size()); + CHECK(!e3->size()); + + CHECK(e1->capacity() == 5); + CHECK(e2->capacity() == 5); + CHECK(e3->capacity() == 5); + + ExtendableSquareMatrix *c1 = e1; + ExtendableSquareMatrix *c2 = e2; + ExtendableSquareMatrix *c3 = e3; + + CHECK(!c1->size()); + CHECK(!c2->size()); + CHECK(!c3->size()); + + CHECK(c1->capacity() == 5); + CHECK(c2->capacity() == 5); + CHECK(c3->capacity() == 5); + + delete e1; + delete e2; + delete e3; + } SECTION("Copying matrices") { From c2d06a03c10cba55b865e9d6bd46f5f8d118a4c9 Mon Sep 17 00:00:00 2001 From: kocotom Date: Fri, 29 Mar 2024 07:01:46 +0100 Subject: [PATCH 21/34] Several tests added to increase the test coverage --- tests/partition-relation-pair.cc | 34 +++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 49bf9054c..9d47ea490 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -462,7 +462,6 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { } SECTION("Empty matrices") { - ExtendableSquareMatrix *e1 = create( Cascade, 5); ExtendableSquareMatrix *e2 = create( @@ -495,6 +494,39 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { delete e3; } + SECTION("Matrices with only one element") { + ExtendableSquareMatrix *e1 = create( + Cascade, 5, 1); + ExtendableSquareMatrix *e2 = create( + Dynamic, 5, 1); + ExtendableSquareMatrix *e3 = create( + Hashed, 5, 1); + + CHECK(e1->size() == 1); + CHECK(e2->size() == 1); + CHECK(e3->size() == 1); + + CHECK(e1->capacity() == 5); + CHECK(e2->capacity() == 5); + CHECK(e3->capacity() == 5); + + ExtendableSquareMatrix *c1 = e1; + ExtendableSquareMatrix *c2 = e2; + ExtendableSquareMatrix *c3 = e3; + + CHECK(c1->size() == 1); + CHECK(c2->size() == 1); + CHECK(c3->size() == 1); + + CHECK(c1->capacity() == 5); + CHECK(c2->capacity() == 5); + CHECK(c3->capacity() == 5); + + delete e1; + delete e2; + delete e3; + } + SECTION("Copying matrices") { ExtendableSquareMatrix *m1 = create(Cascade, 1000, 2); From b581b7b020ba7d2a81721261283cf5c0b82b575d Mon Sep 17 00:00:00 2001 From: kocotom Date: Fri, 29 Mar 2024 07:22:51 +0100 Subject: [PATCH 22/34] Few tests modified --- tests/partition-relation-pair.cc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/partition-relation-pair.cc b/tests/partition-relation-pair.cc index 9d47ea490..ce1d8c572 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition-relation-pair.cc @@ -477,9 +477,9 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e2->capacity() == 5); CHECK(e3->capacity() == 5); - ExtendableSquareMatrix *c1 = e1; - ExtendableSquareMatrix *c2 = e2; - ExtendableSquareMatrix *c3 = e3; + ExtendableSquareMatrix *c1 = e1->clone(); + ExtendableSquareMatrix *c2 = e2->clone(); + ExtendableSquareMatrix *c3 = e3->clone(); CHECK(!c1->size()); CHECK(!c2->size()); @@ -491,7 +491,11 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { delete e1; delete e2; - delete e3; + delete e3; + delete c1; + delete c2; + delete c3; + } SECTION("Matrices with only one element") { @@ -510,9 +514,9 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e2->capacity() == 5); CHECK(e3->capacity() == 5); - ExtendableSquareMatrix *c1 = e1; - ExtendableSquareMatrix *c2 = e2; - ExtendableSquareMatrix *c3 = e3; + ExtendableSquareMatrix *c1 = e1->clone(); + ExtendableSquareMatrix *c2 = e2->clone(); + ExtendableSquareMatrix *c3 = e3->clone(); CHECK(c1->size() == 1); CHECK(c2->size() == 1); @@ -525,6 +529,10 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { delete e1; delete e2; delete e3; + delete c1; + delete c2; + delete c3; + } SECTION("Copying matrices") { From 462fba5a59ffa1d7cafb2fbf4c2520744ed552f0 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 30 Mar 2024 20:49:26 +0100 Subject: [PATCH 23/34] Partition and ExtendableSquareMatrix source files/tests split into more files --- .../mata/utils/extendable-square-matrix.hh | 702 +++++++++ include/mata/utils/partition-relation-pair.hh | 1396 ----------------- include/mata/utils/partition.hh | 261 +++ src/CMakeLists.txt | 1 + src/partition.cc | 560 +++++++ tests/CMakeLists.txt | 3 +- tests/extendable-square-matrix.cc | 238 +++ ...artition-relation-pair.cc => partition.cc} | 236 +-- 8 files changed, 1765 insertions(+), 1632 deletions(-) create mode 100644 include/mata/utils/extendable-square-matrix.hh delete mode 100644 include/mata/utils/partition-relation-pair.hh create mode 100644 include/mata/utils/partition.hh create mode 100644 src/partition.cc create mode 100644 tests/extendable-square-matrix.cc rename tests/{partition-relation-pair.cc => partition.cc} (66%) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh new file mode 100644 index 000000000..cf88abcc2 --- /dev/null +++ b/include/mata/utils/extendable-square-matrix.hh @@ -0,0 +1,702 @@ +/** @file extendable-square-matrix.hh + * @brief Definition of an extendable square matrix + * + * Description: + * + * An extendable square matrix is a n x n square matrix (where n <= capacity) + * which can be extended to the (n+1) x (n+1) matrix (if (n+1) <= capacity) + * whenever it is necessary. + * + * Such a matrix is able to represent a binary relation over a set, a matrix of + * counters etc. The data type of the matrix cells is templated. + * + * This file contains an abstract class ExtendableSquareMatrix which does + * not contain the exact inner representation of the matrix. + * + * The file also contains several concrete subclasses of the + * ExtendableSquareMatrix class, namely: + * - CascadeSquareMatrix + * - DynamicSquareMatrix + * - HashedSquareMatrix + * which implement the inner representation of the matrix on its own. The + * data cells will be accessible exclusively through the methods which are + * virtual in context of the abstract class ExtendableSquareMatrix and which + * are implemented within the subclasses. + * + * Using this class, it is possible to: + * - get a value of the matrix cell using two indices in O(1) + * - set a value of the matrix cell using two indices in O(1) + * - extend a n x n matrix to a (n+1) x (n+1) matrix in O(n) + * - implement and use custom inner representation of the matrix + * - choose a data type of the matrix cells + * + * @author Tomáš Kocourek + */ + +#ifndef _EXTENDABLE_SQUARE_MATRIX_HH_ +#define _EXTENDABLE_SQUARE_MATRIX_HH_ + +#include +#include +#include +#include + +namespace mata::utils { + +/************************************************************************ +* +* +* EXTENDABLE SQUARE MATRIX +* +* +* (RELATIONS AND COUNTERS) +* +* +*************************************************************************/ + +/** + * ExtendableSquareMatrix + * + * @brief interface for extendable square matrix implementations + * + * Square matrix "n x n" which can be extended to "(n+1) x (n+1)" matrix + * if n is less than the maximal capacity. Such a class allows us to + * represent binary relations over carrier set with n elements and adjust + * it to n+1 elements whenever a new element of the carrier set is created + * (for example when we represent relation over partition and a block of + * partition is split in two) or matrices of counters etc. + * + * This abstract class declares methods for accessing elements of the + * matrix, assigning to the cells of matrix and extending the matrix by one row + * and one column (changing the size). + * It defines attributes for maximal capacity (which cannot be changed) and + * current size. + * It does not define the data structure for storing data. Each subclass + * which inherits from this abstract class should: + * - contain the storage for data of datatype T which represents n x n matrix + * - implement methods set, get and extend + * - implement a method clone which creates a deep copy of a matrix + * Then, the ExtendableSquareMatrix can be used independently of the inner + * representation of the matrix. Therefore, one can dynamically choose from + * various of implementations depending on the situation. If any new + * subclass is implemented, one should also modify the 'create' + * function and extend 'MatrixType' enumerator. + * + * Note that in context of an n x n matrix, this implementation uses the word + * 'size' to refer to the number n (number of rows or columns). The word + * 'capacity' corresponds to the maximal allowed size (maximal number + * of rows or columns). + * +**/ + +using MatrixType = enum MatrixType { None, Cascade, Dynamic, Hashed }; + +template +class ExtendableSquareMatrix { + protected: + + // number of rows (or columns) of the current square matrix + size_t size_{0}; + + // maximal allowed number of rows (or columns) of the square matrix + size_t capacity_{0}; + + // type of the matrix which will be chosen as soon as the + // child class will be created + MatrixType m_type{MatrixType::None}; + + public: + + // + // GETTERS + // + + /** Returns a size of the matrix. In this context, + * the size of an n x n matrix corresponds to the value n. + * @brief returns the size of the matrix + * @return size of the matrix + */ + size_t size(void) const { return size_; } + + /** Returns a capacity of the matrix. In this context, + * the capacity of an n x n matrix corresponds to the value n_max such + * that if the matrix is extended to the n_max x n_max matrix, it cannot + * be extended anymore. + * @brief returns the capacity of the matrix + * @return capacity of the matrix + */ + size_t capacity(void) const { return capacity_; } + + /** Returns a type of the matrix. The type specifies the + * way the inner representation of the matrix is implemented. + * @brief returns the type of the matrix + * @return type of the matrix + */ + size_t type(void) const { return m_type; } + + // + // VIRTUAL METHODS + // + // These virtual methods will be implemented in the subclasses according + // to allow to the concrete representation of the matrix. These methods + // provide a way to access the contents of the matrix + // + + /** + * @brief Assigns a value to the cell of the matrix + * @param[in] i row of the matrix + * @param[in] j column of the matrix + * @param[in] value a value which will be assigned to the memory cell + */ + virtual void set(size_t i, size_t j, T value = T()) = 0; + + /** + * @brief Finds a value of the matrix memory cell + * @param[in] i row of the matrix + * @param[in] j column of the matrix + * @return a found value in the matrix + */ + virtual T get(size_t i, size_t j) const = 0; + + /** + * @brief changes the n x n matrix to the (n+1) x (n+1) matrix + * @param[in] placeholde value which will be assigned to the + * newly allocated memory cells + */ + virtual void extend(T placeholder = T()) = 0; + + // + // CLONING + // + + /** + * @brief creates a deep copy of the matrix + * @return deep copy of the matrix + */ + virtual ExtendableSquareMatrix *clone(void) const = 0; + + virtual ~ExtendableSquareMatrix() = default; + + // + // MATRIX PROPERTIES + // + + /** This function checks whether the matrix is reflexive. In this + * context, the matrix is reflexive iff none of the elements on the main + * diagonal is the zero element of the type T + * @brief checks whether the Extendable square matrix is reflexive + * @return true iff the matrix is reflexive + */ + bool is_reflexive(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + if(!get(i, i)) { return false; } + } + return true; + } + + /** This function checks whether the matrix is antisymetric. In this + * context, the matrix is antisymetric iff there are no indices i, j + * where i != j and both matrix[i][j], matrix[j][i] contain nonzero + * elementes of the type T + * @brief checks whether the Extendable square matrix is antisymetric + * @return true iff the matrix is antisymetric + */ + bool is_antisymetric(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { + if(i == j) { continue; } + if(get(i, j) && get(j, i)) { return false; } + } + } + return true; + } + + /** This function checks whether the matrix is transitive. In this + * context, the matrix is transitive iff it holds that the input matrix + * casted to the matrix of booleans (false for zero values of type T, + * otherwise true) remains the same if it is multiplied by itself. + * @brief checks whether the Extendable square matrix is transitive + * @return true iff the matrix is transitive + */ + bool is_transitive(void) { + size_t size = this->size(); + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { + bool found = false; + for(size_t k = 0; k < size; ++k) { + if(get(i, k) && get(k, j)) { + found = true; + break; + } + } + if(!found == static_cast(get(i, j))) { return false; } + } + } + return true; + } + + +}; // ExtendableSquareMatrix + + + +/************************************* +* +* CASCADE SQUARE MATRIX +* +**************************************/ + +/** + * CascadeSquareMatrix + * + * @brief Linearized square matrix implemented using single vector of + * elements which stores data in some kind of "cascading" way + * + * This implementation tries to avoid + * - moving the whole matrix when it is extended + * - allocation of unneccessary data cells + * - violation of data locality + * + * The matrix is represented as a single vector of a type T. Initially, + * the maximal possible capacity is given to the constructor. It reserves + * 'capacity * capacity' data cells for the vector (in the constant time O(1)) + * without need to allocate anything. + * When the matrix is extended, additional (size * 2) + 1 elements of the + * vector are allocated. The matrix is traversed in some kind of "cascading" way + * as follows: + * + * Each number in the matrix corresponds to the order of accessing that element + * using this "cascading traversal". + * + * MATRIX: + * ----------------- + * | 0 | 3 | 8 | 15| + * ----------------- + * | 1 | 2 | 7 | 14| + * ----------------- + * | 4 | 5 | 6 | 13| + * ----------------- + * | 9 | 10| 11| 12| + * ----------------- + * + * VECTOR: + * ----------------------------------------------------------------------- + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + * ----------------------------------------------------------------------- + * + * The data cell matrix[i][j] could be accessed using the formula + * vector[i >= j ? i * i + j : j * j + 2 * j - i]. + * + * Using this approach, there is no need to allocate unnecessary data cells + * when extending n x n matrix to the (n+1) x (n+1) matrix (in contrast with + * using row or column traversal). + * Since 'capacity * capacity' data cells were reserved, the vector won't ever + * be moved in the memory due to the extending. However, it requieres to + * reserve a lot of memory space when the capacity is a huge number. + * The data locality won't be violated since all of the elements will be stored + * as a contiguous vector. + * +**/ +template +class CascadeSquareMatrix : public ExtendableSquareMatrix { + private: + + // data are stored in a single vector + std::vector data_{}; + + public: + + // + // CONSTRUCTORS + // + + /** + * @brief creates a Cascade square matrix + * @param[in] max_rows capacity of the square matrix + * @param[in] init_rows initial size of the square matrix + */ + CascadeSquareMatrix(size_t max_rows, size_t init_rows) { + assert(init_rows <= max_rows && + "Initial size of the matrix cannot be" + "bigger than the capacity"); + + this->m_type = MatrixType::Cascade; + this->capacity_ = max_rows; + data_.reserve(this->capacity_ * this->capacity_); + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < init_rows; ++i) {extend();} + } + + /** This method provides a way to create a copy of a given + * CascadeSquareMatrix and preserves the reserved capacity of the vector + * 'data_'. This goal is achieved using the custom assignment operator. + * @brief copy constructor of a CascadeSquareMatrix + * @param other matrix which should be copied + */ + CascadeSquareMatrix(const CascadeSquareMatrix& other) { + *this = other; + } + + // + // IMPLEMENTED VIRTUAL METHODS + // + + /** + * @brief assings a value to the Cascade square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @param[in] value value to be assigned to the square matrix data cell + */ + void set(size_t i, size_t j, T value) { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + // accessing the matrix in the cascading way + data_[i >= j ? i * i + j : j * j + 2 * j - i] = value; + } + + /** + * @brief returns a value of the Cascade square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @return value found in the square matrix data cell + */ + T get(size_t i, size_t j) const { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + // accessing the matrix in the cascading way + return data_[i >= j ? i * i + j : j * j + 2 * j - i]; + } + + /** + * @brief extends the Cascade square matrix by a new row and column + * @param[in] placeholder a value which will be assigned to all the new + * data cells (optional) + */ + void extend(T placeholder = T()) { + assert(this->size_ < this->capacity_ + && "The matrix cannot be extended anymore"); + + // allocation of 2 * size + 1 new data cells + data_.insert(data_.end(), 2 * this->size_ + 1, placeholder); + + // the size increases + ++this->size_; + } + + // + // CLONING + // + + CascadeSquareMatrix *clone(void) const { + return new CascadeSquareMatrix(*this); } + + // + // OPERATORS + // + + /** This method provides a way to assign a CascadeSquareMatrix + * to the variable. The method ensures us to keep the reserved + * capacity of the vector 'data_' since the default vector + * assignment does not preserve it. + * @brief assignment operator for the CascadeSquareMatrix class + * @param[in] other matrix which should be copied assigned + */ + CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other) { + // initialization of the matrix + this->capacity_ = other.capacity(); + this->size_ = 0; + this->data_ = std::vector(); + this->data_.reserve(this->capacity_ * this->capacity_); + size_t other_size = other.size(); + for(size_t i = 0; i < other_size; ++i) {this->extend();} + + // copying memory cells + for(size_t i = 0; i < this->size_; ++i) { + for(size_t j = 0; j < this->size_; ++j) { + this->set(i, j, other.get(i, j)); + } + } + return *this; + } + +}; // CascadeSquareMatrix + +/************************************* +* +* DYNAMIC SQUARE MATRIX +* +**************************************/ + +/** + * DynamicSquareMatrix + * + * @brief Dynamic square matrix implemented using vector of vectors + * of the type T + * + * This implementation tries to avoid + * - allocation or reservation of data cells which won't ever be used + * + * The matrix is represented as a vector of vectors of the type T. It is + * extended dynamically without any need to allocate or reserve any unnecessary + * memory space before it is used. + * However, the data locality is not ensured. Moreover, if the matrix is + * extended, it could possibly be moved in the memory. +**/ +template +class DynamicSquareMatrix : public ExtendableSquareMatrix { + private: + + // data are stored in a vector of vectors + std::vector> data_{}; + + public: + + // + // CONSTRUCTORS + // + + /** + * @brief creates a Dynamic square matrix + * @param[in] max_rows capacity of the square matrix + * @param[in] init_rows initial size of the square matrix + */ + DynamicSquareMatrix(size_t max_rows, size_t init_rows) { + assert(init_rows <= max_rows && + "Initial size of the matrix cannot" + "be bigger than the capacity"); + + this->m_type = MatrixType::Dynamic; + this->capacity_ = max_rows; + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < init_rows; ++i) {extend();} + } + + // + // IMPLEMENTED VIRTUAL METHODS + // + + /** + * @brief assings a value to the Dynamic square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @param[in] value value to be assigned to the square matrix data cell + */ + T get(size_t i, size_t j) const { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + return data_[i][j]; + } + + /** + * @brief returns a value of the Dynamic square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @return value found in the square matrix data cell + */ + void set(size_t i, size_t j, T value) { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + data_[i][j] = value; + } + + /** + * @brief extends the Dynamic square matrix by a new row and column + * @param[in] placeholder a value which will be assigned to all the + * new data cells + */ + void extend(T placeholder = T()) { + assert(this->size_ < this->capacity_ + && "The matrix cannot be extened anymore"); + + // creating a new column + for(size_t i = 0; i < this->size_; ++i) { + data_[i].push_back(placeholder); + } + + // creating a new row + data_.emplace_back(); + ++this->size_; + data_.back().insert(data_.back().end(), this->size_, placeholder); + } + + // + // CLONING + // + + DynamicSquareMatrix *clone(void) const { + return new DynamicSquareMatrix(*this); } + +}; // DynamicSquareMatrix + + + +/************************************* +* +* HASHED SQUARE MATRIX +* +**************************************/ + +/** + * HashedSquareMatrix + * + * @brief Hashed square matrix implemented using unordered hash map + * + * The matrix is represented as a unordered hash map of the type T indexed + * by the size_t type. It is referred as in context of row-traversal of the + * matrix. To access matrix[i][j], we use map[i * capacity + j]. +**/ +template +class HashedSquareMatrix : public ExtendableSquareMatrix { + private: + + // data are stored in a hashmap + mutable std::unordered_map data_{}; + + public: + + // + // CONSTRUCTORS + // + + /** + * @brief creates a Hashed square matrix + * @param[in] max_rows capacity of the square matrix + * @param[in] init_rows initial size of the square matrix + */ + HashedSquareMatrix(size_t max_rows, size_t init_rows) { + assert(init_rows <= max_rows && + "Initial size of the matrix cannot be" + "bigger than the capacity"); + + this->m_type = MatrixType::Hashed; + this->capacity_ = max_rows; + + // creating the initial size and filling the data cells with + // default values + for(size_t i = 0; i < init_rows; ++i) {extend();} + } + + // implemented virtual functions + + /** + * @brief assings a value to the Hashed square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @param[in] value value to be assigned to the square matrix data cell + */ + void set(size_t i, size_t j, T value) { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + // accessing the hashmap using row matrix traversal + data_[i * this->capacity_ + j] = value; + } + + /** + * @brief returns a value of the Hashed square matrix + * @param[in] i row of the square matrix + * @param[in] j column of the square matrix + * @return value found in the square matrix data cell + */ + T get(size_t i, size_t j) const { + assert(i < this->size_ && "Nonexisting row cannot be accessed"); + assert(j < this->size_ && "Nonexisting column cannot be accessed"); + + // accessing the hashmap using row matrix traversal + return data_[i * this->capacity_ + j]; + } + + /** + * @brief extends the Hashed square matrix by a new row and column + * @param[in] placeholder a value which will be assigned to all the + * new data cells + */ + void extend(T placeholder = T()) { + assert(this->size_ < this->capacity_ + && "Matrix cannot be extened anymore"); + + // creating a new row and column + for(size_t i = 0; i < this->size_; ++i) { + data_[this->size_ * this->capacity_ + i] = placeholder; + data_[i * this->capacity_ + this->size_] = placeholder; + } + data_[this->size_ * this->capacity_ + this->size_] = placeholder; + + // increasing size + ++this->size_; + } + + + // cloning + HashedSquareMatrix *clone(void) const { + return new HashedSquareMatrix(*this); } + +}; // HashedSquareMatrix + +/************************************* +* +* ADDITIONAL FUNCTIONS +* +**************************************/ + +/** +* @brief factory function which creates an ExtendableSquareMatrix of given type +* @param[in] type type of the new matrix +* @param[in] capacity maximal matrix capacity +* @param[in] size initial matrix size +* @return pointer to the newly created matrix +*/ +template +inline ExtendableSquareMatrix *create(MatrixType type, + size_t capacity, size_t size = 0) { + + switch(type) { + case MatrixType::Cascade: + return new CascadeSquareMatrix(capacity, size); + case MatrixType::Dynamic: + return new DynamicSquareMatrix(capacity, size); + case MatrixType::Hashed: + return new HashedSquareMatrix(capacity, size); + default: + return nullptr; + } +} + +/** +* @brief debugging function which allows us to print text representation of +* the Extendable square matrix +* @param[out] output stream +* @param[in] maxtrix which will be printed +* @return output stream +*/ +template +inline std::ostream& operator<<(std::ostream& os, + const ExtendableSquareMatrix& matrix) { + + size_t size = matrix.size(); + size_t capacity = matrix.capacity(); + std::string result = "\nSIZE: " + std::to_string(size) + "\n"; + result += "CAPACITY: " + std::to_string(capacity) + "\n"; + result += "MATRIX:\n"; + for(size_t i = 0; i < size; ++i) { + for(size_t j = 0; j < size; ++j) { + result += std::to_string(matrix.get(i, j)) + " "; + } + result += "\n"; + } + return os << result; +} + +} + +#endif diff --git a/include/mata/utils/partition-relation-pair.hh b/include/mata/utils/partition-relation-pair.hh deleted file mode 100644 index 248a537c1..000000000 --- a/include/mata/utils/partition-relation-pair.hh +++ /dev/null @@ -1,1396 +0,0 @@ -/** @file partition_relation_pair.hh - * @brief Definition of a partition, extendable square matrix - * and partition-relation pair. - * - * This file contains definitions of partition, extendable square - * matrix, partition-relation pair and operations which allow us - * to manipulate with them. - * - * Description: - * - * A partition-relation pair is a tuple (P, Rel). It is an efficient - * representation of a preorder/quasiorder R, which is a reflexive and - * transitive binary relation. - * In this context, we consider a carrier set S which contains all - * natural numbers from 0 to |S|-1. These numbers are called states. - * P is a partition of S which corresponds to an equivalence relation - * induced by the preorder R. - * Rel is a partial order over P. - * Thus, (P, Rel) corresponds to a preorder relation R over states S. - * - * This file provides implementation of a partition P and defines the - * ExtendableSquareMatrix class which can be used to represent - * the binary relation Rel. - * These classes can be combined to represent the preorder R. - * - * @author Tomáš Kocourek - */ - -#ifndef _PARTITION_RELATION_PAIR_HH_ -#define _PARTITION_RELATION_PAIR_HH_ - -#include -#include -#include -#include - -namespace mata::utils { - -/************************************************************************ -* -* -* -* PARTITION -* -* -* -*************************************************************************/ - - -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) {} -}; - -using Block = struct Block { - 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) {} -}; - -/** - * 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>. - * - * 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]. - * - * 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. - * - * 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 - * 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 - * split in two pieces 'B1' and 'B2', we create a brand new block 'B2' - * and modify the former block 'B' such that it will correspond to its - * subblock 'B1'. The former block 'B' is thus not represented - * in the 'blocks_' vector anymore since 'B1' takes over its identity. - * - * NODES: - * Each node represents a current block or a block which has been split before. - * The node can by accessed by its index using nodes_[node_idx]. If the given - * node represents an existing block, such block contains an index of that node. - * In context of nodes which represent former blocks, no block contains their - * 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 - * 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. - * - * 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. - * 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). - * Likewise, the node 2 contains indices 2 (first) and 4 (last) which means that - * the blockItems 2 and 4 surround a contiguous subvector of elements in the - * node 2 (or in the block 1). - * Moreover, we also represent the former block which does not exist anymore - * by the node 0 which contains indices 0 (first) and 4 (last) which means that - * 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}. - * - * - * 0 1 2 3 4 - * ------- ------- ------- ------- ------- - * | 0 | 2 | 1 | 4 | 3 | states_ - * ------- ------- ------- ------- ------- - * ↑ ↑ ↑ ↑ ↑ - * | \ / \ / - * | X X - * | / \ / \ - * 0 ↓ 1 ↓ ↓ 2 3 ↓ ↓ 4 - * ------- ------- ------- ------- ------- - * | 0 | 2 | 1 | 4 | 3 | block_items_ - * --→→→|-------|-------|-------|-------|--------←←←------------ - * | | 0 | 0 | 1 | 1 | 1 | | - * | ------- ------- ------- ------- ------- | - * | | | | | | | - * | 0 ↓ ↓ 1 ↓ ↓ ↓ | - * | ---------------------------------------- | - * | | 1 | 2 | blocks_ | - * | ---------------------------------------- | - * | | | | - * | 0 1 ↓ 2 ↓ | - * | ------- ------- ------- | - * -----| 0 | 0 | 2 | nodes_ | - * |-------|-------|-------| | - * | 4 | 1 | 4 | | - * ------- ------- ------- | - * | | - * ---------------------------------------------------- - * - * Using this class, we can: - * - find the block which contains 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|) - * - 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 - * - */ -class Partition { - private: - - /* indices to the block_items_ vector */ - std::vector states_{}; - - /* indices to the states_ and blocks_ vectors */ - BlockItems block_items_{}; - - /* indices to the nodes_ vector */ - Blocks blocks_{}; - - /* tuples of indices to the block_items_ vectors */ - Nodes nodes_{}; - - public: - - // constructors - Partition(size_t num_of_states, - const StateBlocks& partition = StateBlocks()); - Partition(const Partition& other); - - // sizes of the used vectors - size_t num_of_states(void) const { return states_.size(); } - size_t num_of_block_items(void) const { return block_items_.size(); } - size_t num_of_blocks(void) const { return blocks_.size(); } - size_t num_of_nodes(void) const { return nodes_.size(); } - - // blocks splitting - std::vector split_blocks(const std::vector& marked); - - // basic information about the partition - 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 - 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; - - // converts the partition to the vector of vectors of states - StateBlocks partition(void); - - // operators - Partition& operator=(const Partition& other); - friend std::ostream& operator<<(std::ostream& os, - const Partition& p); - - - - -}; // Partition - -/** 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. -* 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 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 -* to the same block -* @brief constructs the partition -* @param num_of_states cardinality of the carrier set -* @param partition optional initial partition in the form of vectors of -* 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); - - // 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; - 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 - 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) { - assert(!partition[block_idx].empty() && - "Partition class cannot be empty."); - - // iterating through one partition block - for(auto state : partition[block_idx]) { - assert(state < num_of_states && - "Invalid state name detected while creating" - "a partition relation pair."); - assert(!used[state] && - "Partition could not be created." - "Duplicate occurence of a state"); - - used[state] = true; - - // creating a corresponding BlockItem - states_[state] = block_items_.size(); - block_items_.emplace_back(state, block_idx); - - } - - // 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(); - - // creating a corresponding block and node - nodes_.emplace_back(states_[first], states_[last]); - blocks_.emplace_back(block_idx); - } - - // we need to detect whether there is a state which has not be 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; - - // iterating through the vector of flags saying which states has been seen - for(State state = 0; state < num_of_states; ++state) { - // if a state has been already seen and processed, - // there is no need to add it to the additional block - if(used[state]) { - continue; - } - - // if there is at least one unused state, we need to - // create an additional block - if(all_states_used) { - all_states_used = false; - first = state; - ++num_of_blocks; - } - - // creating the new BlockItem - last = state; - states_[state] = block_items_.size(); - block_items_.emplace_back(state, num_of_blocks-1); - } - - // 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); - } -} - -/** -* Custom copy constructor which preserves reserved memory for the -* partition vectors. This method has to be implemented since the custom -* assignment operator is also implemented. The preservation of the reserved -* memory is provided by the custom assignment operator=. -* @brief copy constructor of the Partition -* @param other partition which will be copied -*/ -Partition::Partition(const Partition& other) { - // using the custom assignment operator - *this = other; -} - - -/** -* @brief returns a BlockItem corresponding to the given index -* @param block_item_idx index of the BlockItem -* @return corresponding BlockItem -*/ -const 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]; -} - -/** -* @brief returns a block corresponding to the given index -* @param block_idx index of the block -* @return corresponding block -*/ -const Block& Partition::get_block(size_t block_idx) const { - assert(block_idx < num_of_blocks() && "Nonexisting block index used."); - return blocks_[block_idx]; -} - -/** -* @brief returns a node corresponding to the given index -* @param node_idx index of the node -* @return corresponding node -*/ -const Node& Partition::get_node(size_t node_idx) const { - assert(node_idx < num_of_nodes() && "Nonexisting node index used."); - return nodes_[node_idx]; -} - -/** -* @brief returns a block index corresponding to the given state -* @param 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 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 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 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 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 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 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; -} - -/** -* @brief tests whether the two given states correspond -* to the same partition block -* @param first first state to be checked -* @param second second state to be checked -* @return true iff both given states belong to the same partition block -*/ -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); -} - -/** -* @brief tests whether the given n states correspond to the same partition block -* @param 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()) { 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; } - } - return true; -} - -/** -* @brief find all of the states which share the block with the input state -* @param 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; - - // iterating through BlockItems - for(size_t i = first; i <= last; ++i) { - result.push_back(get_block_item(i).state); - } - - return result; -} - -/** -* @brief transforms inner representation of the partition to the vector of -* vectors of states -* @return vector of vectors of states -*/ -StateBlocks Partition::partition(void) { - 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); - } - return result; -} - -/** Splitting the blocks of existing partition. According to the input -* vector 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 -* - if there is a block whose all elements are unmarked, the block remains -* unchanged -* - if there is a block which contains both marked and unmarked states, it -* will be split in two blocks such that the first one contains marked states -* and the second one contains unmarked states of the original block -* - it means that each block is either unchanged or split in two parts -* - if a block contains states such that corresponding BlockItems form -* contiguous subvector on the interval of natural numbers , the split -* nodes will correspond to such BlockItems that they form contiguous subvectors -* on the intervals of natural numbers and , where a <= k < b. -* The BlockItems on the interval will be swapped such that the property -* above holds. The representant (first BlockItem on the interval) will always -* keep its position (the strategy of swapping will adapt to the fact whether -* the representant is marked or not). Thus, a representant of any node never -* 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: -* - index of the new block which keeps identity of the former block -* - index of the new block which is newly constructed -* - index of the node which is an ancestor of these two blocks -* The function returns a vector of such SplitPairs. -* -* @brief splits blocks of the partition -* @param marked marked states which influence splitting -* @return vector of SplitPair which contains information about split blocks -*/ -std::vector Partition::split_blocks( - const std::vector& 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()) { 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{}; - used_blocks.insert(used_blocks.end(), blocks_.size(), 0); - - // iterating through the marked states to fill used_states and - // used_blocks vectors - 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)]; - } - - size_t old_blocks_size, new_block_idx; - old_blocks_size = new_block_idx = blocks_.size(); - - // iterating through 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(!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; - - // if all states of the processed block have been marked, the block - // won't be split - if(used_blocks[i] >= block_size) { continue; } - - // 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]; - - // We access the first and last element of the subvector of BlockItems - // which forms processed block. We look for the first unmarked element - // from left and first marked element from right (or vice versa since - // the exact strategy is chosen according to the fact whether the first - // element is marked or not). As soon as such elements are found, they - // 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]) { - // this visited state will be part of the former block - ++iter_first; - } - while(repr_marked ^ used_states[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; - --iter_last; - } - - // if the used indices meet, we finish swapping - if(iter_first > iter_last) { - break; - } - - // 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; - - // 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; - - // after the blockItems are swapped, one of them should - // be assigned to the new block - block_items_[iter_last].block_idx = new_block_idx; - - // after the blockItems are swapped, we continue to the - // next blockItems - ++iter_first; - --iter_last; - } 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); - - // split blocks has to refer to the new nodes - blocks_[i].node_idx = nodes_.size() - 2; - blocks_.emplace_back(nodes_.size() - 1); - - // 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); - - // index of the following block which could be created - ++new_block_idx; - } - return split; -} - -/** -* Custom assignment operator which preserves reserved capacities for the -* partition vectors -* @brief assignment of the partition -* @param other partition which will be copied -* @return modified partition -*/ -Partition& Partition::operator=(const Partition& other) { - // since the default copying of the vectors do not preserve - // 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); - - // copying vectors without losing information about reserved capacity - size_t states_num = other.num_of_states(); - 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)); - } - size_t blocks_num = other.num_of_blocks(); - for(size_t i = 0; i < blocks_num; ++i) { - blocks_.push_back(other.get_block(i)); - } - size_t nodes_num = other.num_of_nodes(); - for(size_t i = 0; i < nodes_num; ++i) { - nodes_.push_back(other.get_node(i)); - } - return *this; -} - -// debugging function which allows us to print text representation of -// the partition -std::ostream& operator<<(std::ostream& os, const Partition& p) { - std::string result = std::string(); - result += "NUM OF STATES: " + std::to_string(p.num_of_states()) + "\n"; - result += "NUM OF BLOCKS: " + std::to_string(p.num_of_blocks()) + "\n"; - result += "NUM OF NODES: " + std::to_string(p.num_of_nodes()) + "\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 += "\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 += "\n"; - } - result += "\n"; - - return os << result; - -} - - -/************************************************************************ -* -* -* EXTENDABLE SQUARE MATRIX -* -* -* (RELATIONS AND COUNTERS) -* -* -*************************************************************************/ - -/** - * ExtendableSquareMatrix - * - * @brief interface for extendable square matrix implementations - * - * Square matrix "n x n" which can be extended to "(n+1) x (n+1)" matrix - * if n is less than the maximal capacity. Such a class allows us to - * represent binary relations over carrier set with n elements and adjust - * it to n+1 elements whenever a new element of the carrier set is created - * (for example when we represent relation over partition and a block of - * partition is split in two) or matrices of counters etc. - * - * This abstract class declares methods for accessing elements of the - * matrix, assigning to the cells of matrix and extending the matrix by one row - * and one column (changing the size). - * It defines attributes for maximal capacity (which cannot be changed) and - * current size. - * It does not define the data structure for storing data. Each subclass - * which inherits from this abstract class should: - * - contain the storage for data of datatype T which represents n x n matrix - * - implement methods set, get and extend - * - implement a method clone which creates a deep copy of a matrix - * Then, the ExtendableSquareMatrix can be used independently of the inner - * representation of the matrix. Therefore, one can dynamically choose from - * various of implementations depending on the situation. If any new - * subclass is implemented, one should also modify the 'create' - * function and extend 'MatrixType' enumerator. - * - * Note that in context of an n x n matrix, this implementation uses the word - * 'size' to refer to the number n (number of rows or columns). The word - * 'capacity' corresponds to the maximal allowed size (maximal number - * of rows or columns). - * -**/ - -using MatrixType = enum MatrixType { None, Cascade, Dynamic, Hashed }; - -template -class ExtendableSquareMatrix { - protected: - - // number of rows (or columns) of the current square matrix - size_t size_{0}; - - // maximal allowed number of rows (or columns) of the square matrix - size_t capacity_{0}; - - // type of the matrix which will be chosen as soon as the - // child class will be created - MatrixType m_type{MatrixType::None}; - - public: - - // getters - size_t size(void) const { return size_; } - size_t capacity(void) const { return capacity_; } - size_t type(void) const { return m_type; } - - // virtual functions which will be implemented in the subclasses - // according to the concrete representation of the matrix - virtual void set(size_t i, size_t j, T value = T()) = 0; - virtual T get(size_t i, size_t j) const = 0; - virtual void extend(T placeholder = T()) = 0; - - // cloning - virtual ExtendableSquareMatrix *clone(void) const = 0; - - virtual ~ExtendableSquareMatrix() = default; - - // matrix properties - bool is_reflexive(void); - bool is_antisymetric(void); - bool is_transitive(void); - -}; // ExtendableSquareMatrix - -/** This function checks whether the matrix is reflexive. In this -* context, the matrix is reflexive iff none of the elements on the main -* diagonal is the zero element of the type T -* @brief checks whether the Extendable square matrix is reflexive -* @return true iff the matrix is reflexive -*/ -template -bool ExtendableSquareMatrix::is_reflexive(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - if(!get(i, i)) { return false; } - } - return true; -} - -/** This function checks whether the matrix is antisymetric. In this -* context, the matrix is antisymetric iff there are no indices i, j -* where i != j and both matrix[i][j], matrix[j][i] contain nonzero elementes -* of the type T -* @brief checks whether the Extendable square matrix is antisymetric -* @return true iff the matrix is antisymetric -*/ -template -bool ExtendableSquareMatrix::is_antisymetric(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - for(size_t j = 0; j < size; ++j) { - if(i == j) { continue; } - if(get(i, j) && get(j, i)) { return false; } - } - } - return true; -} - -/** This function checks whether the matrix is transitive. In this -* context, the matrix is transitive iff it holds that the input matrix -* casted to the matrix of booleans (false for zero values of type T, otherwise -* true) remains the same if it is multiplied by itself. -* @brief checks whether the Extendable square matrix is transitive -* @return true iff the matrix is transitive -*/ -template -bool ExtendableSquareMatrix::is_transitive(void) { - size_t size = this->size(); - for(size_t i = 0; i < size; ++i) { - for(size_t j = 0; j < size; ++j) { - bool found = false; - for(size_t k = 0; k < size; ++k) { - if(get(i, k) && get(k, j)) { - found = true; - break; - } - } - if(!found == static_cast(get(i, j))) { return false; } - } - } - return true; -} - -/************************************* -* -* CASCADE SQUARE MATRIX -* -**************************************/ - -/** - * CascadeSquareMatrix - * - * @brief Linearized square matrix implemented using single vector of - * elements which stores data in some kind of "cascading" way - * - * This implementation tries to avoid - * - moving the whole matrix when it is extended - * - allocation of unneccessary data cells - * - violation of data locality - * - * The matrix is represented as a single vector of a type T. Initially, - * the maximal possible capacity is given to the constructor. It reserves - * 'capacity * capacity' data cells for the vector (in the constant time O(1)) - * without need to allocate anything. - * When the matrix is extended, additional (size * 2) + 1 elements of the - * vector are allocated. The matrix is traversed in some kind of "cascading" way - * as follows: - * - * Each number in the matrix corresponds to the order of accessing that element - * using this "cascading traversal". - * - * MATRIX: - * ----------------- - * | 0 | 3 | 8 | 15| - * ----------------- - * | 1 | 2 | 7 | 14| - * ----------------- - * | 4 | 5 | 6 | 13| - * ----------------- - * | 9 | 10| 11| 12| - * ----------------- - * - * VECTOR: - * ----------------------------------------------------------------------- - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - * ----------------------------------------------------------------------- - * - * The data cell matrix[i][j] could be accessed using the formula - * vector[i >= j ? i * i + j : j * j + 2 * j - i]. - * - * Using this approach, there is no need to allocate unnecessary data cells - * when extending n x n matrix to the (n+1) x (n+1) matrix (in contrast with - * using row or column traversal). - * Since 'capacity * capacity' data cells were reserved, the vector won't ever - * be moved in the memory due to the extending. However, it requieres to - * reserve a lot of memory space when the capacity is a huge number. - * The data locality won't be violated since all of the elements will be stored - * as a contiguous vector. - * -**/ -template -class CascadeSquareMatrix : public ExtendableSquareMatrix { - private: - - // data are stored in a single vector - std::vector data_{}; - - public: - - // constructors - CascadeSquareMatrix(size_t max_rows, size_t init_rows = 0); - CascadeSquareMatrix(const CascadeSquareMatrix& other); - - // implemented virtual functions - void set(size_t i, size_t j, T value) override; - T get(size_t i, size_t j) const override; - void extend(T placeholder = T()) override; - - // cloning - CascadeSquareMatrix *clone(void) const { - return new CascadeSquareMatrix(*this); } - - // operators - CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other); - -}; // CascadeSquareMatrix - -/** -* @brief creates a Cascade square matrix -* @param max_rows capacity of the square matrix -* @param init_rows initial size of the square matrix -*/ -template -CascadeSquareMatrix::CascadeSquareMatrix( - size_t max_rows, size_t init_rows) { - - assert(init_rows <= max_rows && - "Initial size of the matrix cannot be bigger than the capacity"); - - this->m_type = MatrixType::Cascade; - this->capacity_ = max_rows; - data_.reserve(this->capacity_ * this->capacity_); - - // creating the initial size and filling the data cells with - // default values - for(size_t i = 0; i < init_rows; ++i) {extend();} -} - -/** This method provides a way to create a copy of a given CascadeSquareMatrix -* and preserves the reserved capacity of the vector 'data_'. This goal is -* achieved using the custom assignment operator. -* @brief copy constructor of a CascadeSquareMatrix -* @param other matrix which should be copied -*/ -template -CascadeSquareMatrix::CascadeSquareMatrix( - const CascadeSquareMatrix& other) { - - *this = other; -} - -/** -* @brief assings a value to the Cascade square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @param value value to be assigned to the square matrix data cell -*/ -template -void CascadeSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - // accessing the matrix in the cascading way - data_[i >= j ? i * i + j : j * j + 2 * j - i] = value; -} - -/** -* @brief returns a value of the Cascade square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @return value found in the square matrix data cell -*/ -template -T CascadeSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - // accessing the matrix in the cascading way - return data_[i >= j ? i * i + j : j * j + 2 * j - i]; -} - -/** -* @brief extends the Cascade square matrix by a new row and column -* @param placeholder a value which will be assigned to all the new data cells -* (optional) -*/ -template -void CascadeSquareMatrix::extend(T placeholder) { - assert(this->size_ < this->capacity_ - && "The matrix cannot be extended anymore"); - - // allocation of 2 * size + 1 new data cells - data_.insert(data_.end(), 2 * this->size_ + 1, placeholder); - - // the size increases - ++this->size_; -} - -/** This method provides a way to assign a CascadeSquareMatrix to the variable. -* The method ensures us to keep the reserved capacity of the vector 'data_' -* since the default vector assignment does not preserve it. -* @brief assignment operator for the CascadeSquareMatrix class -* @param other matrix which should be copied assigned -*/ -template -CascadeSquareMatrix& CascadeSquareMatrix::operator=( - const CascadeSquareMatrix& other) { - - // initialization of the matrix - this->capacity_ = other.capacity(); - this->size_ = 0; - this->data_ = std::vector(); - this->data_.reserve(this->capacity_ * this->capacity_); - size_t other_size = other.size(); - for(size_t i = 0; i < other_size; ++i) {this->extend();} - - // copying memory cells - for(size_t i = 0; i < this->size_; ++i) { - for(size_t j = 0; j < this->size_; ++j) { - this->set(i, j, other.get(i, j)); - } - } - return *this; -} - -/************************************* -* -* DYNAMIC SQUARE MATRIX -* -**************************************/ - -/** - * DynamicSquareMatrix - * - * @brief Dynamic square matrix implemented using vector of vectors - * of the type T - * - * This implementation tries to avoid - * - allocation or reservation of data cells which won't ever be used - * - * The matrix is represented as a vector of vectors of the type T. It is - * extended dynamically without any need to allocate or reserve any unnecessary - * memory space before it is used. - * However, the data locality is not ensured. Moreover, if the matrix is - * extended, it could possibly be moved in the memory. -**/ -template -class DynamicSquareMatrix : public ExtendableSquareMatrix { - private: - - // data are stored in a single vector - std::vector> data_{}; - - public: - - // constructors - DynamicSquareMatrix(size_t max_rows, size_t init_rows = 0); - - // implemented virtual functions - void set(size_t i, size_t j, T value) override; - T get(size_t i, size_t j) const override; - void extend(T placeholder = T()) override; - - // cloning - DynamicSquareMatrix *clone(void) const { - return new DynamicSquareMatrix(*this); } - -}; // DynamicSquareMatrix - -/** -* @brief creates a Dynamic square matrix -* @param max_rows capacity of the square matrix -* @param init_rows initial size of the square matrix -*/ -template -DynamicSquareMatrix::DynamicSquareMatrix( - size_t max_rows, size_t init_rows) { - - assert(init_rows <= max_rows && - "Initial size of the matrix cannot be bigger than the capacity"); - - this->m_type = MatrixType::Dynamic; - this->capacity_ = max_rows; - - // creating the initial size and filling the data cells with - // default values - for(size_t i = 0; i < init_rows; ++i) {extend();} -} - -/** -* @brief assings a value to the Dynamic square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @param value value to be assigned to the square matrix data cell -*/ -template -T DynamicSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - return data_[i][j]; -} - -/** -* @brief returns a value of the Dynamic square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @return value found in the square matrix data cell -*/ -template -void DynamicSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - data_[i][j] = value; -} - -/** -* @brief extends the Dynamic square matrix by a new row and column -* @param placeholder a value which will be assigned to all the new data cells -*/ -template -void DynamicSquareMatrix::extend(T placeholder) { - assert(this->size_ < this->capacity_ - && "The matrix cannot be extened anymore"); - - // creating a new column - for(size_t i = 0; i < this->size_; ++i) { - data_[i].push_back(placeholder); - } - - // creating a new row - data_.emplace_back(); - ++this->size_; - data_.back().insert(data_.back().end(), this->size_, placeholder); -} - -/************************************* -* -* HASHED SQUARE MATRIX -* -**************************************/ - -/** - * HashedSquareMatrix - * - * @brief Hashed square matrix implemented using unordered hash map - * - * The matrix is represented as a unordered hash map of the type T indexed - * by the size_t type. It is referred as in context of row-traversal of the - * matrix. To access matrix[i][j], we use map[i * capacity + j]. -**/ -template -class HashedSquareMatrix : public ExtendableSquareMatrix { - private: - - // data are stored in a hashmap - mutable std::unordered_map data_{}; - - public: - - // constructors - HashedSquareMatrix(size_t max_rows, size_t init_rows = 0); - - // implemented virtual functions - void set(size_t i, size_t j, T value) override; - T get(size_t i, size_t j) const override; - void extend(T placeholder = T()) override; - - // cloning - HashedSquareMatrix *clone(void) const { - return new HashedSquareMatrix(*this); } - -}; // HashedSquareMatrix - -/** -* @brief creates a Hashed square matrix -* @param max_rows capacity of the square matrix -* @param init_rows initial size of the square matrix -*/ -template -HashedSquareMatrix::HashedSquareMatrix( - size_t max_rows, size_t init_rows) { - - assert(init_rows <= max_rows && - "Initial size of the matrix cannot be bigger than the capacity"); - - this->m_type = MatrixType::Hashed; - this->capacity_ = max_rows; - - // creating the initial size and filling the data cells with - // default values - for(size_t i = 0; i < init_rows; ++i) {extend();} -} - -/** -* @brief assings a value to the Hashed square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @param value value to be assigned to the square matrix data cell -*/ -template -void HashedSquareMatrix::set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - // accessing the hashmap using row matrix traversal - data_[i * this->capacity_ + j] = value; -} - -/** -* @brief returns a value of the Hashed square matrix -* @param i row of the square matrix -* @param j column of the square matrix -* @return value found in the square matrix data cell -*/ -template -T HashedSquareMatrix::get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); - - // accessing the hashmap using row matrix traversal - return data_[i * this->capacity_ + j]; -} - -/** -* @brief extends the Hashed square matrix by a new row and column -* @param placeholder a value which will be assigned to all the new data cells -*/ -template -void HashedSquareMatrix::extend(T placeholder) { - assert(this->size_ < this->capacity_ - && "Matrix cannot be extened anymore"); - - // creating a new row and column - for(size_t i = 0; i < this->size_; ++i) { - data_[this->size_ * this->capacity_ + i] = placeholder; - data_[i * this->capacity_ + this->size_] = placeholder; - } - data_[this->size_ * this->capacity_ + this->size_] = placeholder; - - // increasing size - ++this->size_; -} - -/************************************* -* -* ADDITIONAL FUNCTIONS -* -**************************************/ - -/** -* @brief factory function which creates an ExtendableSquareMatrix of given type -* @param type type of the new matrix -* @param capacity maximal matrix capacity -* @param size initial matrix size -* @return pointer to the newly created matrix -*/ -template -inline ExtendableSquareMatrix *create(MatrixType type, - size_t capacity, size_t size = 0) { - - switch(type) { - case MatrixType::Cascade: - return new CascadeSquareMatrix(capacity, size); - case MatrixType::Dynamic: - return new DynamicSquareMatrix(capacity, size); - case MatrixType::Hashed: - return new HashedSquareMatrix(capacity, size); - default: - return nullptr; - } -} - -// debugging function which allows us to print text representation of -// the Extendable square matrix -template -inline std::ostream& operator<<(std::ostream& os, - const ExtendableSquareMatrix& matrix) { - - size_t size = matrix.size(); - size_t capacity = matrix.capacity(); - std::string result = "\nSIZE: " + std::to_string(size) + "\n"; - result += "CAPACITY: " + std::to_string(capacity) + "\n"; - result += "MATRIX:\n"; - for(size_t i = 0; i < size; ++i) { - for(size_t j = 0; j < size; ++j) { - result += std::to_string(matrix.get(i, j)) + " "; - } - result += "\n"; - } - return os << result; -} - -} - -#endif diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh new file mode 100644 index 000000000..a6b188fa7 --- /dev/null +++ b/include/mata/utils/partition.hh @@ -0,0 +1,261 @@ +/** @file partition.hh + * @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. + * Then, partition over S is a set of blocks such that: + * - each block contains only states + * - each state is represented in exactly one block + * - blocks are disjoint + * - there is no state which is not represented in any block + * - no block is empty + * + * This file provides implementation of a partition P which allows us to: + * - 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|) + * - 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 + * + * @author Tomáš Kocourek + */ + +#ifndef _PARTITION_HH_ +#define _PARTITION_HH_ + +#include +#include +#include +#include + +namespace mata::utils { + +/************************************************************************ +* +* +* +* PARTITION +* +* +* +*************************************************************************/ + +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) {} +}; + +using Block = struct Block { + 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) {} +}; + +/** + * 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>. + * + * 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]. + * + * 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. + * + * 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 + * 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 + * split in two pieces 'B1' and 'B2', we create a brand new block 'B2' + * and modify the former block 'B' such that it will correspond to its + * subblock 'B1'. The former block 'B' is thus not represented + * in the 'blocks_' vector anymore since 'B1' takes over its identity. + * + * NODES: + * Each node represents a current block or a block which has been split before. + * The node can by accessed by its index using nodes_[node_idx]. If the given + * node represents an existing block, such block contains an index of that node. + * In context of nodes which represent former blocks, no block contains their + * 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 + * 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. + * + * 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. + * 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). + * Likewise, the node 2 contains indices 2 (first) and 4 (last) which means that + * the blockItems 2 and 4 surround a contiguous subvector of elements in the + * node 2 (or in the block 1). + * Moreover, we also represent the former block which does not exist anymore + * by the node 0 which contains indices 0 (first) and 4 (last) which means that + * 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}. + * + * + * 0 1 2 3 4 + * ------- ------- ------- ------- ------- + * | 0 | 2 | 1 | 4 | 3 | states_ + * ------- ------- ------- ------- ------- + * ↑ ↑ ↑ ↑ ↑ + * | \ / \ / + * | X X + * | / \ / \ + * 0 ↓ 1 ↓ ↓ 2 3 ↓ ↓ 4 + * ------- ------- ------- ------- ------- + * | 0 | 2 | 1 | 4 | 3 | block_items_ + * --→→→|-------|-------|-------|-------|--------←←←------------ + * | | 0 | 0 | 1 | 1 | 1 | | + * | ------- ------- ------- ------- ------- | + * | | | | | | | + * | 0 ↓ ↓ 1 ↓ ↓ ↓ | + * | ---------------------------------------- | + * | | 1 | 2 | blocks_ | + * | ---------------------------------------- | + * | | | | + * | 0 1 ↓ 2 ↓ | + * | ------- ------- ------- | + * -----| 0 | 0 | 2 | nodes_ | + * |-------|-------|-------| | + * | 4 | 1 | 4 | | + * ------- ------- ------- | + * | | + * ---------------------------------------------------- + * + * Using this class, we can: + * - find the block which contains 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|) + * - 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 + * + */ +class Partition { + private: + + /* indices to the block_items_ vector */ + std::vector states_{}; + + /* indices to the states_ and blocks_ vectors */ + BlockItems block_items_{}; + + /* indices to the nodes_ vector */ + Blocks blocks_{}; + + /* tuples of indices to the block_items_ vectors */ + Nodes nodes_{}; + + public: + + // constructors + Partition(size_t num_of_states, + const StateBlocks& partition = StateBlocks()); + Partition(const Partition& other); + + // sizes of the used vectors + size_t num_of_states(void) const { return states_.size(); } + size_t num_of_block_items(void) const { return block_items_.size(); } + size_t num_of_blocks(void) const { return blocks_.size(); } + size_t num_of_nodes(void) const { return nodes_.size(); } + + // blocks splitting + std::vector split_blocks(const std::vector& marked); + + // basic information about the partition + 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 + 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; + + // converts the partition to the vector of vectors of states + StateBlocks partition(void); + + // operators + Partition& operator=(const Partition& other); + friend std::ostream& operator<<(std::ostream& os, + const Partition& p); + + + + +}; // Partition + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3b5bbea0..6660c7de0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(libmata STATIC mintermization.cc parser.cc re2parser.cc + partition.cc nfa/nfa.cc nfa/inclusion.cc nfa/universal.cc diff --git a/src/partition.cc b/src/partition.cc new file mode 100644 index 000000000..29f5a1429 --- /dev/null +++ b/src/partition.cc @@ -0,0 +1,560 @@ +/** @file partition.cc + * @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. + * Then, partition over S is a set of blocks such that: + * - each block contains only states + * - each state is represented in exactly one block + * - blocks are disjoint + * - there is no state which is not represented in any block + * - no block is empty + * + * This file provides implementation of a partition P which allows us to: + * - 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|) + * - 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 + * + * @author Tomáš Kocourek + */ + +#include +#include +#include "mata/utils/partition.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. +* 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 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 +* to the same block +* @brief constructs the partition +* @param[in] num_of_states cardinality of the carrier set +* @param[in] partition optional initial partition in the form of vectors of +* 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); + + // 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; + 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 + 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) { + assert(!partition[block_idx].empty() && + "Partition class cannot be empty."); + + // iterating through one partition block + for(auto state : partition[block_idx]) { + assert(state < num_of_states && + "Invalid state name detected while creating" + "a partition relation pair."); + assert(!used[state] && + "Partition could not be created." + "Duplicate occurence of a state"); + + used[state] = true; + + // creating a corresponding BlockItem + states_[state] = block_items_.size(); + block_items_.emplace_back(state, block_idx); + + } + + // 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(); + + // creating a corresponding block and node + nodes_.emplace_back(states_[first], states_[last]); + blocks_.emplace_back(block_idx); + } + + // we need to detect whether there is a state which has not be 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; + + // iterating through the vector of flags saying which states has been seen + for(State state = 0; state < num_of_states; ++state) { + // if a state has been already seen and processed, + // there is no need to add it to the additional block + if(used[state]) { + continue; + } + + // if there is at least one unused state, we need to + // create an additional block + if(all_states_used) { + all_states_used = false; + first = state; + ++num_of_blocks; + } + + // creating the new BlockItem + last = state; + states_[state] = block_items_.size(); + block_items_.emplace_back(state, num_of_blocks-1); + } + + // 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); + } +} + +/** +* Custom copy constructor which preserves reserved memory for the +* partition vectors. This method has to be implemented since the custom +* assignment operator is also implemented. The preservation of the reserved +* memory is provided by the custom assignment operator=. +* @brief copy constructor of the Partition +* @param[in] other partition which will be copied +*/ +Partition::Partition(const Partition& other) { + // using the custom assignment operator + *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 { + assert(block_item_idx < num_of_block_items() && + "Nonexisting block item index used."); + return block_items_[block_item_idx]; +} + +/** +* @brief returns a block corresponding to the given index +* @param[in] block_idx index of the block +* @return corresponding block +*/ +const Block& Partition::get_block(size_t block_idx) const { + assert(block_idx < num_of_blocks() && "Nonexisting block index used."); + return blocks_[block_idx]; +} + +/** +* @brief returns a node corresponding to the given index +* @param[in] node_idx index of the node +* @return corresponding node +*/ +const Node& Partition::get_node(size_t node_idx) const { + assert(node_idx < num_of_nodes() && "Nonexisting node index used."); + return nodes_[node_idx]; +} + +/** +* @brief returns a block index corresponding to the given state +* @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; +} + +/** +* @brief tests whether the two given states correspond +* to the same partition block +* @param[in] first first state to be checked +* @param[in] second second state to be checked +* @return true iff both given states belong to the same partition block +*/ +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); +} + +/** +* @brief tests whether the given n states correspond to the same partition block +* @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()) { 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; } + } + return true; +} + +/** +* @brief find all of the states which share the block with the input state +* @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; + + // iterating through BlockItems + for(size_t i = first; i <= last; ++i) { + result.push_back(get_block_item(i).state); + } + + return result; +} + +/** +* @brief transforms inner representation of the partition to the vector of +* vectors of states +* @return vector of vectors of states +*/ +StateBlocks Partition::partition(void) { + 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); + } + return result; +} + +/** Splitting the blocks of existing partition. According to the input +* vector 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 +* - if there is a block whose all elements are unmarked, the block remains +* unchanged +* - if there is a block which contains both marked and unmarked states, it +* will be split in two blocks such that the first one contains marked states +* and the second one contains unmarked states of the original block +* - it means that each block is either unchanged or split in two parts +* - if a block contains states such that corresponding BlockItems form +* contiguous subvector on the interval of natural numbers , the split +* nodes will correspond to such BlockItems that they form contiguous subvectors +* on the intervals of natural numbers and , where a <= k < b. +* The BlockItems on the interval will be swapped such that the property +* above holds. The representant (first BlockItem on the interval) will always +* keep its position (the strategy of swapping will adapt to the fact whether +* the representant is marked or not). Thus, a representant of any node never +* 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: +* - index of the new block which keeps identity of the former block +* - index of the new block which is newly constructed +* - index of the node which is an ancestor of these two blocks +* The function returns a vector of such SplitPairs. +* +* @brief splits blocks of the partition +* @param[in] marked marked states which influence splitting +* @return vector of SplitPair which contains information about split blocks +*/ +std::vector Partition::split_blocks( + const std::vector& 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()) { 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{}; + used_blocks.insert(used_blocks.end(), blocks_.size(), 0); + + // iterating through the marked states to fill used_states and + // used_blocks vectors + 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)]; + } + + size_t old_blocks_size, new_block_idx; + old_blocks_size = new_block_idx = blocks_.size(); + + // iterating through 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(!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; + + // if all states of the processed block have been marked, the block + // won't be split + if(used_blocks[i] >= block_size) { continue; } + + // 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]; + + // We access the first and last element of the subvector of BlockItems + // which forms processed block. We look for the first unmarked element + // from left and first marked element from right (or vice versa since + // the exact strategy is chosen according to the fact whether the first + // element is marked or not). As soon as such elements are found, they + // 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]) { + // this visited state will be part of the former block + ++iter_first; + } + while(repr_marked ^ used_states[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; + --iter_last; + } + + // if the used indices meet, we finish swapping + if(iter_first > iter_last) { + break; + } + + // 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; + + // 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; + + // after the blockItems are swapped, one of them should + // be assigned to the new block + block_items_[iter_last].block_idx = new_block_idx; + + // after the blockItems are swapped, we continue to the + // next blockItems + ++iter_first; + --iter_last; + } 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); + + // split blocks has to refer to the new nodes + blocks_[i].node_idx = nodes_.size() - 2; + blocks_.emplace_back(nodes_.size() - 1); + + // 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); + + // index of the following block which could be created + ++new_block_idx; + } + return split; +} + +/** +* Custom assignment operator which preserves reserved capacities for the +* partition vectors +* @brief assignment of the partition +* @param[in] other partition which will be copied +* @return modified partition +*/ +Partition& Partition::operator=(const Partition& other) { + // since the default copying of the vectors do not preserve + // 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); + + // copying vectors without losing information about reserved capacity + size_t states_num = other.num_of_states(); + 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)); + } + size_t blocks_num = other.num_of_blocks(); + for(size_t i = 0; i < blocks_num; ++i) { + blocks_.push_back(other.get_block(i)); + } + size_t nodes_num = other.num_of_nodes(); + for(size_t i = 0; i < nodes_num; ++i) { + nodes_.push_back(other.get_node(i)); + } + return *this; +} + +/** +* @brief debugging function which allows us to print text representation of +* the partition +* @param[out] output stream +* @param[in] partition which will be printed +* @return output stream +*/ +std::ostream& operator<<(std::ostream& os, const Partition& p) { + std::string result = std::string(); + result += "NUM OF STATES: " + std::to_string(p.num_of_states()) + "\n"; + result += "NUM OF BLOCKS: " + std::to_string(p.num_of_blocks()) + "\n"; + result += "NUM OF NODES: " + std::to_string(p.num_of_nodes()) + "\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 += "\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 += "\n"; + } + result += "\n"; + + return os << result; + +} + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 838116b4e..e0bcadc69 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,8 @@ add_executable(tests ord-vector.cc sparse-set.cc - partition-relation-pair.cc + partition.cc + extendable-square-matrix.cc synchronized-iterator.cc main.cc alphabet.cc diff --git a/tests/extendable-square-matrix.cc b/tests/extendable-square-matrix.cc new file mode 100644 index 000000000..dad5c6342 --- /dev/null +++ b/tests/extendable-square-matrix.cc @@ -0,0 +1,238 @@ +#include + +#include "mata/utils/extendable-square-matrix.hh" + +using namespace mata::utils; + +TEST_CASE("mata::utils::ExtendableSquareMatrix") { + + SECTION("CascadeSquareMatrix") { + + ExtendableSquareMatrix *e = create( + Cascade, 5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(0, 0, 1); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(3, 1, 1); + e->set(1, 2, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); + delete e; + } + + SECTION("DynamicSquareMatrix") { + + ExtendableSquareMatrix *e = create( + Dynamic, 5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(0, 0, 1); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(3, 1, 1); + e->set(1, 2, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); + delete e; + } + + SECTION("HashedSquareMatrix") { + + ExtendableSquareMatrix *e = create( + Hashed, 5, 2); + CHECK(e->size() == 2); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 3); + CHECK(e->capacity() == 5); + e->extend(); + CHECK(e->size() == 4); + CHECK(e->capacity() == 5); + CHECK(e->get(0, 0) == 0); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(0, 0, 1); + CHECK(!e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(1, 1, 1); + e->set(2, 2, 1); + e->set(3, 3, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(e->is_transitive()); + e->set(3, 1, 1); + e->set(1, 2, 1); + CHECK(e->is_reflexive()); + CHECK(e->is_antisymetric()); + CHECK(!e->is_transitive()); + delete e; + } + + SECTION("Matrix of the None type") { + + ExtendableSquareMatrix *e = create( + None, 5, 2); + CHECK(e == nullptr); + + delete e; + + } + + SECTION("Empty matrices") { + ExtendableSquareMatrix *e1 = create( + Cascade, 5); + ExtendableSquareMatrix *e2 = create( + Dynamic, 5); + ExtendableSquareMatrix *e3 = create( + Hashed, 5); + + CHECK(!e1->size()); + CHECK(!e2->size()); + CHECK(!e3->size()); + + CHECK(e1->capacity() == 5); + CHECK(e2->capacity() == 5); + CHECK(e3->capacity() == 5); + + ExtendableSquareMatrix *c1 = e1->clone(); + ExtendableSquareMatrix *c2 = e2->clone(); + ExtendableSquareMatrix *c3 = e3->clone(); + + CHECK(!c1->size()); + CHECK(!c2->size()); + CHECK(!c3->size()); + + CHECK(c1->capacity() == 5); + CHECK(c2->capacity() == 5); + CHECK(c3->capacity() == 5); + + delete e1; + delete e2; + delete e3; + delete c1; + delete c2; + delete c3; + + } + + SECTION("Matrices with only one element") { + ExtendableSquareMatrix *e1 = create( + Cascade, 5, 1); + ExtendableSquareMatrix *e2 = create( + Dynamic, 5, 1); + ExtendableSquareMatrix *e3 = create( + Hashed, 5, 1); + + CHECK(e1->size() == 1); + CHECK(e2->size() == 1); + CHECK(e3->size() == 1); + + CHECK(e1->capacity() == 5); + CHECK(e2->capacity() == 5); + CHECK(e3->capacity() == 5); + + ExtendableSquareMatrix *c1 = e1->clone(); + ExtendableSquareMatrix *c2 = e2->clone(); + ExtendableSquareMatrix *c3 = e3->clone(); + + CHECK(c1->size() == 1); + CHECK(c2->size() == 1); + CHECK(c3->size() == 1); + + CHECK(c1->capacity() == 5); + CHECK(c2->capacity() == 5); + CHECK(c3->capacity() == 5); + + delete e1; + delete e2; + delete e3; + delete c1; + delete c2; + delete c3; + + } + + SECTION("Copying matrices") { + + ExtendableSquareMatrix *m1 = create(Cascade, 1000, 2); + ExtendableSquareMatrix *m2 = create(Dynamic, 5, 2); + ExtendableSquareMatrix *m3 = create(Hashed, 5, 2); + + ExtendableSquareMatrix *c1 = m1; + ExtendableSquareMatrix *c2 = m2; + ExtendableSquareMatrix *c3 = m3; + + m1->set(1, 1, true); + m2->set(1, 1, true); + m3->set(1, 1, true); + + CHECK(m1->get(1, 1) == c1->get(1, 1)); + CHECK(m2->get(1, 1) == c2->get(1, 1)); + CHECK(m3->get(1, 1) == c3->get(1, 1)); + + c1 = m1->clone(); + c2 = m2->clone(); + c3 = m3->clone(); + + m1->set(0, 1, true); + m2->set(0, 1, true); + m3->set(0, 1, true); + + CHECK(m1->get(0, 1) != c1->get(0, 1)); + CHECK(m2->get(0, 1) != c2->get(0, 1)); + CHECK(m3->get(0, 1) != c3->get(0, 1)); + + CHECK(!c1->get(0, 1)); + CHECK(!c2->get(0, 1)); + CHECK(!c3->get(0, 1)); + + delete m1; + delete m2; + delete m3; + delete c1; + delete c2; + delete c3; + + } + +} diff --git a/tests/partition-relation-pair.cc b/tests/partition.cc similarity index 66% rename from tests/partition-relation-pair.cc rename to tests/partition.cc index ce1d8c572..36b7b7efb 100644 --- a/tests/partition-relation-pair.cc +++ b/tests/partition.cc @@ -1,6 +1,6 @@ #include -#include "mata/utils/partition-relation-pair.hh" +#include "mata/utils/partition.hh" using namespace mata::utils; @@ -345,237 +345,3 @@ TEST_CASE("mata::utils::Partition") { } } - - -TEST_CASE("mata::utils::ExtendableSquareMatrix") { - - SECTION("CascadeSquareMatrix") { - - ExtendableSquareMatrix *e = create( - Cascade, 5, 2); - CHECK(e->size() == 2); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 3); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 4); - CHECK(e->capacity() == 5); - CHECK(e->get(0, 0) == 0); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(0, 0, 1); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(1, 1, 1); - e->set(2, 2, 1); - e->set(3, 3, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(3, 1, 1); - e->set(1, 2, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(!e->is_transitive()); - delete e; - } - - SECTION("DynamicSquareMatrix") { - - ExtendableSquareMatrix *e = create( - Dynamic, 5, 2); - CHECK(e->size() == 2); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 3); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 4); - CHECK(e->capacity() == 5); - CHECK(e->get(0, 0) == 0); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(0, 0, 1); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(1, 1, 1); - e->set(2, 2, 1); - e->set(3, 3, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(3, 1, 1); - e->set(1, 2, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(!e->is_transitive()); - delete e; - } - - SECTION("HashedSquareMatrix") { - - ExtendableSquareMatrix *e = create( - Hashed, 5, 2); - CHECK(e->size() == 2); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 3); - CHECK(e->capacity() == 5); - e->extend(); - CHECK(e->size() == 4); - CHECK(e->capacity() == 5); - CHECK(e->get(0, 0) == 0); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(0, 0, 1); - CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(1, 1, 1); - e->set(2, 2, 1); - e->set(3, 3, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(e->is_transitive()); - e->set(3, 1, 1); - e->set(1, 2, 1); - CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); - CHECK(!e->is_transitive()); - delete e; - } - - SECTION("Matrix of the None type") { - - ExtendableSquareMatrix *e = create( - None, 5, 2); - CHECK(e == nullptr); - - delete e; - - } - - SECTION("Empty matrices") { - ExtendableSquareMatrix *e1 = create( - Cascade, 5); - ExtendableSquareMatrix *e2 = create( - Dynamic, 5); - ExtendableSquareMatrix *e3 = create( - Hashed, 5); - - CHECK(!e1->size()); - CHECK(!e2->size()); - CHECK(!e3->size()); - - CHECK(e1->capacity() == 5); - CHECK(e2->capacity() == 5); - CHECK(e3->capacity() == 5); - - ExtendableSquareMatrix *c1 = e1->clone(); - ExtendableSquareMatrix *c2 = e2->clone(); - ExtendableSquareMatrix *c3 = e3->clone(); - - CHECK(!c1->size()); - CHECK(!c2->size()); - CHECK(!c3->size()); - - CHECK(c1->capacity() == 5); - CHECK(c2->capacity() == 5); - CHECK(c3->capacity() == 5); - - delete e1; - delete e2; - delete e3; - delete c1; - delete c2; - delete c3; - - } - - SECTION("Matrices with only one element") { - ExtendableSquareMatrix *e1 = create( - Cascade, 5, 1); - ExtendableSquareMatrix *e2 = create( - Dynamic, 5, 1); - ExtendableSquareMatrix *e3 = create( - Hashed, 5, 1); - - CHECK(e1->size() == 1); - CHECK(e2->size() == 1); - CHECK(e3->size() == 1); - - CHECK(e1->capacity() == 5); - CHECK(e2->capacity() == 5); - CHECK(e3->capacity() == 5); - - ExtendableSquareMatrix *c1 = e1->clone(); - ExtendableSquareMatrix *c2 = e2->clone(); - ExtendableSquareMatrix *c3 = e3->clone(); - - CHECK(c1->size() == 1); - CHECK(c2->size() == 1); - CHECK(c3->size() == 1); - - CHECK(c1->capacity() == 5); - CHECK(c2->capacity() == 5); - CHECK(c3->capacity() == 5); - - delete e1; - delete e2; - delete e3; - delete c1; - delete c2; - delete c3; - - } - - SECTION("Copying matrices") { - - ExtendableSquareMatrix *m1 = create(Cascade, 1000, 2); - ExtendableSquareMatrix *m2 = create(Dynamic, 5, 2); - ExtendableSquareMatrix *m3 = create(Hashed, 5, 2); - - ExtendableSquareMatrix *c1 = m1; - ExtendableSquareMatrix *c2 = m2; - ExtendableSquareMatrix *c3 = m3; - - m1->set(1, 1, true); - m2->set(1, 1, true); - m3->set(1, 1, true); - - CHECK(m1->get(1, 1) == c1->get(1, 1)); - CHECK(m2->get(1, 1) == c2->get(1, 1)); - CHECK(m3->get(1, 1) == c3->get(1, 1)); - - c1 = m1->clone(); - c2 = m2->clone(); - c3 = m3->clone(); - - m1->set(0, 1, true); - m2->set(0, 1, true); - m3->set(0, 1, true); - - CHECK(m1->get(0, 1) != c1->get(0, 1)); - CHECK(m2->get(0, 1) != c2->get(0, 1)); - CHECK(m3->get(0, 1) != c3->get(0, 1)); - - CHECK(!c1->get(0, 1)); - CHECK(!c2->get(0, 1)); - CHECK(!c3->get(0, 1)); - - delete m1; - delete m2; - delete m3; - delete c1; - delete c2; - delete c3; - - } - -} From 6dd8a454ae2a19be5e292259817cec0ef225eaa2 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 30 Mar 2024 21:15:07 +0100 Subject: [PATCH 24/34] Parentheses added to clarify expression with a bitwise operation --- src/partition.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/partition.cc b/src/partition.cc index 29f5a1429..f99ccdc38 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -422,8 +422,8 @@ std::vector Partition::split_blocks( // 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 + ^ !used_states[get_block_item(iter_first).state])) { // this visited state will be part of the former block ++iter_first; } From d37989746927dd03b19d9b553c67a560e1fdacb0 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 30 Mar 2024 21:15:47 +0100 Subject: [PATCH 25/34] Parentheses added to clarify expression with a bitwise operation --- src/partition.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partition.cc b/src/partition.cc index f99ccdc38..9c95ba31e 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -427,7 +427,7 @@ std::vector Partition::split_blocks( // 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 ^ used_states[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; --iter_last; From ab669f3862c9cf1c02516bce103bba1e1cf39de9 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 30 Mar 2024 21:27:47 +0100 Subject: [PATCH 26/34] Parentheses added to clarify expression with a bitwise operation --- src/partition.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partition.cc b/src/partition.cc index 9c95ba31e..480199644 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -423,7 +423,7 @@ std::vector Partition::split_blocks( do { // we choose the swapping strategy using XOR operation while((repr_marked - ^ !used_states[get_block_item(iter_first).state])) { + ^ (!used_states[get_block_item(iter_first).state]))) { // this visited state will be part of the former block ++iter_first; } From a3a3d6d62c644d18aa975707a1d981412f68f2b6 Mon Sep 17 00:00:00 2001 From: kocotom Date: Sat, 30 Mar 2024 23:18:07 +0100 Subject: [PATCH 27/34] Manual allocation replaced by smart pointers (unique_ptr) --- .../mata/utils/extendable-square-matrix.hh | 27 ++-- tests/extendable-square-matrix.cc | 118 +++++++----------- 2 files changed, 61 insertions(+), 84 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index cf88abcc2..d0bee400c 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -39,6 +39,7 @@ #include #include #include +#include #include namespace mata::utils { @@ -173,7 +174,8 @@ class ExtendableSquareMatrix { * @brief creates a deep copy of the matrix * @return deep copy of the matrix */ - virtual ExtendableSquareMatrix *clone(void) const = 0; + virtual std::unique_ptr> clone(void) const + = 0; virtual ~ExtendableSquareMatrix() = default; @@ -393,8 +395,9 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { // CLONING // - CascadeSquareMatrix *clone(void) const { - return new CascadeSquareMatrix(*this); } + std::unique_ptr> clone(void) const override { + return std::make_unique>(*this); + } // // OPERATORS @@ -533,8 +536,9 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { // CLONING // - DynamicSquareMatrix *clone(void) const { - return new DynamicSquareMatrix(*this); } + std::unique_ptr> clone(void) const override { + return std::make_unique>(*this); + } }; // DynamicSquareMatrix @@ -638,8 +642,9 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { // cloning - HashedSquareMatrix *clone(void) const { - return new HashedSquareMatrix(*this); } + std::unique_ptr> clone(void) const override { + return std::make_unique>(*this); + } }; // HashedSquareMatrix @@ -657,16 +662,16 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { * @return pointer to the newly created matrix */ template -inline ExtendableSquareMatrix *create(MatrixType type, +inline std::unique_ptr> create(MatrixType type, size_t capacity, size_t size = 0) { switch(type) { case MatrixType::Cascade: - return new CascadeSquareMatrix(capacity, size); + return std::make_unique>(capacity, size); case MatrixType::Dynamic: - return new DynamicSquareMatrix(capacity, size); + return std::make_unique>(capacity, size); case MatrixType::Hashed: - return new HashedSquareMatrix(capacity, size); + return std::make_unique>(capacity, size); default: return nullptr; } diff --git a/tests/extendable-square-matrix.cc b/tests/extendable-square-matrix.cc index dad5c6342..1174bf30b 100644 --- a/tests/extendable-square-matrix.cc +++ b/tests/extendable-square-matrix.cc @@ -8,8 +8,8 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { SECTION("CascadeSquareMatrix") { - ExtendableSquareMatrix *e = create( - Cascade, 5, 2); + std::unique_ptr> e = + create(Cascade, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -37,13 +37,12 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->is_reflexive()); CHECK(e->is_antisymetric()); CHECK(!e->is_transitive()); - delete e; } SECTION("DynamicSquareMatrix") { - ExtendableSquareMatrix *e = create( - Dynamic, 5, 2); + std::unique_ptr> e = + create(Dynamic, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -70,14 +69,13 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { e->set(1, 2, 1); CHECK(e->is_reflexive()); CHECK(e->is_antisymetric()); - CHECK(!e->is_transitive()); - delete e; + CHECK(!e->is_transitive()); } SECTION("HashedSquareMatrix") { - ExtendableSquareMatrix *e = create( - Hashed, 5, 2); + std::unique_ptr> e = + create(Hashed, 5, 2); CHECK(e->size() == 2); CHECK(e->capacity() == 5); e->extend(); @@ -104,27 +102,24 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { e->set(1, 2, 1); CHECK(e->is_reflexive()); CHECK(e->is_antisymetric()); - CHECK(!e->is_transitive()); - delete e; + CHECK(!e->is_transitive()); } SECTION("Matrix of the None type") { - ExtendableSquareMatrix *e = create( - None, 5, 2); + std::unique_ptr> e = + create(None, 5, 2); CHECK(e == nullptr); - - delete e; } SECTION("Empty matrices") { - ExtendableSquareMatrix *e1 = create( - Cascade, 5); - ExtendableSquareMatrix *e2 = create( - Dynamic, 5); - ExtendableSquareMatrix *e3 = create( - Hashed, 5); + std::unique_ptr> e1 = + create(Cascade, 5, 0); + std::unique_ptr> e2 = + create(Dynamic, 5, 0); + std::unique_ptr> e3 = + create(Hashed, 5, 0); CHECK(!e1->size()); CHECK(!e2->size()); @@ -134,9 +129,12 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e2->capacity() == 5); CHECK(e3->capacity() == 5); - ExtendableSquareMatrix *c1 = e1->clone(); - ExtendableSquareMatrix *c2 = e2->clone(); - ExtendableSquareMatrix *c3 = e3->clone(); + std::unique_ptr> c1 = + e1->clone(); + std::unique_ptr> c2 = + e2->clone(); + std::unique_ptr> c3 = + e3->clone(); CHECK(!c1->size()); CHECK(!c2->size()); @@ -146,22 +144,16 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(c2->capacity() == 5); CHECK(c3->capacity() == 5); - delete e1; - delete e2; - delete e3; - delete c1; - delete c2; - delete c3; - } SECTION("Matrices with only one element") { - ExtendableSquareMatrix *e1 = create( - Cascade, 5, 1); - ExtendableSquareMatrix *e2 = create( - Dynamic, 5, 1); - ExtendableSquareMatrix *e3 = create( - Hashed, 5, 1); + std::unique_ptr> e1 = + create(Cascade, 5, 1); + std::unique_ptr> e2 = + create(Dynamic, 5, 1); + std::unique_ptr> e3 = + create(Hashed, 5, 1); + CHECK(e1->size() == 1); CHECK(e2->size() == 1); @@ -171,9 +163,12 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e2->capacity() == 5); CHECK(e3->capacity() == 5); - ExtendableSquareMatrix *c1 = e1->clone(); - ExtendableSquareMatrix *c2 = e2->clone(); - ExtendableSquareMatrix *c3 = e3->clone(); + std::unique_ptr> c1 = + e1->clone(); + std::unique_ptr> c2 = + e2->clone(); + std::unique_ptr> c3 = + e3->clone(); CHECK(c1->size() == 1); CHECK(c2->size() == 1); @@ -181,38 +176,22 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(c1->capacity() == 5); CHECK(c2->capacity() == 5); - CHECK(c3->capacity() == 5); - - delete e1; - delete e2; - delete e3; - delete c1; - delete c2; - delete c3; + CHECK(c3->capacity() == 5); } SECTION("Copying matrices") { - ExtendableSquareMatrix *m1 = create(Cascade, 1000, 2); - ExtendableSquareMatrix *m2 = create(Dynamic, 5, 2); - ExtendableSquareMatrix *m3 = create(Hashed, 5, 2); + std::unique_ptr> m1 = + create(Cascade, 1000, 2); + std::unique_ptr> m2 = + create(Dynamic, 5, 2); + std::unique_ptr> m3 = + create(Hashed, 5, 2); - ExtendableSquareMatrix *c1 = m1; - ExtendableSquareMatrix *c2 = m2; - ExtendableSquareMatrix *c3 = m3; - - m1->set(1, 1, true); - m2->set(1, 1, true); - m3->set(1, 1, true); - - CHECK(m1->get(1, 1) == c1->get(1, 1)); - CHECK(m2->get(1, 1) == c2->get(1, 1)); - CHECK(m3->get(1, 1) == c3->get(1, 1)); - - c1 = m1->clone(); - c2 = m2->clone(); - c3 = m3->clone(); + std::unique_ptr> c1 = m1->clone(); + std::unique_ptr> c2 = m2->clone(); + std::unique_ptr> c3 = m3->clone(); m1->set(0, 1, true); m2->set(0, 1, true); @@ -226,13 +205,6 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(!c2->get(0, 1)); CHECK(!c3->get(0, 1)); - delete m1; - delete m2; - delete m3; - delete c1; - delete c2; - delete c3; - } } From 9067a1f47f58281a3536e0a651680a346e2c7f2c Mon Sep 17 00:00:00 2001 From: kocotom Date: Sun, 31 Mar 2024 04:35:08 +0200 Subject: [PATCH 28/34] [[unlikely]] attribute added, comments fixed --- include/mata/utils/extendable-square-matrix.hh | 4 ++-- src/partition.cc | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index d0bee400c..aceb5f3c0 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -208,7 +208,7 @@ class ExtendableSquareMatrix { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { - if(i == j) { continue; } + if(i == j) [[unlikely]] { continue; } if(get(i, j) && get(j, i)) { return false; } } } @@ -408,7 +408,7 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { * capacity of the vector 'data_' since the default vector * assignment does not preserve it. * @brief assignment operator for the CascadeSquareMatrix class - * @param[in] other matrix which should be copied assigned + * @param[in] other matrix which should be assigned */ CascadeSquareMatrix& operator=(const CascadeSquareMatrix& other) { // initialization of the matrix diff --git a/src/partition.cc b/src/partition.cc index 480199644..9ab9278ed 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -118,7 +118,9 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // if there is at least one unused state, we need to // create an additional block - if(all_states_used) { + // this branch will be executed only once (first time it is reached) + // and then it will be never executed again + if(all_states_used) [[unlikely]] { all_states_used = false; first = state; ++num_of_blocks; @@ -275,7 +277,7 @@ bool Partition::in_same_block(State first, State second) const { * @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()) { return true; } + 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."); @@ -366,7 +368,7 @@ std::vector Partition::split_blocks( std::vector split{}; // if there is no marked state, no block could be split - if(marked.empty()) { return 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 From 3c29f8547a0d40d19bd3349fb656e978928ad382 Mon Sep 17 00:00:00 2001 From: kocotom Date: Mon, 15 Apr 2024 02:26:54 +0200 Subject: [PATCH 29/34] Partition BlockItems, Blocks and Nodes reimplemented --- include/mata/utils/partition.hh | 370 ++++++++++++++++++----- src/partition.cc | 367 +++++++++++------------ tests/partition.cc | 502 +++++++++++++++++++++++--------- 3 files changed, 831 insertions(+), 408 deletions(-) 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; + } + } } From 47b5930b5c2b81a910c6b618f9d72860ecc2a03f Mon Sep 17 00:00:00 2001 From: kocotom Date: Mon, 15 Apr 2024 15:12:33 +0200 Subject: [PATCH 30/34] extend_and_copy method added to the ExtendableSquareMatrix class --- .../mata/utils/extendable-square-matrix.hh | 219 +++++++++++++++++- tests/extendable-square-matrix.cc | 75 ++++++ 2 files changed, 291 insertions(+), 3 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index aceb5f3c0..740e63e66 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -166,6 +166,27 @@ class ExtendableSquareMatrix { */ virtual void extend(T placeholder = T()) = 0; + /** + * Changes the n x n matrix to the (n+1) x (n+1) matrix by duplicating + * existing row and column. The row parameter is an index of the row + * which should be duplicated and added as a (n+1)th row, while the + * col parameter is an index of the column which should be duplicated + * and added as an (n+1)th row. If the row parameter equals n, then + * it will be initialized using default values of the type T. If the + * col parameter equals n, the new column will be also initialized + * with the defauls values of the type T. Using this approach, one is + * able to copy only a row or column and initialize the other one with + * default values. Calling extend_and_copy(n, n) has the same effect as + * calling extend(). + * The element at the position [n, n] will be always initialized using + * the default value of the type T. + * @brief changes the n x n matrix to the (n+1) x (n+1) matrix with + * copying of the existing data + * @param[in] placeholde value which will be assigned to the + * newly allocated memory cells + */ + virtual void extend_and_copy(size_t row, size_t col) = 0; + // // CLONING // @@ -390,6 +411,70 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { // the size increases ++this->size_; } + + /** + * Extendes the n x n cascade matrix to the (n+1) x (n+1) matrix by + * duplicating an existing row and column. The row parameter is an index + * of the row which should be duplicated and added as a (n+1)th row, + * while the col parameter is an index of the column which should be + * duplicated and added as an (n+1)th row. If the row parameter equals n, + * then it will be initialized using default values of the type T. If the + * col parameter equals n, the new column will be also initialized + * with the defauls values of the type T. Using this approach, one is + * able to copy only a row or column and initialize the other one with + * default values. Calling extend_and_copy(n, n) has the same effect as + * calling extend(). + * The element at the position [n, n] will be always initialized using + * the default value of the type T. + * @brief changes the n x n matrix to the (n+1) x (n+1) matrix with + * copying of the existing data + * @param[in] row index of the row which should be copied + * @param[in] col index of the column which should be copied + */ + void extend_and_copy(size_t row, size_t col) { + assert(this->size_ < this->capacity_ + && "The matrix cannot be extended anymore"); + std::cout << this->size_ << " " << row << std::endl; + assert(row <= this->size_ + && "Index of the copied row cannot be bigger than the size"); + assert(col <= this->size_ + && "Index of the copied col cannot be bigger than the size"); + + // if the row index corresponds to the index of the newly created + // row, it will be initialized using default values of the type T + if(row == this->size_) { + for(size_t i = 0; i < this->size_; ++i) { + data_.push_back(T()); + } + // otherwise, the row with the index 'row' will be duplicated + // to create a new row + } else { + for(size_t i = 0; i < this->size_; ++i) { + data_.push_back(get(row, i)); + } + } + + // element at the position [n, n] will be always initialized using + // the default value of the type T + data_.push_back(T()); + + // if the column index corresponds to the index of the newly created + // column, it will be initialized using default values of the type T + if(col == this->size_) { + for(size_t i = 0; i < this->size_; ++i) { + data_.push_back(T()); + } + // otherwise, the column with the index 'col' will be duplicated + // to create a new column + } else { + for(size_t i = 0; i < this->size_; ++i) { + data_.push_back(get(this->size_ - 1 - i, col)); + } + } + + // the size of the matrix increases after extending + ++this->size_; + } // // CLONING @@ -532,6 +617,66 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { data_.back().insert(data_.back().end(), this->size_, placeholder); } + /** + * Extendes the n x n dynamic matrix to the (n+1) x (n+1) matrix by + * duplicating an existing row and column. The row parameter is an index + * of the row which should be duplicated and added as a (n+1)th row, + * while the col parameter is an index of the column which should be + * duplicated and added as an (n+1)th row. If the row parameter equals n, + * then it will be initialized using default values of the type T. If the + * col parameter equals n, the new column will be also initialized + * with the defauls values of the type T. Using this approach, one is + * able to copy only a row or column and initialize the other one with + * default values. Calling extend_and_copy(n, n) has the same effect as + * calling extend(). + * The element at the position [n, n] will be always initialized using + * the default value of the type T. + * @brief changes the n x n matrix to the (n+1) x (n+1) matrix with + * copying of the existing data + * @param[in] row index of the row which should be copied + * @param[in] col index of the column which should be copied + */ + void extend_and_copy(size_t row, size_t col) { + assert(this->size_ < this->capacity_ + && "The matrix cannot be extended anymore"); + assert(row <= this->size_ + && "Index of the copied row cannot be bigger than the size"); + assert(col <= this->size_ + && "Index of the copied col cannot be bigger than the size"); + + // if the row index corresponds to the index of the newly created + // row, it will be initialized using default values of the type T + if(row == this->size_) { + data_.emplace_back(); + for(size_t i = 0; i < this->size_; ++i) { + data_[this->size_].emplace_back(T()); + } + // otherwise, the row at the index 'row' will be duplicated + } else { + data_.push_back(data_[row]); + } + + // if the column index corresponds to the index of the newly created + // column, it will be initialized using default values of the type T + if(col == this->size_) { + for(size_t i = 0; i < this->size_; ++i) { + data_[i].emplace_back(T()); + } + // otherwise, the column at the index 'col' will be duplicated + } else { + for(size_t i = 0; i < this->size_; ++i) { + data_[i].push_back(data_[i][col]); + } + } + + // element at the position [n, n] will be always initialized using + // the default value of the type T + data_[this->size_].emplace_back(T()); + + // the size of the matrix increases after extending + ++this->size_; + } + // // CLONING // @@ -590,7 +735,9 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { for(size_t i = 0; i < init_rows; ++i) {extend();} } - // implemented virtual functions + // + // IMPLEMENTED VIRTUAL METHODS + // /** * @brief assings a value to the Hashed square matrix @@ -640,8 +787,73 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { ++this->size_; } + /** + * Extendes the n x n hashed matrix to the (n+1) x (n+1) matrix by + * duplicating an existing row and column. The row parameter is an index + * of the row which should be duplicated and added as a (n+1)th row, + * while the col parameter is an index of the column which should be + * duplicated and added as an (n+1)th row. If the row parameter equals n, + * then it will be initialized using default values of the type T. If the + * col parameter equals n, the new column will be also initialized + * with the defauls values of the type T. Using this approach, one is + * able to copy only a row or column and initialize the other one with + * default values. Calling extend_and_copy(n, n) has the same effect as + * calling extend(). + * The element at the position [n, n] will be always initialized using + * the default value of the type T. + * @brief changes the n x n matrix to the (n+1) x (n+1) matrix with + * copying of the existing data + * @param[in] row index of the row which should be copied + * @param[in] col index of the column which should be copied + */ + void extend_and_copy(size_t row, size_t col) { + assert(this->size_ < this->capacity_ + && "The matrix cannot be extended anymore"); + assert(row <= this->size_ + && "Index of the copied row cannot be bigger than the size"); + assert(col <= this->size_ + && "Index of the copied col cannot be bigger than the size"); + + // if the row index corresponds to the index of the newly created + // row, it will be initialized using default values of the type T + if(row == this->size_) { + for(size_t i = 0; i < this->size_; ++i) { + data_[this->size_ * this->capacity_ + i] = T(); + } + // otherwise, the row at the index 'row' will be duplicated + } else { + for(size_t i = 0; i < this->size_; ++i) { + data_[this->size_ * this->capacity_ + i] = + data_[row * this->capacity_ + i]; + } + } + + // if the col index corresponds to the index of the newly created + // column, it will be initialized using default values of the type T + if(col == this->size_) { + for(size_t i = 0; i < this->size_; ++i) { + data_[i * this->capacity_ + this->size_] = T(); + } + // otherwise, the column at the index 'col' will be duplicated + } else { + for(size_t i = 0; i < this->size_; ++i) { + data_[i * this->capacity_ + this->size_] = + data_[i * this->capacity_ + col]; + } + } - // cloning + // element at the position [n, n] will be always initialized using + // the default value of the type T + data_[this->size_ * this->capacity_ + this->size_] = T(); + + // the size of the matrix increases after extending + ++this->size_; + } + + // + // CLONING + // + std::unique_ptr> clone(void) const override { return std::make_unique>(*this); } @@ -695,7 +907,8 @@ inline std::ostream& operator<<(std::ostream& os, result += "MATRIX:\n"; for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { - result += std::to_string(matrix.get(i, j)) + " "; + result += std::to_string( + static_cast(matrix.get(i, j))) + " "; } result += "\n"; } diff --git a/tests/extendable-square-matrix.cc b/tests/extendable-square-matrix.cc index 1174bf30b..d83fd7d3f 100644 --- a/tests/extendable-square-matrix.cc +++ b/tests/extendable-square-matrix.cc @@ -207,4 +207,79 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { } + SECTION("Extend and copy") { + + std::unique_ptr> m1 = + create(Cascade, 5, 3); + std::unique_ptr> m2 = + create(Dynamic, 5, 3); + std::unique_ptr> m3 = + create(Hashed, 5, 3); + + m1->set(0, 0, true); + m1->set(1, 0, true); + m1->set(1, 1, true); + m1->set(1, 2, true); + m2->set(0, 0, true); + m2->set(1, 0, true); + m2->set(1, 1, true); + m2->set(1, 2, true); + m3->set(0, 0, true); + m3->set(1, 0, true); + m3->set(1, 1, true); + m3->set(1, 2, true); + + CHECK(m1->size() == 3); + CHECK(m2->size() == 3); + CHECK(m3->size() == 3); + CHECK(m1->capacity() == 5); + CHECK(m2->capacity() == 5); + CHECK(m3->capacity() == 5); + + m1->extend_and_copy(3, 3); + m2->extend_and_copy(3, 3); + m3->extend_and_copy(3, 3); + m1->extend_and_copy(1, 0); + m2->extend_and_copy(1, 0); + m3->extend_and_copy(1, 0); + + CHECK(m1->size() == 5); + CHECK(m2->size() == 5); + CHECK(m3->size() == 5); + CHECK(m1->capacity() == 5); + CHECK(m2->capacity() == 5); + CHECK(m3->capacity() == 5); + + CHECK(m1->get(0, 4)); + CHECK(m1->get(1, 4)); + CHECK(!m1->get(2, 4)); + CHECK(!m1->get(3, 4)); + CHECK(m1->get(4, 0)); + CHECK(m1->get(4, 1)); + CHECK(m1->get(4, 2)); + CHECK(!m1->get(4, 3)); + CHECK(!m1->get(4, 4)); + + CHECK(m2->get(0, 4)); + CHECK(m2->get(1, 4)); + CHECK(!m2->get(2, 4)); + CHECK(!m2->get(3, 4)); + CHECK(m2->get(4, 0)); + CHECK(m2->get(4, 1)); + CHECK(m2->get(4, 2)); + CHECK(!m2->get(4, 3)); + CHECK(!m2->get(4, 4)); + + CHECK(m3->get(0, 4)); + CHECK(m3->get(1, 4)); + CHECK(!m3->get(2, 4)); + CHECK(!m3->get(3, 4)); + CHECK(m3->get(4, 0)); + CHECK(m3->get(4, 1)); + CHECK(m3->get(4, 2)); + CHECK(!m3->get(4, 3)); + CHECK(!m3->get(4, 4)); + + } + } From 10f0cd2bebb17e7e8f5ef933ec607b22b28eb062 Mon Sep 17 00:00:00 2001 From: kocotom Date: Wed, 17 Apr 2024 17:15:23 +0200 Subject: [PATCH 31/34] Methods BlockItem::first, BlockItem::last, Node::contains_block added with tests --- include/mata/utils/partition.hh | 8 ++++ tests/partition.cc | 84 ++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh index f6193615f..4a77ff5ac 100644 --- a/include/mata/utils/partition.hh +++ b/include/mata/utils/partition.hh @@ -301,6 +301,8 @@ class Partition { } const Node& node(void) const { return block().node(); } const BlockItem& repr(void) const { return node().repr(); } + const BlockItem& first(void) const { return node().first(); } + const BlockItem& last(void) const { return node().last(); } }; /** @@ -430,6 +432,12 @@ class Partition { size_t size(void) const { return last().idx() - first().idx() + 1; } + + bool contains_block(size_t block_idx) const { + const Block& block = partition_.get_block(block_idx); + return first_ <= block.first().idx() && + last_ >= block.last().idx(); + } }; public: diff --git a/tests/partition.cc b/tests/partition.cc index df63be8f2..1f53b6f8e 100644 --- a/tests/partition.cc +++ b/tests/partition.cc @@ -23,6 +23,8 @@ TEST_CASE("mata::utils::Partition") { 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).first().idx() == 0); + CHECK(p.get_block_item(i).last().idx() == 9); 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); @@ -30,11 +32,14 @@ TEST_CASE("mata::utils::Partition") { 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).first().idx() == 0); + CHECK(p.get_block(0).last().idx() == 9); 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); + CHECK(p.get_node(0).contains_block(0)); for(auto& block_item : p.get_block(0)) { CHECK(block_item.block().idx() == 0); } @@ -62,6 +67,8 @@ TEST_CASE("mata::utils::Partition") { 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).first().idx() == 0); + CHECK(p.get_block_item(i).last().idx() == 9); 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); @@ -69,11 +76,14 @@ TEST_CASE("mata::utils::Partition") { 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).first().idx() == 0); + CHECK(p.get_block(0).last().idx() == 9); 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); + CHECK(p.get_node(0).contains_block(0)); for(auto& block_item : p.get_block(0)) { CHECK(block_item.block().idx() == 0); } @@ -103,8 +113,14 @@ TEST_CASE("mata::utils::Partition") { CHECK(p[0].block().idx() == 0); CHECK(p[0].node().idx() == 0); CHECK(p.get_block_item(0).node().idx() == 0); + CHECK(p.get_block_item(0).repr().idx() == 0); + CHECK(p.get_block_item(0).first().idx() == 0); + CHECK(p.get_block_item(0).last().idx() == 2); CHECK(p[1].idx() == 3); CHECK(p.get_block_item(3).state() == 1); + CHECK(p.get_block_item(3).repr().state() == 1); + CHECK(p.get_block_item(3).first().state() == 1); + CHECK(p.get_block_item(3).last().state() == 9); CHECK(p[1].block().idx() == 1); CHECK(p[1].node().idx() == 1); CHECK(p.get_block_item(3).block().idx() == 1); @@ -117,12 +133,16 @@ TEST_CASE("mata::utils::Partition") { 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(0).contains_block(0)); + CHECK(!p.get_node(0).contains_block(1)); 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_node(1).contains_block(0)); + CHECK(p.get_node(1).contains_block(1)); 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); @@ -150,11 +170,17 @@ TEST_CASE("mata::utils::Partition") { 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_item(0).repr().idx() == 0); + CHECK(p.get_block_item(0).first().idx() == 0); + CHECK(p.get_block_item(0).last().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_item(1).repr().idx() == 1); + CHECK(p.get_block_item(1).first().idx() == 1); + CHECK(p.get_block_item(1).last().idx() == 2); CHECK(p.get_block(0).repr().state() == 0); CHECK(p.get_block(1).repr().state() == 1); CHECK(p.get_block(2).repr().state() == 3); @@ -166,13 +192,25 @@ TEST_CASE("mata::utils::Partition") { 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_node(2).last().idx() == 5); + CHECK(p.get_node(0).contains_block(0)); + CHECK(!p.get_node(0).contains_block(1)); + CHECK(!p.get_node(0).contains_block(2)); + CHECK(!p.get_node(1).contains_block(0)); + CHECK(p.get_node(1).contains_block(1)); + CHECK(!p.get_node(1).contains_block(2)); + CHECK(!p.get_node(2).contains_block(0)); + CHECK(!p.get_node(2).contains_block(1)); + CHECK(p.get_node(2).contains_block(2)); 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.get_block_item(3).repr().idx() == 3); + CHECK(p.get_block_item(3).first().idx() == 3); + CHECK(p.get_block_item(3).last().idx() == 5); 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); @@ -191,6 +229,7 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.states_in_same_block(0).size() == 10); CHECK(p.partition().size() == 1); CHECK(p.get_block(0).size() == 10); + CHECK(p.get_node(0).contains_block(0)); p.split_blocks({0, 1, 2, 3, 4}); CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); @@ -205,6 +244,12 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.partition().size() == 2); CHECK(p.get_block(0).size() == 5); CHECK(p.get_block(1).size() == 5); + CHECK(p.get_node(0).contains_block(0)); + CHECK(p.get_node(0).contains_block(1)); + CHECK(p.get_node(1).contains_block(0)); + CHECK(!p.get_node(1).contains_block(1)); + CHECK(!p.get_node(2).contains_block(0)); + CHECK(p.get_node(2).contains_block(1)); p.split_blocks({0, 1, 2, 5, 6, 7}); CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); @@ -221,6 +266,34 @@ TEST_CASE("mata::utils::Partition") { CHECK(p.states_in_same_block(5).size() == 3); CHECK(p.states_in_same_block(8).size() == 2); CHECK(p.partition().size() == 4); + CHECK(p.get_node(0).contains_block(0)); + CHECK(p.get_node(0).contains_block(1)); + CHECK(p.get_node(0).contains_block(2)); + CHECK(p.get_node(0).contains_block(3)); + CHECK(p.get_node(1).contains_block(0)); + CHECK(!p.get_node(1).contains_block(1)); + CHECK(p.get_node(1).contains_block(2)); + CHECK(!p.get_node(1).contains_block(3)); + CHECK(!p.get_node(2).contains_block(0)); + CHECK(p.get_node(2).contains_block(1)); + CHECK(!p.get_node(2).contains_block(2)); + CHECK(p.get_node(2).contains_block(3)); + CHECK(p.get_node(3).contains_block(0)); + CHECK(!p.get_node(3).contains_block(1)); + CHECK(!p.get_node(3).contains_block(2)); + CHECK(!p.get_node(3).contains_block(3)); + CHECK(!p.get_node(4).contains_block(0)); + CHECK(!p.get_node(4).contains_block(1)); + CHECK(p.get_node(4).contains_block(2)); + CHECK(!p.get_node(4).contains_block(3)); + CHECK(!p.get_node(5).contains_block(0)); + CHECK(p.get_node(5).contains_block(1)); + CHECK(!p.get_node(5).contains_block(2)); + CHECK(!p.get_node(5).contains_block(3)); + CHECK(!p.get_node(6).contains_block(0)); + CHECK(!p.get_node(6).contains_block(1)); + CHECK(!p.get_node(6).contains_block(2)); + CHECK(p.get_node(6).contains_block(3)); p.split_blocks({0, 3, 5, 8}); CHECK(p.num_of_states() == 10); CHECK(p.num_of_block_items() == 10); @@ -370,11 +443,17 @@ TEST_CASE("mata::utils::Partition") { 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_item(0).repr().idx() == 0); + CHECK(p.get_block_item(0).first().idx() == 0); + CHECK(p.get_block_item(0).last().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_item(1).repr().idx() == 1); + CHECK(p.get_block_item(1).first().idx() == 1); + CHECK(p.get_block_item(1).last().idx() == 2); CHECK(p.get_block(0).repr().state() == 0); CHECK(p.get_block(1).repr().state() == 1); CHECK(p.get_block(2).repr().state() == 3); @@ -390,6 +469,9 @@ TEST_CASE("mata::utils::Partition") { 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_item(3).repr().idx() == 3); + CHECK(p.get_block_item(3).first().idx() == 3); + CHECK(p.get_block_item(3).last().idx() == 5); 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); From a5a7ffcf33ce1e6be83b44183a9177b4e6e8ee1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Chocholat=C3=BD?= Date: Mon, 24 Jun 2024 08:50:29 +0200 Subject: [PATCH 32/34] Rename header guards --- include/mata/utils/extendable-square-matrix.hh | 8 ++++---- include/mata/utils/partition.hh | 6 +++--- src/partition.cc | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index 740e63e66..285881faf 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -33,8 +33,8 @@ * @author Tomáš Kocourek */ -#ifndef _EXTENDABLE_SQUARE_MATRIX_HH_ -#define _EXTENDABLE_SQUARE_MATRIX_HH_ +#ifndef EXTENDABLE_SQUARE_MATRIX_HH_ +#define EXTENDABLE_SQUARE_MATRIX_HH_ #include #include @@ -915,6 +915,6 @@ inline std::ostream& operator<<(std::ostream& os, return os << result; } -} +} // namespace mata::utils -#endif +#endif // EXTENDABLE_SQUARE_MATRIX_HH_ diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh index 4a77ff5ac..e07623d51 100644 --- a/include/mata/utils/partition.hh +++ b/include/mata/utils/partition.hh @@ -27,8 +27,8 @@ * @author Tomáš Kocourek */ -#ifndef _PARTITION_HH_ -#define _PARTITION_HH_ +#ifndef PARTITION_HH_ +#define PARTITION_HH_ #include #include @@ -482,4 +482,4 @@ class Partition { } -#endif +#endif // PARTITION_HH_ diff --git a/src/partition.cc b/src/partition.cc index 38ac2ade2..0ebdcafea 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -530,4 +530,4 @@ std::ostream& operator<<(std::ostream& os, const Partition& p) { return os << result; } -} +} // namespace mata::utils From 337c8d069a2475a2c8707c489406828ebd1ccf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Chocholat=C3=BD?= Date: Mon, 24 Jun 2024 08:53:20 +0200 Subject: [PATCH 33/34] Remove `void` in function arguments --- .../mata/utils/extendable-square-matrix.hh | 19 ++++---- include/mata/utils/partition.hh | 46 +++++++++---------- src/partition.cc | 2 +- tests/extendable-square-matrix.cc | 24 +++++----- 4 files changed, 45 insertions(+), 46 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index 285881faf..9ff14c4ce 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -117,7 +117,7 @@ class ExtendableSquareMatrix { * @brief returns the size of the matrix * @return size of the matrix */ - size_t size(void) const { return size_; } + size_t size() const { return size_; } /** Returns a capacity of the matrix. In this context, * the capacity of an n x n matrix corresponds to the value n_max such @@ -126,14 +126,14 @@ class ExtendableSquareMatrix { * @brief returns the capacity of the matrix * @return capacity of the matrix */ - size_t capacity(void) const { return capacity_; } + size_t capacity() const { return capacity_; } /** Returns a type of the matrix. The type specifies the * way the inner representation of the matrix is implemented. * @brief returns the type of the matrix * @return type of the matrix */ - size_t type(void) const { return m_type; } + size_t type() const { return m_type; } // // VIRTUAL METHODS @@ -195,8 +195,7 @@ class ExtendableSquareMatrix { * @brief creates a deep copy of the matrix * @return deep copy of the matrix */ - virtual std::unique_ptr> clone(void) const - = 0; + virtual std::unique_ptr> clone() const = 0; virtual ~ExtendableSquareMatrix() = default; @@ -210,7 +209,7 @@ class ExtendableSquareMatrix { * @brief checks whether the Extendable square matrix is reflexive * @return true iff the matrix is reflexive */ - bool is_reflexive(void) { + bool is_reflexive() const { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { if(!get(i, i)) { return false; } @@ -225,7 +224,7 @@ class ExtendableSquareMatrix { * @brief checks whether the Extendable square matrix is antisymetric * @return true iff the matrix is antisymetric */ - bool is_antisymetric(void) { + bool is_antisymmetric() const { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { @@ -243,7 +242,7 @@ class ExtendableSquareMatrix { * @brief checks whether the Extendable square matrix is transitive * @return true iff the matrix is transitive */ - bool is_transitive(void) { + bool is_transitive() const { size_t size = this->size(); for(size_t i = 0; i < size; ++i) { for(size_t j = 0; j < size; ++j) { @@ -480,7 +479,7 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { // CLONING // - std::unique_ptr> clone(void) const override { + std::unique_ptr> clone() const override { return std::make_unique>(*this); } @@ -681,7 +680,7 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { // CLONING // - std::unique_ptr> clone(void) const override { + std::unique_ptr> clone() const override { return std::make_unique>(*this); } diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh index e07623d51..da6a724e2 100644 --- a/include/mata/utils/partition.hh +++ b/include/mata/utils/partition.hh @@ -292,17 +292,17 @@ class Partition { partition_(partition) {} // getters - size_t idx(void) const { return idx_; } - size_t state(void) const { return state_; } + size_t idx() const { return idx_; } + size_t state() const { return state_; } // methods which refer to the partition vectors - const Block& block(void) const { + const Block& block() const { return partition_.blocks_[block_idx_]; } - const Node& node(void) const { return block().node(); } - const BlockItem& repr(void) const { return node().repr(); } - const BlockItem& first(void) const { return node().first(); } - const BlockItem& last(void) const { return node().last(); } + const Node& node() const { return block().node(); } + const BlockItem& repr() const { return node().repr(); } + const BlockItem& first() const { return node().first(); } + const BlockItem& last() const { return node().last(); } }; /** @@ -333,15 +333,15 @@ class Partition { idx_(idx), node_idx_(node_idx), partition_(partition) {} // getters - size_t idx(void) const { return idx_; } + size_t idx() const { return idx_; } // methods which refer to the partition vectors - const Node& node(void) const { + const Node& node() 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(); } + const BlockItem& repr() const { return node().repr(); } + const BlockItem& first() const { return node().first(); } + const BlockItem& last() const { return node().last(); } // iterators using const_iterator = typename BlockItems::const_iterator; @@ -359,7 +359,7 @@ class Partition { } // information about the block - size_t size(void) const { + size_t size() const { return last().idx() - first().idx() + 1; } @@ -399,16 +399,16 @@ class Partition { {} // getters - size_t idx(void) const { return idx_; } + size_t idx() const { return idx_; } // methods which refer to the partition vectors - const BlockItem& first(void) const { + const BlockItem& first() const { return partition_.block_items_[first_]; } - const BlockItem& last(void) const { + const BlockItem& last() const { return partition_.block_items_[last_]; } - const BlockItem& repr(void) const { + const BlockItem& repr() const { return partition_.block_items_[first_]; } @@ -429,7 +429,7 @@ class Partition { } // information about the node - size_t size(void) const { + size_t size() const { return last().idx() - first().idx() + 1; } @@ -449,10 +449,10 @@ class Partition { Partition(const Partition& other); // sizes of the used vectors - size_t num_of_states(void) const { return states_.size(); } - size_t num_of_block_items(void) const { return block_items_.size(); } - size_t num_of_blocks(void) const { return blocks_.size(); } - size_t num_of_nodes(void) const { return nodes_.size(); } + size_t num_of_states() const { return states_.size(); } + size_t num_of_block_items() const { return block_items_.size(); } + size_t num_of_blocks() const { return blocks_.size(); } + size_t num_of_nodes() const { return nodes_.size(); } // blocks splitting std::vector split_blocks(const SparseSet& marked); @@ -469,7 +469,7 @@ class Partition { const Node& get_node(size_t node_idx) const; // converts the partition to the vector of vectors of states - StateBlocks partition(void) const; + StateBlocks partition() const; // operators Partition& operator=(const Partition& other); diff --git a/src/partition.cc b/src/partition.cc index 0ebdcafea..bbdedc17d 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -263,7 +263,7 @@ std::vector Partition::states_in_same_block(size_t state_idx) const { * vectors of states * @return vector of vectors of states */ -StateBlocks Partition::partition(void) const { +StateBlocks Partition::partition() const { StateBlocks result{}; for(const Block& block : blocks_) { result.emplace_back(); diff --git a/tests/extendable-square-matrix.cc b/tests/extendable-square-matrix.cc index d83fd7d3f..f7f0f4d5a 100644 --- a/tests/extendable-square-matrix.cc +++ b/tests/extendable-square-matrix.cc @@ -20,22 +20,22 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(0, 0, 1); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(!e->is_transitive()); } @@ -53,22 +53,22 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(0, 0, 1); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(!e->is_transitive()); } @@ -86,22 +86,22 @@ TEST_CASE("mata::utils::ExtendableSquareMatrix") { CHECK(e->capacity() == 5); CHECK(e->get(0, 0) == 0); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(0, 0, 1); CHECK(!e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(1, 1, 1); e->set(2, 2, 1); e->set(3, 3, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(e->is_transitive()); e->set(3, 1, 1); e->set(1, 2, 1); CHECK(e->is_reflexive()); - CHECK(e->is_antisymetric()); + CHECK(e->is_antisymmetric()); CHECK(!e->is_transitive()); } From f37352b6f017a6b6fa6ec9437eb3626e1646ed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Chocholat=C3=BD?= Date: Mon, 24 Jun 2024 08:54:17 +0200 Subject: [PATCH 34/34] Fix typos --- .../mata/utils/extendable-square-matrix.hh | 74 +++++++++---------- include/mata/utils/partition.hh | 14 ++-- src/partition.cc | 34 ++++----- 3 files changed, 60 insertions(+), 62 deletions(-) diff --git a/include/mata/utils/extendable-square-matrix.hh b/include/mata/utils/extendable-square-matrix.hh index 9ff14c4ce..fc4c2c776 100644 --- a/include/mata/utils/extendable-square-matrix.hh +++ b/include/mata/utils/extendable-square-matrix.hh @@ -103,7 +103,7 @@ class ExtendableSquareMatrix { size_t capacity_{0}; // type of the matrix which will be chosen as soon as the - // child class will be created + // child class is created MatrixType m_type{MatrixType::None}; public: @@ -161,7 +161,7 @@ class ExtendableSquareMatrix { /** * @brief changes the n x n matrix to the (n+1) x (n+1) matrix - * @param[in] placeholde value which will be assigned to the + * @param[in] placeholder value which will be assigned to the * newly allocated memory cells */ virtual void extend(T placeholder = T()) = 0; @@ -174,7 +174,7 @@ class ExtendableSquareMatrix { * and added as an (n+1)th row. If the row parameter equals n, then * it will be initialized using default values of the type T. If the * col parameter equals n, the new column will be also initialized - * with the defauls values of the type T. Using this approach, one is + * with the default values of the type T. Using this approach, one is * able to copy only a row or column and initialize the other one with * default values. Calling extend_and_copy(n, n) has the same effect as * calling extend(). @@ -182,7 +182,7 @@ class ExtendableSquareMatrix { * the default value of the type T. * @brief changes the n x n matrix to the (n+1) x (n+1) matrix with * copying of the existing data - * @param[in] placeholde value which will be assigned to the + * @param[in] placeholde value which will be assigned to the * newly allocated memory cells */ virtual void extend_and_copy(size_t row, size_t col) = 0; @@ -217,12 +217,12 @@ class ExtendableSquareMatrix { return true; } - /** This function checks whether the matrix is antisymetric. In this - * context, the matrix is antisymetric iff there are no indices i, j + /** This function checks whether the matrix is antisymmetric. In this + * context, the matrix is antisymmetric iff there are no indices i, j * where i != j and both matrix[i][j], matrix[j][i] contain nonzero - * elementes of the type T - * @brief checks whether the Extendable square matrix is antisymetric - * @return true iff the matrix is antisymetric + * elements of the type T + * @brief checks whether the Extendable square matrix is antisymmetric + * @return true iff the matrix is antisymmetric */ bool is_antisymmetric() const { size_t size = this->size(); @@ -278,7 +278,7 @@ class ExtendableSquareMatrix { * * This implementation tries to avoid * - moving the whole matrix when it is extended - * - allocation of unneccessary data cells + * - allocation of unnecessary data cells * - violation of data locality * * The matrix is represented as a single vector of a type T. Initially, @@ -368,14 +368,14 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { // /** - * @brief assings a value to the Cascade square matrix + * @brief Assigns a value to the Cascade square matrix. * @param[in] i row of the square matrix * @param[in] j column of the square matrix * @param[in] value value to be assigned to the square matrix data cell */ void set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); // accessing the matrix in the cascading way data_[i >= j ? i * i + j : j * j + 2 * j - i] = value; @@ -388,8 +388,8 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { * @return value found in the square matrix data cell */ T get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); // accessing the matrix in the cascading way return data_[i >= j ? i * i + j : j * j + 2 * j - i]; @@ -412,14 +412,14 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { } /** - * Extendes the n x n cascade matrix to the (n+1) x (n+1) matrix by + * Extends the n x n cascade matrix to the (n+1) x (n+1) matrix by * duplicating an existing row and column. The row parameter is an index * of the row which should be duplicated and added as a (n+1)th row, * while the col parameter is an index of the column which should be * duplicated and added as an (n+1)th row. If the row parameter equals n, * then it will be initialized using default values of the type T. If the * col parameter equals n, the new column will be also initialized - * with the defauls values of the type T. Using this approach, one is + * with the defaults values of the type T. Using this approach, one is * able to copy only a row or column and initialize the other one with * default values. Calling extend_and_copy(n, n) has the same effect as * calling extend(). @@ -453,7 +453,7 @@ class CascadeSquareMatrix : public ExtendableSquareMatrix { } } - // element at the position [n, n] will be always initialized using + // element at the position [n, n] will always be initialized using // the default value of the type T data_.push_back(T()); @@ -571,14 +571,14 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { // /** - * @brief assings a value to the Dynamic square matrix + * @brief Assigns a value to the Dynamic square matrix. * @param[in] i row of the square matrix * @param[in] j column of the square matrix * @param[in] value value to be assigned to the square matrix data cell */ T get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); return data_[i][j]; } @@ -590,8 +590,8 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { * @return value found in the square matrix data cell */ void set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); data_[i][j] = value; } @@ -603,7 +603,7 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { */ void extend(T placeholder = T()) { assert(this->size_ < this->capacity_ - && "The matrix cannot be extened anymore"); + && "The matrix cannot be extended anymore"); // creating a new column for(size_t i = 0; i < this->size_; ++i) { @@ -617,14 +617,14 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { } /** - * Extendes the n x n dynamic matrix to the (n+1) x (n+1) matrix by + * Extends the n x n dynamic matrix to the (n+1) x (n+1) matrix by * duplicating an existing row and column. The row parameter is an index * of the row which should be duplicated and added as a (n+1)th row, * while the col parameter is an index of the column which should be * duplicated and added as an (n+1)th row. If the row parameter equals n, * then it will be initialized using default values of the type T. If the * col parameter equals n, the new column will be also initialized - * with the defauls values of the type T. Using this approach, one is + * with the defaults values of the type T. Using this approach, one is * able to copy only a row or column and initialize the other one with * default values. Calling extend_and_copy(n, n) has the same effect as * calling extend(). @@ -668,7 +668,7 @@ class DynamicSquareMatrix : public ExtendableSquareMatrix { } } - // element at the position [n, n] will be always initialized using + // element at the position [n, n] will always be initialized using // the default value of the type T data_[this->size_].emplace_back(T()); @@ -739,14 +739,14 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { // /** - * @brief assings a value to the Hashed square matrix + * @brief Assigns a value to the Hashed square matrix. * @param[in] i row of the square matrix * @param[in] j column of the square matrix * @param[in] value value to be assigned to the square matrix data cell */ void set(size_t i, size_t j, T value) { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); // accessing the hashmap using row matrix traversal data_[i * this->capacity_ + j] = value; @@ -759,8 +759,8 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { * @return value found in the square matrix data cell */ T get(size_t i, size_t j) const { - assert(i < this->size_ && "Nonexisting row cannot be accessed"); - assert(j < this->size_ && "Nonexisting column cannot be accessed"); + assert(i < this->size_ && "Nonexistent row cannot be accessed"); + assert(j < this->size_ && "Nonexistent column cannot be accessed"); // accessing the hashmap using row matrix traversal return data_[i * this->capacity_ + j]; @@ -773,7 +773,7 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { */ void extend(T placeholder = T()) { assert(this->size_ < this->capacity_ - && "Matrix cannot be extened anymore"); + && "Matrix cannot be extended anymore"); // creating a new row and column for(size_t i = 0; i < this->size_; ++i) { @@ -787,14 +787,14 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { } /** - * Extendes the n x n hashed matrix to the (n+1) x (n+1) matrix by + * Extends the n x n hashed matrix to the (n+1) x (n+1) matrix by * duplicating an existing row and column. The row parameter is an index * of the row which should be duplicated and added as a (n+1)th row, * while the col parameter is an index of the column which should be * duplicated and added as an (n+1)th row. If the row parameter equals n, * then it will be initialized using default values of the type T. If the * col parameter equals n, the new column will be also initialized - * with the defauls values of the type T. Using this approach, one is + * with the defaults values of the type T. Using this approach, one is * able to copy only a row or column and initialize the other one with * default values. Calling extend_and_copy(n, n) has the same effect as * calling extend(). @@ -841,7 +841,7 @@ class HashedSquareMatrix : public ExtendableSquareMatrix { } } - // element at the position [n, n] will be always initialized using + // element at the position [n, n] will always be initialized using // the default value of the type T data_[this->size_ * this->capacity_ + this->size_] = T(); @@ -892,7 +892,7 @@ inline std::unique_ptr> create(MatrixType type, * @brief debugging function which allows us to print text representation of * the Extendable square matrix * @param[out] output stream -* @param[in] maxtrix which will be printed +* @param[in] matrix which will be printed * @return output stream */ template diff --git a/include/mata/utils/partition.hh b/include/mata/utils/partition.hh index da6a724e2..f97261f62 100644 --- a/include/mata/utils/partition.hh +++ b/include/mata/utils/partition.hh @@ -245,16 +245,16 @@ class Partition { using Blocks = std::vector; using Nodes = std::vector; - /**< vector of states refering to the block items */ + /**< vector of states referring to the block items */ States states_{}; - /**< vector of block items refering to the states and blocks */ + /**< vector of block items referring to the states and blocks */ BlockItems block_items_{}; - /**< vector of blocks refering to the nodes */ + /**< vector of blocks referring to the nodes */ Blocks blocks_{}; - /**< vector of nodes refering to the first and last block item + /**< vector of nodes referring to the first and last block item of the node */ Nodes nodes_{}; @@ -307,7 +307,7 @@ class Partition { /** * @class Block - * @brief Contains infromation about block from the current generation + * @brief Contains information about block from the current generation * of the partition. **/ class Block { @@ -367,7 +367,7 @@ class Partition { /** * @class Node - * @brief Contains infromation about block from the current generation + * @brief Contains information about block from the current generation * of the partition or from the previous generation of the partition. **/ class Node { @@ -444,7 +444,7 @@ class Partition { // constructors Partition() = default; - Partition(size_t num_of_states, + explicit Partition(size_t num_of_states, const StateBlocks& partition = StateBlocks()); Partition(const Partition& other); diff --git a/src/partition.cc b/src/partition.cc index bbdedc17d..1508f124f 100644 --- a/src/partition.cc +++ b/src/partition.cc @@ -42,7 +42,7 @@ namespace mata::utils { * 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 construction +* - if there is any nonexistent 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 @@ -89,7 +89,7 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { "a partition relation pair."); assert(!used[state_idx] && "Partition could not be created." - "Duplicate occurence of a state"); + "Duplicate occurrence of a state"); used[state_idx] = true; @@ -121,7 +121,7 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { size_t first = 0; size_t last = 0; - // iterating through the vector of flags saying which states have been seen. + // 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) { @@ -134,7 +134,7 @@ Partition::Partition(size_t num_of_states, const StateBlocks& partition) { // if there is at least one unused state, we need to // create an additional block // this branch will be executed only once (first time it is reached) - // and then it will be never executed again + // and then it will never be executed again if(all_states_used) [[unlikely]] { all_states_used = false; first = state_idx; @@ -179,7 +179,7 @@ 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."); + "Nonexistent block item index used."); return block_items_[block_item_idx]; } @@ -189,7 +189,7 @@ const Partition::BlockItem& Partition::get_block_item( * @return corresponding block */ const Partition::Block& Partition::get_block(size_t block_idx) const { - assert(block_idx < num_of_blocks() && "Nonexisting block index used."); + assert(block_idx < num_of_blocks() && "Nonexistent block index used."); return blocks_[block_idx]; } @@ -199,7 +199,7 @@ const Partition::Block& Partition::get_block(size_t block_idx) const { * @return corresponding node */ const Partition::Node& Partition::get_node(size_t node_idx) const { - assert(node_idx < num_of_nodes() && "Nonexisting node index used."); + assert(node_idx < num_of_nodes() && "Nonexistent node index used."); return nodes_[node_idx]; } @@ -209,7 +209,7 @@ const Partition::Node& Partition::get_node(size_t node_idx) const { * @return corresponding block index */ size_t Partition::get_block_idx(State state) const { - assert(state < num_of_states() && "Nonexisting state udsed"); + assert(state < num_of_states() && "Nonexistent state used"); return block_items_[states_[state]].block_idx_; } @@ -296,7 +296,7 @@ StateBlocks Partition::partition() const { * 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 . -* - if an nonexisting state is used, the function detects it and fails +* - if an nonexistent state is used, the function detects it and fails * * If a block is split, the function creates a structure SplitPair which * contains: @@ -309,8 +309,7 @@ StateBlocks Partition::partition() const { * @param[in] marked marked states which influence splitting * @return vector of SplitPairs which contain information about split blocks */ -std::vector Partition::split_blocks( - const SparseSet& marked) { +std::vector Partition::split_blocks(const SparseSet& marked) { // the vector which will be returned as the result std::vector split{}; @@ -318,11 +317,11 @@ std::vector Partition::split_blocks( // if there is no marked state, no block could be split if(marked.empty()) [[unlikely]] { return split; } - // this vector contains information about blocks whose states has been - // marked and keeps number of states of each block which has been marked + // this vector contains information about blocks whose states have been + // marked and keep 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()); } + if(!blocks_.empty()) [[likely]] { used_blocks.reserve(blocks_.size()); } used_blocks.insert(used_blocks.end(), blocks_.size(), 0); // iterating through the marked states to fill used_blocks vector @@ -455,18 +454,17 @@ Partition& Partition::operator=(const Partition& other) { for(size_t i = 0; i < states_num; ++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)); + block_items_.emplace_back(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) { const Block& b = other.get_block(i); - blocks_.emplace_back(Block(b.idx_, b.node_idx_, *this)); + blocks_.emplace_back(b.idx_, b.node_idx_, *this); } size_t nodes_num = other.num_of_nodes(); for(size_t i = 0; i < nodes_num; ++i) { const Node& n = other.get_node(i); - nodes_.emplace_back(Node(n.idx_, n.first_, n.last_, *this)); + nodes_.emplace_back(n.idx_, n.first_, n.last_, *this); } return *this; }