Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:55:03

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/cont/Bitset.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <climits>
0010 #include <cstdint>
0011 #include <type_traits>
0012 
0013 #include "corecel/Config.hh"
0014 
0015 #include "corecel/Assert.hh"
0016 #include "corecel/Macros.hh"
0017 #include "corecel/Types.hh"
0018 #include "corecel/math/Algorithms.hh"
0019 
0020 namespace celeritas
0021 {
0022 //---------------------------------------------------------------------------//
0023 /*!
0024  * Device-compatible bitset implementation.
0025  *
0026  * This implementation is based on libstdc++'s std::bitset implementation.
0027  * It it a subset of the C++ standard, but it should be sufficient
0028  * for our current use case. Given that GPU typically use 32-bit words, this
0029  * uses unsigned int as the word type instead of the unsigned long used by the
0030  * standard library. This container is not thread-safe, multiple threads are
0031  * likely to manipulate the same word, even when accessing different indices.
0032  *
0033  * The following methods are not implemented:
0034  * - conversions to string, to_ulong, to_ullong
0035  * - stream operators
0036  * - shift operators
0037  * - hash support
0038  * - construct from string, from_ullong
0039  */
0040 template<size_type N>
0041 class Bitset
0042 {
0043     static_assert(N > 0, "Zero-sized bitset is not supported");
0044 
0045   public:
0046     //!@{
0047     //! \name Type aliases
0048     using word_type = std::conditional_t<
0049         (N <= 8),
0050         std::uint8_t,
0051         std::conditional_t<(N <= 16), std::uint16_t, size_type>>;
0052     //!@}
0053 
0054     class reference;
0055 
0056   public:
0057     //// CONSTRUCTORS ////
0058 
0059     // Default construct with zeros for all bits
0060     constexpr Bitset() = default;
0061 
0062     // Construct implicitly from a bitset encoded as an integer
0063     CELER_CONSTEXPR_FUNCTION Bitset(word_type value) noexcept;
0064 
0065     //// COMPARISON ////
0066 
0067     // Test equality with another bitset
0068     CELER_CONSTEXPR_FUNCTION bool
0069     operator==(Bitset const& other) const noexcept;
0070 
0071     // Test equality with another bitset
0072     CELER_CONSTEXPR_FUNCTION bool
0073     operator!=(Bitset const& other) const noexcept;
0074 
0075     //// ACCESSORS ////
0076 
0077     // Access to a single bit
0078     CELER_CONSTEXPR_FUNCTION bool operator[](size_type pos) const
0079         noexcept(!CELERITAS_DEBUG);
0080 
0081     // Mutable access to a single bit
0082     CELER_CONSTEXPR_FUNCTION reference
0083     operator[](size_type pos) noexcept(!CELERITAS_DEBUG);
0084 
0085     // Check if all bits are set
0086     CELER_CONSTEXPR_FUNCTION bool all() const noexcept;
0087 
0088     // Check if any bits are set
0089     CELER_CONSTEXPR_FUNCTION bool any() const noexcept;
0090 
0091     // Check if no bits are set
0092     CELER_CONSTEXPR_FUNCTION bool none() const noexcept;
0093 
0094     // Number of bits set to true
0095     CELER_CONSTEXPR_FUNCTION size_type count() const noexcept;
0096 
0097     //! Number of bits in the bitset
0098     CELER_CONSTEXPR_FUNCTION size_type size() const noexcept { return N; }
0099 
0100     //// MUTATORS ////
0101 
0102     // Bitwise AND with another bitset
0103     CELER_CONSTEXPR_FUNCTION Bitset& operator&=(Bitset const& other) noexcept;
0104 
0105     // Bitwise OR with another bitset
0106     CELER_CONSTEXPR_FUNCTION Bitset& operator|=(Bitset const& other) noexcept;
0107 
0108     // Bitwise XOR with another bitset
0109     CELER_CONSTEXPR_FUNCTION Bitset& operator^=(Bitset const& other) noexcept;
0110 
0111     // Return a copy with all bits flipped (bianry NOT)
0112     CELER_CONSTEXPR_FUNCTION Bitset operator~() const noexcept;
0113 
0114     // Set all bits
0115     CELER_CONSTEXPR_FUNCTION Bitset& set() noexcept;
0116 
0117     // Set the bit at position pos
0118     CELER_CONSTEXPR_FUNCTION Bitset&
0119     set(size_type pos, bool value = true) noexcept(!CELERITAS_DEBUG);
0120 
0121     // Reset all bits
0122     CELER_CONSTEXPR_FUNCTION Bitset& reset() noexcept;
0123 
0124     // Reset the bit at position pos
0125     CELER_CONSTEXPR_FUNCTION Bitset&
0126     reset(size_type pos) noexcept(!CELERITAS_DEBUG);
0127 
0128     // Flip all bits
0129     CELER_CONSTEXPR_FUNCTION Bitset& flip() noexcept;
0130 
0131     // Flip the bit at position pos
0132     CELER_CONSTEXPR_FUNCTION Bitset&
0133     flip(size_type pos) noexcept(!CELERITAS_DEBUG);
0134 
0135   private:
0136     //// CONSTANTS ////
0137 
0138     static constexpr size_type bits_per_word_ = CHAR_BIT * sizeof(word_type);
0139     static constexpr size_type num_words_ = ceil_div(N, bits_per_word_);
0140 
0141     //// DATA ////
0142 
0143     //! Storage, default-initialized to zero
0144     word_type words_[num_words_]{};
0145 
0146     //// HELPER FUNCTIONS ////
0147 
0148     // Find the word index for a given bit position
0149     static CELER_CONSTEXPR_FUNCTION size_type which_word(size_type pos) noexcept;
0150     // Find the bit index in a word
0151     static CELER_CONSTEXPR_FUNCTION size_type which_bit(size_type pos) noexcept;
0152 
0153     // Create a mask for a given bit index
0154     static CELER_CONSTEXPR_FUNCTION word_type mask(size_type pos) noexcept;
0155 
0156     // Create a negative mask for a given bit index
0157     static CELER_CONSTEXPR_FUNCTION word_type neg_mask(size_type pos) noexcept;
0158 
0159     // Get the word for a given bit position
0160     CELER_CONSTEXPR_FUNCTION word_type get_word(size_type pos) const
0161         noexcept(!CELERITAS_DEBUG);
0162 
0163     // Get the word for a given bit position
0164     CELER_CONSTEXPR_FUNCTION word_type&
0165     get_word(size_type pos) noexcept(!CELERITAS_DEBUG);
0166 
0167     // Get the last word of the bitset
0168     CELER_CONSTEXPR_FUNCTION word_type& last_word() noexcept;
0169 
0170     // Get the last word of the bitset
0171     CELER_CONSTEXPR_FUNCTION word_type last_word() const noexcept;
0172 
0173     // Clear unused bits from the last word
0174     CELER_CONSTEXPR_FUNCTION void sanitize() noexcept;
0175 };
0176 
0177 //---------------------------------------------------------------------------//
0178 // Bitset::reference definitions
0179 //---------------------------------------------------------------------------//
0180 /*!
0181  * Reference to a single bit in the bitset.
0182  *
0183  * This is used to implement the mutable operator[].
0184  */
0185 template<size_type N>
0186 class Bitset<N>::reference
0187 {
0188   public:
0189     CELER_CONSTEXPR_FUNCTION
0190     reference(Bitset& b, size_type pos) noexcept
0191         : word_pointer_(&b.get_word(pos)), bit_pos_(Bitset::which_bit(pos))
0192     {
0193     }
0194 
0195     constexpr reference(reference const&) = default;
0196 
0197     ~reference() noexcept = default;
0198 
0199     //! Assignment for b[i] = x;
0200     CELER_CONSTEXPR_FUNCTION
0201     reference& operator=(bool x) noexcept
0202     {
0203         if (x)
0204         {
0205             *word_pointer_ |= Bitset::mask(bit_pos_);
0206         }
0207         else
0208         {
0209             *word_pointer_ &= Bitset::neg_mask(bit_pos_);
0210         }
0211         return *this;
0212     }
0213 
0214     //! Assignment for b[i] = b[j];
0215     CELER_CONSTEXPR_FUNCTION
0216     reference& operator=(reference const& j) noexcept
0217     {
0218         if (this != &j)
0219         {
0220             if (*j.word_pointer_ & Bitset::mask(j.bit_pos_))
0221             {
0222                 *word_pointer_ |= Bitset::mask(bit_pos_);
0223             }
0224             else
0225             {
0226                 *word_pointer_ &= Bitset::neg_mask(bit_pos_);
0227             }
0228         }
0229         return *this;
0230     }
0231 
0232     //! Flips the bit
0233     CELER_CONSTEXPR_FUNCTION
0234     bool operator~() const noexcept { return !static_cast<bool>(*this); }
0235 
0236     //! Conversion for bool x = b[i];
0237     CELER_CONSTEXPR_FUNCTION
0238     operator bool() const noexcept
0239     {
0240         return (*word_pointer_ & Bitset::mask(bit_pos_)) != 0;
0241     }
0242 
0243     //! To support b[i].flip();
0244     CELER_CONSTEXPR_FUNCTION
0245     reference& flip() noexcept
0246     {
0247         *word_pointer_ ^= Bitset::mask(bit_pos_);
0248         return *this;
0249     }
0250 
0251   private:
0252     word_type* word_pointer_{nullptr};
0253     size_type bit_pos_{0};
0254 };
0255 
0256 //---------------------------------------------------------------------------//
0257 // INLINE MEMBER FUNCTION DEFINITIONS
0258 //---------------------------------------------------------------------------//
0259 //! Construct from a word value
0260 template<size_type N>
0261 CELER_CONSTEXPR_FUNCTION Bitset<N>::Bitset(word_type value) noexcept
0262     : words_{value}
0263 {
0264     if constexpr (num_words_ == 1)
0265     {
0266         this->sanitize();
0267     }
0268 }
0269 
0270 //---------------------------------------------------------------------------//
0271 //! Compare for equality
0272 template<size_type N>
0273 CELER_CONSTEXPR_FUNCTION bool
0274 Bitset<N>::operator==(Bitset const& other) const noexcept
0275 {
0276     for (size_type i = 0; i < num_words_; ++i)
0277     {
0278         if (words_[i] != other.words_[i])
0279         {
0280             return false;
0281         }
0282     }
0283     return true;
0284 }
0285 
0286 //---------------------------------------------------------------------------//
0287 //! Compare for inequality
0288 template<size_type N>
0289 CELER_CONSTEXPR_FUNCTION bool
0290 Bitset<N>::operator!=(Bitset const& other) const noexcept
0291 {
0292     return !(*this == other);
0293 }
0294 
0295 //---------------------------------------------------------------------------//
0296 //! Access a single bit
0297 template<size_type N>
0298 CELER_CONSTEXPR_FUNCTION bool Bitset<N>::operator[](size_type pos) const
0299     noexcept(!CELERITAS_DEBUG)
0300 {
0301     CELER_EXPECT(pos < N);
0302     return (this->get_word(pos) & Bitset::mask(pos))
0303            != static_cast<word_type>(0);
0304 }
0305 
0306 //---------------------------------------------------------------------------//
0307 //! Mutable access to a single bit
0308 template<size_type N>
0309 CELER_CONSTEXPR_FUNCTION auto
0310 Bitset<N>::operator[](size_type pos) noexcept(!CELERITAS_DEBUG) -> reference
0311 {
0312     CELER_EXPECT(pos < N);
0313     return reference(*this, pos);
0314 }
0315 
0316 //---------------------------------------------------------------------------//
0317 //! Check if all bits are set
0318 template<size_type N>
0319 CELER_CONSTEXPR_FUNCTION bool Bitset<N>::all() const noexcept
0320 {
0321     for (size_type i = 0; i < num_words_ - 1; ++i)
0322     {
0323         if (words_[i] != static_cast<word_type>(~word_type(0)))
0324         {
0325             return false;
0326         }
0327     }
0328 
0329     // Only compare the last word up to the last bit of the bitset
0330     return this->last_word()
0331            == (static_cast<word_type>(~word_type(0))
0332                >> (num_words_ * bits_per_word_ - N));
0333 }
0334 
0335 //---------------------------------------------------------------------------//
0336 //! Check if any bits are set
0337 template<size_type N>
0338 CELER_CONSTEXPR_FUNCTION bool Bitset<N>::any() const noexcept
0339 {
0340     for (size_type i = 0; i < num_words_; ++i)
0341     {
0342         if (words_[i] != word_type(0))
0343         {
0344             return true;
0345         }
0346     }
0347 
0348     return false;
0349 }
0350 
0351 //---------------------------------------------------------------------------//
0352 //! Check if no bits are set
0353 template<size_type N>
0354 CELER_CONSTEXPR_FUNCTION bool Bitset<N>::none() const noexcept
0355 {
0356     return !this->any();
0357 }
0358 
0359 //---------------------------------------------------------------------------//
0360 //! Number of bits set to true
0361 template<size_type N>
0362 CELER_CONSTEXPR_FUNCTION size_type Bitset<N>::count() const noexcept
0363 {
0364     size_type count = 0;
0365     for (size_type i = 0; i < num_words_; ++i)
0366     {
0367         count += celeritas::popcount(words_[i]);
0368     }
0369 
0370     return count;
0371 }
0372 
0373 //---------------------------------------------------------------------------//
0374 //! Bitwise AND with another bitset
0375 template<size_type N>
0376 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0377 Bitset<N>::operator&=(Bitset const& other) noexcept
0378 {
0379     for (size_type i = 0; i < num_words_; ++i)
0380     {
0381         words_[i] &= other.words_[i];
0382     }
0383     return *this;
0384 }
0385 
0386 //---------------------------------------------------------------------------//
0387 //! Bitwise OR with another bitset
0388 template<size_type N>
0389 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0390 Bitset<N>::operator|=(Bitset const& other) noexcept
0391 {
0392     for (size_type i = 0; i < num_words_; ++i)
0393     {
0394         words_[i] |= other.words_[i];
0395     }
0396     return *this;
0397 }
0398 
0399 //---------------------------------------------------------------------------//
0400 //! Bitwise XOR with another bitset
0401 template<size_type N>
0402 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0403 Bitset<N>::operator^=(Bitset const& other) noexcept
0404 {
0405     for (size_type i = 0; i < num_words_; ++i)
0406     {
0407         words_[i] ^= other.words_[i];
0408     }
0409     return *this;
0410 }
0411 
0412 //---------------------------------------------------------------------------//
0413 //! Return a copy with all bits flipped (bianry NOT)
0414 template<size_type N>
0415 CELER_CONSTEXPR_FUNCTION Bitset<N> Bitset<N>::operator~() const noexcept
0416 {
0417     return Bitset{*this}.flip();
0418 }
0419 
0420 //---------------------------------------------------------------------------//
0421 //! Set all bits
0422 template<size_type N>
0423 CELER_CONSTEXPR_FUNCTION Bitset<N>& Bitset<N>::set() noexcept
0424 {
0425     for (size_type i = 0; i < num_words_; ++i)
0426     {
0427         words_[i] = static_cast<word_type>(~word_type(0));
0428     }
0429 
0430     // Clear unused bits on the last word
0431     this->sanitize();
0432 
0433     return *this;
0434 }
0435 
0436 //---------------------------------------------------------------------------//
0437 //! Set the bit at position pos
0438 template<size_type N>
0439 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0440 Bitset<N>::set(size_type pos, bool value) noexcept(!CELERITAS_DEBUG)
0441 {
0442     CELER_EXPECT(pos < N);
0443     (*this)[pos] = value;
0444     return *this;
0445 }
0446 
0447 //---------------------------------------------------------------------------//
0448 //! Reset all bits
0449 template<size_type N>
0450 CELER_CONSTEXPR_FUNCTION Bitset<N>& Bitset<N>::reset() noexcept
0451 {
0452     for (size_type i = 0; i < num_words_; ++i)
0453     {
0454         words_[i] = word_type(0);
0455     }
0456 
0457     return *this;
0458 }
0459 
0460 //---------------------------------------------------------------------------//
0461 //! Reset the bit at position pos
0462 template<size_type N>
0463 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0464 Bitset<N>::reset(size_type pos) noexcept(!CELERITAS_DEBUG)
0465 {
0466     CELER_EXPECT(pos < N);
0467     this->get_word(pos) &= Bitset::neg_mask(pos);
0468     return *this;
0469 }
0470 
0471 //---------------------------------------------------------------------------//
0472 //! Flip all bits
0473 template<size_type N>
0474 CELER_CONSTEXPR_FUNCTION Bitset<N>& Bitset<N>::flip() noexcept
0475 {
0476     for (size_type i = 0; i < num_words_; ++i)
0477     {
0478         words_[i] = ~words_[i];
0479     }
0480 
0481     // Clear unused bits on the last word
0482     this->sanitize();
0483 
0484     return *this;
0485 }
0486 
0487 //---------------------------------------------------------------------------//
0488 //! Flip the bit at position pos
0489 template<size_type N>
0490 CELER_CONSTEXPR_FUNCTION Bitset<N>&
0491 Bitset<N>::flip(size_type pos) noexcept(!CELERITAS_DEBUG)
0492 {
0493     CELER_EXPECT(pos < N);
0494     this->get_word(pos) ^= Bitset::mask(pos);
0495     return *this;
0496 }
0497 
0498 //---------------------------------------------------------------------------//
0499 //! Find the word index for a given bit position
0500 template<size_type N>
0501 CELER_CONSTEXPR_FUNCTION size_type Bitset<N>::which_word(size_type pos) noexcept
0502 {
0503     return pos / bits_per_word_;
0504 }
0505 
0506 //---------------------------------------------------------------------------//
0507 //! Find the bit index in a word
0508 template<size_type N>
0509 CELER_CONSTEXPR_FUNCTION size_type Bitset<N>::which_bit(size_type pos) noexcept
0510 {
0511     return pos % bits_per_word_;
0512 }
0513 
0514 //---------------------------------------------------------------------------//
0515 //! Create a mask for a given bit index
0516 template<size_type N>
0517 CELER_CONSTEXPR_FUNCTION auto
0518 Bitset<N>::mask(size_type pos) noexcept -> word_type
0519 {
0520     return word_type(1) << Bitset::which_bit(pos);
0521 }
0522 
0523 //---------------------------------------------------------------------------//
0524 /*!
0525  * Create a negative mask (a single 0 bit) for a given bit index. The purpose
0526  * of this function is to cast a potentially promoted word_type (from ~) back
0527  * to the original word_type.
0528  */
0529 template<size_type N>
0530 CELER_CONSTEXPR_FUNCTION auto
0531 Bitset<N>::neg_mask(size_type pos) noexcept -> word_type
0532 {
0533     return ~(word_type(1) << Bitset::which_bit(pos));
0534 }
0535 
0536 //---------------------------------------------------------------------------//
0537 //! Get the word for a given bit position
0538 template<size_type N>
0539 CELER_CONSTEXPR_FUNCTION auto Bitset<N>::get_word(size_type pos) const
0540     noexcept(!CELERITAS_DEBUG) -> word_type
0541 {
0542     CELER_EXPECT(pos < N);
0543     return words_[Bitset::which_word(pos)];
0544 }
0545 
0546 //---------------------------------------------------------------------------//
0547 //! Get the word for a given bit position
0548 template<size_type N>
0549 CELER_CONSTEXPR_FUNCTION auto
0550 Bitset<N>::get_word(size_type pos) noexcept(!CELERITAS_DEBUG) -> word_type&
0551 {
0552     CELER_EXPECT(pos < N);
0553     return words_[Bitset::which_word(pos)];
0554 }
0555 
0556 //---------------------------------------------------------------------------//
0557 //! Get the last word of the bitset
0558 template<size_type N>
0559 CELER_CONSTEXPR_FUNCTION auto Bitset<N>::last_word() noexcept -> word_type&
0560 {
0561     return words_[num_words_ - 1];
0562 }
0563 
0564 //---------------------------------------------------------------------------//
0565 //! Get the last word of the bitset
0566 template<size_type N>
0567 CELER_CONSTEXPR_FUNCTION auto Bitset<N>::last_word() const noexcept -> word_type
0568 {
0569     return words_[num_words_ - 1];
0570 }
0571 
0572 //---------------------------------------------------------------------------//
0573 //! Clear unused bits in the last word
0574 template<size_type N>
0575 CELER_CONSTEXPR_FUNCTION void Bitset<N>::sanitize() noexcept
0576 {
0577     constexpr size_type extra_bits = N % bits_per_word_;
0578     if constexpr (extra_bits != 0)
0579     {
0580         this->last_word() &= static_cast<word_type>(
0581             ~(static_cast<word_type>(~word_type(0)) << extra_bits));
0582     }
0583 }
0584 
0585 //---------------------------------------------------------------------------//
0586 // FREE FUNCTIONS
0587 //---------------------------------------------------------------------------//
0588 //! Bitwise AND
0589 template<size_type N>
0590 CELER_CONSTEXPR_FUNCTION Bitset<N>
0591 operator&(Bitset<N> const& lhs, Bitset<N> const& rhs)
0592 {
0593     return Bitset{lhs} &= rhs;
0594 }
0595 
0596 //---------------------------------------------------------------------------//
0597 //! Bitwise OR
0598 template<size_type N>
0599 CELER_CONSTEXPR_FUNCTION Bitset<N>
0600 operator|(Bitset<N> const& lhs, Bitset<N> const& rhs)
0601 {
0602     return Bitset{lhs} |= rhs;
0603 }
0604 
0605 //---------------------------------------------------------------------------//
0606 //! Bitwise XOR
0607 template<size_type N>
0608 CELER_CONSTEXPR_FUNCTION Bitset<N>
0609 operator^(Bitset<N> const& lhs, Bitset<N> const& rhs)
0610 {
0611     return Bitset{lhs} ^= rhs;
0612 }
0613 
0614 //---------------------------------------------------------------------------//
0615 }  // namespace celeritas