/// @file dpf/twobit.hpp /// @brief `dpf::twobit`, a 2-bit output lane in the ring Z/4Z. /// @details Values are `0..3`. Scalar `+` and `-` wrap mod 4. A leaf node /// packs one lane every two bits, low lane in the low bits of the /// first byte, matching `dpf::bit`. Leaf addition is not XOR: a /// carry stays inside the 2-bit lane. See `packed_lane_arithmetic.hpp`. #ifndef LIBDPF_INCLUDE_DPF_TWOBIT_HPP__ #define LIBDPF_INCLUDE_DPF_TWOBIT_HPP__ #include #include #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/utils.hpp" namespace dpf { /// @brief 2-bit unsigned ring element, packed two bits per lane enum class twobit : std::uint8_t { zero = 0, one = 1, two = 2, three = 3 }; HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::twobit to_twobit(unsigned value) noexcept { return static_cast(value & 3u); } HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::twobit to_twobit(unsigned long long value) noexcept { return static_cast(value & 3ull); } /// @brief parse one character as a 2-bit digit /// @param zero character for 0 (default `'0'`) /// @throws std::domain_error if `value` is not one of the four digits template HEDLEY_ALWAYS_INLINE static constexpr dpf::twobit to_twobit(CharT value, CharT zero = CharT('0')) { auto delta = static_cast(value) - static_cast(zero); if (delta > 3u) { throw std::domain_error("Unrecognized twobit character"); } return static_cast(delta); } inline std::string to_string(dpf::twobit value) { return std::string(1, static_cast('0' + static_cast(value))); } template std::basic_ostream & operator<<(std::basic_ostream & os, dpf::twobit value) { return os << static_cast('0' + static_cast(value)); } template std::basic_istream & operator>>(std::basic_istream & is, dpf::twobit & value) { try { value = to_twobit(is.get(), is.widen('0')); } catch (const std::exception &) { is.setstate(std::ios::failbit); } return is; } /// @brief addition in Z/4Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::twobit operator+(dpf::twobit lhs, dpf::twobit rhs) noexcept { return static_cast( (static_cast(lhs) + static_cast(rhs)) & 3u); } /// @brief subtraction in Z/4Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::twobit operator-(dpf::twobit lhs, dpf::twobit rhs) noexcept { return static_cast( (static_cast(lhs) - static_cast(rhs)) & 3u); } /// @brief additive inverse in Z/4Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::twobit operator-(dpf::twobit value) noexcept { return static_cast((0u - static_cast(value)) & 3u); } /// @brief multiplication in Z/4Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::twobit operator*(dpf::twobit lhs, dpf::twobit rhs) noexcept { return static_cast( (static_cast(lhs) * static_cast(rhs)) & 3u); } namespace utils { 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 {}; template <> struct packed_lane_bits : public std::integral_constant {}; template <> struct make_from_integral_value { using integral_type = std::uint8_t; HEDLEY_NO_THROW constexpr dpf::twobit operator()(integral_type val) const noexcept { return dpf::to_twobit(val); } }; } // namespace utils namespace literals { namespace twobit { constexpr dpf::twobit operator""_twobit(unsigned long long x) { return dpf::to_twobit(x); } } // namespace twobit } // namespace literals } // namespace dpf namespace std { template <> class numeric_limits : public numeric_limits { public: static constexpr int digits = 2; static constexpr int digits10 = 0; HEDLEY_NO_THROW static constexpr dpf::twobit min() noexcept { return dpf::twobit::zero; } HEDLEY_NO_THROW static constexpr dpf::twobit max() noexcept { return dpf::twobit::three; } HEDLEY_NO_THROW static constexpr dpf::twobit lowest() noexcept { return min(); } }; template <> class numeric_limits : public numeric_limits {}; template <> class numeric_limits : public numeric_limits {}; template <> class numeric_limits : public numeric_limits {}; } // namespace std #endif // LIBDPF_INCLUDE_DPF_TWOBIT_HPP__