bva 0.4.1

bva is a rust crate for manipulating and doing arithmetics on bit vectors of fixed but arbitrary size.
Documentation
use num_bigint::{BigInt, ToBigInt, ToBigUint};
use std::mem::size_of;

use rand::{thread_rng, Rng};

use crate::utils::{IArray, IArrayMut, Integer, StaticCast};

macro_rules! test_type_combination {
    ($func:ident, $types:tt, $size:tt) => {
        test_type_combination!(@1 $func, $types, $types, $size);
    };
    //(@1 $func:ident, $i1:tt, $i2:tt, {$($size:literal),+}) => {
    (@1 $func:ident, {$($i1:ty),+}, $i2:tt, $size:tt) => {
        $(
            test_type_combination!(@2 $func, $i1, $i2, $size);
        )+
    };
    //(@2 $func:ident, {$($i1:ty),+}, $i2:tt, $size:literal) => {
    (@2 $func:ident, $i1:ty, {$($i2:ty),+}, $size:tt) => {
        $(
            test_type_combination!(@3 $func, $i1, $i2, $size);
        )+
    };
    //(@3 $func:ident, $i1:ty, {$($i2:ty),+}, $size:literal) => {
    (@3 $func:ident, $i1:ty, $i2:ty, {$($size:literal),+}) => {
        $(
            $func::<$i1, $i2, $size>();
        )+
    };
}

fn iarray_len_inner<I1: Integer, I2: Integer, const N1: usize>()
where
    I1: StaticCast<I1>,
    I1: StaticCast<I2>,
{
    let array = [I1::ZERO; N1];
    let byte_len = size_of::<I1>() * N1;
    let n2 = (byte_len + size_of::<I2>() - 1) / size_of::<I2>();

    assert_eq!(N1, IArray::int_len::<I1>(array.as_ref()));
    assert_eq!(n2, IArray::int_len::<I2>(array.as_ref()));
    assert_eq!(None, IArray::get_int::<I1>(array.as_ref(), N1));
    assert_eq!(None, IArray::get_int::<I2>(array.as_ref(), n2));
    for i in 0..N1 {
        assert_eq!(IArray::get_int::<I1>(array.as_ref(), i), Some(I1::ZERO));
    }
    for i in 0..n2 {
        assert_eq!(IArray::get_int::<I2>(array.as_ref(), i), Some(I2::ZERO));
    }
}

#[test]
fn iarray_len() {
    test_type_combination!(iarray_len_inner, {u8, u16, u32, u64, u128, usize}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
}

fn iarray_get_set_inner<I1: Integer, I2: Integer, const N1: usize>()
where
    I1: Integer + StaticCast<I1> + StaticCast<I2>,
    I2: Integer + StaticCast<I1> + StaticCast<usize>,
{
    let mut array = [I1::ZERO; N1];
    for i in 0..IArray::int_len::<I2>(array.as_ref()) {
        assert_eq!(
            IArrayMut::set_int::<I2>(array.as_mut(), i, I2::cast_from(i)),
            Some(I2::ZERO)
        );
    }
    for i in 0..IArray::int_len::<I2>(array.as_ref()) {
        assert_eq!(
            IArray::get_int::<I2>(array.as_ref(), i),
            Some(I2::cast_from(i))
        );
    }
}

#[test]
fn iarray_get_set() {
    test_type_combination!(iarray_get_set_inner, {u8, u16, u32, u64, u128, usize}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
}

fn mask_inner<I: Integer>() {
    for l in 0..I::BITS {
        let mask = I::mask(l);
        assert_eq!(mask.trailing_ones() as usize, l);
        assert_eq!(mask.leading_zeros() as usize, I::BITS - l);
    }
}

#[test]
fn mask() {
    mask_inner::<u8>();
    mask_inner::<u16>();
    mask_inner::<u32>();
    mask_inner::<u64>();
    mask_inner::<u128>();
    mask_inner::<usize>();
}

fn integer_cadd_inner<I: Integer + ToBigUint>() {
    let mut rng = thread_rng();
    for _ in 0..100 {
        let a = I::cast_from(rng.gen::<u128>());
        let b = I::cast_from(rng.gen::<u128>());
        let c = I::cast_from(rng.gen::<bool>() as u8);
        let mut res = a;
        let carry = res.cadd(b, c);

        let sum = a.to_biguint().unwrap() + b.to_biguint().unwrap() + c.to_biguint().unwrap();
        if sum <= I::MAX.to_biguint().unwrap() {
            assert_eq!(res.to_biguint().unwrap(), sum);
            assert_eq!(carry, I::ZERO);
        } else {
            assert_eq!(
                res.to_biguint().unwrap(),
                &sum - (I::MAX.to_biguint().unwrap() + 1u32)
            );
            assert_eq!(carry, I::ONE);
        }
    }
}

#[test]
fn integer_cadd() {
    integer_cadd_inner::<u8>();
    integer_cadd_inner::<u16>();
    integer_cadd_inner::<u32>();
    integer_cadd_inner::<u64>();
    integer_cadd_inner::<u128>();
    integer_cadd_inner::<usize>();
}

fn integer_csub_inner<I: Integer + ToBigInt>() {
    let mut rng = thread_rng();
    for _ in 0..100 {
        let a = I::cast_from(rng.gen::<u128>());
        let b = I::cast_from(rng.gen::<u128>());
        let c = I::cast_from(rng.gen::<bool>() as u8);
        let mut res = a;
        let carry = res.csub(b, c);

        let diff = a.to_bigint().unwrap() - b.to_bigint().unwrap() - c.to_bigint().unwrap();
        if &diff >= &BigInt::ZERO {
            assert_eq!(res.to_bigint().unwrap(), diff);
            assert_eq!(carry, I::ZERO);
        } else {
            assert_eq!(
                res.to_bigint().unwrap(),
                &diff + (I::MAX.to_bigint().unwrap() + 1u32)
            );
            assert_eq!(carry, I::ONE);
        }
    }
}

#[test]
fn integer_csub() {
    integer_csub_inner::<u8>();
    integer_csub_inner::<u16>();
    integer_csub_inner::<u32>();
    integer_csub_inner::<u64>();
    integer_csub_inner::<usize>();
}

fn integer_wmul_inner<I: Integer + ToBigUint>() {
    let mut rng = thread_rng();
    for _ in 0..100 {
        let a = I::cast_from(rng.gen::<u128>());
        let b = I::cast_from(rng.gen::<u128>());
        let (low, high) = a.wmul(b);

        let prod = a.to_biguint().unwrap() * b.to_biguint().unwrap();
        assert_eq!(high.to_biguint().unwrap(), &prod >> (size_of::<I>() * 8));
        assert_eq!(
            low.to_biguint().unwrap(),
            &prod % (I::MAX.to_biguint().unwrap() + 1u32)
        );
    }
}

#[test]
fn integer_wmul() {
    integer_wmul_inner::<u8>();
    integer_wmul_inner::<u16>();
    integer_wmul_inner::<u32>();
    integer_wmul_inner::<u64>();
    integer_wmul_inner::<usize>();
}

#[test]
fn integer_etc() {
    let mut rng = thread_rng();
    let a = rng.gen::<usize>();
    assert_eq!(Integer::leading_zeros(&a), a.leading_zeros() as usize);
    assert_eq!(Integer::leading_ones(&a), a.leading_ones() as usize);
    assert_eq!(Integer::trailing_zeros(&a), a.trailing_zeros() as usize);
    assert_eq!(Integer::trailing_ones(&a), a.trailing_ones() as usize);
}