/// @file dpf/bit.hpp /// @brief defines `dpf::bit` and associated helpers /// @details A `dpf::bit` is a binary type whose representation can be packed /// into one bit. It is implemented as an `enum` with two values: /// `zero` and `one`. This type is intended for us as an [output type](@ref output_types) /// for a DPF, in which case leaf nodes will be packed in much /// the ways as in an `std::bitset` or `std::vector`. /// /// In addition to `dpf::bit`, this file defines three overloaded /// variants of a `dpf::to_bit` function that respectively convert /// a `bool, a `char`, or (the least significant bit of) an `int` to /// a `dpf::bit`. Likewise, it defines `dpf::to_string` to convert /// a `dpf::bit` into an `std::string`. Finally, it overloads stream /// input and output operators (`<<` and `>>`) for `dpf::bit`. /// @author Ryan Henry /// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors) /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see LICENSE.md for details. #ifndef LIBDPF_INCLUDE_DPF_BIT_HPP__ #define LIBDPF_INCLUDE_DPF_BIT_HPP__ #include #include #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/utils.hpp" /// @brief the dpf namespace namespace dpf { /// @brief binary type whose representation can be packed into one bit /// @note Not a DPF domain. `msb_of` does not compile. A 1-bit /// index is `dpf::modint<1>` or `dpf::xint<1>`. Leaf `+` and `-` are /// XOR. Lanes pack low-bit first. /// @see dpf::twobit /// @see dpf::nyble /// @see dpf::modint /// @see dpf::xint /// @see output_types enum bit : bool { zero = false, ///< `0`, `false`, "unset", "off" one = true ///< `1`, `true`, "set", "on" }; /// @brief converts a value to a `dpf::bit` /// @{ /// @brief converts (the lsb of) an `int` to a `dpf::bit` /// @details Convert an `int` to a `dpf::bit`. The resulting `dpf::bit` is /// equal to `dpf::bit::one` if the *least-significant bit* of /// `value` is `1` and `dpf::bit::zero` otherwise. /// @param value the `int` to convert /// @return `static_cast(value & 1)` HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::bit to_bit(int value) noexcept { return static_cast(value & 1); } /// @brief converts the least-significant bit of an integer literal to a `dpf::bit` /// @details This overload exists so `operator""_bit` does not select the /// character converter, which is an exact match for `unsigned long long`. /// @param value the value to convert or store /// @return the returned `dpf::bit` HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::bit to_bit(unsigned long long value) noexcept { return static_cast(value & 1ull); } /// @brief converts a `bool` to a `dpf::bit` /// @details Convert a `bool` to a `dpf::bit`. The resulting `dpf::bit` is /// equal to `dpf::bit::one` if `value==true` and `dpf::bit::zero` /// otherwise. /// @param value the `bool` to convert /// @return `static_cast(value)` HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::bit to_bit(bool value) noexcept { return static_cast(value); } /// @brief converts a character to a `dpf::bit` /// @details Convert a character to a `dpf::bit`. The resulting `dpf::bit` is /// equal to `dpf::bit::one` if `value==one` and `dpf::bit::zero` /// otherwise. /// @tparam CharT character type /// @tparam Traits character traits /// @param value the character to convert /// @param zero character used to represent `0` (default: ``CharT('0')``) /// @param one character used to represent `1` (default: ``CharT('1')``) /// @return `static_cast(0)` if `value==0` or /// `static_cast(1)` if `value==1` /// @throws std::domain_error if `value != zero && value != one` template > HEDLEY_ALWAYS_INLINE static constexpr dpf::bit to_bit( CharT value, CharT zero = CharT('0'), CharT one = CharT('1')) { if (!Traits::eq(value, zero) && !Traits::eq(value, one)) { throw std::domain_error("Unrecognized character"); } return Traits::eq(value, zero) ? dpf::bit::zero : dpf::bit::one; } /// @} /// @brief converts a `dpf::bit` to a `std::basic_string` /// @details Converts the contents of a `dpf::bit` to a `std::string` for /// human-friendly printing. Uses `zero` to represent the value /// `0` and `one` to the value `1`. /// @tparam CharT character type /// @tparam Traits character traits /// @tparam Allocator allocator type /// @param value the `dpf::bit` to convert /// @param zero character to use to represent `false`/`0` (default: ``CharT('0')``) /// @param one character to use to represent `true`/`1` (default: ``CharT('1')``) /// @return `(value == 0) ? zero : one` template , typename Allocator = std::allocator> static std::basic_string to_string( dpf::bit value, CharT zero = CharT('0'), CharT one = CharT('1')) { auto ch = (value == dpf::bit::zero) ? zero : one; return std::basic_string(1, ch, Allocator{}); } /// @brief performs stream input and output on `dpf::bit`s /// @{ /// @brief performs stream output on a `dpf::bit` /// @details Writes a `dpf::bit` to the character stream `os` as if by first /// converting it to a `std::basic_string` using /// `dpf::to_string()`, and then writing it into `os` using `operator<<` /// (which is a `FormattedOutputFunction` for strings). The /// characters to use for zero and one are obtained from the /// currently-imbued locale by calling `os.widen()` with `0` and `1` /// as the arguments. /// @tparam CharT character type /// @tparam Traits character traits /// @param os a character output stream /// @param value the `dpf::bit` to insert into the output stream /// @return `os` template std::basic_ostream & operator<<(std::basic_ostream & os, const dpf::bit & value) { return os << to_string(value, os.widen('0'), os.widen('1')); } /// @brief performs stream input on a `dpf::bit` /// @details Extracts one character from `is` and attempts to convert it to /// a `dpf::bit` using `dpf::to_bit()`. If successful, the result is /// stored in `value`. The characters to use for zero and one are /// obtained from the currently-imbued locale by calling `is.widen()` /// with `0` and `1` as the arguments. /// @tparam CharT character type /// @tparam Traits character traits /// @param is a character input stream /// @param value the `dpf::bit` to extract from the input stream /// @return `is` template std::basic_istream & operator>>(std::basic_istream & is, dpf::bit & value) { try { value = to_bit(is.get(), is.widen('0'), is.widen('1')); } catch(const std::exception & e) { is.setstate(std::ios::failbit); } return is; } /// @} HEDLEY_NO_THROW inline constexpr dpf::bit operator+(dpf::bit lhs, dpf::bit rhs) noexcept { return static_cast(static_cast(lhs) ^ static_cast(rhs)); } /// @brief GF(2) subtraction. Identical to `operator+`. /// @param lhs the left-hand operand /// @param rhs the right-hand operand /// @return GF(2) subtraction HEDLEY_NO_THROW inline constexpr dpf::bit operator-(dpf::bit lhs, dpf::bit rhs) noexcept { return lhs + rhs; } namespace utils { /// @brief specializes `dpf::utils::bitlength_of` for `dpf::bit` template <> struct bitlength_of : public std::integral_constant { }; template struct bitlength_of_output : public std::integral_constant { }; template <> struct is_packed_subbyte : std::true_type {}; /// @brief Packed bits add by XOR (`bit::one + bit::one == bit::zero`). template <> struct has_characteristic_two : std::true_type {}; template <> struct packed_lane_bits : public std::integral_constant {}; template <> struct make_from_integral_value { HEDLEY_NO_THROW constexpr dpf::bit operator()(bool val) const noexcept { return val ? dpf::bit::one : dpf::bit::zero; } }; } // namespace utils namespace literals { namespace bit { constexpr static auto operator "" _bit(unsigned long long int x) { return dpf::to_bit(x); } } // namespace bit } // namespace literals } // namespace dpf namespace std { /// @{ /// @brief specializes `std::numeric_limits` for `dpf::bit` template<> class numeric_limits : public numeric_limits { }; /// @brief specializes `std::numeric_limits` for `dpf::bit const` template<> class numeric_limits : public numeric_limits {}; /// @brief specializes `std::numeric_limits` for `dpf::bit volatile` template<> class numeric_limits : public numeric_limits {}; /// @brief specializes `std::numeric_limits` for `dpf::bit const volatile` template<> class numeric_limits : public numeric_limits {}; /// @} } // namespace std #endif // LIBDPF_INCLUDE_DPF_BIT_HPP__