#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
use super::U4;
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use crate::extensions::*;
use crate::math::{ConstOne, ConstZero};
#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
#[cfg_attr(
feature = "wincode",
derive(wincode::SchemaWrite, wincode::SchemaRead)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "c_compatible", repr(C))]
pub struct U2 {
pub b0: bool,
pub b1: bool,
}
#[cfg(feature = "num_traits")]
impl num_traits::ToPrimitive for U2 {
fn to_f32(&self) -> Option<f32> {
Some(self.value().into())
}
fn to_f64(&self) -> Option<f64> {
Some(self.value().into())
}
fn to_i128(&self) -> Option<i128> {
Some(self.value().into())
}
fn to_i16(&self) -> Option<i16> {
Some(self.value().into())
}
fn to_i32(&self) -> Option<i32> {
Some(self.value().into())
}
fn to_i64(&self) -> Option<i64> {
Some(self.value().into())
}
fn to_i8(&self) -> Option<i8> {
self.value().to_i8()
}
fn to_isize(&self) -> Option<isize> {
Some(self.value().into())
}
fn to_u128(&self) -> Option<u128> {
Some(self.value().into())
}
fn to_u16(&self) -> Option<u16> {
Some(self.value().into())
}
fn to_u32(&self) -> Option<u32> {
Some(self.value().into())
}
fn to_u64(&self) -> Option<u64> {
Some(self.value().into())
}
fn to_u8(&self) -> Option<u8> {
Some(self.value())
}
}
#[cfg(feature = "num_traits")]
impl num_traits::NumCast for U2 {
fn from<T: num_traits::ToPrimitive>(n: T) -> Option<Self> {
Some(Self::new(unsafe { n.to_u8().unwrap_unchecked() }))
}
}
impl U2 {
#[must_use]
pub fn new(val: u8) -> Self {
assert!(val <= 0b11, "Value out of range for U2 (must be 0..=3)");
Self {
b0: (val & 0b01) != 0,
b1: (val & 0b10) != 0,
}
}
#[must_use]
pub const fn from_u8_trunc(val: u8) -> Self {
Self {
b0: (val & 0b01) != 0,
b1: (val & 0b10) != 0,
}
}
#[must_use]
pub const fn value(self) -> u8 {
(self.b0 as u8) | ((self.b1 as u8) << 1)
}
#[must_use]
pub const fn is_zero(self) -> bool {
!self.b0 && !self.b1
}
#[must_use]
pub const fn is_max(self) -> bool {
self.b0 && self.b1
}
#[must_use]
pub const fn wrapping_add(self, other: Self) -> Self {
Self::from_u8_trunc(self.value().wrapping_add(other.value()))
}
#[must_use]
pub const fn wrapping_sub(self, other: Self) -> Self {
Self::from_u8_trunc(self.value().wrapping_sub(other.value()))
}
#[must_use]
pub fn to_u4(self) -> U4 {
self.into()
}
#[must_use]
pub const fn combine_high_with(self, other: Self) -> U4 {
U4::from_u2_pair(self, other)
}
#[must_use]
pub const fn combine_low_with(self, other: Self) -> U4 {
U4::from_u2_pair(other, self)
}
}
impl const core::ops::Not for U2 {
type Output = Self;
fn not(self) -> Self {
Self::from_u8_trunc(!self.value())
}
}
impl const core::ops::BitAnd for U2 {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self::from_u8_trunc(self.value() & rhs.value())
}
}
impl const core::ops::BitOr for U2 {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self::from_u8_trunc(self.value() | rhs.value())
}
}
impl const core::ops::BitXor for U2 {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
Self::from_u8_trunc(self.value() ^ rhs.value())
}
}
impl const core::ops::Shl<usize> for U2 {
type Output = Self;
fn shl(self, rhs: usize) -> Self {
Self::from_u8_trunc(self.value() << rhs)
}
}
impl const core::ops::Shr<usize> for U2 {
type Output = Self;
fn shr(self, rhs: usize) -> Self {
Self::from_u8_trunc(self.value() >> rhs)
}
}
impl const core::ops::Add for U2 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self.wrapping_add(rhs)
}
}
impl const core::ops::Sub for U2 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self.wrapping_sub(rhs)
}
}
impl const core::ops::Mul for U2 {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
Self::from_u8_trunc(self.value().wrapping_mul(rhs.value()))
}
}
impl const core::ops::Div for U2 {
type Output = Self;
fn div(self, rhs: Self) -> Self {
Self::from_u8_trunc(self.value().wrapping_div(rhs.value()))
}
}
#[macro_export]
macro_rules! u2 {
($val:expr) => {
U2::new($val)
};
}
macro_rules! impl_u2_conversion {
($($t:ty),* $(,)?) => {
$(
impl From<$t> for U2 {
fn from(val: $t) -> Self {
assert!((0..=3).contains(&val), "Value out of range for U2 (must be 0..=3)");
U2 {
b0: (val & 0b01) != 0,
b1: (val & 0b10) != 0,
}
}
}
#[cfg(feature = "num_traits")]
impl From<U2> for $t {
fn from(val: U2) -> Self {
let raw = (val.b0 as u8) | ((val.b1 as u8) << 1);
unsafe{num_traits::NumCast::from(raw).unwrap_unchecked()}
}
}
)*
};
}
macro_rules! impl_u2_float_conversion {
($($f:ty),* $(,)?) => {
$(
impl From<$f> for U2 {
fn from(val: $f) -> Self {
assert!(val.is_finite(), "Cannot convert non-finite float to U2");
assert!(val.fract() == 0.0, "Cannot convert non-integer float to U2");
let as_int = val as i128;
assert!((0..=3).contains(&as_int), "Float value out of U2 range (must be 0.0 to 3.0)");
let val = as_int as u8;
U2 {
b0: (val & 0b01) != 0,
b1: (val & 0b10) != 0,
}
}
}
impl From<U2> for $f {
fn from(val: U2) -> Self {
let raw = (val.b0 as u8) | ((val.b1 as u8) << 1);
raw as $f
}
}
)*
};
}
impl_u2_conversion!(u8, u16, u32, u64, u128, usize);
impl_u2_conversion!(i8, i16, i32, i64, i128, isize);
impl_u2_float_conversion!(f32, f64);
impl core::ops::Rem for U2 {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
Self::from_u8_trunc(self.value() % rhs.value())
}
}
#[cfg(feature = "num_traits")]
impl num_traits::One for U2 {
fn is_one(&self) -> bool
where
Self: PartialEq,
{
self.value() == 1
}
fn one() -> Self {
Self::from_u8_trunc(1)
}
}
#[cfg(feature = "num_traits")]
impl num_traits::Zero for U2 {
fn zero() -> Self {
Self::from_u8_trunc(0)
}
fn is_zero(&self) -> bool {
self.value() == 0
}
}
#[cfg(feature = "num_traits")]
impl num_traits::Num for U2 {
fn from_str_radix(
str: &str,
radix: u32,
) -> Result<Self, Self::FromStrRadixErr> {
let result = <u8 as num_traits::Num>::from_str_radix(str, radix);
match result {
Ok(r) => Result::Ok(Self::from_u8_trunc(r)),
Err(e) => Result::Err(e),
}
}
type FromStrRadixErr = ::core::num::ParseIntError;
}
impl ConstOne for U2 {
const ONE: Self = Self::from_u8_trunc(1);
}
impl ConstZero for U2 {
const ZERO: Self = Self::from_u8_trunc(0);
}