use std::{
fmt::{
self,
Display,
Formatter,
},
ops::{
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
Not,
},
};
use crate::{
Bit,
IterableNybble,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Nybble {
bit_0: Bit,
bit_1: Bit,
bit_2: Bit,
bit_3: Bit,
}
impl Nybble {
#[must_use]
pub const fn new(first: Bit, second: Bit, third: Bit, fourth: Bit) -> Self {
Self {
bit_0: fourth, bit_1: third,
bit_2: second,
bit_3: first, }
}
pub fn set_bit(&mut self, index: u8) {
match index {
0 => self.bit_0.set(),
1 => self.bit_1.set(),
2 => self.bit_2.set(),
3 => self.bit_3.set(),
_ => panic!("Index out of bounds"),
}
}
pub fn unset_bit(&mut self, index: u8) {
match index {
0 => self.bit_0.unset(),
1 => self.bit_1.unset(),
2 => self.bit_2.unset(),
3 => self.bit_3.unset(),
_ => panic!("Index out of bounds"),
}
}
#[must_use]
pub fn get_bit(&self, index: u8) -> Bit {
match index {
0 => self.bit_0,
1 => self.bit_1,
2 => self.bit_2,
3 => self.bit_3,
_ => panic!("Index out of bounds"),
}
}
#[must_use]
pub fn get_bit_ref(&self, index: u8) -> &Bit {
match index {
0 => &self.bit_0,
1 => &self.bit_1,
2 => &self.bit_2,
3 => &self.bit_3,
_ => panic!("Index out of bounds"),
}
}
pub fn flip_bit(&mut self, index: u8) {
match index {
0 => self.bit_0.flip(),
1 => self.bit_1.flip(),
2 => self.bit_2.flip(),
3 => self.bit_3.flip(),
_ => panic!("Index out of bounds"),
}
}
pub fn flip(&mut self) {
self.bit_0.flip();
self.bit_1.flip();
self.bit_2.flip();
self.bit_3.flip();
}
#[allow(clippy::cast_possible_truncation)]
pub fn increment(&mut self) {
let zero = self.iter().position(|bit| bit == Bit::Zero);
if let Some(index) = zero {
for i in 0..=index as u8 {
self.flip_bit(i);
}
} else {
self.flip();
}
}
#[allow(clippy::cast_possible_truncation)]
pub fn decrement(&mut self) {
let one = self.iter().position(|bit| bit == Bit::One);
if let Some(index) = one {
for i in 0..=index as u8 {
self.flip_bit(i);
}
}
}
#[must_use]
pub const fn iter(&self) -> IterableNybble {
IterableNybble::new(self)
}
}
impl Display for Nybble {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let number: u8 = self.into();
write!(f, "{number:#03X}")
}
}
impl Default for Nybble {
fn default() -> Self {
Self::new(Bit::zero(), Bit::zero(), Bit::zero(), Bit::zero())
}
}
impl From<u8> for Nybble {
fn from(n: u8) -> Self {
let n = n & 0b0000_1111;
let mut nybble = Self::default();
if n & 0b0001 != 0 {
nybble.bit_0.set();
};
if n & 0b0010 != 0 {
nybble.bit_1.set();
};
if n & 0b0100 != 0 {
nybble.bit_2.set();
};
if n & 0b1000 != 0 {
nybble.bit_3.set();
};
nybble
}
}
impl From<&Nybble> for u8 {
fn from(nybble: &Nybble) -> Self {
let mut n = 0;
for i in 0..4 {
if nybble.get_bit(i) == Bit::One {
n |= 1 << i;
}
}
n
}
}
impl Not for Nybble {
type Output = Self;
fn not(self) -> Self::Output {
let mut nybble = self;
nybble.flip();
nybble
}
}
impl BitAnd for Nybble {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
let mut nybble = self;
nybble.bit_0 &= rhs.bit_0;
nybble.bit_1 &= rhs.bit_1;
nybble.bit_2 &= rhs.bit_2;
nybble.bit_3 &= rhs.bit_3;
nybble
}
}
impl BitAndAssign for Nybble {
fn bitand_assign(&mut self, rhs: Self) {
self.bit_0 &= rhs.bit_0;
self.bit_1 &= rhs.bit_1;
self.bit_2 &= rhs.bit_2;
self.bit_3 &= rhs.bit_3;
}
}
impl BitOr for Nybble {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
let mut nybble = self;
nybble.bit_0 |= rhs.bit_0;
nybble.bit_1 |= rhs.bit_1;
nybble.bit_2 |= rhs.bit_2;
nybble.bit_3 |= rhs.bit_3;
nybble
}
}
impl BitOrAssign for Nybble {
fn bitor_assign(&mut self, rhs: Self) {
self.bit_0 |= rhs.bit_0;
self.bit_1 |= rhs.bit_1;
self.bit_2 |= rhs.bit_2;
self.bit_3 |= rhs.bit_3;
}
}
impl BitXor for Nybble {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
let mut nybble = self;
nybble.bit_0 ^= rhs.bit_0;
nybble.bit_1 ^= rhs.bit_1;
nybble.bit_2 ^= rhs.bit_2;
nybble.bit_3 ^= rhs.bit_3;
nybble
}
}
impl BitXorAssign for Nybble {
fn bitxor_assign(&mut self, rhs: Self) {
self.bit_0 ^= rhs.bit_0;
self.bit_1 ^= rhs.bit_1;
self.bit_2 ^= rhs.bit_2;
self.bit_3 ^= rhs.bit_3;
}
}
impl<'a> IntoIterator for &'a Nybble {
type IntoIter = IterableNybble<'a>;
type Item = Bit;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_u8() {
let nybble = Nybble::from(10);
assert_eq!(u8::from(&nybble), 0b1010);
}
#[test]
fn test_from_u8_zero() {
let nybble = Nybble::from(0);
assert_eq!(u8::from(&nybble), 0);
}
#[test]
fn test_from_u8_all_ones() {
let nybble = Nybble::from(0b1111);
assert_eq!(u8::from(&nybble), 0b1111);
}
#[test]
fn test_from_u8_high_bits() {
let nybble = Nybble::from(0b10101010);
assert_eq!(u8::from(&nybble), 0b1010);
}
#[test]
fn test_get_bit() {
let nybble = Nybble::from(12);
assert_eq!(nybble.get_bit(0), Bit::zero());
assert_eq!(nybble.get_bit(1), Bit::zero());
assert_eq!(nybble.get_bit(2), Bit::one());
assert_eq!(nybble.get_bit(3), Bit::one());
}
#[test]
#[allow(unused_variables)]
#[should_panic(expected = "Index out of bounds")]
fn test_get_bit_oob() {
let nybble = Nybble::from(12);
let p = nybble.get_bit(4);
}
#[test]
fn test_set_bit() {
let mut nybble = Nybble::default();
nybble.set_bit(0);
nybble.set_bit(1);
nybble.set_bit(2);
nybble.set_bit(3);
assert_eq!(u8::from(&nybble), 15);
assert_eq!(nybble.to_string(), "0xF");
}
#[test]
#[should_panic(expected = "Index out of bounds")]
fn test_set_bit_oob() {
let mut nybble = Nybble::from(12);
nybble.set_bit(4);
}
#[test]
fn test_unset_bit() {
let mut nybble = Nybble::from(15);
nybble.unset_bit(0);
nybble.unset_bit(1);
nybble.unset_bit(2);
nybble.unset_bit(3);
assert_eq!(u8::from(&nybble), 0);
assert_eq!(nybble.to_string(), "0x0");
}
#[test]
#[should_panic(expected = "Index out of bounds")]
fn test_unset_bit_oob() {
let mut nybble = Nybble::from(12);
nybble.unset_bit(4);
}
#[test]
fn test_flip_bit() {
let mut nybble = Nybble::from(15);
nybble.flip_bit(0);
nybble.flip_bit(1);
nybble.flip_bit(2);
nybble.flip_bit(3);
assert_eq!(u8::from(&nybble), 0);
assert_eq!(nybble.to_string(), "0x0");
}
#[test]
#[should_panic(expected = "Index out of bounds")]
fn test_flip_bit_oob() {
let mut nybble = Nybble::from(12);
nybble.flip_bit(4);
}
#[test]
fn test_flip() {
let mut nybble = Nybble::from(15);
nybble.flip();
assert_eq!(u8::from(&nybble), 0);
assert_eq!(nybble.to_string(), "0x0");
}
#[test]
fn test_not() {
let nybble = Nybble::from(15);
let nybble_not = !nybble;
assert_eq!(u8::from(&nybble_not), 0);
assert_eq!(nybble_not.to_string(), "0x0");
}
#[test]
fn test_and() {
let nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
let nybble_3 = nybble_1 & nybble_2;
assert_eq!(u8::from(&nybble_3), 0b1000);
assert_eq!(nybble_3.to_string(), "0x8");
}
#[test]
fn test_and_assign() {
let mut nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
nybble_1 &= nybble_2;
assert_eq!(u8::from(&nybble_1), 0b1000);
assert_eq!(nybble_1.to_string(), "0x8");
}
#[test]
fn test_or() {
let nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
let nybble_3 = nybble_1 | nybble_2;
assert_eq!(u8::from(&nybble_3), 0b1110);
assert_eq!(nybble_3.to_string(), "0xE");
}
#[test]
fn test_or_assign() {
let mut nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
nybble_1 |= nybble_2;
assert_eq!(u8::from(&nybble_1), 0b1110);
assert_eq!(nybble_1.to_string(), "0xE");
}
#[test]
fn test_xor() {
let nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
let nybble_3 = nybble_1 ^ nybble_2;
assert_eq!(u8::from(&nybble_3), 0b0110);
assert_eq!(nybble_3.to_string(), "0x6");
}
#[test]
fn test_xor_assign() {
let mut nybble_1 = Nybble::from(0b1010);
let nybble_2 = Nybble::from(0b1100);
nybble_1 ^= nybble_2;
assert_eq!(u8::from(&nybble_1), 0b0110);
assert_eq!(nybble_1.to_string(), "0x6");
}
#[test]
fn test_display() {
let nybble = Nybble::from(10);
assert_eq!(format!("{}", nybble), "0xA");
}
#[test]
fn test_iterator() {
let nybble = Nybble::from(10);
let mut iter = nybble.iter();
assert_eq!(iter.next(), Some(Bit::zero()));
assert_eq!(iter.next(), Some(Bit::one()));
assert_eq!(iter.next(), Some(Bit::zero()));
assert_eq!(iter.next(), Some(Bit::one()));
assert_eq!(iter.next(), None);
}
#[test]
fn test_increment() {
let mut nybble = Nybble::from(10);
nybble.increment();
assert_eq!(u8::from(&nybble), 11);
assert_eq!(nybble.to_string(), "0xB");
}
#[test]
fn test_decrement() {
let mut nybble = Nybble::from(10);
nybble.decrement();
assert_eq!(u8::from(&nybble), 9);
assert_eq!(nybble.to_string(), "0x9");
}
#[test]
fn test_increment_boundary() {
let mut nybble = Nybble::from(15);
nybble.increment();
assert_eq!(u8::from(&nybble), 0);
assert_eq!(nybble.to_string(), "0x0");
}
#[test]
fn test_decrement_boundary() {
let mut nybble = Nybble::from(0);
nybble.decrement();
assert_eq!(u8::from(&nybble), 0);
assert_eq!(nybble.to_string(), "0x0");
}
#[test]
fn test_into_iter() {
let byte = Nybble::from(0b1010); let mut iter = (&byte).into_iter();
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), Some(Bit::One));
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), Some(Bit::One));
assert_eq!(iter.next(), None); }
#[test]
fn test_into_iter_empty_byte() {
let byte = Nybble::from(0b0000); let mut iter = (&byte).into_iter();
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), Some(Bit::Zero));
assert_eq!(iter.next(), None); }
#[test]
fn test_get_bit_ref() {
let nybble = Nybble::new(Bit::One, Bit::Zero, Bit::One, Bit::Zero);
assert_eq!(
nybble.get_bit_ref(0),
&Bit::Zero,
"Bit at index 0 should be Zero"
);
assert_eq!(
nybble.get_bit_ref(1),
&Bit::One,
"Bit at index 1 should be One"
);
assert_eq!(
nybble.get_bit_ref(2),
&Bit::Zero,
"Bit at index 2 should be Zero"
);
assert_eq!(
nybble.get_bit_ref(3),
&Bit::One,
"Bit at index 3 should be One"
);
}
#[test]
#[should_panic(expected = "Index out of bounds")]
fn test_get_bit_ref_out_of_bounds() {
let nybble = Nybble::new(Bit::One, Bit::Zero, Bit::One, Bit::Zero); let _ = nybble.get_bit_ref(4); }
}