use core::ops::{
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
ShrAssign,
};
use crate::core_type::I128;
impl<const SCALE: u32> BitAnd for I128<SCALE> {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl<const SCALE: u32> BitAndAssign for I128<SCALE> {
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl<const SCALE: u32> BitOr for I128<SCALE> {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl<const SCALE: u32> BitOrAssign for I128<SCALE> {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl<const SCALE: u32> BitXor for I128<SCALE> {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
}
impl<const SCALE: u32> BitXorAssign for I128<SCALE> {
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
}
impl<const SCALE: u32> Shl<u32> for I128<SCALE> {
type Output = Self;
#[inline]
fn shl(self, n: u32) -> Self {
Self(self.0 << n)
}
}
impl<const SCALE: u32> ShlAssign<u32> for I128<SCALE> {
#[inline]
fn shl_assign(&mut self, n: u32) {
self.0 <<= n;
}
}
impl<const SCALE: u32> Shr<u32> for I128<SCALE> {
type Output = Self;
#[inline]
fn shr(self, n: u32) -> Self {
Self(self.0 >> n)
}
}
impl<const SCALE: u32> ShrAssign<u32> for I128<SCALE> {
#[inline]
fn shr_assign(&mut self, n: u32) {
self.0 >>= n;
}
}
impl<const SCALE: u32> Not for I128<SCALE> {
type Output = Self;
#[inline]
fn not(self) -> Self {
Self(!self.0)
}
}
impl<const SCALE: u32> I128<SCALE> {
#[inline]
pub const fn unsigned_shr(self, n: u32) -> Self {
Self(((self.0 as u128) >> n) as i128)
}
#[inline]
pub const fn rotate_left(self, n: u32) -> Self {
Self(self.0.rotate_left(n))
}
#[inline]
pub const fn rotate_right(self, n: u32) -> Self {
Self(self.0.rotate_right(n))
}
#[inline]
pub const fn leading_zeros(self) -> u32 {
self.0.leading_zeros()
}
#[inline]
pub const fn trailing_zeros(self) -> u32 {
self.0.trailing_zeros()
}
#[inline]
pub const fn count_ones(self) -> u32 {
self.0.count_ones()
}
#[inline]
pub const fn count_zeros(self) -> u32 {
self.0.count_zeros()
}
#[inline]
pub const fn is_power_of_two(self) -> bool {
(self.0 as u128).is_power_of_two()
}
#[inline]
pub const fn next_power_of_two(self) -> Self {
Self((self.0 as u128).next_power_of_two() as i128)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core_type::I128s12;
#[test]
fn bitand_clears_bits() {
let a = I128s12::from_bits(0xF0);
let b = I128s12::from_bits(0x0F);
assert_eq!(a & b, I128s12::from_bits(0x00));
}
#[test]
fn bitand_assign_in_place() {
let mut a = I128s12::from_bits(0xFF);
a &= I128s12::from_bits(0x0F);
assert_eq!(a, I128s12::from_bits(0x0F));
}
#[test]
fn bitor_sets_bits() {
let zero = I128s12::ZERO;
let one_lsb = I128s12::from_bits(1);
assert_eq!(zero | one_lsb, one_lsb);
}
#[test]
fn bitor_assign_in_place() {
let mut a = I128s12::from_bits(0xF0);
a |= I128s12::from_bits(0x0F);
assert_eq!(a, I128s12::from_bits(0xFF));
}
#[test]
fn bitxor_toggles_bits() {
let a = I128s12::from_bits(0b1100);
let b = I128s12::from_bits(0b1010);
assert_eq!(a ^ b, I128s12::from_bits(0b0110));
}
#[test]
fn bitxor_assign_in_place() {
let mut a = I128s12::from_bits(0xFF);
a ^= I128s12::from_bits(0x0F);
assert_eq!(a, I128s12::from_bits(0xF0));
}
#[test]
fn bitxor_self_is_zero() {
let a = I128s12::from_bits(0xDEAD_BEEF_i128);
assert_eq!(a ^ a, I128s12::ZERO);
}
#[test]
fn shl_doubles_lsb() {
assert_eq!(I128s12::from_bits(1) << 1u32, I128s12::from_bits(2));
}
#[test]
fn shr_halves_lsb() {
assert_eq!(I128s12::from_bits(2) >> 1u32, I128s12::from_bits(1));
}
#[test]
fn shr_is_sign_extending() {
assert_eq!(I128s12::from_bits(-1) >> 1u32, I128s12::from_bits(-1));
}
#[test]
fn shr_negative_stays_negative() {
assert_eq!(I128s12::from_bits(-8) >> 1u32, I128s12::from_bits(-4));
}
#[test]
fn shl_assign_in_place() {
let mut a = I128s12::from_bits(1);
a <<= 4u32;
assert_eq!(a, I128s12::from_bits(16));
}
#[test]
fn shr_assign_in_place() {
let mut a = I128s12::from_bits(16);
a >>= 2u32;
assert_eq!(a, I128s12::from_bits(4));
}
#[test]
fn not_zero_is_neg_one() {
assert_eq!(!I128s12::ZERO, I128s12::from_bits(-1));
}
#[test]
fn not_neg_one_is_zero() {
assert_eq!(!I128s12::from_bits(-1), I128s12::ZERO);
}
#[test]
fn not_is_self_inverse() {
let a = I128s12::from_bits(0xCAFE);
assert_eq!(!!a, a);
}
#[test]
fn unsigned_shr_zero_fills_negative() {
assert_eq!(
I128s12::from_bits(-1).unsigned_shr(1),
I128s12::from_bits(i128::MAX)
);
}
#[test]
fn unsigned_shr_positive_matches_arithmetic_shr() {
let a = I128s12::from_bits(0xFF);
assert_eq!(a.unsigned_shr(4), a >> 4u32);
assert_eq!(a.unsigned_shr(4), I128s12::from_bits(0x0F));
}
#[test]
fn unsigned_shr_zero_amount_identity() {
let a = I128s12::from_bits(-42);
assert_eq!(a.unsigned_shr(0), a);
}
#[test]
fn rotate_left_low_bits() {
assert_eq!(
I128s12::from_bits(0b111).rotate_left(1),
I128s12::from_bits(0b1110)
);
}
#[test]
fn rotate_right_low_bit_wraps_to_top() {
assert_eq!(
I128s12::from_bits(1).rotate_right(1),
I128s12::from_bits(i128::MIN)
);
}
#[test]
fn rotate_left_full_width_is_identity() {
let a = I128s12::from_bits(0xDEAD_BEEF_i128);
assert_eq!(a.rotate_left(128), a);
}
#[test]
fn rotate_right_round_trip() {
let a = I128s12::from_bits(0xCAFE_F00D_i128);
assert_eq!(a.rotate_left(13).rotate_right(13), a);
}
#[test]
fn leading_zeros_lsb_is_127() {
assert_eq!(I128s12::from_bits(1).leading_zeros(), 127);
}
#[test]
fn leading_zeros_zero_is_128() {
assert_eq!(I128s12::ZERO.leading_zeros(), 128);
}
#[test]
fn leading_zeros_neg_one_is_zero() {
assert_eq!(I128s12::from_bits(-1).leading_zeros(), 0);
}
#[test]
fn trailing_zeros_eight_is_three() {
assert_eq!(I128s12::from_bits(8).trailing_zeros(), 3);
}
#[test]
fn trailing_zeros_zero_is_128() {
assert_eq!(I128s12::ZERO.trailing_zeros(), 128);
}
#[test]
fn trailing_zeros_one_is_zero() {
assert_eq!(I128s12::from_bits(1).trailing_zeros(), 0);
}
#[test]
fn count_ones_pattern() {
assert_eq!(I128s12::from_bits(0b101).count_ones(), 2);
}
#[test]
fn count_zeros_pattern() {
assert_eq!(I128s12::from_bits(0b101).count_zeros(), 126);
}
#[test]
fn count_ones_zero_is_zero() {
assert_eq!(I128s12::ZERO.count_ones(), 0);
}
#[test]
fn count_ones_neg_one_is_128() {
assert_eq!(I128s12::from_bits(-1).count_ones(), 128);
}
#[test]
fn count_zeros_complement_relation() {
let a = I128s12::from_bits(0xDEAD_BEEF_CAFE_i128);
assert_eq!(a.count_ones() + a.count_zeros(), 128);
}
#[test]
fn is_power_of_two_true_for_eight() {
assert!(I128s12::from_bits(8).is_power_of_two());
}
#[test]
fn is_power_of_two_false_for_seven() {
assert!(!I128s12::from_bits(7).is_power_of_two());
}
#[test]
fn is_power_of_two_false_for_zero() {
assert!(!I128s12::ZERO.is_power_of_two());
}
#[test]
fn is_power_of_two_false_for_negative() {
assert!(!I128s12::from_bits(-1).is_power_of_two());
}
#[test]
fn is_power_of_two_storage_not_value_semantic() {
assert!(!I128s12::ONE.is_power_of_two());
}
#[test]
fn next_power_of_two_seven_is_eight() {
assert_eq!(
I128s12::from_bits(7).next_power_of_two(),
I128s12::from_bits(8)
);
}
#[test]
fn next_power_of_two_eight_is_eight() {
assert_eq!(
I128s12::from_bits(8).next_power_of_two(),
I128s12::from_bits(8)
);
}
#[test]
fn next_power_of_two_one_is_one() {
assert_eq!(
I128s12::from_bits(1).next_power_of_two(),
I128s12::from_bits(1)
);
}
#[test]
fn ops_work_at_scale_six() {
type D6 = I128<6>;
let a = D6::from_bits(0b1100);
let b = D6::from_bits(0b1010);
assert_eq!(a & b, D6::from_bits(0b1000));
assert_eq!(a | b, D6::from_bits(0b1110));
assert_eq!(a ^ b, D6::from_bits(0b0110));
assert_eq!(D6::from_bits(1) << 3u32, D6::from_bits(8));
assert_eq!(D6::from_bits(8) >> 3u32, D6::from_bits(1));
assert_eq!(!D6::ZERO, D6::from_bits(-1));
assert_eq!(D6::from_bits(8).count_ones(), 1);
assert!(D6::from_bits(8).is_power_of_two());
}
}