libdpf/include/dpf/nyble.hpp

228 lines
5.9 KiB
C++

/// @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`.
/// @see packed_lane_arithmetic.hpp
#ifndef LIBDPF_INCLUDE_DPF_NYBLE_HPP__
#define LIBDPF_INCLUDE_DPF_NYBLE_HPP__
#include <cstddef>
#include <cstdint>
#include <istream>
#include <limits>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#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<dpf::nyble>(value & 0x0fu);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static constexpr dpf::nyble to_nyble(unsigned long long value) noexcept
{
return static_cast<dpf::nyble>(value & 0x0full);
}
/// @brief parse one hex digit as a nibble
/// @tparam CharT character type
/// @param value the value to convert or store
/// @return the returned `dpf::nyble`
/// @throws std::domain_error if `value` is not `0-9`, `a-f`, or `A-F`
template <typename CharT>
static constexpr dpf::nyble to_nyble(CharT value)
{
auto u = static_cast<unsigned>(value);
if (u >= static_cast<unsigned>(CharT('0')) && u <= static_cast<unsigned>(CharT('9')))
{
return static_cast<dpf::nyble>(u - static_cast<unsigned>(CharT('0')));
}
if (u >= static_cast<unsigned>(CharT('a')) && u <= static_cast<unsigned>(CharT('f')))
{
return static_cast<dpf::nyble>(10u + u - static_cast<unsigned>(CharT('a')));
}
if (u >= static_cast<unsigned>(CharT('A')) && u <= static_cast<unsigned>(CharT('F')))
{
return static_cast<dpf::nyble>(10u + u - static_cast<unsigned>(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<unsigned>(value) & 0x0fu]);
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits> &
operator<<(std::basic_ostream<CharT, Traits> & os, dpf::nyble value)
{
constexpr char digits[] = "0123456789abcdef";
return os << digits[static_cast<unsigned>(value) & 0x0fu];
}
template <typename CharT, typename Traits>
std::basic_istream<CharT, Traits> &
operator>>(std::basic_istream<CharT, Traits> & is, dpf::nyble & value)
{
try
{
value = to_nyble<CharT>(is.get());
}
catch (const std::exception &)
{
is.setstate(std::ios::failbit);
}
return is;
}
/// @brief addition in Z/16Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::nyble>(
(static_cast<unsigned>(lhs) + static_cast<unsigned>(rhs)) & 0x0fu);
}
/// @brief subtraction in Z/16Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::nyble>(
(static_cast<unsigned>(lhs) - static_cast<unsigned>(rhs)) & 0x0fu);
}
/// @brief additive inverse in Z/16Z
/// @param value the value to convert or store
/// @return additive inverse in Z/16Z
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr dpf::nyble operator-(dpf::nyble value) noexcept
{
return static_cast<dpf::nyble>((0u - static_cast<unsigned>(value)) & 0x0fu);
}
/// @brief multiplication in Z/16Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::nyble>(
(static_cast<unsigned>(lhs) * static_cast<unsigned>(rhs)) & 0x0fu);
}
namespace utils
{
template <>
struct bitlength_of<dpf::nyble>
: public std::integral_constant<std::size_t, 4> {};
template <typename NodeT>
struct bitlength_of_output<dpf::nyble, NodeT>
: public std::integral_constant<std::size_t, 4> {};
template <>
struct is_packed_subbyte<dpf::nyble> : std::true_type {};
template <>
struct packed_lane_bits<dpf::nyble>
: public std::integral_constant<std::size_t, 4> {};
template <>
struct make_from_integral_value<dpf::nyble>
{
using integral_type = std::uint8_t;
HEDLEY_NO_THROW
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<dpf::nyble> : public numeric_limits<std::uint8_t>
{
public:
static constexpr int digits = 4;
static constexpr int digits10 = 1;
HEDLEY_NO_THROW
static constexpr dpf::nyble min() noexcept { return dpf::nyble::zero; }
HEDLEY_NO_THROW
static constexpr dpf::nyble max() noexcept { return dpf::nyble{15}; }
HEDLEY_NO_THROW
static constexpr dpf::nyble lowest() noexcept { return min(); }
};
template <>
class numeric_limits<dpf::nyble const> : public numeric_limits<dpf::nyble> {};
template <>
class numeric_limits<dpf::nyble volatile> : public numeric_limits<dpf::nyble> {};
template <>
class numeric_limits<dpf::nyble const volatile>
: public numeric_limits<dpf::nyble> {};
} // namespace std
#endif // LIBDPF_INCLUDE_DPF_NYBLE_HPP__