/* * Copyright (c) 2018 Jaroslav Jindrak * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBCPP_BITSET #define LIBCPP_BITSET #include #include namespace std { /** * 20.6, class template bitset: */ template class bitset { public: class reference { friend class bitset; public: ~reference() noexcept = default; reference& operator=(bool val) noexcept { if (val) data_ |= mask_; else data_ &= ~mask_; } reference& operator=(const reference& other) noexcept { if (other) data_ |= mask_; else data_ &= ~mask_; } bool operator~() const noexcept { return data_ & mask_; } operator bool() const noexcept { return data_ & mask_; } reference& flip() noexcept { data_ ^= mask_; return *this; } private: reference() noexcept = delete; using data_type = typename bitset::data_type; data_type& data_; data_type mask_; reference(data_type& data, size_t idx) : data_{data}, mask_{bitset::one_ << idx} { /* DUMMY BODY */ } }; /** * 20.6.1, constructors: */ constexpr bitset() noexcept { init_(zero_); } constexpr bitset(unsigned long long val) noexcept { init_(val); } template explicit bitset( const basic_string& str, typename basic_string::size_type pos = 0, typename basic_string::size_type n = basic_string::npos, Char zero = Char('0'), Char one = Char('1') ) { // TODO: throw out_of_range if pos > str.size() init_(zero_); auto len = n < (str.size() - pos) ? n : (str.size() - pos); len = len < N ? len : N; for (size_t i = 0, j = pos + len - 1; i < len; ++i, --j) { if (Traits::eq(str[j], zero)) set(i, false); else if (Traits::eq(str[j], one)) set(i, true); // TODO: else throw invalid_argument } } template explicit bitset( const Char* str, typename basic_string::size_type n = basic_string::npos, Char zero = Char('0'), Char one = Char('1') ) : bitset{ n == basic_string::npos ? basic_string{str} : basic_string{str, n}, 0, n, zero, one } { /* DUMMY BODY */ } /** * 20.6.2, bitset operations: */ bitset& operator&=(const bitset& rhs) noexcept { for (size_t i = 0; i < data_size_; ++i) data_[i] &= rhs.data_[i]; return *this; } bitset& operator|=(const bitset& rhs) noexcept { for (size_t i = 0; i < data_size_; ++i) data_[i] |= rhs.data_[i]; return *this; } bitset& operator^=(const bitset& rhs) noexcept { for (size_t i = 0; i < data_size_; ++i) data_[i] ^= rhs.data_[i]; return *this; } bitset& operator<<=(size_t pos) noexcept { for (size_t i = N; i > 1; --i) { if (i < pos) set(i - 1, false); else set(i - 1, test(i - 1 - pos)); } return *this; } bitset& operator>>=(size_t pos) noexcept { for (size_t i = 0; i < N; ++i) { if (pos >= N - i) set(i, false); else set(i, test(i + pos)); } return *this; } bitset& set() noexcept { for (size_t i = 0; i < N; ++i) set(i); return *this; } bitset& set(size_t pos, bool val = true) { // TODO: throw out_of_range if pos > N set_bit_(get_data_idx_(pos), get_bit_idx_(pos), val); return *this; } bitset& reset() noexcept { for (size_t i = 0; i < N; ++i) reset(i); return *this; } bitset& reset(size_t pos) { set_bit_(get_data_idx_(pos), get_bit_idx_(pos), false); return *this; } bitset operator~() const noexcept { bitset res{*this}; return res.flip(); } bitset& flip() noexcept { for (size_t i = 0; i < N; ++i) flip(i); return *this; } bitset& flip(size_t pos) { return set(pos, !test(pos)); } constexpr bool operator[](size_t pos) const { /** * Note: The standard doesn't mention any exception * here being thrown at any time, so we shouldn't * use test(). */ return get_bit_(data_[get_data_idx_(pos)], get_bit_idx_(pos)); } reference operator[](size_t pos) { return reference{data_[get_data_idx_(pos)], get_bit_idx_(pos)}; } unsigned long to_ulong() const { // TODO: throw overflow_error if N > bits in ulong return static_cast(data_[0] & (~0UL)); } unsigned long long to_ullong() const { // TODO: throw overflow_error if N > bits_in_data_type_ return data_[0]; } template< class Char = char, class Traits = char_traits, class Allocator = allocator > basic_string to_string(Char zero = Char('0'), Char one = Char('1')) const { basic_string res{}; res.reserve(N); for (size_t i = N; i > 0; --i) { if (test(i - 1)) res.push_back(one); else res.push_back(zero); } return res; } size_t count() const noexcept { size_t res{}; for (size_t i = 0; i < N; ++i) { if (test(i)) ++res; } return res; } constexpr size_t size() const noexcept { return N; } bool operator==(const bitset& rhs) const noexcept { for (size_t i = 0; i < data_size_; ++i) { if (data_[i] != rhs.data_[i]) return false; } return true; } bool operator!=(const bitset& rhs) const noexcept { return !(*this == rhs); } bool test(size_t pos) const { // TODO: throw out of range if pos > N return get_bit_(data_[get_data_idx_(pos)], get_bit_idx_(pos)); } bool all() const noexcept { return count() == size(); } bool any() const noexcept { return count() != 0; } bool none() const noexcept { return count() == 0; } bitset operator<<(size_t pos) const noexcept { return bitset{*this} <<= pos; } bitset operator>>(size_t pos) const noexcept { return bitset{*this} >>= pos; } private: /** * While this might be a bit more wasteful * than using unsigned or unsigned long, * it will make parts of out code easier * to read. */ using data_type = unsigned long long; static constexpr size_t bits_in_data_type_ = sizeof(data_type) * 8; static constexpr size_t data_size_ = N / bits_in_data_type_ + 1; /** * These are useful for masks, if we use literals * then forgetting to add ULL or changing the data * type could result in wrong indices being computed. */ static constexpr data_type zero_ = data_type{0}; static constexpr data_type one_ = data_type{1}; data_type data_[data_size_]; void init_(data_type val) { data_type mask{~zero_}; if (N < bits_in_data_type_) mask >>= N; data_[0] = val & mask; for (size_t i = 1; i < data_size_; ++i) data_[i] = data_type{}; } size_t get_data_idx_(size_t pos) const { return pos / bits_in_data_type_; } size_t get_bit_idx_(size_t pos) const { return pos % bits_in_data_type_; } bool get_bit_(data_type data, size_t bit_idx) const { return data & (one_ << bit_idx); } void set_bit_(size_t data_idx, size_t bit_idx, bool val) { if (val) data_[data_idx] |= (one_ << bit_idx); else data_[data_idx] &= ~(one_ << bit_idx); } }; /** * 20.6.3 hash support: */ template struct hash; template struct hash> { size_t operator()(const bitset& set) const noexcept { return hash{}(set.to_ullong()); } using argument_type = bitset; using result_type = size_t; }; /** * 20.6.4, bitset operators: */ template bitset operator&(const bitset& lhs, const bitset& rhs) noexcept { return bitset{lhs} &= rhs; } template bitset operator|(const bitset& lhs, const bitset& rhs) noexcept { return bitset{lhs} |= rhs; } template bitset operator^(const bitset& lhs, const bitset& rhs) noexcept { return bitset{lhs} ^= rhs; } template basic_istream& operator>>(basic_istream& is, bitset& set) { size_t i{}; Char c{}; Char zero{is.widen('0')}; Char one{is.widen('1')}; basic_string str{}; while (i < N) { if (is.eof()) break; is.get(c); if (!Traits::eq(c, one) && !Traits::eq(c, zero)) { is.putback(c); break; } str.push_back(c); ++i; } if (i == 0) is.setstate(ios_base::failbit); set = bitset{str}; return is; } template basic_ostream& operator<<(basic_ostream& os, const bitset& set) { return os << set.template to_string>( use_facet>(os.getloc()).widen('0'), use_facet>(os.getloc()).widen('1') ); } } #endif