/// @file dpf/nyble.hpp /// @brief `dpf::nyble`, a 4-bit output lane in the ring Z/16Z. /// @details Values are `0..15`. Scalar `+` and `-` wrap mod 16. A leaf node /// packs one lane every four bits, low nibble first. Leaf addition /// is not XOR and is not `add_epi8`: a carry must not cross into /// the neighbouring nibble. See `packed_lane_arithmetic.hpp`. #ifndef LIBDPF_INCLUDE_DPF_NYBLE_HPP__ #define LIBDPF_INCLUDE_DPF_NYBLE_HPP__ #include #include #include #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/utils.hpp" namespace dpf { /// @brief 4-bit unsigned ring element, packed one nibble per lane enum class nyble : std::uint8_t { zero = 0 }; HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::nyble to_nyble(unsigned value) noexcept { return static_cast(value & 0x0fu); } HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE static constexpr dpf::nyble to_nyble(unsigned long long value) noexcept { return static_cast(value & 0x0full); } /// @brief parse one hex digit as a nibble /// @throws std::domain_error if `value` is not `0-9`, `a-f`, or `A-F` template static constexpr dpf::nyble to_nyble(CharT value) { auto u = static_cast(value); if (u >= static_cast(CharT('0')) && u <= static_cast(CharT('9'))) { return static_cast(u - static_cast(CharT('0'))); } if (u >= static_cast(CharT('a')) && u <= static_cast(CharT('f'))) { return static_cast(10u + u - static_cast(CharT('a'))); } if (u >= static_cast(CharT('A')) && u <= static_cast(CharT('F'))) { return static_cast(10u + u - static_cast(CharT('A'))); } throw std::domain_error("Unrecognized nyble character"); } inline std::string to_string(dpf::nyble value) { constexpr char digits[] = "0123456789abcdef"; return std::string(1, digits[static_cast(value) & 0x0fu]); } template std::basic_ostream & operator<<(std::basic_ostream & os, dpf::nyble value) { constexpr char digits[] = "0123456789abcdef"; return os << digits[static_cast(value) & 0x0fu]; } template std::basic_istream & operator>>(std::basic_istream & is, dpf::nyble & value) { try { value = to_nyble(is.get()); } catch (const std::exception &) { is.setstate(std::ios::failbit); } return is; } /// @brief addition in Z/16Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::nyble operator+(dpf::nyble lhs, dpf::nyble rhs) noexcept { return static_cast( (static_cast(lhs) + static_cast(rhs)) & 0x0fu); } /// @brief subtraction in Z/16Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::nyble operator-(dpf::nyble lhs, dpf::nyble rhs) noexcept { return static_cast( (static_cast(lhs) - static_cast(rhs)) & 0x0fu); } /// @brief additive inverse in Z/16Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::nyble operator-(dpf::nyble value) noexcept { return static_cast((0u - static_cast(value)) & 0x0fu); } /// @brief multiplication in Z/16Z HEDLEY_CONST HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr dpf::nyble operator*(dpf::nyble lhs, dpf::nyble rhs) noexcept { return static_cast( (static_cast(lhs) * static_cast(rhs)) & 0x0fu); } 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; constexpr dpf::nyble operator()(integral_type val) const noexcept { return dpf::to_nyble(val); } }; } // namespace utils namespace literals { namespace nyble { constexpr dpf::nyble operator""_nyble(unsigned long long x) { return dpf::to_nyble(x); } } // namespace nyble } // namespace literals } // namespace dpf namespace std { template <> class numeric_limits : public numeric_limits { public: static constexpr int digits = 4; static constexpr int digits10 = 1; static constexpr dpf::nyble min() noexcept { return dpf::nyble::zero; } static constexpr dpf::nyble max() noexcept { return dpf::nyble{15}; } static constexpr dpf::nyble 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_NYBLE_HPP__