use super::super::Packable;
use bnum_0_13::*;
macro_rules! impl_packable {
($($suffix:ident ($storage:literal)), +$(,)?) => {
paste::paste! {
$(
#[doc = "Zigzag encode `B" $suffix "<N>` value"]
#[inline]
pub const fn [< zigzag_encode_ $suffix:snake >]<const N: usize>(value: &[< B $suffix:camel >]<N>) -> [< BU $suffix >]<N> {
if N == 0 {
return [< BU $suffix >]::<N>::ZERO;
}
let bits = match [< B $suffix:camel >]::<N>::BITS.checked_sub(1) {
Some(val) => val,
None => 0,
};
value.shl(1).bitxor(value.shr(bits as u32)).to_bits()
}
#[doc = "Zigzag decode `B" $suffix:camel "<N>` value"]
#[inline]
pub const fn [< zigzag_decode_ $suffix:snake >]<const N: usize>(value: &[< BU $suffix >]<N>) -> [< B $suffix:camel >]<N> {
if N == 0 {
return [< B $suffix:camel >]::<N>::ZERO;
}
let a = [< B $suffix:camel >]::<N>::from_bits(value.shr(1));
let b = [< B $suffix:camel >]::<N>::from_bits(value.bitand([< BU $suffix >]::<N>::from_digit(1))).neg();
a.bitxor(b)
}
#[doc = "Packs `BU`" $suffix "<L>` and `BU" $suffix "<R>` into a single `BU" $suffix "<P>` value."]
pub const fn [< pack_u $suffix:snake>]<const L: usize, const R: usize, const P: usize>(a: &[< BU $suffix >]<L>, b: &[< BU $suffix >]<R>) -> [< BU $suffix >]<P> {
assert!(L <= P && R <= P - L, "L + R must be less than or equal to P");
if L == 0 && R == 0 {
return [< BU $suffix >]::<P>::ZERO;
}
let mut low = [0; P];
let mut high = [0; P];
let (high_bits, high_value, low_value, low_bits) = if L > R {
let b_digits = b.digits();
let mut idx = 0;
while idx < R {
low[idx] = b_digits[idx];
idx += 1;
}
let a_digits = a.digits();
idx = 0;
while idx < L {
high[idx] = a_digits[idx];
idx += 1;
}
let low = [< BU $suffix >]::<P>::from_digits(low);
let high = [< BU $suffix >]::<P>::from_digits(high);
(L * $storage, high, low, R * $storage)
} else {
let a_digits = a.digits();
let mut idx = 0;
while idx < L {
low[idx] = a_digits[idx];
idx += 1;
}
let b_digits = b.digits();
idx = 0;
while idx < R {
high[idx] = b_digits[idx];
idx += 1;
}
let low = [< BU $suffix >]::<P>::from_digits(low);
let high = [< BU $suffix >]::<P>::from_digits(high);
(R * $storage, high, low, L * $storage)
};
let low_mask = if low_bits == { P * $storage } {
[< BU $suffix >]::<P>::MAX
} else {
[< BU $suffix >]::<P>::ONE.shl(low_bits as u32).sub([< BU $suffix >]::<P>::ONE)
};
let masked_low = low_value.bitand(low_mask);
let high_mask = if high_bits == { P * $storage } {
[< BU $suffix >]::<P>::MAX
} else {
[< BU $suffix >]::<P>::ONE.shl(high_bits as u32).sub([< BU $suffix >]::<P>::ONE)
};
let masked_high = high_value.bitand(high_mask);
masked_high.shl(low_bits as u32).bitor(masked_low)
}
#[doc = "Unpacks `BU`" $suffix "<P>` into `BU" $suffix "<L>` and `BU" $suffix "<R>`"]
pub const fn [< unpack_u $suffix:snake >]<const L: usize, const R: usize, const P: usize>(packed: [< BU $suffix >]<P>) -> ([< BU $suffix >]<L>, [< BU $suffix >]<R>) {
assert!(L <= P && R <= P - L, "L + R must be less than or equal to P");
if L == 0 && R == 0 {
return ([< BU $suffix >]::<L>::ZERO, [< BU $suffix >]::<R>::ZERO);
}
let low_bits = if L > R {
R * $storage
} else {
L * $storage
};
let low_mask = if low_bits == { P * $storage } {
[< BU $suffix >]::<P>::MAX
} else {
[< BU $suffix >]::<P>::ONE.shl(low_bits as u32).sub([< BU $suffix >]::<P>::ONE)
};
let low_value = packed.bitand(low_mask);
let high_value = packed.shr(low_bits as u32);
if L > R {
let mut lhs = [0; L];
let high_digits = high_value.digits();
let mut idx = 0;
while idx < L {
lhs[idx] = high_digits[idx];
idx += 1;
}
let lhs = [< BU $suffix >]::<L>::from_digits(lhs);
let mut rhs = [0; R];
let low_digits = low_value.digits();
idx = 0;
while idx < R {
rhs[idx] = low_digits[idx];
idx += 1;
}
let rhs = [< BU $suffix >]::<R>::from_digits(rhs);
(lhs, rhs)
} else {
let mut lhs = [0; L];
let low_digits = low_value.digits();
let mut idx = 0;
while idx < L {
lhs[idx] = low_digits[idx];
idx += 1;
}
let lhs = [< BU $suffix >]::<L>::from_digits(lhs);
let mut rhs = [0; R];
let high_digits = high_value.digits();
idx = 0;
while idx < R {
rhs[idx] = high_digits[idx];
idx += 1;
}
let rhs = [< BU $suffix >]::<R>::from_digits(rhs);
(lhs, rhs)
}
}
#[doc = "Packs `B`" $suffix:camel "<L>` and `B" $suffix:camel "<R>` into a single `BU" $suffix "<P>` value."]
#[inline]
pub const fn [< pack_ $suffix:snake>]<const L: usize, const R: usize, const P: usize>(a: &[< B $suffix:camel >]<L>, b: &[< B $suffix:camel >]<R>) -> [< BU $suffix >]<P> {
if L == 0 && R == 0 {
return [< BU $suffix >]::<P>::ZERO;
}
let a = [< zigzag_encode_ $suffix:snake >](&a);
let b = [< zigzag_encode_ $suffix:snake >](&b);
[< pack_u $suffix:snake >](&a, &b)
}
#[doc = "Unpacks `BU`" $suffix "<P>` into `B" $suffix:camel "<L>` and `B" $suffix:camel "<R>`"]
#[inline]
pub const fn [< unpack_ $suffix:snake >]<const L: usize, const R: usize, const P: usize>(packed: [< BU $suffix >]<P>) -> ([< B $suffix:camel >]<L>, [< B $suffix:camel >]<R>) {
if L == 0 && R == 0 {
return ([< B $suffix:camel >]::<L>::ZERO, [< B $suffix:camel >]::<R>::ZERO);
}
let (a, b) = [< unpack_u $suffix:snake >](packed);
let a = [< zigzag_decode_ $suffix:snake >](&a);
let b = [< zigzag_decode_ $suffix:snake >](&b);
(a, b)
}
#[doc = "Packs `B`" $suffix:camel "<L>` and `BU" $suffix "<R>` into a single `BU" $suffix "<P>` value."]
#[inline]
pub const fn [< pack_ $suffix:snake _u $suffix:snake>]<const L: usize, const R: usize, const P: usize>(a: &[< B $suffix:camel >]<L>, b: &[< BU $suffix >]<R>) -> [< BU $suffix >]<P> {
if L == 0 && R == 0 {
return [< BU $suffix >]::<P>::ZERO;
}
let a = [< zigzag_encode_ $suffix:snake >](&a);
[< pack_u $suffix:snake >](&a, b)
}
#[doc = "Unpacks `BU`" $suffix "<P>` into `B" $suffix:camel "<L>` and `BU" $suffix "<R>`"]
#[inline]
pub const fn [< unpack_ $suffix:snake _u $suffix:snake >]<const L: usize, const R: usize, const P: usize>(packed: [< BU $suffix >]<P>) -> ([< B $suffix:camel >]<L>, [< BU $suffix >]<R>) {
if L == 0 && R == 0 {
return ([< B $suffix:camel >]::<L>::ZERO, [< BU $suffix >]::<R>::ZERO);
}
let (a, b) = [< unpack_u $suffix:snake >](packed);
let a = [< zigzag_decode_ $suffix:snake >](&a);
(a, b)
}
#[doc = "Packs `B`" $suffix:camel "<L>` and `BU" $suffix "<R>` into a single `BU" $suffix "<P>` value."]
#[inline]
pub const fn [< pack_u $suffix:snake _ $suffix:snake>]<const L: usize, const R: usize, const P: usize>(a: &[< BU $suffix >]<L>, b: &[< B $suffix:camel >]<R>) -> [< BU $suffix >]<P> {
if L == 0 && R == 0 {
return [< BU $suffix >]::<P>::ZERO;
}
let b = [< zigzag_encode_ $suffix:snake >](&b);
[< pack_u $suffix:snake >](a, &b)
}
#[doc = "Unpacks `BU`" $suffix "<P>` into `BU" $suffix "<L>` and `B" $suffix:camel "<R>`"]
#[inline]
pub const fn [< unpack_u $suffix:snake _ $suffix:snake >]<const L: usize, const R: usize, const P: usize>(packed: [< BU $suffix >]<P>) -> ([< BU $suffix >]<L>, [< B $suffix:camel >]<R>) {
if L == 0 && R == 0 {
return ([< BU $suffix >]::<L>::ZERO, [< B $suffix:camel >]::<R>::ZERO);
}
let (a, b) = [< unpack_u $suffix:snake >](packed);
let b = [< zigzag_decode_ $suffix:snake >](&b);
(a, b)
}
impl<const L: usize, const R: usize, const P: usize> Packable<[< BU $suffix>]<R>, [< BU $suffix>]<P>> for [< BU $suffix>]<L> {
fn pack(&self, rhs: &[< BU $suffix>]<R>) -> [< BU $suffix>]<P> {
[< pack_u $suffix:snake >](self, rhs)
}
fn unpack(packed: [< BU $suffix>]<P>) -> (Self, [< BU $suffix>]<R>)
where
Self: Sized,
BUint<R>: Sized
{
[< unpack_u $suffix:snake >](packed)
}
}
impl<const L: usize, const R: usize, const P: usize> Packable<[< B $suffix:camel>]<R>, [< BU $suffix>]<P>> for [< B $suffix:camel >]<L> {
fn pack(&self, rhs: &[< B $suffix:camel >]<R>) -> [< BU $suffix>]<P> {
[< pack_ $suffix:snake >](self, rhs)
}
fn unpack(packed: [< BU $suffix>]<P>) -> (Self, [< B $suffix:camel>]<R>)
where
Self: Sized,
BUint<R>: Sized
{
[< unpack_ $suffix:snake >](packed)
}
}
impl<const L: usize, const R: usize, const P: usize> Packable<[< B $suffix:camel>]<R>, [< BU $suffix>]<P>> for [< BU $suffix>]<L> {
fn pack(&self, rhs: &[< B $suffix:camel>]<R>) -> [< BU $suffix>]<P> {
[< pack_u $suffix:snake _ $suffix:snake >](self, rhs)
}
fn unpack(packed: [< BU $suffix>]<P>) -> (Self, [< B $suffix:camel>]<R>)
where
Self: Sized,
BUint<R>: Sized
{
[< unpack_u $suffix:snake _ $suffix:snake >](packed)
}
}
impl<const L: usize, const R: usize, const P: usize> Packable<[< BU $suffix>]<R>, [< BU $suffix>]<P>> for [< B $suffix:camel>]<L> {
fn pack(&self, rhs: &[< BU $suffix>]<R>) -> [< BU $suffix>]<P> {
[< pack_ $suffix:snake _u $suffix:snake >](self, rhs)
}
fn unpack(packed: [< BU $suffix>]<P>) -> (Self, [< BU $suffix>]<R>)
where
Self: Sized,
BUint<R>: Sized
{
[< unpack_ $suffix:snake _u $suffix:snake >](packed)
}
}
#[cfg(test)]
fn [< roundtrip_u $suffix:snake _test>]<const L: usize, const R: usize, const P: usize>(lhs: [< BU $suffix>]<L>, rhs: [< BU $suffix>]<R>) -> bool {
let packed = <[< BU $suffix>]<L> as Packable<[< BU $suffix>]<R>, [< BU $suffix>]<P>>>::pack(&lhs, &rhs);
let (lhs2, rhs2) = <[< BU $suffix>]<L> as Packable<[< BU $suffix>]<R>, _>>::unpack(packed);
lhs == lhs2 && rhs == rhs2
}
#[cfg(test)]
fn [< roundtrip_ $suffix:snake _test>]<const L: usize, const R: usize, const P: usize>(lhs: [< B $suffix: camel>]<L>, rhs: [< B $suffix: camel>]<R>) -> bool {
let packed = <[< B $suffix: camel>]<L> as Packable<[< B $suffix:camel>]<R>, [< BU $suffix>]<P>>>::pack(&lhs, &rhs);
let (lhs2, rhs2) = <[< B $suffix: camel>]<L> as Packable<[< B $suffix: camel>]<R>, _>>::unpack(packed);
lhs == lhs2 && rhs == rhs2
}
#[cfg(test)]
fn [< roundtrip_u $suffix:snake _ $suffix:snake _test>]<const L: usize, const R: usize, const P: usize>(lhs: [< BU $suffix>]<L>, rhs: [< B $suffix:camel>]<R>) -> bool {
let packed = <[< BU $suffix>]<L> as Packable<[< B $suffix:camel>]<R>, [< BU $suffix>]<P>>>::pack(&lhs, &rhs);
let (lhs2, rhs2) = <[< BU $suffix>]<L> as Packable<[< B $suffix:camel>]<R>, _>>::unpack(packed);
lhs == lhs2 && rhs == rhs2
}
#[cfg(test)]
fn [< roundtrip_ $suffix:snake _u $suffix:snake _test>]<const L: usize, const R: usize, const P: usize>(lhs: [< B $suffix:camel>]<L>, rhs: [< BU $suffix>]<R>) -> bool {
let packed = <[< B $suffix:camel>]<L> as Packable<[< BU $suffix>]<R>, [< BU $suffix>]<P>>>::pack(&lhs, &rhs);
let (lhs2, rhs2) = <[< B $suffix:camel>]<L> as Packable<[< BU $suffix>]<R>, _>>::unpack(packed);
lhs == lhs2 && rhs == rhs2
}
#[cfg(test)]
#[test]
fn [< packable_u $suffix:snake _width_order_roundtrips>]() {
const LESS_LHS: [< BU $suffix>]<1> = [< BU $suffix>]::from_digits([0x12]);
const LESS_RHS: [< BU $suffix>]<2> = [< BU $suffix>]::from_digits([0x34, 0x56]);
const LESS_PACKED: [< BU $suffix>]<3> =
[< pack_u $suffix:snake >]::<1, 2, 3>(&LESS_LHS, &LESS_RHS);
const LESS_UNPACKED: ([< BU $suffix>]<1>, [< BU $suffix>]<2>) =
[< unpack_u $suffix:snake >]::<1, 2, 3>(LESS_PACKED);
assert_eq!(LESS_PACKED.digits(), &[0x12, 0x34, 0x56]);
assert_eq!(LESS_UNPACKED, (LESS_LHS, LESS_RHS));
assert!([< roundtrip_u $suffix:snake _test>]::<1, 2, 3>(LESS_LHS, LESS_RHS));
const EQUAL_LHS: [< BU $suffix>]<2> = [< BU $suffix>]::from_digits([0x12, 0x34]);
const EQUAL_RHS: [< BU $suffix>]<2> = [< BU $suffix>]::from_digits([0x56, 0x78]);
const EQUAL_PACKED: [< BU $suffix>]<4> =
[< pack_u $suffix:snake >]::<2, 2, 4>(&EQUAL_LHS, &EQUAL_RHS);
const EQUAL_UNPACKED: ([< BU $suffix>]<2>, [< BU $suffix>]<2>) =
[< unpack_u $suffix:snake >]::<2, 2, 4>(EQUAL_PACKED);
assert_eq!(EQUAL_PACKED.digits(), &[0x12, 0x34, 0x56, 0x78]);
assert_eq!(EQUAL_UNPACKED, (EQUAL_LHS, EQUAL_RHS));
assert!([< roundtrip_u $suffix:snake _test>]::<2, 2, 4>(EQUAL_LHS, EQUAL_RHS));
const GREATER_LHS: [< BU $suffix>]<2> = [< BU $suffix>]::from_digits([0x12, 0x34]);
const GREATER_RHS: [< BU $suffix>]<1> = [< BU $suffix>]::from_digits([0x56]);
const GREATER_PACKED: [< BU $suffix>]<3> =
[< pack_u $suffix:snake >]::<2, 1, 3>(&GREATER_LHS, &GREATER_RHS);
const GREATER_UNPACKED: ([< BU $suffix>]<2>, [< BU $suffix>]<1>) =
[< unpack_u $suffix:snake >]::<2, 1, 3>(GREATER_PACKED);
assert_eq!(GREATER_PACKED.digits(), &[0x56, 0x12, 0x34]);
assert_eq!(GREATER_UNPACKED, (GREATER_LHS, GREATER_RHS));
assert!([< roundtrip_u $suffix:snake _test>]::<2, 1, 3>(GREATER_LHS, GREATER_RHS));
}
)*
}
};
}
impl_packable!(intD8(8), intD16(16), intD32(32), int(64),);
macro_rules! fuzzy_packable {
( $( $suffix:ident ($($bits: literal), +$(,)?) ), +$(,)? ) => {
paste::paste! {
$(
$(
#[cfg(test)]
quickcheck::quickcheck! {
fn [< fuzzy_u $suffix:snake _ $bits _roundtrip>](a: [< BU $suffix>]<{ $bits / 8 }>, b: [< BU $suffix>]<{ $bits / 8 }>) -> bool {
[< roundtrip_u $suffix:snake _test>]::<{$bits / 8}, {$bits / 8}, { ($bits / 8) * 2}>(a, b)
}
fn [< fuzzy_ $suffix:snake _ $bits _roundtrip>](a: [< B $suffix:camel>]<{ $bits / 8 }>, b: [< B $suffix:camel>]<{ $bits / 8 }>) -> bool {
[< roundtrip_ $suffix:snake _test>]::<{$bits / 8}, {$bits / 8}, { ($bits / 8) * 2}>(a, b)
}
fn [< fuzzy_u $suffix:snake _ $suffix:snake _ $bits _roundtrip>](a: [< BU $suffix>]<{ $bits / 8 }>, b: [< B $suffix:camel>]<{ $bits / 8 }>) -> bool {
[< roundtrip_u $suffix:snake _ $suffix:snake _test>]::<{$bits / 8}, {$bits / 8}, { ($bits / 8) * 2}>(a, b)
}
fn [< fuzzy_ $suffix:snake _u $suffix:snake _ $bits _roundtrip>](a: [< B $suffix:camel>]<{ $bits / 8 }>, b: [< BU $suffix>]<{ $bits / 8 }>) -> bool {
[< roundtrip_ $suffix:snake _u $suffix:snake _test>]::<{$bits / 8}, {$bits / 8}, { ($bits / 8) * 2}>(a, b)
}
}
)*
)*
}
};
}
fuzzy_packable!(
intD8(8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192),
intD16(8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192),
intD32(8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192),
int(8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192),
);