diff --git a/Control/CxxUtils/CMakeLists.txt b/Control/CxxUtils/CMakeLists.txt index 857605b47933b18076de7255878d91d92eb01060..8cbdc62f799f14afbc872b244933aa601ce8b535 100644 --- a/Control/CxxUtils/CMakeLists.txt +++ b/Control/CxxUtils/CMakeLists.txt @@ -11,7 +11,8 @@ atlas_depends_on_subdirs( PRIVATE AtlasTest/TestTools ) # External dependencies: -find_package( Boost COMPONENTS program_options regex filesystem thread system ) +find_package( Boost COMPONENTS program_options regex filesystem thread system timer ) +find_package( TBB ) # The main library of the package: atlas_add_library( CxxUtils @@ -79,13 +80,29 @@ if( TARGET CxxUtils_bitscan_test_portable ) PRIVATE "-DTEST_PORTABLE" ) endif() +if ( IS_DIRECTORY /usr/include/ck ) + set( CK_LIBRARIES ck ) + set( CK_INCLUDE /usr/include/ck ) +else() + set( CK_LIBRARIES ) + set( CK_INCLUDE ) +endif() +atlas_add_test( ConcurrentBitset_test + SOURCES test/ConcurrentBitset_test.cxx + INCLUDE_DIRS ${TBB_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${CK_INCLUDE} + LINK_LIBRARIES CxxUtils ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CK_LIBRARIES} ) +if ( IS_DIRECTORY /usr/include/ck ) + target_compile_definitions(CxxUtils_ConcurrentBitset_test + PRIVATE "-DHAVE_CK" ) +endif() + # Set up the "simple" tests: foreach( test sincos_test copyif_test ArrayScanner_test Arrayrep_test Array_test PackedArray_test pointer_list_test FloatPacker_test fpcompare_test StrFormat_test prefetch_test ClassName_test make_unique_test ones_test exctrace1_test exctrace2_test bitscan_test ConcurrentRangeMap_test - CachedValue_test CachedPointer_test ) + CachedValue_test CachedPointer_test atomic_fetch_minmax_test ) atlas_add_test( ${test} SOURCES test/${test}.cxx LINK_LIBRARIES CxxUtils ) diff --git a/Control/CxxUtils/CxxUtils/ConcurrentBitset.h b/Control/CxxUtils/CxxUtils/ConcurrentBitset.h new file mode 100644 index 0000000000000000000000000000000000000000..141291cbfd4b69cddaae1505be3af096c947f14f --- /dev/null +++ b/Control/CxxUtils/CxxUtils/ConcurrentBitset.h @@ -0,0 +1,1040 @@ +// This file's extension implies that it's C, but it's really -*- C++ -*-. +/* + * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration. + */ +// $Id$ +/** + * @file CxxUtils/ConcurrentBitset.h + * @author scott snyder <snyder@bnl.gov> + * @date Nov, 2017 + * @brief Variable-sized bitset allowing (mostly) concurrent access. + */ + + +#ifndef CXXUTILS_CONCURRENTBITSET_H +#define CXXUTILS_CONCURRENTBITSET_H + + +#include "CxxUtils/atomic_fetch_minmax.h" +#include "CxxUtils/bitscan.h" +#include "CxxUtils/ones.h" +#include <climits> +#include <vector> +#include <algorithm> +#include <iterator> +#include <atomic> +#include <mutex> +#include <memory> +#include <type_traits> +#include <cstddef> + + +namespace CxxUtils { + + +/** + * @brief Variable-sized bitset allowing (mostly) concurrent access. + * + * This class represents a set of bits. One can think of such an object as either + * like a std::bitset or like a std::set<unsigned>. This class provides + * basically the union of the two interfaces. In a few cases, the same method + * has different semantics in the two interfaces, most notably @c size(). + * We follow here the semantics that we would get from std::set<unsigned>. + * (Rationale: The motivation for this class is to replace the existing use + * of unordered_set<unsigned> for auxid_set_t. Supplying the same interface + * makes this easier to drop in as a replacement. However, in some cases, + * the set operations as provided by a bitset-like interface can be much more + * efficient, so we'd want to provide those as well.) + * + * The size of the bitset is specified at runtime, to the constructor. Most operations + * do not change the size. However, the @c insert() methods, as well as @c operator=, + * may be used to grow the size of the set. + * + * Most methods can execute completely concurrently. However, the methods which + * can change the size of the container, @c insert() and @c operator=, are not + * compatible with other concurrent writes. (Concurrent reads are ok, though.) + * + * Some methods can operate on the entire set (e.g., @c clear()). Such methods + * may run concurrently with others, but they will not necessarily be atomic: + * other threads may be able to see the operation partially completed. + * + * An iterator is provided that iterates over all bits that are set (like iterating + * over a set<unsigned>). + * + * When the container is expanded, the internal representation needs to be reallocated. + * The old object is not, however, deleted immediately, as other threads may + * still be referencing it. If if some point you know that no threads can be + * referencing old versions any more, you can clean them up by calling @c emptyGarbage + * (otherwise, all versions will be deleted when the set object is destroyed). + * + * Some notes on motivation: + * + * The use case that motivated this class was auxid_set_t, which tells which + * auxiliary variables are available for a given container. This is logically + * a relatively sparse set of relatively small integers. (Maximum value is ~2000, + * with a set having ~100 entries.) + * + * This had been implemented as a std::unordered_set<size_t>, but this was + * not entirely satisfactory. First, in some cases, there was a significant overhead + * in inserting and deleting items from the set. Second, unordered_set does not + * allow concurrent reading and writing. This meant that we ended up maintaining + * thread-local copies of each set, which adds extra overhead and complexity. + * + * The threading issue suggests that we would like to use a container that allows + * readers to run currently with at least one writer. The fact that the maximum + * value to be stored in these sets is not too large suggests that a bitmap + * might be a good representation, as long as the time required to iterate + * over the set doesn't blow up due to the sparseness of the map. + * + * To study this, a reconstruction job was instrumented to dump out all unique + * auxid_set_t values. This corpus was then used to run a set of timing tests + * for different set implementations. This test is built into the ConcurrentBitset + * unit test, and the corpus is available in the CxxUtils package. Run like: + * + *@code + * .../ConcurrentBitset_test.exe --perf .../CxxUtils/share/auxids.uniq + @endcode + * + * Set implementations that have been tested so far include: + * - std::set + * - std::unordered_set + * - concurrent_unordered_set, from TBB + * - ck_hs, from ConcurrencyKit + * - ck_bitmap, from ConcurrencyKit + * - ConcurrentBitset + * + * Representative results (times in seconds; lower is better): + * + *@code + * |--------------------------+------+------+---------+--------| + * | | fill | copy | iterate | lookup | + * |--------------------------+------+------+---------+--------| + * | set | 0.92 | 0.66 | 10.95 | 0.53 | + * | unordered_set | 1.63 | 0.93 | 6.56 | 0.50 | + * | concurrent_unordered_set | 1.79 | 1.70 | 9.55 | 1.20 | + * | ck_hs | 0.78 | 0.83 | 18.92 | 0.88 | + * | ck_bitmap | 0.13 | 0.21 | 5.52 | 0.05 | + * | ConcurrentBitset | 0.31 | 0.06 | 4.48 | 0.08 | + * |--------------------------+------+------+---------+--------| + @endcode + * + * For this workload, the bitmaps are the clear winner. + * + * Implementation notes: + * + * The implementation here is inspired by ck_bitmap from ConcurrencyKit + * (http://concurrencykit.org/), though the code is all new. + * ck_bitmap itself isn't suitable because it doesn't allow for the set to grow, + * and also because it's a pure C interface, while we want something with + * a C++ style interface (and in particular something which supports interfaces + * close to unordered_set<size_t> in order to ease migration). + * + * The bitset is stored as an array of std::atomic<Block_t> objects, where Block_t + * is the largest unsigned type for which atomic is lockless. This is stored + * in a separate implementation object in order to allow the container to grow. + * The implementation object has a fixed-size header followed by the variable-size + * array of blocks. + * + * To speed up iteration for the typical case of a sparse set, we maintain + * a `high-water' mark, m_hwm, which is the index of the highest block that + * might have a bit set. m_hwm can only increase unless the set is cleared. + */ +class ConcurrentBitset +{ +private: + // Forward declaration of implementation class. + class Impl; + + /// Internal type used to hold the bitset data. + /// The bitset is an array of std::atomic<Block_t>. + /// This type should generally be the largest unsigned type for which + /// std::atomic is lockless. + typedef unsigned long Block_t; + + /// Size, in bits, of @c Block_t. + static const size_t BLOCKSIZE = sizeof(Block_t) * CHAR_BIT; + + /// Mask to select out the bit offset within one @Block_t. + static const size_t MASK = BLOCKSIZE-1; + + +#if __cplusplus >= 201700 + static_assert (std::atomic<Block_t>::is_always_lock_free); +#else +# if ATOMIC_LONG_LOCK_FREE != 2 +# error Code assumes std::atomic<unsigned long> is lock free. +# endif +#endif + + +public: + /// A bit number. + typedef size_t bit_t; + + + //======================================================================== + /** @name Constructors, destructors, assignment. */ + //@{ + + + /** + * @brief Constructor. + * @param nbits Initial number of bits to allocate for the map. + */ + ConcurrentBitset (bit_t nbits = 0); + + + /** + * @brief Copy constructor. + * @param other Container to copy. + * + * The copy is not atomic. If a non-atomic update is simultaneously made + * to @c other, then the copy may have this update only partially completed. + */ + ConcurrentBitset (const ConcurrentBitset& other); + + + /** + * @brief Constructor from an initializer list. + * @param List of values to set. + * @param nbits Number of bits to allocate for the map. + * If 0, then set the size based on the maximum value in the list. + * + * This allows setting specific bits in the set, like + *@code + * ConcurrentBitset bs { 1, 5, 10}; + @endcode + */ + ConcurrentBitset (std::initializer_list<bit_t> l, bit_t nbits = 0); + + + /** + * @brief Destructor. + */ + ~ConcurrentBitset(); + + + /** + * @brief Assignment. + * @param other Bitset from which to assign. + * + * The copy is not atomic. If a non-atomic update is simultaneously made + * to @c other, then the copy may have this update only partially completed. + */ + ConcurrentBitset& operator= (const ConcurrentBitset& other); + + + /** + * @brief Clean up old versions of the set. + * + * The insert and assignment operations may need to grow the set. + * The original version of the set is not deleted immediately, since other threads + * may still be accessing it. Call this when no other threads can be accessing + * old versions in order to clean them up. + */ + void emptyGarbage(); + + + //@} + //======================================================================== + /** @name Size, bit testing */ + //@{ + + + /** + * @brief The number of bits that this container can hold. + */ + bit_t capacity() const; + + + /** + * @brief Count the number of 1 bits in the set. + */ + bit_t count() const; + + + /** + * @brief Count the number of 1 bits in the set. + * + * Note: If you regard this like a std::bitset, you would expect this to return + * the number of bits that the set can hold, while if you regard this like + * a set<bit_t>, then you would expect this to return the number of 1 bits. + * We follow the latter here. + */ + bit_t size() const; + + + /** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return true if the bit is set; false otherwise. + * + * Returns false if @c bit is beyond the end of the set. + */ + bool test (bit_t bit) const; + + + /** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return 1 if the bit is set; 0 otherwise. + * + * Returns 0 if @c bit is beyond the end of the set. + */ + size_t count (bit_t bit) const; + + + /** + * @brief Return true if there are no 1 bits in the set. + */ + bool empty() const; + + + /** + * @brief Return true if there are no 1 bits in the set. + */ + bool none() const; + + + /** + * @brief Return true if all bits in the set are 1. + */ + bool all() const; + + + /** + * @brief Return true if there are any 1 bits in the set. + */ + bool any() const; + + + //@} + //======================================================================== + /** @name Single-bit manipulation. */ + //@{ + + + /** + * @brief Turn on one bit. + * @param bit The bit to turn on. + * + * Does nothing if @c bit beyond the end of the set. + */ + ConcurrentBitset& set (bit_t bit); + + + /** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ + ConcurrentBitset& reset (bit_t bit); + + + /** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ + ConcurrentBitset& erase (bit_t bit); + + + /** + * @brief Flip the value of one bit. + * @param bit The bit to turn flip. + * + * Does nothing if @c bit beyond the end of the set. + */ + ConcurrentBitset& flip (bit_t bit); + + + /** + * @brief Set the value of one bit. + * @param bit The bit to turn set. + * @param val The value to which to set it. + * + * Does nothing if @c bit beyond the end of the set. + */ + ConcurrentBitset& set (bit_t bit, bool val); + + + //@} + //======================================================================== + /** @name Set operations. */ + //@{ + + + /** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& clear(); + + + /** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& reset(); + + + /** + * @brief Turn on all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& set(); + + + /** + * @brief Flip the state of all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& flip(); + + + /** + * @brief AND this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& operator&= (const ConcurrentBitset& other); + + + /** + * @brief OR this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& operator|= (const ConcurrentBitset& other); + + + /** + * @brief XOR this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& operator^= (const ConcurrentBitset& other); + + + /** + * @brief Subtract another set from this set. + * @param other The other set. + * + * This is the same as (*this) &= ~other; + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + ConcurrentBitset& operator-= (const ConcurrentBitset& other); + + + /** + * @brief Return a new set that is the complement of this set. + */ + ConcurrentBitset operator~() const; + + + //@} + //======================================================================== + /** @name Comparison. */ + //@{ + + + /** + * @brief Test two sets for equality. + * @param other The other set to test. + */ + bool operator== (const ConcurrentBitset& other) const; + + + /** + * @brief Test two sets for inequality. + * @param other The other set to test. + */ + bool operator!= (const ConcurrentBitset& other) const; + + + //@} + //======================================================================== + /** @name Insert. */ + //@{ + + + /** + * @brief Set a bit to 1. Expand the set if needed. + * @param bit Number of the bit to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * If @c bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + */ + ConcurrentBitset& insert (bit_t bit, bit_t new_nbits = 0); + + + /** + * @brief Set several bits to 1. Expand the set if needed. + * @param beg Start of range of bits to set. + * @param end End of range of bits to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * The iteration range should be over something convertible to @c bit_t. + * If any bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + * + * Example: + *@code + * std::vector<bit_t> bits { 4, 10, 12}; + * CxxUtils::ConcurrentBitset bs = ...; + * bs.insert (bits.begin(), bits.end()); + @endcode + */ + // The enable_if is needed to keep this something like + // bs.insert (1, 2) + // from matching this overload. + template <class ITERATOR, + typename = typename std::enable_if< + std::is_base_of< + typename std::forward_iterator_tag, + typename std::iterator_traits<ITERATOR>::iterator_category + >::value> > + ConcurrentBitset& insert (ITERATOR beg, ITERATOR end, bit_t new_nbits = 0); + + + /** + * @brief Set several bits to 1. Expand the set if needed. + * @param l List of bits to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * If any bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + * + * Example: + *@code + * std::vector<bit_t> bits { 4, 10, 12}; + * bs.insert ({4, 10, 12}); + @endcode + */ + ConcurrentBitset& insert (std::initializer_list<bit_t> l, bit_t new_nbits = 0); + + + /** + * @brief Turn on bits listed in another set. + * @param other Set of bits to turn on. + * + * This is the same as @c operator|=, except that if the size of @c other is + * larger than this set, then this set will be expanded to match @c other. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + */ + ConcurrentBitset& insert (const ConcurrentBitset& other); + + + //@} + //======================================================================== + /** @name Array-like element access. */ + //@{ + + + /** + * @brief A reference to one bit in a set. + * + * These references are invalidated by calls to insert() or operator=. + */ + class reference + { + private: + friend class ConcurrentBitset; + + + /** + * @brief Constructor. + * @param impl ConcurrentBitset implementation object. + * @param bit Bit number to which this reference refers. + */ + reference (Impl& impl, bit_t bit); + + + public: + /** + * @brief Set the referenced bit to a given value. + * @param val Value to which to set the referenced bit. + */ + reference& operator= (bool val) noexcept; + + + /** + * @brief Copy the value of another referenced bit. + * @param r The other reference. + * + * To allow: + *@code + * ConcurrentBitset& bs1 = ...; + * ConcurrentBitset& bs2 = ...; + * bs1[1] = bs2[1]; + @endcode + */ + reference& operator= (const reference& r) noexcept; + + + /** + * @brief Return the value of the referenced bit. + */ + operator bool() const noexcept; + + + /** + * @brief Return the complement of the value of the referenced bit. + */ + bool operator~() const noexcept; + + + /** + * @brief Invert the referenced bit. + */ + reference& flip() noexcept; + + + private: + /// Pointer to the block containing the referenced bit. + std::atomic<Block_t>* m_block; + + /// Mask of the referenced bit within the block. + Block_t m_mask; + }; + + + /** + * @brief Return the value of one bit. + * @param bit The number of the bit to test. + */ + bool operator[] (bit_t bit) const; + + + /** + * @brief Return a reference to one bit. + * @param bit The number of the bit to reference. + * + * The reference will be invalidated by calls to @c insert() or @c operator=. + * Effects are undefined if @c bit is past the end of the set. + */ + reference operator[] (bit_t bit); + + + //@} + //======================================================================== + /** @name Iterator operations. */ + //@{ + + + /** + * @brief Iterator over all 1 bits in the set. + * + * This iterator will visit all 1 bits in the set (that is, it works + * like the iterator for a set<bit_t>). The state of an iterator consists of: + * + * - m_bit -- The number of the current bit we're referencing. + * - m_data -- Pointer to the current block we're referencing. + * - m_cache -- Cached value of the current block. This is shifted such that + * the referenced bit has just fallen off the right. + * - m_end -- Pointer to one past the last block we should consider + * in the iteration. (This may be less than the full size + * of the set if we know that the end of the set is all 0's. + * This is tracked by Impl::m_hwm.) + * + * The end iterator is marked by setting m_bit to -1; none of the other fields + * matter in that case. Thus, we can compare iterators by simply comparing m_bit. + * + * To initialize an iterator in begin(), we set the iterator to point to the + * block preceding the first block of the set, with the cache set to 0. + * We can then use the increment operator to advance the iterator to the + * first 1 bit. + */ + struct const_iterator + { + typedef std::forward_iterator_tag iterator_category; + typedef size_t value_type; + typedef ptrdiff_t difference_type; + typedef const value_type* pointer; + typedef const value_type& reference; + + + private: + friend ConcurrentBitset; + + + /** + * @brief Constructor. + * @param cache Cached block at the current iteration point, shifted such + * that bit number @c bit has just been shifted off the right. + * @param bit Bit number the at which the iterator is currently pointing. + * @param data Pointer to the block at which the iterator is currently pointing. + * @param end One past the last block of the iteration range. + */ + const_iterator (Block_t cache, + bit_t bit, + const std::atomic<Block_t>* data, + const std::atomic<Block_t>* end); + + + public: + /** + * @brief Return the bit number which the iterator is currently referencing. + */ + bit_t operator*() const; + + + /** + * @brief Advance the iterator to the next set bit (preincrement). + */ + const_iterator& operator++(); + + + /** + * @brief Advance the iterator to the next set bit (postincrement). + */ + const_iterator operator++(int); + + + /** + * @brief Compare two iterators. + * @param other The other iterator to compare. + */ + bool operator== (const const_iterator& other) const; + + + /** + * @brief Compare two iterators. + * @param other The other iterator to compare. + */ + bool operator!= (const const_iterator& other) const; + + + private: + /// Cache of the block to which we're currently pointing. + /// Should be shifted such that the bit we're referencing has just + /// been shifted off the right. + Block_t m_cache; + + /// Bit number which we're currently referencing. + bit_t m_bit; + + /// Pointer to the block containing the bit which we're currently referencing. + const std::atomic<Block_t>* m_data; + + + /// Pointer to one past the last block in the set. + const std::atomic<Block_t>* m_end; + }; + + + /** + * @brief Return a begin iterator. + */ + const_iterator begin() const; + + + /** + * @brief Return an end iterator. + */ + const_iterator end() const; + + + /** + * @brief If bit @c bit is set, return an iterator pointing to it. + * Otherwise, return an end iterator. + * @param bit Bit number to test. + */ + const_iterator find (bit_t bit) const; + + + + //@} +private: + //======================================================================== + /** @name Implementation. */ + //@{ + + + /** + * @brief Find number of blocks needed to hold a given number of bits. + * @param nbits The number of bits. + */ + static bit_t nBlocks (bit_t nbits); + + + /** + * @brief Create a new, uninitialized implementation object. + * @param nbits Number of bits to allocate. + * + * This will allocate memory for the Impl object, + * but will does not run the constructor. + */ + Impl* newImpl (bit_t nbits); + + + /** + * @brief Expand the container. + * @param new_nbits The desired new size of the container. + */ + void expand (bit_t new_nbits); + + + /** + * @brief Expand the container: out-of-line portion. + * @param new_nbits The desired new size of the container. + */ + void expandOol (bit_t new_nbits); + + + /** + * @brief Implementation object. + * + * An instance of this holds the set data for a fixed size. + * If the set needs to be expanded, a new implementation object must be allocated + * and the data copied. + * + * This object consists of a fixed header, followed by a variable-sized array + * containing the actual set data. In this class, the array is declared with + * a size of 1; however, we allocate these objects (using the @c newImpl function) + * with enough space to hold the entire set. + * + * This class also contains the basic methods for operating on the set. + */ + class Impl + { + public: + /** + * @brief Constructor. + * @param nbits Number of bits in the set. + */ + Impl (bit_t nbits); + + + /** + * @brief Copy constructor. + * @brief Other object to copy. + * @brief Number of bits to use for this container. + * + * If @c nbits is smaller than the size of @c other, then the size of @c other + * will be used instead. + */ + Impl (const Impl& other, bit_t nbits = 0); + + + /** + * @brief Copy from another instance. + * @param other Object from which to copy. + * + * This does not change the size of the container. + * If This container is larger than @c other, then the remainder will be + * filled with zeros. If @c other is larger than this container, then the + * remainder will be ignored. + */ + void assign (const Impl& other); + + + /** + * @brief Return the number of bits in the set. + */ + bit_t nbits() const; + + + /** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return true if the bit is set; false otherwise. + * + * Returns false if @c bit is beyond the end of the set. + */ + bool test (bit_t bit) const; + + + /** + * @brief Count the number of 1 bits in the set. + */ + bit_t count() const; + + + /** + * @brief Return true if there are no 1 bits in the set. + */ + bool none() const; + + + /** + * @brief Return true if all bits in the set are 1. + */ + bool all() const; + + + /** + * @brief Return a pointer to the block containing @c bit. + * @param bit Desired bit number. + * + * Returns nullptr if @c bit is past the end of the set. + */ + std::atomic<Block_t>* block (bit_t bit); + + + /** + * @brief Turn on one bit. + * @param bit The bit to turn on. + * + * Does nothing if @c bit beyond the end of the set. + */ + void set (bit_t bit); + + + /** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ + void reset (bit_t bit); + + + /** + * @brief Flip the value of one bit. + * @param bit The bit to turn flip. + * + * Does nothing if @c bit beyond the end of the set. + */ + void flip (bit_t bit); + + + /** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + void clear(); + + + /** + * @brief Turn on all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + void set(); + + + /** + * @brief Flip the state of all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ + void flip(); + + + typedef void operator_t (std::atomic<Block_t>& a, Block_t v); + /** + * @brief Apply a binary operation. + * @param op Operation to apply. + * @param other Second set for the operation. + * + * Each block B in this set is replaced by B OP OTHER, where OTHER is + * the corresponding block in the other container. (If this set + * is larger than @c other, then the trailing blocks will be 0.) + */ + void operate (operator_t op, const Impl& other); + + + /** + * @brief Compare with another set. + * @param other Other set with which to compare. + */ + bool operator== (const Impl& other) const; + + + + /** + * @brief Return an iterator referencing the first 1 bit. + */ + const_iterator begin() const; + + + /** + * @brief Return the end iterator. + */ + const_iterator end() const; + + + /** + * @brief If bit @c bit is set, return an iterator pointing to it. + * Otherwise, return an end iterator. + * @param bit Bit number to test. + */ + const_iterator find (bit_t bit) const; + + + private: + /// Number of bits in the container. + size_t m_nbits; + + /// Number of blocks in the container. + size_t m_nblocks; + + /// High-water mark: index of last block with a 1 bit. + std::atomic<size_t> m_hwm; + + /// The set data. + /// The implementation objects are allocated such that there are actually + /// m_nblocks entries available in this array. + std::atomic<Block_t> m_data[1]; + }; + + + //@} + + +private: + /// The current implementation object. + std::atomic<Impl*> m_impl; + + /// Old implementation objects, pending deletion. + std::vector<Impl*> m_garbage; + + /// Mutex used for synchronization when switching to a new implementation object. + typedef std::mutex mutex_t; + typedef std::lock_guard<mutex_t> lock_t; + mutex_t m_mutex; +}; + + +} // namespace CxxUtils + + +#include "CxxUtils/ConcurrentBitset.icc" + + +#endif // not CXXUTILS_CONCURRENTBITSET_H diff --git a/Control/CxxUtils/CxxUtils/ConcurrentBitset.icc b/Control/CxxUtils/CxxUtils/ConcurrentBitset.icc new file mode 100644 index 0000000000000000000000000000000000000000..083474666baeccc08302027193fe90fb3bac0da9 --- /dev/null +++ b/Control/CxxUtils/CxxUtils/ConcurrentBitset.icc @@ -0,0 +1,1102 @@ +// $Id$ +/** + * @file CxxUtils/ConcurrentBitset.icc + * @author scott snyder <snyder@bnl.gov> + * @date Nov, 2017 + * @brief Variable-sized bitset allowing (mostly) concurrent access. + */ + + +#include "CxxUtils/AthUnlikelyMacros.h" + + +namespace CxxUtils { + + +//********************************************************************************* +// Constructors, destructors, assignment. + + +/** + * @brief The number of bits that this container can hold. + */ +inline +ConcurrentBitset::bit_t ConcurrentBitset::capacity() const +{ + return (*m_impl).nbits(); +} + + +/** + * @brief Count the number of 1 bits in the set. + */ +inline +ConcurrentBitset::bit_t ConcurrentBitset::count() const +{ + return (*m_impl).count(); +} + + +/** + * @brief Count the number of 1 bits in the set. + * + * Note: If you regard this like a std::bitset, you would expect this to return + * the number of bits that the set can hold, while if you regard this like + * a set<bit_t>, then you would expect this to return the number of 1 bits. + * We follow the latter here. + */ +inline +ConcurrentBitset::bit_t ConcurrentBitset::size() const +{ + return (*m_impl).count(); +} + + +/** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return true if the bit is set; false otherwise. + * + * Returns false if @c bit is beyond the end of the set. + */ +inline +bool ConcurrentBitset::test (bit_t bit) const +{ + return (*m_impl).test (bit); +} + +/** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return 1 if the bit is set; 0 otherwise. + * + * Returns 0 if @c bit is beyond the end of the set. + */ +inline +size_t ConcurrentBitset::count (bit_t bit) const +{ + return test (bit); +} + + +/** + * @brief Return true if there are no 1 bits in the set. + */ +inline +bool ConcurrentBitset::empty() const +{ + return none(); +} + + +/** + * @brief Return true if there are no 1 bits in the set. + */ +inline +bool ConcurrentBitset::none() const +{ + return (*m_impl).none(); +} + + +/** + * @brief Return true if all bits in the set are 1. + */ +inline +bool ConcurrentBitset::all() const +{ + return (*m_impl).all(); +} + + +/** + * @brief Return true if there are any 1 bits in the set. + */ +inline +bool ConcurrentBitset::any() const +{ + return !none(); +} + + +//********************************************************************************* +// Single-bit manipulation. + + +/** + * @brief Turn on one bit. + * @param bit The bit to turn on. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +ConcurrentBitset& ConcurrentBitset::set (bit_t bit) +{ + (*m_impl).set (bit); + return *this; +} + + +/** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +ConcurrentBitset& ConcurrentBitset::reset (bit_t bit) +{ + (*m_impl).reset (bit); + return *this; +} + + +/** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +ConcurrentBitset& ConcurrentBitset::erase (bit_t bit) +{ + (*m_impl).reset (bit); + return *this; +} + + +/** + * @brief Flip the value of one bit. + * @param bit The bit to turn flip. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +ConcurrentBitset& ConcurrentBitset::flip (bit_t bit) +{ + (*m_impl).flip (bit); + return *this; +} + + +/** + * @brief Set the value of one bit. + * @param bit The bit to turn set. + * @param val The value to which to set it. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +ConcurrentBitset& ConcurrentBitset::set (bit_t bit, bool val) +{ + if (val) { + (*m_impl).set (bit); + } + else { + (*m_impl).reset (bit); + } + return *this; +} + + +//********************************************************************************* +// Set operations. + + +/** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::clear() +{ + (*m_impl).clear(); + return *this; +} + + +/** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::reset() +{ + (*m_impl).clear(); + return *this; +} + + +/** + * @brief Turn on all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::set() +{ + (*m_impl).set(); + return *this; +} + +/** + * @brief Flip the state of all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::flip() +{ + (*m_impl).flip(); + return *this; +} + + +/** + * @brief AND this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::operator&= (const ConcurrentBitset& other) +{ + (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= v; }, + *other.m_impl); + return *this; +} + + +/** + * @brief OR this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::operator|= (const ConcurrentBitset& other) +{ + (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; }, + *other.m_impl); + return *this; +} + + +/** + * @brief XOR this set with another set. + * @param other The other set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::operator^= (const ConcurrentBitset& other) +{ + (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a ^= v; }, + *other.m_impl); + return *this; +} + + +/** + * @brief Subtract another set from this set. + * @param other The other set. + * + * This is the same as (*this) &= ~other; + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +ConcurrentBitset& ConcurrentBitset::operator-= (const ConcurrentBitset& other) +{ + (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a &= ~v; }, + *other.m_impl); + return *this; +} + + +/** + * @brief Return a new set that is the complement of this set. + */ +inline +ConcurrentBitset ConcurrentBitset::operator~() const +{ + ConcurrentBitset b = *this; + (*b.m_impl).flip(); + return b; +} + + +//********************************************************************************* +// Comparison. + + +/** + * @brief Test two sets for equality. + * @param other The other set to test. + */ +inline +bool ConcurrentBitset::operator== (const ConcurrentBitset& other) const +{ + return (*m_impl) == (*other.m_impl); +} + + +/** + * @brief Test two sets for inequality. + * @param other The other set to test. + */ +inline +bool ConcurrentBitset::operator!= (const ConcurrentBitset& other) const +{ + return ! ((*m_impl) == (*other.m_impl)); +} + + +//********************************************************************************* +// Insert. + + +/** + * @brief Set a bit to 1. Expand the set if needed. + * @param bit Number of the bit to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * If @c bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + */ +inline +ConcurrentBitset& ConcurrentBitset::insert (bit_t bit, bit_t new_nbits /*= 0*/) +{ + if (bit >= (*m_impl).nbits()) { + if (new_nbits > bit) + ; + else if (bit < 64) + new_nbits = 128; + else + new_nbits = (bit * 2 + BLOCKSIZE-1) & ~(BLOCKSIZE-1); + expand (new_nbits); + } + set (bit); + return *this; +} + + +/** + * @brief Set several bits to 1. Expand the set if needed. + * @param beg Start of range of bits to set. + * @param end End of range of bits to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * The iteration range should be over something convertible to @c bit_t. + * If any bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + * + * Example: + *@code + * std::vector<bit_t> bits { 4, 10, 12}; + * CxxUtils::ConcurrentBitset bs = ...; + * bs.insert (bits.begin(), bits.end()); + @endcode +*/ +template <class ITERATOR, + typename = typename std::enable_if< + std::is_base_of< + typename std::forward_iterator_tag, + typename std::iterator_traits<ITERATOR>::iterator_category + >::value> > +inline +ConcurrentBitset& +ConcurrentBitset::insert (ITERATOR beg, ITERATOR end, bit_t new_nbits /*= 0*/) +{ + for (; beg != end; ++beg) { + insert (*beg, new_nbits); + } + return *this; +} + + +/** + * @brief Set several bits to 1. Expand the set if needed. + * @param l List of bits to set. + * @param new_nbits Hint for new size of set, if it needs to be expanded. + * + * If any bit is past the end of the container, then the container will be + * expanded as needed. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + * + * Example: + *@code + * std::vector<bit_t> bits { 4, 10, 12}; + * bs.insert ({4, 10, 12}); + @endcode +*/ +inline +ConcurrentBitset& +ConcurrentBitset::insert (std::initializer_list<bit_t> l, bit_t new_nbits /*= 0*/) +{ + auto max_it = std::max_element (l.begin(), l.end()); + size_t bmax = max_it == l.end() ? 0 : *max_it + 1; + if (new_nbits > bmax) new_nbits = bmax; + expand (bmax); + for (size_t x : l) { + set (x); + } + return *this; +} + + +/** + * @brief Turn on bits listed in another set. + * @param other Set of bits to turn on. + * + * This is the same as @c operator|=, except that if the size of @c other is + * larger than this set, then this set will be expanded to match @c other. + * + * This operation is incompatible with any other simultaneous writes + * to the same set (reads are ok). + */ +inline +ConcurrentBitset& ConcurrentBitset::insert (const ConcurrentBitset& other) +{ + const Impl* otherImpl = other.m_impl; + expand (otherImpl->nbits()); + (*m_impl).operate ([] (std::atomic<Block_t>& a, Block_t v) { a |= v; }, + *otherImpl); + return *this; +} + + +//********************************************************************************* +// Array-like element access. + + +/** + * @brief Constructor. + * @param impl ConcurrentBitset implementation object. + * @param bit Bit number to which this reference refers. + */ +inline +ConcurrentBitset::reference::reference (Impl& impl, bit_t bit) + : m_block (impl.block(bit)), + m_mask (1UL << (bit&MASK)) +{ +} + + +/** + * @brief Set the referenced bit to a given value. + * @param val Value to which to set the referenced bit. + */ +inline +ConcurrentBitset::reference& +ConcurrentBitset::reference::operator= (bool val) noexcept +{ + if (val) + *m_block |= m_mask; + else + *m_block &= ~m_mask; + return *this; +} + + +/** + * @brief Copy the value of another referenced bit. + * @param r The other reference. + * + * To allow: + *@code + * ConcurrentBitset& bs1 = ...; + * ConcurrentBitset& bs2 = ...; + * bs1[1] = bs2[1]; + @endcode +*/ +inline +ConcurrentBitset::reference& +ConcurrentBitset::reference::operator= (const reference& r) noexcept +{ + if (this != &r) { + *this = static_cast<bool> (r); + } + return *this; +} + + +/** + * @brief Return the value of the referenced bit. + */ +inline +ConcurrentBitset::reference::operator bool() const noexcept +{ + return (*m_block & m_mask) != 0; +} + + +/** + * @brief Return the complement of the value of the referenced bit. + */ +inline +bool ConcurrentBitset::reference::operator~() const noexcept +{ + return (*m_block & m_mask) == 0; +} + + +/** + * @brief Invert the referenced bit. + */ +inline +ConcurrentBitset::reference& +ConcurrentBitset::reference::flip() noexcept +{ + *m_block ^= m_mask; + return *this; +} + + +/** + * @brief Return the value of one bit. + * @param bit The number of the bit to test. + */ +inline +bool ConcurrentBitset::operator[] (bit_t bit) const +{ + return (*m_impl).test (bit); +} + + +/** + * @brief Return a reference to one bit. + * @param bit The number of the bit to reference. + * + * The reference will be invalidated by calls to @c insert() or @c operator=. + * Effects are undefined if @c bit is past the end of the set. + */ +inline +ConcurrentBitset::reference ConcurrentBitset::operator[] (bit_t bit) +{ + return reference (*m_impl, bit); +} + + +//********************************************************************************* +// Iterator operations. + + +/** + * @brief Constructor. + * @param cache Cached block at the current iteration point, shifted such + * that bit number @c bit has just been shifted off the right. + * @param bit Bit number the at which the iterator is currently pointing. + * @param data Pointer to the block at which the iterator is currently pointing. + * @param end One past the last block of the iteration range. + */ +inline +ConcurrentBitset::const_iterator::const_iterator (Block_t cache, + bit_t bit, + const std::atomic<Block_t>* data, + const std::atomic<Block_t>* end) + : m_cache (cache), m_bit (bit), m_data(data), m_end (end) +{ +} + + +/** + * @brief Return the bit number which the iterator is currently referencing. + */ +inline +ConcurrentBitset::bit_t +ConcurrentBitset::const_iterator::operator*() const +{ + return m_bit; +} + + +/** + * @brief Advance the iterator to the next set bit. + */ +inline +ConcurrentBitset::const_iterator& +ConcurrentBitset::const_iterator::operator++() +{ + // Are there any more bits set in this block? + Block_t cache = m_cache; + if (ATH_LIKELY (cache != 0)) { + // Yes --- find the first set bit. + // Shift until that bit just falls off the right and adjust + // the current position. + // We know that @c b will be less than @c BLOCKSIZE + // (avoiding undefined behavior), since at least one bit must have + // already been shifted off of the cache. + int b = CxxUtils::count_trailing_zeros (cache)+1; + m_bit += b; + m_cache = cache >> b; + } + else { + // No, move to the next block. + // Bit number at the start of the next block. + unsigned int bit = (m_bit + BLOCKSIZE) & ~MASK; + + // Pointers to the next block, and the end of iteration. + const std::atomic<Block_t>* data = m_data + 1; + const std::atomic<Block_t>* end = m_end; + + // Iterate until we find a block with at least one bit set. + while (data < end) { + // Read the current block, tests to see if there are any set bits. + cache = *data; + if (ATH_UNLIKELY (cache != 0)) { + // Found a block with at least one bit set. Which is the first? + int b = CxxUtils::count_trailing_zeros (cache); + + // Adjust the current position. + m_bit = bit + b; + m_data = data; + + // Shift the bit found off of cache. + // Need to to it in two steps, because @c b might be @c BLOCKSIZE-1 + // (and shifting by @c BLOCKSIZE is undefined). + m_cache = (cache>>1) >> b; + return *this; + } + + // Move to the next block. + ++data; + bit += BLOCKSIZE; + } + + // Reached the end without finding any more bits set. + // Mark that we hit the end. + m_bit = ~static_cast<bit_t>(0); + } + + return *this; +} + + +/** + * @brief Advance the iterator to the next set bit (postincrement). + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::const_iterator::operator++(int) +{ + const_iterator tmp = *this; + ++*this; + return tmp; +} + + +/** + * @brief Compare two iterators. + * @param other The other iterator to compare. + */ +inline +bool ConcurrentBitset::const_iterator::operator== (const const_iterator& other) const +{ + return m_bit == other.m_bit; +} + + +/** + * @brief Compare two iterators. + * @param other The other iterator to compare. + */ +inline +bool ConcurrentBitset::const_iterator::operator!= (const const_iterator& other) const +{ + return m_bit != other.m_bit; +} + + +/** + * @brief Return a begin iterator. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::begin() const +{ + return (*m_impl).begin(); +} + + +/** + * @brief Return an end iterator. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::end() const +{ + return (*m_impl).end(); +} + + +/** + * @brief If bit @c bit is set, return an iterator pointing to it. + * Otherwise, return an end iterator. + * @param bit Bit number to test. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::find (bit_t bit) const +{ + return (*m_impl).find(bit); +} + + +//********************************************************************************* +// Implementation. + + +/** + * @brief Find number of blocks needed to hold a given number of bits. + * @param nbits The number of bits. + */ +inline +ConcurrentBitset::bit_t +ConcurrentBitset::nBlocks (bit_t nbits) +{ + return (nbits+BLOCKSIZE-1) / BLOCKSIZE; +} + + +/** + * @brief Create a new, uninitialized implementation object. + * @param nbits Number of bits to allocate. + * + * This will allocate memory for the Impl object, + * but will does not run the constructor. + */ +inline +ConcurrentBitset::Impl* +ConcurrentBitset::newImpl (bit_t nbits) +{ + bit_t nblocks = nBlocks (nbits); + // The Impl structure contains one Block_t at the end. + return reinterpret_cast<Impl*>(malloc (sizeof(Impl) + (nblocks-1)*sizeof(Block_t))); +} + + +/** + * @brief Expand the container. + * @param new_nbits The desired new size of the container. + */ +inline +void ConcurrentBitset::expand (bit_t new_nbits) +{ + // Check the size of the container. + // Call the out-of-line portion if we actually need to expand. + if (new_nbits > (*m_impl).nbits()) { + expandOol (new_nbits); + } +} + + +/** + * @brief Constructor. + * @param nbits Number of bits in the set. + */ +inline +ConcurrentBitset::Impl::Impl (bit_t nbits) + : m_nbits (nbits), + m_nblocks (nBlocks (nbits)) +{ + // Start with all bits 0. + clear(); +} + + +/** + * @brief Copy constructor. + * @brief Other object to copy. + * @brief Number of bits to use for this container. + * + * If @c nbits is smaller than the size of @c other, then the size of @c other + * will be used instead. + */ +inline +ConcurrentBitset::Impl::Impl (const Impl& other, bit_t nbits /*= 0*/) + : m_nbits (std::max (other.m_nbits, nbits)), + m_nblocks ((m_nbits+BLOCKSIZE-1) / BLOCKSIZE), + m_hwm (static_cast<size_t> (other.m_hwm)) +{ + // Copy, then clear the remainder. + // We don't care about the relative ordering, so to this with relaxed + // memory ordering, and add a barrier at the end. + for (bit_t i=0; i < other.m_nblocks; i++) { + m_data[i].store (other.m_data[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + for (bit_t i=other.m_nblocks; i < m_nblocks; i++) { + m_data[i].store (0, std::memory_order_relaxed); + } + std::atomic_thread_fence (std::memory_order_seq_cst); +} + + +/** + * @brief Copy from another instance. + * @param other Object from which to copy. + * + * This does not change the size of the container. + * If This container is larger than @c other, then the remainder will be + * filled with zeros. If @c other is larger than this container, then the + * remainder will be ignored. + */ +inline +void ConcurrentBitset::Impl::assign (const Impl& other) +{ + // Copy, then clear the remainder. + // We don't care about the relative ordering, so to this with relaxed + // memory ordering, and add a barrier at the end. + bit_t ncopy = std::min (m_nblocks, other.m_nblocks); + for (bit_t i=0; i < ncopy; i++) { + m_data[i].store (other.m_data[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + for (bit_t i=ncopy; i < m_nblocks; i++) { + m_data[i].store (0, std::memory_order_relaxed); + } + std::atomic_thread_fence (std::memory_order_seq_cst); + + // Copy hwm last. It can only increase, so as long as we're after the barrier, + // we should get something that's correct. + m_hwm = static_cast<bit_t> (other.m_hwm); +} + + +/** + * @brief Return the number of bits in the set. + */ +inline +ConcurrentBitset::bit_t ConcurrentBitset::Impl::nbits() const +{ + return m_nbits; +} + + +/** + * @brief Test to see if a bit is set. + * @param bit Number of the bit to test. + * @return true if the bit is set; false otherwise. + * + * Returns false if @c bit is beyond the end of the set. + */ +inline +bool ConcurrentBitset::Impl::test (bit_t bit) const +{ + if (bit >= m_nbits) return false; + bit_t pos = bit / BLOCKSIZE; + return (m_data[pos] & (1UL<<(bit&MASK))) != 0; +} + + +/** + * @brief Return a pointer to the block containing @c bit. + * @param bit Desired bit number. + * + * Returns nullptr if @c bit is past the end of the set. + */ +inline +std::atomic<ConcurrentBitset::Block_t>* +ConcurrentBitset::Impl::block (bit_t bit) +{ + if (bit >= m_nbits) return nullptr; + bit_t pos = bit / BLOCKSIZE; + return &m_data[pos]; +} + + +/** + * @brief Turn on one bit. + * @param bit The bit to turn on. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +void ConcurrentBitset::Impl::set (bit_t bit) +{ + if (bit >= m_nbits) return; + bit_t pos = bit / BLOCKSIZE; + // Update HWM if pos is larger. + CxxUtils::atomic_fetch_max (&m_hwm, pos); + m_data[pos] |= 1UL<<(bit&MASK); +} + + +/** + * @brief Turn off one bit. + * @param bit The bit to turn off. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +void ConcurrentBitset::Impl::reset (bit_t bit) +{ + if (bit >= m_nbits) return; + bit_t pos = bit / BLOCKSIZE; + m_data[pos] &= ~(1UL<<(bit&MASK)); +} + + +/** + * @brief Flip the value of one bit. + * @param bit The bit to turn flip. + * + * Does nothing if @c bit beyond the end of the set. + */ +inline +void ConcurrentBitset::Impl::flip (bit_t bit) +{ + if (bit >= m_nbits) return; + size_t pos = bit / BLOCKSIZE; + // Update HWM if pos is larger. + CxxUtils::atomic_fetch_max (&m_hwm, pos); + m_data[pos] ^= 1UL<<(bit&MASK); +} + + +/** + * @brief Clear all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +inline +void ConcurrentBitset::Impl::clear() +{ + for (bit_t i=0; i<m_nblocks; i++) { + m_data[i].store (0, std::memory_order_relaxed); + } + std::atomic_thread_fence (std::memory_order_seq_cst); + m_hwm = 0; +} + + +/** + * @brief Apply a binary operation. + * @param op Operation to apply. + * @param other Second set for the operation. + * + * Each block B in this set is replaced by B OP OTHER, where OTHER is + * the corresponding block in the other container. (If this set + * is larger than @c other, then the trailing blocks will be 0.) + */ +inline +void ConcurrentBitset::Impl::operate (operator_t op, const Impl& other) +{ + bit_t nblocks = std::min (m_nblocks, other.m_nblocks); + bit_t hwm = m_hwm; + for (bit_t i = 0; i < nblocks; i++) { + op (m_data[i], other.m_data[i]); + if (m_data[i]) hwm = i; + } + for (bit_t i = nblocks; i < m_nblocks; i++) { + op (m_data[i], static_cast<Block_t> (0)); + if (m_data[i]) hwm = i; + } + CxxUtils::atomic_fetch_max (&m_hwm, hwm); +} + + +/** + * @brief Compare with another set. + * @param other Other set with which to compare. + */ +inline +bool ConcurrentBitset::Impl::operator== (const Impl& other) const +{ + bit_t ntest = std::min (m_nblocks, other.m_nblocks); + for (bit_t i = 0; i < ntest; i++) { + if (m_data[i] != other.m_data[i]) return false; + } + + for (bit_t i = ntest; i < m_nblocks; i++) { + if (m_data[i] != 0) return false; + } + + for (bit_t i = ntest; i < other.m_nblocks; i++) { + if (other.m_data[i] != 0) return false; + } + + return true; +} + + +/** + * @brief Return an iterator referencing the first 1 bit. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::Impl::begin() const +{ + // Set the iterator to just before the start of the container. + // Then use the increment operator to search for the first 1. + bit_t offs = m_nblocks ? (static_cast<bit_t>(m_hwm) + 1) : 0; + const_iterator it (0, + static_cast<bit_t>(-BLOCKSIZE), + &m_data[0] - 1, + &m_data[0] + offs); + ++it; + return it; +} + + +/** + * @brief Return the end iterator. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::Impl::end() const +{ + return const_iterator (0, + ~static_cast<bit_t>(0), + nullptr, nullptr); +} + + +/** + * @brief If bit @c bit is set, return an iterator pointing to it. + * Otherwise, return an end iterator. + * @param bit Bit number to test. + */ +inline +ConcurrentBitset::const_iterator +ConcurrentBitset::Impl::find (bit_t bit) const +{ + if (test (bit)) { + // The bit's set. + // Construct an iterator pointing at this bit. + bit_t pos = bit / BLOCKSIZE; + const std::atomic<Block_t>* data = m_data + pos; + Block_t cache = *data; + bit_t offs = bit&MASK; + cache >>= 1; + if (offs > 0) { + cache >>= offs; + } + return const_iterator (cache, bit, m_data+pos, + &m_data[0] + m_hwm + 1); + } + return end(); +} + + +} // namespace CxxUtils diff --git a/Control/CxxUtils/CxxUtils/atomic_fetch_minmax.h b/Control/CxxUtils/CxxUtils/atomic_fetch_minmax.h new file mode 100644 index 0000000000000000000000000000000000000000..5404bbacfc7f92bb66e307cfc59b425bd6c01e5e --- /dev/null +++ b/Control/CxxUtils/CxxUtils/atomic_fetch_minmax.h @@ -0,0 +1,82 @@ +// This file's extension implies that it's C, but it's really -*- C++ -*-. +/* + * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration. + */ +// $Id$ +/** + * @file CxxUtils/atomic_fetch_minmax.h + * @author scott snyder <snyder@bnl.gov> + * @date Nov, 2017 + * @brief Atomic min/max functions. + * + * These add atomic operations for finding the minimum or maximum. + * These have been proposed for addition to the standard, but have not + * yet made it in: + * + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3696.htm + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0493r0.pdf + */ + + +#ifndef CXXUTILS_ATOMIC_FETCH_MINMAX_H +#define CXXUTILS_ATOMIC_FETCH_MINMAX_H + + +#include "CxxUtils/stall.h" +#include <algorithm> +#include <atomic> + + +namespace CxxUtils { + + +/** + * @brief Atomically calculate maximum. + * @param a Pointer to the atomic value. + * @param v The other value. + * @param memorder Memory ordering. + * + * Computes max(*a, v) and stores it in *a. Returns the original value of *a. + */ +template <class T> +inline +T atomic_fetch_max (std::atomic<T>* a, T v, + std::memory_order memorder = std::memory_order_seq_cst) +{ + T orig = a->load (memorder); + T max = std::max (orig, v); + while (!a->compare_exchange_strong (orig, max, memorder)) { + CxxUtils::stall(); + max = std::max (orig, v); + } + return orig; +} + + +/** + * @brief Atomically calculate minimum. + * @param a Pointer to the atomic value. + * @param v The other value. + * @param memorder Memory ordering. + * + * Computes min(*a, v) and stores it in *a. Returns the original value of *a. + */ +template <class T> +inline +T atomic_fetch_min (std::atomic<T>* a, T v, + std::memory_order memorder = std::memory_order_seq_cst) +{ + T orig = a->load (memorder); + T min = std::min (orig, v); + while (!a->compare_exchange_strong (orig, min, memorder)) { + CxxUtils::stall(); + min = std::min (orig, v); + } + return orig; +} + + +} // namespace CxxUtils + + +#endif // not CXXUTILS_ATOMIC_FETCH_MINMAX_H diff --git a/Control/CxxUtils/CxxUtils/bitscan.h b/Control/CxxUtils/CxxUtils/bitscan.h index a472b61690d3d7aba3ee6ad08c73cfe6803c2314..8fc2edc393e165d5aeedd8ff69cf0924750e9cf7 100644 --- a/Control/CxxUtils/CxxUtils/bitscan.h +++ b/Control/CxxUtils/CxxUtils/bitscan.h @@ -17,7 +17,8 @@ #ifndef CXXUTILS_BITSCAN_H #define CXXUTILS_BITSCAN_H -#include <stdint.h> +#include <climits> +#include <cstdint> #include <type_traits> namespace CxxUtils { @@ -48,6 +49,17 @@ namespace CxxUtils { for (n = 0; (x & msb) == 0; ++n, x <<= 1); return n; } + + template <typename T> + inline unsigned popcount_portable(T x) { + static_assert(std::is_integral<T>::value, "An integer type is required."); + unsigned n = 0; + while (x != 0) { + n += (x&1); + x >>= 1; + } + return n; + } } /** @@ -72,6 +84,14 @@ namespace CxxUtils { #endif } + inline unsigned count_trailing_zeros(unsigned long long x) { +#if defined (__GNUC__) || defined(__clang__) + return (x!=0 ? __builtin_ctzll(x) : 0); +#else + return detail::ctz_portable(x); +#endif + } + /** * Count number of leading zeros * @@ -80,7 +100,7 @@ namespace CxxUtils { */ inline unsigned count_leading_zeros(unsigned x) { #if defined (__GNUC__) || defined(__clang__) - return (x!=0 ? __builtin_clz(x) : sizeof(x)*8); + return (x!=0 ? __builtin_clz(x) : sizeof(x)*CHAR_BIT); #else return detail::clz_portable(x); #endif @@ -88,9 +108,48 @@ namespace CxxUtils { inline unsigned count_leading_zeros(unsigned long x) { #if defined (__GNUC__) || defined(__clang__) - return (x!=0 ? __builtin_clzl(x) : sizeof(x)*8); + return (x!=0 ? __builtin_clzl(x) : sizeof(x)*CHAR_BIT); #else return detail::clz_portable(x); +#endif + } + + inline unsigned count_leading_zeros(unsigned long long x) { +#if defined (__GNUC__) || defined(__clang__) + return (x!=0 ? __builtin_clzll(x) : sizeof(x)*CHAR_BIT); +#else + return detail::clz_portable(x); +#endif + } + + + /** + * Count number of set bits. + * + * @param x Number to check + * @return Number of bits set in x. + */ + inline unsigned count_ones(unsigned x) { +#if defined (__GNUC__) || defined(__clang__) + return __builtin_popcount(x); +#else + return detail::popcount_portable(x); +#endif + } + + inline unsigned count_ones(unsigned long x) { +#if defined (__GNUC__) || defined(__clang__) + return __builtin_popcountl(x); +#else + return detail::popcount_portable(x); +#endif + } + + inline unsigned count_ones(unsigned long long x) { +#if defined (__GNUC__) || defined(__clang__) + return __builtin_popcountll(x); +#else + return detail::popcount_portable(x); #endif } } diff --git a/Control/CxxUtils/Root/ConcurrentBitset.cxx b/Control/CxxUtils/Root/ConcurrentBitset.cxx new file mode 100644 index 0000000000000000000000000000000000000000..30e09735369001a49bedc2b8456fe8b222c81862 --- /dev/null +++ b/Control/CxxUtils/Root/ConcurrentBitset.cxx @@ -0,0 +1,231 @@ +// $Id$ +/** + * @file CxxUtils/src/ConcurrentBitset.cxx + * @author scott snyder <snyder@bnl.gov> + * @date Nov, 2017 + * @brief Variable-sized bitset allowing (mostly) concurrent access. + */ + + +#include "CxxUtils/ConcurrentBitset.h" + + +namespace CxxUtils { + + +//********************************************************************************* +// Constructors, destructors, assignment. + + +/** + * @brief Constructor. + * @param nbits Initial number of bits to allocate for the map. + */ +ConcurrentBitset::ConcurrentBitset (bit_t nbits /*= 0*/) + : m_impl (newImpl (nbits)) +{ + new (m_impl) Impl (nbits); +} + + +/** + * @brief Copy constructor. + * @param other Container to copy. + * + * The copy is not atomic. If a non-atomic update is simultaneously made + * to @c other, then the copy may have this update only partially completed. + */ +ConcurrentBitset::ConcurrentBitset (const ConcurrentBitset& other) + : m_impl (newImpl ((*other.m_impl).nbits())) +{ + new (m_impl) Impl ((*other.m_impl)); +} + + +/** + * @brief Constructor from an initializer list. + * @param List of values to set. + * @param nbits Number of bits to allocate for the map. + * If 0, then set the size based on the maximum value in the list. + * + * This allows setting specific bits in the set, like + *@code + * ConcurrentBitset bs { 1, 5, 10}; + @endcode +*/ +ConcurrentBitset::ConcurrentBitset (std::initializer_list<bit_t> l, + bit_t nbits /*= 0*/) +{ + if (nbits == 0) { + // Set the size of the set based on the maximum value in the list. + auto max_it = std::max_element (l.begin(), l.end()); + if (max_it != l.end()) { + nbits = *max_it + 1; + } + // Round up. + nbits = (nbits + BLOCKSIZE-1) & ~MASK; + } + m_impl = newImpl (nbits); + new (m_impl) Impl (nbits); + for (bit_t b : l) { + set (b); + } +} + + +/** + * @brief Destructor. + */ +ConcurrentBitset::~ConcurrentBitset() +{ + free (m_impl); + emptyGarbage(); +} + + +/** + * @brief Assignment. + * @param other Bitset from which to assign. + * + * The copy is not atomic. If a non-atomic update is simultaneously made + * to @c other, then the copy may have this update only partially completed. + */ +ConcurrentBitset& ConcurrentBitset::operator= (const ConcurrentBitset& other) +{ + if (this != &other) { + const Impl* otherImpl = other.m_impl; + expand (otherImpl->nbits()); + (*m_impl).assign (*otherImpl); + } + return *this; +} + + +/** + * @brief Clean up old versions of the set. + * + * The insert and assignment operations may need to grow the set. + * The original version of the set is not deleted immediately, since other threads + * may still be accessing it. Call this when no other threads can be accessing + * old versions in order to clean them up. + */ +void ConcurrentBitset::emptyGarbage() +{ + lock_t lock (m_mutex); + for (Impl* p : m_garbage) { + free (p); + } + m_garbage.clear(); +} + + +/** + * @brief Expand the container: out-of-line portion. + * @param new_nbits The desired new size of the container. + */ +void ConcurrentBitset::expandOol (bit_t new_nbits) +{ + // Need to take out the lock. + lock_t lock (m_mutex); + // Check the size again while we're holding the lock. + bit_t nbits = (*m_impl).nbits(); + if (new_nbits > nbits) { + // We need to expand. Allocate a new implementation and initialize it, + // copying from the existing implementation. + Impl* i = newImpl (new_nbits); + new (i) Impl (*m_impl, new_nbits); + + // Remember that we need to delete the old object. + m_garbage.push_back (m_impl); + + // Publish the new one. + m_impl = i; + } +} + + +//********************************************************************************* +// Implementation. + + +/** + * @brief Count the number of 1 bits in the set. + */ +ConcurrentBitset::bit_t +ConcurrentBitset::Impl::count() const +{ + bit_t n = 0; + for (bit_t i=0; i<m_nblocks; i++) { + n += CxxUtils::count_ones (m_data[i]); + } + return n; +} + + +/** + * @brief Return true if there are no 1 bits in the set. + */ +bool ConcurrentBitset::Impl::none() const +{ + for (bit_t i = 0; i < m_nblocks; i++) { + if (m_data[i]) return false; + } + return true; +} + + +/** + * @brief Return true if all bits in the set are 1. + */ +bool ConcurrentBitset::Impl::all() const +{ + // Check all blocks except the last. + for (bit_t i = 0; i < m_nblocks-1; i++) { + if (m_data[i] != ~static_cast<Block_t>(0)) return false; + } + // Special case for the last, since the last block may not be full. + if (m_nblocks > 0) { + if (m_data[m_nblocks] != ones<Block_t> (m_nbits - (m_nblocks-1)*BLOCKSIZE)) { + return false; + } + } + return true; +} + + +/** + * @brief Turn on all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +void ConcurrentBitset::Impl::set() +{ + for (bit_t i=0; i<m_nblocks; i++) { + m_data[i].store (~static_cast<Block_t>(0), std::memory_order_relaxed); + } + std::atomic_thread_fence (std::memory_order_seq_cst); + if (m_nblocks > 0) { + m_hwm = m_nblocks-1; + } +} + + +/** + * @brief Flip the state of all bits in the set. + * + * This operation is not necessarily atomic; a simultaneous read may be able + * to see the operation partially done. + */ +void ConcurrentBitset::Impl::flip() +{ + for (bit_t i=0; i<m_nblocks; i++) { + m_data[i] ^= ~static_cast<Block_t>(0); + } + if (m_nblocks > 0) { + m_hwm = m_nblocks-1; + } +} + + +} // namespace CxxUtils diff --git a/Control/CxxUtils/ispellwords b/Control/CxxUtils/ispellwords index 074254c8425368c3a77975c1e46808a4d518b203..3cc4ac8178c83801ed62c6e156885fe2d5778a4b 100644 --- a/Control/CxxUtils/ispellwords +++ b/Control/CxxUtils/ispellwords @@ -1102,3 +1102,25 @@ bitscan DTEST libasan testXXXXXX +bitset +auxid +emptyGarbage +ConcurrentBitset +perf +TBB +hs +ConcurrencyKit +hwm +initializer +bs +impl +bs1 +bs2 +preincrement +postincrement +newImpl +nblocks +BLOCKSIZE +memorder +MINMAX +italic diff --git a/Control/CxxUtils/share/ConcurrentBitset_test.ref b/Control/CxxUtils/share/ConcurrentBitset_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..b88456cd4303c3205b141942c30c4a1831d72e52 --- /dev/null +++ b/Control/CxxUtils/share/ConcurrentBitset_test.ref @@ -0,0 +1,11 @@ +test_basic +test_reference +test_iterate +test_find +test_insert +test_copy +test_initlist +test_compare +test_operators +test_anyall +test_mt diff --git a/Control/CxxUtils/share/atomic_fetch_minmax_test.ref b/Control/CxxUtils/share/atomic_fetch_minmax_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..bae42c55f9e0a4e297a4d197d8aadfe147ef269b --- /dev/null +++ b/Control/CxxUtils/share/atomic_fetch_minmax_test.ref @@ -0,0 +1,2 @@ +test1 +test2 diff --git a/Control/CxxUtils/share/auxids.uniq b/Control/CxxUtils/share/auxids.uniq new file mode 100644 index 0000000000000000000000000000000000000000..d2b9af1fe7947fc3dbdf183ae715032ccb2e972e --- /dev/null +++ b/Control/CxxUtils/share/auxids.uniq @@ -0,0 +1,277 @@ + +1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 +1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1115 1116 1117 1118 1145 1146 +11 +11 12 13 14 15 261 262 263 486 487 488 646 1012 1013 1156 1157 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 +11 12 13 14 15 36 37 38 486 487 488 646 1012 1013 1156 1157 1571 1572 1573 +11 12 13 14 15 486 487 488 1012 1013 1156 1157 +11 12 13 14 15 486 487 488 1012 1013 1156 1157 1344 +11 12 13 486 487 488 1012 1013 1156 +11 12 13 486 487 488 1012 1013 1156 1157 +11 12 13 486 487 488 646 1012 1013 1156 1157 1388 1389 1390 1391 +11 1344 +1115 1116 1117 1118 1145 1146 +11 261 262 263 646 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 +11 36 37 38 646 1571 1572 1573 +11 486 487 488 1012 1013 1156 1157 +1163 1164 1165 1166 +1167 1168 1169 1170 1171 1206 1255 +1206 +12 13 14 15 25 486 487 488 1012 1013 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 +12 13 14 15 25 486 487 488 1012 1013 1147 1148 1149 1151 1152 1153 1154 1155 1156 1157 +12 13 14 15 261 262 263 486 487 488 646 1012 1013 1156 1157 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 +12 13 14 15 36 37 38 486 487 488 646 1012 1013 1156 1157 1571 1572 1573 +12 13 14 15 486 487 488 1012 1013 1156 1157 +12 13 14 15 486 487 488 1012 1013 1156 1157 1344 +1 2 3 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 +1 2 3 4 5 6 7 8 9 285 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 +12 36 37 38 104 105 106 107 108 109 110 643 644 645 646 647 648 649 650 651 652 653 654 +12 36 37 38 39 265 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 637 1258 1259 1260 1261 1262 1263 1267 1268 +12 36 37 38 39 265 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 637 1258 1259 1267 1268 +12 36 37 38 39 265 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 579 580 581 583 584 585 587 588 589 591 592 593 602 637 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1406 1407 1408 1590 1591 1592 1593 1594 1595 1596 1700 +12 36 37 38 39 265 332 333 334 335 336 337 338 339 340 341 342 343 344 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 579 580 581 587 588 589 602 637 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1406 1408 1590 1591 1592 1593 1594 1595 1596 1700 +12 36 37 38 39 265 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 +12 36 37 38 39 265 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 579 580 581 1275 1340 1342 1343 1590 +12 36 37 38 39 265 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 382 383 384 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 579 580 581 583 584 585 1275 1340 1342 1343 1590 +12 36 37 38 39 265 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 +1256 +1279 1280 1281 1282 1283 +1281 1282 1283 +1321 +1322 +1340 1342 1343 1410 1411 +1340 1379 +1344 +1375 1376 1377 1378 +1375 1376 1377 1378 1380 +1399 1400 +1445 1446 1447 +1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 +1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1488 1491 +1597 1598 1599 1600 +1602 1603 1604 1605 1606 1607 +1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 +16 17 18 19 20 480 481 482 +16 17 18 19 20 480 481 482 490 491 492 493 494 495 496 497 498 +1672 1673 1674 1675 1676 1677 +1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 +1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 +1790 1791 1792 1793 1794 1795 +198 385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1342 1343 1413 1414 1415 1416 +21 22 39 261 262 263 264 266 483 484 +21 22 39 261 262 263 264 266 483 484 1340 1342 1343 1410 1411 +21 22 39 261 262 263 264 266 483 484 499 500 +21 22 39 261 262 263 264 266 483 484 499 500 1257 +21 22 39 261 262 263 264 266 483 484 901 902 903 904 905 906 907 908 917 918 919 920 921 922 923 924 1342 1343 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1437 +230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 +232 1668 1669 1670 1671 +232 279 280 1668 1669 1670 1671 +23 24 483 485 486 487 488 489 +25 1147 1148 1149 1150 1151 1152 1153 1154 1155 +25 1147 1148 1149 1151 1152 1153 1154 1155 +261 262 263 264 265 266 +261 262 263 486 487 488 980 1012 1013 1340 1345 1346 1347 1348 1349 1350 1351 1352 1379 +261 262 263 486 487 488 980 1012 1013 1345 1346 1347 1348 1349 1350 1351 1352 +261 262 263 486 487 488 980 1012 1013 1345 1346 1347 1348 1349 1350 1351 1352 1375 1376 1377 1378 +261 262 263 486 487 488 980 1012 1013 1345 1346 1347 1348 1349 1350 1351 1352 1375 1376 1377 1378 1380 +261 262 263 646 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 +270 397 408 418 419 420 421 422 424 1274 1287 1601 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1698 1699 +270 397 418 419 420 421 422 424 1287 1601 1632 1633 1634 1635 1636 1639 1640 1641 1642 1643 1644 1645 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1698 1699 +27 29 31 33 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 +27 29 31 33 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1279 1280 1281 1282 1283 +27 29 31 33 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1281 1282 1283 +279 280 +284 285 +285 441 442 443 444 445 446 447 448 449 450 451 452 458 459 460 461 462 463 464 465 466 467 478 479 +286 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 +286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 +29 31 33 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1084 1085 1086 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1279 1280 1281 1282 1283 +29 31 33 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1084 1085 1086 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1118 1121 1122 1135 1139 1141 1142 1143 1144 1145 1146 1281 1282 1283 +29 31 33 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1085 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1279 1280 1281 1282 1283 1564 1565 1566 1567 1568 1569 1570 1574 1575 1576 1577 1583 +29 31 33 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1085 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1279 1280 1281 1282 1283 1565 1566 1567 1568 1569 1570 1574 1575 1576 1577 1583 +29 31 33 1084 1085 1086 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1279 1280 1281 1282 1283 +29 31 33 1084 1085 1086 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1118 1121 1122 1135 1139 1141 1142 1143 1144 1145 1146 1281 1282 1283 +29 31 33 1085 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1279 1280 1281 1282 1283 1564 1565 1566 1567 1568 1569 1570 1574 1575 1576 1577 1583 +29 31 33 1085 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1279 1280 1281 1282 1283 1565 1566 1567 1568 1569 1570 1574 1575 1576 1577 1583 +311 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 +311 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1256 +311 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 +311 1174 1175 1182 1183 1184 1186 1188 1190 1191 1192 1194 1195 1196 1197 1198 1199 1200 1201 1202 1255 1335 1336 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 1174 1175 1182 1183 1184 1186 1188 1190 1191 1192 1194 1195 1196 1197 1198 1199 1200 1201 1202 1255 1335 1336 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1335 1336 1337 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1335 1336 1337 1338 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1255 1335 1336 1337 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1206 1255 1335 1336 1337 1338 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1202 1203 1204 1205 1206 1255 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1186 1188 1189 1190 1191 1192 1193 1194 1195 1196 1201 1206 1255 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1256 +311 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1335 1336 1337 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1255 1335 1336 1337 1338 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1255 1335 1336 1337 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1206 1255 1335 1336 1337 1338 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1202 1203 1204 1205 1206 1255 +311 554 555 1167 1168 1169 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1186 1188 1189 1190 1191 1192 1193 1194 1195 1196 1201 1206 1255 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 373 374 375 376 377 378 379 380 381 382 383 384 579 580 581 583 584 585 587 588 589 591 592 593 602 637 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1406 1407 1408 1590 1591 1592 1593 1594 1595 1596 1700 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 373 374 375 376 377 378 379 380 381 382 383 384 579 581 583 585 637 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1408 1590 1591 1592 1593 1594 1595 1596 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 637 1258 1259 1260 1261 1262 1263 1267 1268 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 637 1258 1259 1267 1268 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 637 1259 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 637 1259 1260 1261 1262 1263 +330 331 332 333 334 335 340 341 342 343 345 346 347 348 351 352 354 355 356 357 358 359 360 361 363 364 365 366 637 1259 1260 1261 1262 1263 1275 1276 1277 1278 +332 333 334 335 340 341 342 343 351 352 354 355 356 357 358 359 360 361 363 364 365 366 373 374 375 376 377 378 379 380 381 382 383 384 579 580 581 587 588 589 602 637 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1406 1408 1590 1591 1592 1593 1594 1595 1596 1700 +332 333 334 335 340 341 342 343 351 352 354 355 356 357 358 359 360 361 363 364 365 366 373 374 375 376 377 378 379 380 381 382 383 384 579 581 637 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1408 1590 1591 1592 1593 1594 1595 1596 +34 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 +34 35 37 38 523 524 525 526 527 528 529 530 531 +34 35 37 38 523 524 525 534 535 536 +34 523 +36 265 336 337 523 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 +36 336 337 350 523 612 613 614 615 616 617 621 622 623 625 626 627 +36 37 38 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 184 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 443 523 643 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 +36 37 38 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 184 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 443 643 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 822 823 824 825 826 827 828 829 830 831 832 833 834 835 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 871 872 873 874 +36 37 38 265 523 876 881 882 883 884 885 886 887 888 +36 37 38 265 875 876 877 878 879 880 +36 37 38 39 +36 37 38 39 1492 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1488 1491 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 1602 1603 1604 1605 1606 1607 +36 37 38 39 265 1438 1439 1440 1441 1442 1443 1444 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 +36 37 38 39 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 580 581 583 584 585 604 605 606 607 608 637 1259 +36 37 38 39 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 580 581 583 584 585 604 605 606 607 608 637 1259 1260 1261 1262 1263 +36 37 38 39 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 580 581 583 584 585 604 605 606 607 608 637 1259 1260 1261 1262 1263 1275 1276 1277 1278 +36 37 38 39 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 581 583 585 604 605 606 607 608 637 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1408 1590 1591 1592 1593 1594 1595 1596 +36 37 38 39 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 562 637 +36 37 38 39 332 333 334 335 336 337 338 339 340 341 342 343 344 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 581 604 605 606 607 608 637 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1275 1276 1277 1278 1340 1342 1343 1405 1408 1590 1591 1592 1593 1594 1595 1596 +36 37 38 39 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 562 563 564 579 580 581 583 584 585 604 605 606 607 608 +36 37 38 39 336 337 338 339 344 349 350 353 362 367 368 369 370 371 372 562 563 564 604 605 606 607 608 +36 37 38 39 40 41 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 275 276 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 275 276 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 55 275 276 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 55 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1584 1585 1586 1587 1588 1589 +36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1225 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +36 37 38 39 57 58 59 60 61 765 1659 1660 1661 1662 1663 1664 1665 1666 1667 +36 37 38 39 57 58 64 76 77 78 79 80 81 82 83 84 85 86 265 270 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 410 418 419 420 421 422 423 424 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 1274 1287 1601 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1698 1699 +36 37 38 39 57 58 64 76 77 78 79 80 81 82 83 84 85 86 265 270 394 395 396 397 398 399 400 401 402 403 404 405 406 407 410 418 419 420 421 422 423 424 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 1287 1601 1632 1633 1634 1635 1636 1639 1640 1641 1642 1643 1644 1645 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1698 1699 +36 37 38 39 57 58 64 76 77 78 79 80 81 82 83 84 85 86 265 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 423 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 1274 +36 37 38 39 57 58 64 76 77 78 79 80 81 82 83 84 85 86 265 394 395 396 398 399 400 401 402 403 404 405 406 407 410 423 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 +36 37 38 59 87 797 +36 37 38 59 87 797 1597 1598 1599 1600 +36 37 38 646 1571 1572 1573 +36 37 38 88 265 278 373 374 375 376 377 378 379 380 381 563 806 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 +36 37 38 88 265 278 373 374 375 376 377 378 379 380 381 563 806 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 +36 37 38 88 265 278 373 374 375 376 377 378 379 380 381 563 806 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 +36 799 800 801 802 804 +37 38 340 343 344 350 352 353 364 542 543 628 629 630 631 632 633 634 635 636 637 638 639 +37 38 501 502 503 504 505 506 507 508 509 510 511 512 +37 38 511 513 514 515 516 +37 38 511 517 518 519 520 521 522 +37 38 523 532 533 +382 383 384 579 580 581 1275 1340 1342 1343 1590 +382 383 384 579 580 581 583 584 585 1275 1340 1342 1343 1590 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1034 1037 1039 1056 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1034 1039 1046 1049 1050 1053 1054 1055 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1034 1039 1056 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1039 1041 1056 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1039 1046 1047 1049 1050 1053 1054 1056 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1039 1046 1049 1050 1053 1054 1055 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1039 1046 1050 1053 1054 1055 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1023 1024 1028 1029 1039 1046 1050 1053 1054 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1056 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 1412 +38 385 386 387 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1254 1257 +385 387 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1339 1340 1341 1342 1343 1412 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1340 1341 1342 1343 1384 1385 1386 1387 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1340 1342 1343 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1339 1340 1341 1342 1343 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1341 1342 1343 1386 1387 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1342 1343 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +385 387 1207 1208 1209 1210 1211 1212 1213 1214 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +385 387 1214 1339 1340 1341 1342 1343 +385 387 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +38 58 198 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1342 1343 1413 1414 1415 1416 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1339 1340 1341 1342 1343 1412 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1340 1341 1342 1343 1384 1385 1386 1387 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1257 1340 1342 1343 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1339 1340 1341 1342 1343 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1341 1342 1343 1386 1387 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1254 1340 1342 1343 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1207 1208 1209 1210 1211 1212 1213 1214 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1214 1339 1340 1341 1342 1343 +38 58 385 386 387 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1769 1770 1771 1772 1773 1774 1775 1776 1782 1783 1784 1785 1786 +38 58 386 893 894 895 896 897 898 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 +389 390 391 +38 997 998 999 1000 1001 1002 1003 1004 +397 408 409 1274 +42 43 44 45 46 47 48 49 50 51 52 53 55 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1584 1585 1586 1587 1588 1589 +42 43 44 45 46 47 48 49 50 51 52 53 90 273 274 275 276 277 798 1215 1216 1217 1218 1219 1220 1221 1225 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1285 1286 1287 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 +426 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1206 +426 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1206 1255 +426 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1255 +426 537 538 539 540 541 542 543 545 546 547 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1255 +426 537 538 539 540 549 550 551 552 553 554 555 1167 1168 1169 +426 554 555 1206 +426 554 555 556 557 558 559 560 561 1167 1168 1169 1206 1255 +426 554 555 556 557 558 559 560 561 1167 1168 1169 1255 +427 428 429 430 431 432 433 434 435 436 437 438 439 440 +4 5 6 7 8 9 285 478 479 +486 487 488 1012 1013 1156 +486 487 488 1156 +488 +490 491 492 493 494 495 496 497 498 +499 500 +499 500 1257 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 1206 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1170 1171 1206 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1170 1171 1206 1255 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 1167 1168 1169 1206 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1167 1168 1169 1170 1171 1206 1255 +537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 1256 +537 549 550 551 552 553 554 555 1167 1168 1169 +538 +538 539 540 +538 539 540 541 542 543 549 553 554 555 1167 1168 1169 1170 1171 +549 550 551 +550 551 +554 555 1167 1168 1169 1170 1171 1206 +554 555 1167 1168 1169 1170 1171 1206 1255 +554 555 1167 1168 1169 1206 +562 624 +57 1673 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 +623 640 +632 636 680 681 1158 1159 1160 1161 1162 +641 +641 642 +655 656 657 658 659 660 661 662 663 664 665 666 +667 668 +669 670 +671 672 673 674 675 676 677 678 679 680 681 682 683 684 +80 +80 261 262 263 264 265 266 +806 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 +89 +89 232 1668 1669 1670 1671 +901 902 903 904 905 906 907 908 917 918 919 920 921 922 923 924 1342 1343 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1437 +91 +92 +93 94 95 96 97 98 99 100 101 102 103 1061 1062 1063 1064 1065 1066 +93 94 95 96 97 98 99 100 101 102 103 1061 1062 1064 1066 +93 94 95 96 97 98 99 100 101 102 103 523 1061 1062 1063 1064 1065 1066 diff --git a/Control/CxxUtils/share/bitscan_test.ref b/Control/CxxUtils/share/bitscan_test.ref index 8ebc8c0d3fa5411ef381137004d6dcc931d8d6c4..22a84bcdd03f5616a860fee3a18cef452d76abfa 100644 --- a/Control/CxxUtils/share/bitscan_test.ref +++ b/Control/CxxUtils/share/bitscan_test.ref @@ -1,13 +1,20 @@ -00000000000000000000000000000000 32=32 0=0 -00000000000000000000000000000001 31=31 0=0 -10000000000000000000000000000000 0=0 31=31 -11001011101010100011111100000000 0=0 8=8 -11111111000111000010001100000001 0=0 0=0 -00000000000110101111111100000100 11=11 2=2 +00000000000000000000000000000000 32=32 0=0 0=0 +00000000000000000000000000000001 31=31 0=0 1=1 +10000000000000000000000000000000 0=0 31=31 1=1 +11001011101010100011111100000000 0=0 8=8 15=15 +11111111000111000010001100000001 0=0 0=0 15=15 +00000000000110101111111100000100 11=11 2=2 12=12 -0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 -0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 -1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 -0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 -0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 -0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 +0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 0=0 +0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 1=1 +1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 1=1 +0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 28=28 +0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 1=1 +0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 1=1 + +0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 0=0 +0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 1=1 +1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 1=1 +0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 28=28 +0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 1=1 +0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 1=1 diff --git a/Control/CxxUtils/share/bitscan_test_portable.ref b/Control/CxxUtils/share/bitscan_test_portable.ref index 8ebc8c0d3fa5411ef381137004d6dcc931d8d6c4..22a84bcdd03f5616a860fee3a18cef452d76abfa 100644 --- a/Control/CxxUtils/share/bitscan_test_portable.ref +++ b/Control/CxxUtils/share/bitscan_test_portable.ref @@ -1,13 +1,20 @@ -00000000000000000000000000000000 32=32 0=0 -00000000000000000000000000000001 31=31 0=0 -10000000000000000000000000000000 0=0 31=31 -11001011101010100011111100000000 0=0 8=8 -11111111000111000010001100000001 0=0 0=0 -00000000000110101111111100000100 11=11 2=2 +00000000000000000000000000000000 32=32 0=0 0=0 +00000000000000000000000000000001 31=31 0=0 1=1 +10000000000000000000000000000000 0=0 31=31 1=1 +11001011101010100011111100000000 0=0 8=8 15=15 +11111111000111000010001100000001 0=0 0=0 15=15 +00000000000110101111111100000100 11=11 2=2 12=12 -0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 -0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 -1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 -0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 -0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 -0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 +0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 0=0 +0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 1=1 +1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 1=1 +0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 28=28 +0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 1=1 +0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 1=1 + +0000000000000000000000000000000000000000000000000000000000000000 64=64 0=0 0=0 +0000000000000000000000000000000000000000000000000000000000000001 63=63 0=0 1=1 +1000000000000000000000000000000000000000000000000000000000000000 0=0 63=63 1=1 +0001001000110100010101100111100011001011101010100011111100000000 3=3 8=8 28=28 +0000000000000000000000000000000000010000000000000000000000000000 35=35 28=28 1=1 +0000000000000000000000000001000000000000000000000000000000000000 27=27 36=36 1=1 diff --git a/Control/CxxUtils/test/CachedValue_test.cxx b/Control/CxxUtils/test/CachedValue_test.cxx index 10595580112a0cc778e4a9cfca5515a45b054238..46f446f017ad61010387896113e109ad03d41943 100644 --- a/Control/CxxUtils/test/CachedValue_test.cxx +++ b/Control/CxxUtils/test/CachedValue_test.cxx @@ -242,8 +242,9 @@ void test4() std::cout << "test4\n"; ThreadingTest test; - for (int i=0; i < 10; i++) + for (int i=0; i < 5; i++) { test.threadedTest(); + } } diff --git a/Control/CxxUtils/test/ConcurrentBitset_test.cxx b/Control/CxxUtils/test/ConcurrentBitset_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2bc6a61a8e5392e85481b4611aa28519e0c57e34 --- /dev/null +++ b/Control/CxxUtils/test/ConcurrentBitset_test.cxx @@ -0,0 +1,1148 @@ +/* + * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration. + */ + +// $Id$ +/** + * @file CxxUtils/test/ConcurrentBitset_test.cxx + * @author scott snyder + * @date Nov, 2017 + * @brief Regression test for ConcurrentBitset. + */ + + +#undef NDEBUG +#include "CxxUtils/ConcurrentBitset.h" +#include "TestTools/random.h" +#include "tbb/concurrent_unordered_set.h" +#include "boost/timer/timer.hpp" +#ifdef HAVE_CK +extern "C" { +#include "ck_hs.h" +#include "ck_bitmap.h" +} +#endif +#include <mutex> +#include <thread> +#include <bitset> +#include <shared_mutex> +#include <algorithm> +#include <vector> +#include <string> +#include <set> +#include <unordered_set> +#include <sstream> +#include <fstream> +#include <iostream> +#include <cassert> +#include <cstring> + + +// Basic tests. +void test_basic() +{ + std::cout << "test_basic\n"; + + CxxUtils::ConcurrentBitset bs0; + assert (bs0.capacity() == 0); + assert (bs0.size() == 0); + assert (bs0.count() == 0); + assert (!bs0.test(0)); + assert (bs0.empty()); + + CxxUtils::ConcurrentBitset bs (128); + assert (bs.capacity() == 128); + assert (bs.size() == 0); + assert (bs.count() == 0); + assert (bs.empty()); + + // set, test, reset, etc. + assert (!bs.test (10)); + assert (!bs.test (20)); + assert (!bs.test (30)); + bs.set (10); + bs.set (30); + assert (bs.test (10)); + assert (!bs.test (20)); + assert (bs.test (30)); + assert (bs.size() == 2); + assert (!bs.empty()); + + bs.insert (100); + assert (bs.count (100) == 1); + assert (bs.count (101) == 0); + assert (bs.count() == 3); + + bs.reset (30); + assert (!bs.test (30)); + assert (bs.size() == 2); + + bs.flip (110); + assert (bs.test (110)); + assert (bs.size() == 3); + bs.flip (10); + assert (!bs.test (10)); + assert (bs.size() == 2); + + bs.set (10, true); + assert (bs.test (10)); + assert (bs.size() == 3); + bs.set (10, false); + assert (!bs.test (10)); + assert (bs.size() == 2); + + // Set to all / clear. + bs.reset(); + assert (bs.size() == 0); + assert (!bs.test (10)); + assert (!bs.test (50)); + assert (!bs.test (100)); + assert (!bs.test (101)); + + bs.set(); + assert (bs.size() == 128); + assert (bs.test (10)); + assert (bs.test (50)); + assert (bs.test (100)); + assert (bs.test (101)); + + bs.clear(); + assert (bs.size() == 0); + assert (!bs.test (10)); + assert (!bs.test (50)); + assert (!bs.test (100)); + assert (!bs.test (101)); +} + + +// Reference. +void test_reference() +{ + std::cout << "test_reference\n"; + + CxxUtils::ConcurrentBitset bs (128); + const CxxUtils::ConcurrentBitset& cbs = bs; + + assert (!bs[10]); + assert (!cbs[10]); + assert (~bs[10]); + + bs[10] = true; + assert (cbs[10]); + assert (bs.count() == 1); + + bs[20] = bs[10]; + assert (cbs[10]); + assert (cbs[20]); + assert (bs.count() == 2); + + bs[20] = bs[21]; + assert (cbs[10]); + assert (!cbs[20]); + assert (bs.count() == 1); + + bs[30].flip(); + assert (cbs[10]); + assert (!cbs[20]); + assert (cbs[30]); + assert (bs.count() == 2); + + bs[10] = false; + assert (!cbs[10]); + assert (!cbs[20]); + assert (cbs[30]); + assert (bs.count() == 1); +} + + +// Iterate. +void test_iterate() +{ + std::cout << "test_iterate\n"; + + CxxUtils::ConcurrentBitset bs0; + CxxUtils::ConcurrentBitset::const_iterator beg = bs0.begin(); + CxxUtils::ConcurrentBitset::const_iterator end = bs0.end(); + for (; beg != end; ++beg) + std::cout << "elt\n"; + + //std::vector<size_t> v0 (bs0.begin(), bs0.end()); + //assert (v0.empty()); + + CxxUtils::ConcurrentBitset bs (2048); + bs.set (100); + bs.set (200); + bs.set (300); + bs.set (400); + + std::vector<size_t> v1 (bs.begin(), bs.end()); + assert (v1 == (std::vector<size_t> {100, 200, 300, 400})); + + bs.set (1000); + std::vector<size_t> v2 (bs.begin(), bs.end()); + assert (v2 == (std::vector<size_t> {100, 200, 300, 400, 1000})); + + bs.reset (200); + std::vector<size_t> v3 (bs.begin(), bs.end()); + assert (v3 == (std::vector<size_t> {100, 300, 400, 1000})); + + bs.reset (1000); + std::vector<size_t> v4 (bs.begin(), bs.end()); + assert (v4 == (std::vector<size_t> {100, 300, 400})); + + bs.set (0); + bs.set (2047); + std::vector<size_t> v5 (bs.begin(), bs.end()); + assert (v5 == (std::vector<size_t> {0, 100, 300, 400, 2047})); + + bs.set (101); + bs.set (102); + bs.set (103); + std::vector<size_t> v6 (bs.begin(), bs.end()); + assert (v6 == (std::vector<size_t> {0, 100, 101, 102, 103, 300, 400, 2047})); + + bs.set (3); + std::vector<size_t> v7 (bs.begin(), bs.end()); + assert (v7 == (std::vector<size_t> {0, 3, 100, 101, 102, 103, 300, 400, 2047})); + + bs.set (1); + std::vector<size_t> v8 (bs.begin(), bs.end()); + assert (v8 == (std::vector<size_t> {0, 1, 3, 100, 101, 102, 103, 300, 400, 2047})); + + bs.set (64); + std::vector<size_t> v9 (bs.begin(), bs.end()); + assert (v9 == (std::vector<size_t> {0, 1, 3, 64, 100, 101, 102, 103, 300, 400, 2047})); + + bs.set (65); + std::vector<size_t> v10 (bs.begin(), bs.end()); + assert (v10 == (std::vector<size_t> {0, 1, 3, 64, 65, 100, 101, 102, 103, 300, 400, 2047})); + + bs.set (63); + std::vector<size_t> v11 (bs.begin(), bs.end()); + assert (v11 == (std::vector<size_t> {0, 1, 3, 63, 64, 65, 100, 101, 102, 103, 300, 400, 2047})); + + bs.reset (0); + bs.reset (1); + std::vector<size_t> v12 (bs.begin(), bs.end()); + assert (v12 == (std::vector<size_t> {3, 63, 64, 65, 100, 101, 102, 103, 300, 400, 2047})); +} + + +// Find. +void test_find() +{ + std::cout << "test_find\n"; + + CxxUtils::ConcurrentBitset bs (2048); + assert (bs.find (123) == bs.end()); + + bs.set (0); + bs.set (63); + bs.set (64); + bs.set (65); + bs.set (2047); + std::vector<size_t> v0 (bs.find(0), bs.end()); + assert (v0 == (std::vector<size_t> {0, 63, 64, 65, 2047})); + std::vector<size_t> v1 (bs.find(63), bs.end()); + assert (v1 == (std::vector<size_t> {63, 64, 65, 2047})); + std::vector<size_t> v2 (bs.find(64), bs.end()); + assert (v2 == (std::vector<size_t> {64, 65, 2047})); + std::vector<size_t> v3 (bs.find(65), bs.end()); + assert (v3 == (std::vector<size_t> {65, 2047})); + std::vector<size_t> v4 (bs.find(2047), bs.end()); + assert (v4 == (std::vector<size_t> {2047})); +} + + +// Insert. +void test_insert() +{ + std::cout << "test_insert\n"; + + CxxUtils::ConcurrentBitset bs (3); + assert (bs.capacity() == 3); + + assert (!bs.test (35)); + bs.set (35); + assert (!bs.test (35)); + assert (bs.capacity() == 3); + + bs.insert (35); + assert (bs.test (35)); + assert (bs.capacity() == 128); + + assert (!bs.test (300)); + bs.set (300); + assert (!bs.test (300)); + assert (bs.capacity() == 128); + + bs.insert (300); + assert (bs.test (300)); + assert (bs.capacity() == 640); + + bs.emptyGarbage(); + + CxxUtils::ConcurrentBitset bs2 (1024); + std::vector<size_t> v2 { 4, 10, 100, 500 }; + bs2.insert (v2.begin(), v2.end()); + assert (bs2.size() == 4); + assert (bs2.test (4)); + assert (bs2.test (10)); + assert (bs2.test (100)); + assert (bs2.test (500)); + + bs2.insert ({ 10, 11, 12}); + std::vector<size_t> v3 (bs2.begin(), bs2.end()); + assert (v3 == (std::vector<size_t> {4, 10, 11, 12, 100, 500})); + + bs2.insert ({ 3000, 3001 }); + std::vector<size_t> v4 (bs2.begin(), bs2.end()); + assert (v4 == (std::vector<size_t> {4, 10, 11, 12, 100, 500, 3000, 3001})); + + CxxUtils::ConcurrentBitset bs3 (128); + bs3.insert ({3, 10, 100}); + CxxUtils::ConcurrentBitset bs4 (256); + bs4.insert ({2, 10, 200}); + + bs3.insert (bs4); + std::vector<size_t> v5 (bs3.begin(), bs3.end()); + assert (v5 == (std::vector<size_t> {2, 3, 10, 100, 200})); +} + + +// Copy/assign +void test_copy() +{ + std::cout << "test_copy\n"; + + CxxUtils::ConcurrentBitset bs1 (128); + assert (bs1.capacity() == 128); + bs1.set (5); + bs1.set (45); + bs1.set (87); + + CxxUtils::ConcurrentBitset bs2 (bs1); + assert (bs2.capacity() == 128); + assert (bs2.count() == 3); + assert (bs2.test(5)); + assert (bs2.test(45)); + assert (!bs2.test(46)); + assert (bs2.test(87)); + + bs2.insert (500, 1024); + assert (bs2.capacity() == 1024); + assert (bs2.test(500)); + + bs2 = bs1; + assert (bs2.capacity() == 1024); + assert (bs2.test(5)); + assert (bs2.test(45)); + assert (!bs2.test(46)); + assert (bs2.test(87)); + assert (!bs2.test(500)); + + bs2.set (501); + bs1 = bs2; + assert (bs1.capacity() == 1024); + assert (bs1.test(5)); + assert (bs1.test(45)); + assert (!bs1.test(46)); + assert (bs1.test(87)); + assert (!bs1.test(500)); + assert (bs1.test(501)); + + CxxUtils::ConcurrentBitset bs3; + bs3 = bs1; + std::vector<size_t> v3 (bs3.begin(), bs3.end()); + assert (v3 == (std::vector<size_t> {5, 45, 87, 501})); +} + + +// Initializer list +void test_initlist() +{ + std::cout << "test_initlist\n"; + + CxxUtils::ConcurrentBitset bs1 { 10, 50 }; + assert (bs1.size() == 2); + assert (bs1.test (10)); + assert (!bs1.test (9)); + assert (bs1.test (50)); + assert (bs1.capacity() == 64); + + CxxUtils::ConcurrentBitset bs2 { 10, 40, 90, 100 }; + assert (bs2.size() == 4); + assert (bs2.test (10)); + assert (bs2.test (40)); + assert (bs2.test (90)); + assert (bs2.test (100)); + assert (!bs2.test (9)); + assert (!bs2.test (91)); + assert (bs2.capacity() == 128); +} + + +// Comparison +void test_compare() +{ + std::cout << "test_compare\n"; + + CxxUtils::ConcurrentBitset bs1 { 10, 40, 50, 60 }; + CxxUtils::ConcurrentBitset bs2 { 10, 40, 50, 60 }; + + assert (bs1 == bs2); + assert (!(bs1 != bs2)); + + bs1.set (15); + assert (!(bs1 == bs2)); + assert (bs1 != bs2); + + bs2.set (15); + assert (bs1 == bs2); + assert (!(bs1 != bs2)); + + bs1.insert (1000); + assert (!(bs1 == bs2)); + assert (bs1 != bs2); + bs1.reset (1000); + assert (bs1 == bs2); + assert (!(bs1 != bs2)); + + bs2.insert (2000); + assert (!(bs1 == bs2)); + assert (bs1 != bs2); + bs2.reset (2000); + assert (bs1 == bs2); + assert (!(bs1 != bs2)); + + bs1.insert (2000); + bs2.insert (2000); + assert (bs1 == bs2); + assert (!(bs1 != bs2)); +} + + +// Operators +void test_operators() +{ + std::cout << "test_operators\n"; + + CxxUtils::ConcurrentBitset bs1 (256); + CxxUtils::ConcurrentBitset bs2 (128); + + bs1.insert ({2, 10, 50, 70, 100, 150, 200}); + bs2.insert ({2, 12, 48, 70, 100}); + + assert ((bs1 &= bs2).size() == 3); + std::vector<size_t> v1 (bs1.begin(), bs1.end()); + assert (v1 == (std::vector<size_t> {2, 70, 100})); + + bs1.insert ({2, 10, 50, 70, 100, 150, 200}); + assert ((bs1 |= bs2).size() == 9); + std::vector<size_t> v2 (bs1.begin(), bs1.end()); + assert (v2 == (std::vector<size_t> {2, 10, 12, 48, 50, 70, 100, 150, 200})); + + assert ((bs1 ^= bs2).size() == 4); + std::vector<size_t> v3 (bs1.begin(), bs1.end()); + assert (v3 == (std::vector<size_t> {10, 50, 150, 200})); + + bs2.insert ({50, 150}); + assert ((bs1 -= bs2).size() == 2); + std::vector<size_t> v4 (bs1.begin(), bs1.end()); + assert (v4 == (std::vector<size_t> {10, 200})); + + bs1.flip(); + std::vector<size_t> v5; + for (size_t i = 0; i < 256; i++) { + if (!bs1.test (i)) v5.push_back (i); + } + assert (v5 == (std::vector<size_t> {10, 200})); + + CxxUtils::ConcurrentBitset bs4 = ~bs1; + std::vector<size_t> v6 (bs4.begin(), bs4.end()); + assert (v6 == (std::vector<size_t> {10, 200})); +} + + +// any/all/none/empty +void test_anyall() +{ + std::cout << "test_anyall\n"; + + CxxUtils::ConcurrentBitset bs (128); + assert (bs.none()); + assert (bs.empty()); + assert (!bs.all()); + assert (!bs.any()); +} + + +//************************ MT test. + + +// Have N threads, M bitsets. +// Within each bitset, thread i accesses bits b where b mod N == i. +// Thread i can expand bitset s where s mod N == i. +// Each thread has a set of random bitstrings. +// It fills all bitsets with strings, checks them, then rotates the strings. + + +std::shared_timed_mutex start_mutex; + + +class test_mt_Thread +{ +public: + static constexpr const size_t maxbit = 65536; + static constexpr const float prob = 0.2; + static const int niter = 500; + + test_mt_Thread (int slot, int nsets, int nslots, + std::vector<CxxUtils::ConcurrentBitset>& sets, + std::mutex* mutexes); + void operator()(); + void dowrite (int iset, int irand, + CxxUtils::ConcurrentBitset& flipsave); + + +private: + int m_slot; + int m_nsets; + int m_nslots; + std::vector<CxxUtils::ConcurrentBitset>& m_sets; + std::mutex* m_mutexes; + typedef std::bitset<maxbit> randset_t; + std::vector<randset_t> m_rands; +}; +constexpr const size_t test_mt_Thread::maxbit; + + +test_mt_Thread::test_mt_Thread (int slot, + int nsets, + int nslots, + std::vector<CxxUtils::ConcurrentBitset>& sets, + std::mutex* mutexes) + : m_slot (slot), + m_nsets (nsets), + m_nslots (nslots), + m_sets (sets), + m_mutexes (mutexes) +{ + uint32_t seed = (slot+1) * 2531; + m_rands.resize (nsets); + for (int i=0; i < nsets; i++) { + for (size_t j=0; j < maxbit; j++) { + if (Athena_test::randf_seed (seed, 1.0) < prob) { + m_rands[i].set (j); + } + } + } +} + + +void test_mt_Thread::dowrite (int iset, int irand, + CxxUtils::ConcurrentBitset& flipsave) +{ + CxxUtils::ConcurrentBitset& set = m_sets[iset]; + randset_t& rand = m_rands[irand]; + size_t nbits = set.capacity(); + if (irand == 0) { + flipsave = set; + for (size_t ibit = m_slot; ibit < nbits; ibit += m_nslots) { + set.flip (ibit); + } + } + else { + for (size_t ibit = m_slot; ibit < nbits; ibit += m_nslots) { + if (rand.test(ibit)) { + set.set (ibit); + } + else { + set.reset (ibit); + } + } + } +} + + +void test_mt_Thread::operator()() +{ + std::shared_lock<std::shared_timed_mutex> lock (start_mutex); + + int randoff = 0; + int expoff = m_slot; + for (int iter = 0; iter < niter; iter++) { + + std::vector<size_t> sizes (m_nsets); + CxxUtils::ConcurrentBitset flipsave (maxbit); + + // set + for (int iset = 0; iset < m_nsets; iset++) { + sizes[iset] = m_sets[iset].capacity(); + int irand = (iset + randoff) % m_nsets; + std::lock_guard<std::mutex> lock (m_mutexes[iset]); + dowrite (iset, irand, flipsave); + } + + // check + for (int iset = 0; iset < m_nsets; iset++) { + CxxUtils::ConcurrentBitset& set = m_sets[iset]; + int irand = (iset + randoff) % m_nsets; + randset_t& rand = m_rands[irand]; + size_t nbits = sizes[iset]; + assert (nbits <= set.capacity()); + if (irand == 0) { + for (size_t ibit = m_slot; ibit < nbits; ibit += m_nslots) { + if (flipsave.test (ibit)) { + assert (!set.test (ibit)); + } + else { + assert (set.test (ibit)); + } + } + } + else { + for (size_t ibit = m_slot; ibit < nbits; ibit += m_nslots) { + if (rand.test(ibit)) { + assert (set.test (ibit)); + } + else { + assert (!set.test (ibit)); + } + } + } + } + + { + CxxUtils::ConcurrentBitset& set = m_sets[expoff]; + size_t nbits = set.capacity(); + if (nbits < maxbit) { + size_t newsize = std::min (nbits + 128, maxbit); + std::lock_guard<std::mutex> lock (m_mutexes[expoff]); + set.insert (newsize-1, newsize); + } + + expoff += m_nslots; + if (expoff >= m_nsets) { + expoff = m_slot; + } + } + + randoff++; + if (randoff >= m_nsets) randoff = 0; + } +} + + +void test_mt_iter() +{ + const int nthread = 4; + const int nsets = 100; + + std::vector<CxxUtils::ConcurrentBitset> bitsets + (nsets, CxxUtils::ConcurrentBitset (128)); + std::mutex mutexes[nsets]; + + std::thread threads[nthread]; + start_mutex.lock(); + + for (int i=0; i < nthread; i++) { + threads[i] = std::thread (test_mt_Thread (i, nsets, nthread, bitsets, mutexes)); + } + + // Try to get the threads starting as much at the same time as possible. + start_mutex.unlock(); + for (int i=0; i < nthread; i++) { + threads[i].join(); + } +} + + +void test_mt() +{ + std::cout << "test_mt\n"; + + for (int i=0; i < 5; i++) { + test_mt_iter(); + } +} + + +//************************ performance testing. + + +class TestVectors +{ +public: + typedef std::vector<size_t> vec_t; + + void read (const std::string& fname); + size_t size() const { return m_vecs.size(); } + const vec_t& vec (size_t i) const + { + if (i >= m_vecs.size()) std::abort(); + return m_vecs[i]; + } + + std::vector<vec_t>::const_iterator begin() const { return m_vecs.begin(); } + std::vector<vec_t>::const_iterator end() const { return m_vecs.end(); } + +private: + std::vector<vec_t> m_vecs; +}; + + +void TestVectors::read (const std::string& fname) +{ + std::ifstream s (fname); + std::string str; + while (std::getline (s, str)) { + std::istringstream ss (str); + size_t id; + std::vector<size_t> v; + while (ss >> id) { + v.push_back (id); + } + m_vecs.push_back (std::move (v)); + } +} + + +class SetAdapter + : public std::set<size_t> +{ +public: + bool test (size_t id) const + { + return find(id) != end(); + } + static std::string name() { return "set"; } +}; + + +class USetAdapter + : public std::unordered_set<size_t> +{ +public: + bool test (size_t id) const + { + return find(id) != end(); + } + static std::string name() { return "unordered_set"; } +}; + + +class CUSetAdapter + : public tbb::concurrent_unordered_set<size_t> +{ +public: + bool test (size_t id) const + { + return find(id) != end(); + } + static std::string name() { return "concurrent_unordered_set"; } +}; + + +#ifdef HAVE_CK +class CKHSAdapter +{ +public: + CKHSAdapter(); + CKHSAdapter (const CKHSAdapter& other); + + static std::string name() { return "ck_hs"; } + + void insert (size_t id) + { + if (!ck_hs_put (&m_hs, id, reinterpret_cast<const void*> (id))) { + std::cout << "ck_hs_put error\n"; + } + } + + bool test (size_t id) const + { + return ck_hs_get (const_cast<ck_hs_t*>(&m_hs), id, reinterpret_cast<const void*> (id)) != nullptr; + } + + + struct const_iterator + : public ck_hs_iterator_t + { + const_iterator(const ck_hs_t* hs) : m_hs (const_cast<ck_hs_t*>(hs)) + { cursor = nullptr; offset = 0; } + const_iterator() : const_iterator(nullptr) {} + size_t operator* () const { return reinterpret_cast<size_t> (m_elt); } + const_iterator& operator++() + { + if (!ck_hs_next (m_hs, this, &m_elt)) { + cursor = nullptr; + offset = 0; + } + return *this; + } + + bool operator== (const const_iterator& other) const + { + return cursor == other.cursor && offset == other.offset; + } + bool operator!= (const const_iterator& other) const + { + return !(*this == other); + } + + ck_hs_t* m_hs; + void* m_elt = nullptr; + }; + + + const_iterator begin() const + { + const_iterator it (&m_hs); + ++it; + return it; + } + const_iterator end() const { return const_iterator(); } + + +private: + static unsigned long hash (const void* p, unsigned long /*seed*/) + { return reinterpret_cast<unsigned long> (p); } + + static bool compare (const void* p1, const void* p2) + { return p1 == p2; } + + static void hs_free (void *p, size_t /*b*/, bool /*r*/) { free(p); } + + ck_malloc m_alloc; + ck_hs_t m_hs; +}; + + +CKHSAdapter::CKHSAdapter() +{ + m_alloc.malloc = malloc; + m_alloc.free = hs_free; + if (!ck_hs_init (&m_hs, + CK_HS_MODE_SPMC | CK_HS_MODE_DIRECT, + hash, // ck_hs_hash_cb_t + compare, // ck_hs_compare_cb_t + &m_alloc, // ck_malloc* + 128, // initial size + 6602834)) + { + std::cout << "ck_hs_init error\n"; + } +} + + +CKHSAdapter::CKHSAdapter (const CKHSAdapter& other) + : CKHSAdapter() +{ + ck_hs_iterator it { nullptr, 0}; + void* obj; + while (ck_hs_next (const_cast<ck_hs_t*>(&other.m_hs), &it, &obj)) { + insert (reinterpret_cast<size_t> (obj)); + } +} + + +class CKBitmapAdapter +{ +public: + typedef CK_BITMAP_INSTANCE(2048) Bitmap_t; + + CKBitmapAdapter(); + CKBitmapAdapter (const CKBitmapAdapter& other); + + static std::string name() { return "ck_bitmap"; } + + void insert (size_t id) + { CK_BITMAP_SET (&m_map, id); } + + bool test (size_t id) const + { return CK_BITMAP_TEST (&m_map, id); } + + struct const_iterator + : public ck_bitmap_iterator + { + const_iterator() : m_map(nullptr), m_bit(-1) {} + const_iterator (const Bitmap_t* map) : m_map(map), m_bit(0) + { CK_BITMAP_ITERATOR_INIT (this, map); } + size_t operator*() const { return m_bit; } + const_iterator& operator++() + { + if (!CK_BITMAP_NEXT (m_map, this, &m_bit)) { + m_bit = -1; + } + return *this; + } + + bool operator!= (const const_iterator& /*other*/) + { return m_bit != static_cast<unsigned int>(-1); } + + + private: + const Bitmap_t* m_map; + unsigned int m_bit; + }; + + const_iterator begin() const + { + const_iterator it(&m_map); + ++it; + return it; + } + const_iterator end() const { return const_iterator(); } + +private: + Bitmap_t m_map; +}; + + +CKBitmapAdapter::CKBitmapAdapter() +{ + CK_BITMAP_INIT (&m_map, 2048, false); +} + + +CKBitmapAdapter::CKBitmapAdapter (const CKBitmapAdapter& other) + : CKBitmapAdapter() +{ + CK_BITMAP_UNION (&m_map, &other.m_map); +} +#endif + + +class ConcurrentBitsetAdapter + : public CxxUtils::ConcurrentBitset +{ +public: + ConcurrentBitsetAdapter() : CxxUtils::ConcurrentBitset (2048) {} + static std::string name() { return "ConcurrentBitset"; } +}; + + +class Timer +{ +public: + Timer(); + + class RunTimer + { + public: + RunTimer (boost::timer::cpu_timer& timer) : m_timer (&timer) + { timer.resume(); } + RunTimer (RunTimer&& other) : m_timer (other.m_timer) { other.m_timer = nullptr; } + ~RunTimer() { if (m_timer) m_timer->stop(); } + private: + boost::timer::cpu_timer* m_timer; + }; + RunTimer run() { return RunTimer (m_timer); } + + std::string format() const { return m_timer.format(3); } + +private: + boost::timer::cpu_timer m_timer; +}; + + +Timer::Timer() +{ + m_timer.stop(); +} + + +class TesterBase +{ +public: + TesterBase (const TestVectors& tv); + + const TestVectors& tv() const { return m_tv; } + + Timer::RunTimer run_fill_timer() { return m_fill_timer.run(); } + Timer::RunTimer run_copy_timer() { return m_copy_timer.run(); } + Timer::RunTimer run_iterate_timer() { return m_iterate_timer.run(); } + Timer::RunTimer run_lookup_timer() { return m_lookup_timer.run(); } + + void report(); + +private: + const TestVectors& m_tv; + Timer m_fill_timer; + Timer m_copy_timer; + Timer m_iterate_timer; + Timer m_lookup_timer; +}; + + +TesterBase::TesterBase (const TestVectors& tv) + : m_tv (tv) +{ +} + + +void TesterBase::report() +{ + std::cout << "fill: " << m_fill_timer.format(); + std::cout << "copy: " << m_copy_timer.format(); + std::cout << "iterate: " << m_iterate_timer.format(); + std::cout << "lookup: " << m_lookup_timer.format(); +} + + +template <class CONT> +class Tester + : public TesterBase +{ +public: + static const int NEACH = 1000; + + Tester (const TestVectors& tv); + void fill_test(); + void copy_test(); + size_t iterate_test(); + int lookup_test(); + + void test(); + std::string name() { return CONT::name(); } + +private: + std::vector<CONT> m_conts; +}; + + +template <class CONT> +Tester<CONT>::Tester (const TestVectors& tv) + : TesterBase (tv), + m_conts (tv.size()) +{ + for (size_t i = 0; i < tv.size(); i++) { + const TestVectors::vec_t& v = tv.vec(i); + for (size_t id : v) { + m_conts[i].insert (id); + } + } +} + + +template <class CONT> +void Tester<CONT>::fill_test() +{ + auto timer = run_fill_timer(); + for (int i=0; i < NEACH; i++) { + for (const TestVectors::vec_t& vec : tv()) { + CONT cont; + for (size_t id : vec) { + cont.insert (id); + } + } + } +} + + +template <class CONT> +void Tester<CONT>::copy_test() +{ + auto timer = run_copy_timer(); + for (const CONT& cont : m_conts) { + for (int i=0; i < NEACH; i++) { + CONT new_cont (cont); + } + } +} + + +template <class CONT> +size_t Tester<CONT>::iterate_test() +{ + size_t tot = 0; + auto timer = run_iterate_timer(); + for (int i=0; i < NEACH*10 * 10; i++) { + for (const CONT& cont : m_conts) { + for (size_t id : cont) { + tot += id; + } + } + } + return tot; +} + +template <class CONT> +int Tester<CONT>::lookup_test() +{ + int tot = 0; + auto timer = run_lookup_timer(); + size_t sz = m_conts.size(); + for (int j=0; j < NEACH; j++) { + for (size_t icont = 0; icont < sz; icont++) { + const CONT& cont = m_conts[icont]; + const TestVectors::vec_t& vec = tv().vec(icont); + for (size_t id : vec) { + tot += cont.test (id); + tot += cont.test (id+1); + } + } + } + return tot; +} + + +template <class CONT> +void Tester<CONT>::test() +{ + fill_test(); + copy_test(); + iterate_test(); + lookup_test(); +} + + +template <class CONT> +void perftest_one (const TestVectors& tv) +{ + Tester<CONT> tester (tv); + std::cout << tester.name() << "\n"; + tester.test(); + tester.report(); +} + + +void perftest (const char* setfile) +{ + TestVectors tv; + tv.read (setfile); + + perftest_one<SetAdapter> (tv); + perftest_one<USetAdapter> (tv); + perftest_one<CUSetAdapter> (tv); +#ifdef HAVE_CK + perftest_one<CKHSAdapter> (tv); + perftest_one<CKBitmapAdapter> (tv); +#endif + perftest_one<ConcurrentBitsetAdapter> (tv); +} + + +int main (int argc, char** argv) +{ + if (argc > 2 && strcmp (argv[1], "--perf") == 0) { + perftest (argv[2]); + return 0; + } + + test_basic(); + test_reference(); + test_iterate(); + test_find(); + test_insert(); + test_copy(); + test_initlist(); + test_compare(); + test_operators(); + test_anyall(); + test_mt(); + return 0; +} + + diff --git a/Control/CxxUtils/test/atomic_fetch_minmax_test.cxx b/Control/CxxUtils/test/atomic_fetch_minmax_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2415ad3b536effa8a2804529fe5251432a64b503 --- /dev/null +++ b/Control/CxxUtils/test/atomic_fetch_minmax_test.cxx @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration. + */ +// $Id$ +/** + * @file CxxUtils/atomic_fetch_minmax_test.h + * @author scott snyder <snyder@bnl.gov> + * @date Nov, 2017 + * @brief Unit tests for atomic_fetch_min/max. + */ + + +#undef NDEBUG +#include "CxxUtils/atomic_fetch_minmax.h" +#include "TestTools/random.h" +#include <mutex> +#include <thread> +#include <shared_mutex> +#include <limits> +#include <iostream> +#include <cassert> + +using CxxUtils::atomic_fetch_max; +using CxxUtils::atomic_fetch_min; + + +template <class T> +void test1_test() +{ + std::atomic<T> v; + v = 10; + assert (atomic_fetch_max (&v, static_cast<T>(20)) == 10); + assert (v == 20); + assert (atomic_fetch_max (&v, static_cast<T>(15)) == 20); + assert (v == 20); + assert (atomic_fetch_min (&v, static_cast<T>(10)) == 20); + assert (v == 10); + assert (atomic_fetch_min (&v, static_cast<T>(15)) == 10); + assert (v == 10); +} + + +void test1() +{ + std::cout << "test1\n"; + + test1_test<char>(); + test1_test<signed char>(); + test1_test<unsigned char>(); + test1_test<short>(); + test1_test<unsigned short>(); + test1_test<int>(); + test1_test<unsigned int>(); + test1_test<long>(); + test1_test<unsigned long>(); + test1_test<long long>(); + test1_test<unsigned long long>(); + test1_test<char16_t>(); + test1_test<char32_t>(); + test1_test<wchar_t>(); +} + + +std::shared_timed_mutex start_mutex; + + +template <class T> +class test2_Thread +{ +public: + test2_Thread (int ithread, std::atomic<T>& xmin, std::atomic<T>& xmax); + void operator()(); + +private: + int m_ithread; + uint32_t m_seed; + std::atomic<T>& m_xmin; + std::atomic<T>& m_xmax; +}; + + +template <class T> +test2_Thread<T>::test2_Thread (int ithread, std::atomic<T>& xmin, std::atomic<T>& xmax) + : m_ithread (ithread), + m_seed (ithread * 123), + m_xmin (xmin), + m_xmax (xmax) +{ +} + + +template <class T> +void test2_Thread<T>::operator()() +{ + std::shared_lock<std::shared_timed_mutex> lock (start_mutex); + + const int niter = 1000000; + + for (int i=0; i < niter; i++) { + T val = static_cast<T> (Athena_test::rng_seed (m_seed)); + T omax = atomic_fetch_max (&m_xmax, val); + T omin = atomic_fetch_min (&m_xmin, val); + assert (omax <= m_xmax); + assert (omin >= m_xmin); + assert (m_xmax >= val); + assert (m_xmin <= val); + } +} + + +template <class T> +void test2_test() +{ + const int nthread = 4; + std::thread threads[nthread]; + start_mutex.lock(); + + std::atomic<T> xmin; + std::atomic<T> xmax; + + xmin = std::numeric_limits<T>::max(); + xmax = std::numeric_limits<T>::min(); + + threads[0] = std::thread (test2_Thread<T> (0, xmin, xmax)); + threads[1] = std::thread (test2_Thread<T> (1, xmin, xmax)); + threads[2] = std::thread (test2_Thread<T> (2, xmin, xmax)); + threads[3] = std::thread (test2_Thread<T> (3, xmin, xmax)); + + // Try to get the threads starting as much at the same time as possible. + start_mutex.unlock(); + for (int i=0; i < nthread; i++) + threads[i].join(); +} + + +void test2() +{ + std::cout << "test2\n"; + + test2_test<char>(); + test2_test<signed char>(); + test2_test<unsigned char>(); + test2_test<short>(); + test2_test<unsigned short>(); + test2_test<int>(); + test2_test<unsigned int>(); + test2_test<long>(); + test2_test<unsigned long>(); + test2_test<long long>(); + test2_test<unsigned long long>(); + test2_test<char16_t>(); + test2_test<char32_t>(); + test2_test<wchar_t>(); +} + + +int main() +{ + test1(); + test2(); + return 0; +} diff --git a/Control/CxxUtils/test/bitscan_test.cxx b/Control/CxxUtils/test/bitscan_test.cxx index db3d71229822b45da6a78b00bf58b7f106824e80..21277be0d5d289f72a510314377991ccf97fb0d2 100644 --- a/Control/CxxUtils/test/bitscan_test.cxx +++ b/Control/CxxUtils/test/bitscan_test.cxx @@ -34,12 +34,15 @@ int check(const std::vector<T>& v) unsigned lz2 = detail::clz_portable(t); unsigned tz1 = count_trailing_zeros(t); unsigned tz2 = detail::ctz_portable(t); + unsigned nz1 = count_ones(t); + unsigned nz2 = detail::popcount_portable(t); - bool ok = (lz1==lz2) && (tz1==tz2); + bool ok = (lz1==lz2) && (tz1==tz2) && (nz1 == nz2); if (!ok) ++error; std::cout << std::bitset<sizeof(T)*8>(t) << " " << lz1 << "=" << lz2 << " " - << tz1 << "=" << tz2 + << tz1 << "=" << tz2 << " " + << nz1 << "=" << nz2 << (ok ? "" : " ERROR") << std::endl; } @@ -56,10 +59,15 @@ int main() std::vector<unsigned long> t2 = {0x0, 0x1, 0x8000000000000000, 0x12345678cbaa3f00, 0x10000000, 0x1000000000}; + std::vector<unsigned long long> t3 = {0x0, 0x1, 0x8000000000000000, + 0x12345678cbaa3f00, 0x10000000, 0x1000000000}; + int rc(0); rc += check(t1); std::cout << std::endl; rc += check(t2); + std::cout << std::endl; + rc += check(t3); return rc; }