libdpf/include/dpf/twobit.hpp

235 lines
6 KiB
C++
Raw Permalink Normal View History

/// @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`.
/// @see packed_lane_arithmetic.hpp
#ifndef LIBDPF_INCLUDE_DPF_TWOBIT_HPP__
#define LIBDPF_INCLUDE_DPF_TWOBIT_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 2-bit unsigned ring element, packed two bits per lane
/// @note Not a DPF domain. A 2-bit index is `dpf::modint<2>` or `dpf::xint<2>`.
/// Scalar `+` and `-` wrap mod 4. Leaf addition keeps the carry inside
/// the 2-bit lane.
/// @see dpf::bit
/// @see dpf::nyble
/// @see dpf::modint
/// @see dpf::packed
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<dpf::twobit>(value & 3u);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static constexpr dpf::twobit to_twobit(unsigned long long value) noexcept
{
return static_cast<dpf::twobit>(value & 3ull);
}
/// @brief parse one character as a 2-bit digit
/// @tparam CharT character type
/// @param zero character for 0 (default `'0'`)
/// @param value the value to convert or store
/// @return the returned `dpf::twobit`
/// @throws std::domain_error if `value` is not one of the four digits
template <typename CharT>
HEDLEY_ALWAYS_INLINE
static constexpr dpf::twobit to_twobit(CharT value, CharT zero = CharT('0'))
{
auto delta = static_cast<unsigned>(value) - static_cast<unsigned>(zero);
if (delta > 3u)
{
throw std::domain_error("Unrecognized twobit character");
}
return static_cast<dpf::twobit>(delta);
}
inline std::string to_string(dpf::twobit value)
{
return std::string(1, static_cast<char>('0' + static_cast<unsigned>(value)));
}
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits> &
operator<<(std::basic_ostream<CharT, Traits> & os, dpf::twobit value)
{
return os << static_cast<char>('0' + static_cast<unsigned>(value));
}
template <typename CharT, typename Traits>
std::basic_istream<CharT, Traits> &
operator>>(std::basic_istream<CharT, Traits> & is, dpf::twobit & value)
{
try
{
value = to_twobit<CharT>(is.get(), is.widen('0'));
}
catch (const std::exception &)
{
is.setstate(std::ios::failbit);
}
return is;
}
/// @brief addition in Z/4Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::twobit>(
(static_cast<unsigned>(lhs) + static_cast<unsigned>(rhs)) & 3u);
}
/// @brief subtraction in Z/4Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::twobit>(
(static_cast<unsigned>(lhs) - static_cast<unsigned>(rhs)) & 3u);
}
/// @brief additive inverse in Z/4Z
/// @param value the value to convert or store
/// @return additive inverse in Z/4Z
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr dpf::twobit operator-(dpf::twobit value) noexcept
{
return static_cast<dpf::twobit>((0u - static_cast<unsigned>(value)) & 3u);
}
/// @brief multiplication in Z/4Z
/// @param lhs the left-hand operand
/// @param rhs the right-hand operand
/// @return 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<dpf::twobit>(
(static_cast<unsigned>(lhs) * static_cast<unsigned>(rhs)) & 3u);
}
namespace utils
{
template <>
struct bitlength_of<dpf::twobit>
: public std::integral_constant<std::size_t, 2> {};
template <typename NodeT>
struct bitlength_of_output<dpf::twobit, NodeT>
: public std::integral_constant<std::size_t, 2> {};
template <>
struct is_packed_subbyte<dpf::twobit> : std::true_type {};
/// @brief Two-bit lanes add mod 2 (XOR of the packed field).
template <>
struct has_characteristic_two<dpf::twobit> : std::true_type {};
template <>
struct packed_lane_bits<dpf::twobit>
: public std::integral_constant<std::size_t, 2> {};
template <>
struct make_from_integral_value<dpf::twobit>
{
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<dpf::twobit> : public numeric_limits<std::uint8_t>
{
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<dpf::twobit const> : public numeric_limits<dpf::twobit> {};
template <>
class numeric_limits<dpf::twobit volatile> : public numeric_limits<dpf::twobit> {};
template <>
class numeric_limits<dpf::twobit const volatile>
: public numeric_limits<dpf::twobit> {};
} // namespace std
#endif // LIBDPF_INCLUDE_DPF_TWOBIT_HPP__