malachite-base 0.3.2

A collection of utilities, including new arithmetic traits and iterators that generate all values of a type
Documentation
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::signeds::PrimitiveSigned;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::WrappingFrom;
use malachite_base::rounding_modes::RoundingMode;
use malachite_base::test_util::generators::{
    signed_rounding_mode_pair_gen, signed_unsigned_pair_gen_var_1, signed_unsigned_pair_gen_var_16,
    signed_unsigned_pair_gen_var_17, signed_unsigned_pair_gen_var_8,
    signed_unsigned_rounding_mode_triple_gen_var_1, unsigned_pair_gen_var_14,
    unsigned_pair_gen_var_2, unsigned_pair_gen_var_21, unsigned_rounding_mode_pair_gen,
    unsigned_unsigned_rounding_mode_triple_gen_var_3,
};
use std::panic::catch_unwind;

#[test]
fn test_round_to_multiple_of_power_of_2() {
    fn test<T: PrimitiveInt>(n: T, pow: u64, rm: RoundingMode, out: T) {
        assert_eq!(n.round_to_multiple_of_power_of_2(pow, rm), out);

        let mut n = n;
        n.round_to_multiple_of_power_of_2_assign(pow, rm);
        assert_eq!(n, out);
    }
    test::<u8>(0, 10, RoundingMode::Exact, 0);
    test::<u8>(17, 0, RoundingMode::Exact, 17);

    test::<u8>(10, 2, RoundingMode::Floor, 8);
    test::<u16>(10, 2, RoundingMode::Ceiling, 12);
    test::<u32>(10, 2, RoundingMode::Down, 8);
    test::<u64>(10, 2, RoundingMode::Up, 12);
    test::<u128>(10, 2, RoundingMode::Nearest, 8);
    test::<usize>(12, 2, RoundingMode::Exact, 12);

    test::<i8>(-10, 2, RoundingMode::Floor, -12);
    test::<i16>(-10, 2, RoundingMode::Ceiling, -8);
    test::<i32>(-10, 2, RoundingMode::Down, -8);
    test::<i64>(-10, 2, RoundingMode::Up, -12);
    test::<i128>(-10, 2, RoundingMode::Nearest, -8);
    test::<isize>(-12, 2, RoundingMode::Exact, -12);

    test::<u8>(0xff, 4, RoundingMode::Down, 0xf0);
    test::<u8>(0xff, 4, RoundingMode::Floor, 0xf0);
    test::<u8>(0xef, 4, RoundingMode::Up, 0xf0);
    test::<u8>(0xef, 4, RoundingMode::Ceiling, 0xf0);
    test::<u8>(0xe8, 4, RoundingMode::Nearest, 0xe0);
    test::<u8>(1, 8, RoundingMode::Nearest, 0);

    test::<i8>(0x7f, 4, RoundingMode::Down, 0x70);
    test::<i8>(0x7f, 4, RoundingMode::Floor, 0x70);
    test::<i8>(0x6f, 4, RoundingMode::Up, 0x70);
    test::<i8>(0x6f, 4, RoundingMode::Ceiling, 0x70);
    test::<i8>(0x68, 4, RoundingMode::Nearest, 0x60);
    test::<i8>(-0x7f, 4, RoundingMode::Down, -0x70);
    test::<i8>(-0x7f, 4, RoundingMode::Floor, -0x80);
    test::<i8>(-0x7f, 4, RoundingMode::Up, -0x80);
    test::<i8>(-0x7f, 4, RoundingMode::Ceiling, -0x70);
    test::<i8>(-0x78, 4, RoundingMode::Nearest, -0x80);
}

fn round_to_multiple_of_power_of_2_fail_helper<T: PrimitiveInt>() {
    assert_panic!(T::exact_from(10).round_to_multiple_of_power_of_2(4, RoundingMode::Exact));
    assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, RoundingMode::Up));
    assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, RoundingMode::Ceiling));
    assert_panic!(T::MAX.round_to_multiple_of_power_of_2(4, RoundingMode::Nearest));
    assert_panic!(T::ONE.round_to_multiple_of_power_of_2(T::WIDTH, RoundingMode::Up));

    assert_panic!(T::exact_from(10).round_to_multiple_of_power_of_2_assign(4, RoundingMode::Exact));
    assert_panic!({
        let mut n = T::MAX;
        n.round_to_multiple_of_power_of_2_assign(4, RoundingMode::Up);
    });
    assert_panic!({
        let mut n = T::MAX;
        n.round_to_multiple_of_power_of_2_assign(4, RoundingMode::Ceiling);
    });
    assert_panic!({
        let mut n = T::MAX;
        n.round_to_multiple_of_power_of_2_assign(4, RoundingMode::Nearest);
    });
    assert_panic!({
        let mut n = T::ONE;
        n.round_to_multiple_of_power_of_2_assign(T::WIDTH, RoundingMode::Up);
    });
}

fn round_to_multiple_of_power_of_2_signed_fail_helper<T: PrimitiveSigned>() {
    assert_panic!((-T::MAX).round_to_multiple_of_power_of_2(T::WIDTH, RoundingMode::Up));
    assert_panic!((-T::MAX).round_to_multiple_of_power_of_2(T::WIDTH, RoundingMode::Floor));

    assert_panic!((-T::MAX).round_to_multiple_of_power_of_2_assign(T::WIDTH, RoundingMode::Up));
    assert_panic!({
        (-T::MAX).round_to_multiple_of_power_of_2_assign(T::WIDTH, RoundingMode::Floor);
    });
}

#[test]
fn round_to_multiple_of_power_of_2_fail() {
    apply_fn_to_primitive_ints!(round_to_multiple_of_power_of_2_fail_helper);
    apply_fn_to_signeds!(round_to_multiple_of_power_of_2_signed_fail_helper);
}

fn round_to_multiple_of_power_of_2_properties_helper_unsigned<T: PrimitiveUnsigned>() {
    unsigned_unsigned_rounding_mode_triple_gen_var_3::<T>().test_properties(|(n, pow, rm)| {
        let rounded = n.round_to_multiple_of_power_of_2(pow, rm);

        let mut mut_n = n;
        mut_n.round_to_multiple_of_power_of_2_assign(pow, rm);
        assert_eq!(mut_n, rounded);

        assert!(rounded.divisible_by_power_of_2(pow));
        match rm {
            RoundingMode::Floor | RoundingMode::Down => {
                assert!(rounded <= n)
            }
            RoundingMode::Ceiling | RoundingMode::Up => {
                assert!(rounded >= n)
            }
            RoundingMode::Exact => assert_eq!(rounded, n),
            RoundingMode::Nearest => {
                if let Some(k) = T::ONE.arithmetic_checked_shl(pow) {
                    let mut closest = None;
                    let mut second_closest = None;
                    if rounded <= n {
                        if let Some(above) = rounded.checked_add(k) {
                            closest = Some(n - rounded);
                            second_closest = Some(above - n);
                        }
                    } else if let Some(below) = rounded.checked_sub(k) {
                        closest = Some(rounded - n);
                        second_closest = Some(n - below);
                    }
                    if let (Some(closest), Some(second_closest)) = (closest, second_closest) {
                        assert!(closest <= second_closest);
                        if closest == second_closest {
                            assert!(!rounded.get_bit(pow));
                        }
                    }
                }
            }
        }
    });

    unsigned_pair_gen_var_2::<T, u64>().test_properties(|(n, pow)| {
        if pow < T::WIDTH {
            if let Some(shifted) = n.arithmetic_checked_shl(pow) {
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Down),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Up),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Floor),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Ceiling),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Nearest),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Exact),
                    shifted
                );
            }
        }
    });

    unsigned_pair_gen_var_14::<T, u64>().test_properties(|(n, pow)| {
        let down = n.round_to_multiple_of_power_of_2(pow, RoundingMode::Down);
        if let Some(k) = T::ONE.arithmetic_checked_shl(pow) {
            if let Some(up) = down.checked_add(k) {
                assert_eq!(n.round_to_multiple_of_power_of_2(pow, RoundingMode::Up), up);
                assert_eq!(
                    n.round_to_multiple_of_power_of_2(pow, RoundingMode::Floor),
                    down
                );
                assert_eq!(
                    n.round_to_multiple_of_power_of_2(pow, RoundingMode::Ceiling),
                    up
                );
                let nearest = n.round_to_multiple_of_power_of_2(pow, RoundingMode::Nearest);
                assert!(nearest == down || nearest == up);
            }
        }
    });

    unsigned_pair_gen_var_21::<T, u64>().test_properties(|(n, pow)| {
        if let Some(shift) = pow.checked_add(T::WIDTH) {
            assert_eq!(
                n.round_to_multiple_of_power_of_2(shift, RoundingMode::Down),
                T::ZERO
            );
            assert_eq!(
                n.round_to_multiple_of_power_of_2(shift, RoundingMode::Floor),
                T::ZERO
            );
            if let Some(extra_shift) = shift.checked_add(1) {
                assert_eq!(
                    n.round_to_multiple_of_power_of_2(extra_shift, RoundingMode::Nearest),
                    T::ZERO
                );
            }
        }
    });

    unsigned_rounding_mode_pair_gen::<T>().test_properties(|(n, rm)| {
        assert_eq!(n.round_to_multiple_of_power_of_2(0, rm), n);
    });

    unsigned_rounding_mode_pair_gen().test_properties(|(pow, rm)| {
        assert_eq!(T::ZERO.round_to_multiple_of_power_of_2(pow, rm), T::ZERO);
    });
}

fn round_to_multiple_of_power_of_2_properties_helper_signed<
    U: PrimitiveUnsigned + WrappingFrom<S>,
    S: PrimitiveSigned + WrappingFrom<U>,
>() {
    signed_unsigned_rounding_mode_triple_gen_var_1::<S>().test_properties(|(n, pow, rm)| {
        let rounded = n.round_to_multiple_of_power_of_2(pow, rm);

        let mut mut_n = n;
        mut_n.round_to_multiple_of_power_of_2_assign(pow, rm);
        assert_eq!(mut_n, rounded);

        assert!(rounded.divisible_by_power_of_2(pow));
        match rm {
            RoundingMode::Floor => assert!(rounded <= n),
            RoundingMode::Ceiling => assert!(rounded >= n),
            RoundingMode::Down => assert!(rounded.le_abs(&n)),
            RoundingMode::Up => assert!(rounded.ge_abs(&n)),
            RoundingMode::Exact => assert_eq!(rounded, n),
            RoundingMode::Nearest => {
                if let Some(k) = S::ONE.arithmetic_checked_shl(pow) {
                    let mut closest = None;
                    let mut second_closest = None;
                    if rounded <= n {
                        if let Some(above) = rounded.checked_add(k) {
                            closest = Some(n - rounded);
                            second_closest = Some(above - n);
                        }
                    } else if let Some(below) = rounded.checked_sub(k) {
                        closest = Some(rounded - n);
                        second_closest = Some(n - below);
                    }
                    if let (Some(closest), Some(second_closest)) = (closest, second_closest) {
                        assert!(closest <= second_closest);
                        if closest == second_closest {
                            assert!(!rounded.get_bit(pow));
                        }
                    }
                }
            }
        }
    });

    signed_unsigned_pair_gen_var_1::<S, u64>().test_properties(|(n, pow)| {
        if pow < S::WIDTH {
            if let Some(shifted) = n.arithmetic_checked_shl(pow) {
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Down),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Up),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Floor),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Ceiling),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Nearest),
                    shifted
                );
                assert_eq!(
                    shifted.round_to_multiple_of_power_of_2(pow, RoundingMode::Exact),
                    shifted
                );
            }
        }
    });

    signed_unsigned_pair_gen_var_8::<S, u64>().test_properties(|(n, pow)| {
        let down = n.round_to_multiple_of_power_of_2(pow, RoundingMode::Down);
        if let Some(k) = S::ONE.arithmetic_checked_shl(pow) {
            if let Some(up) = if n >= S::ZERO {
                down.checked_add(k)
            } else {
                down.checked_sub(k)
            } {
                assert_eq!(n.round_to_multiple_of_power_of_2(pow, RoundingMode::Up), up);
                if n >= S::ZERO {
                    assert_eq!(
                        n.round_to_multiple_of_power_of_2(pow, RoundingMode::Floor),
                        down
                    );
                    assert_eq!(
                        n.round_to_multiple_of_power_of_2(pow, RoundingMode::Ceiling),
                        up
                    );
                } else {
                    assert_eq!(
                        n.round_to_multiple_of_power_of_2(pow, RoundingMode::Floor),
                        up
                    );
                    assert_eq!(
                        n.round_to_multiple_of_power_of_2(pow, RoundingMode::Ceiling),
                        down
                    );
                }
                let nearest = n.round_to_multiple_of_power_of_2(pow, RoundingMode::Nearest);
                assert!(nearest == down || nearest == up);
            }
        }
    });

    signed_unsigned_pair_gen_var_16::<S, u64>().test_properties(|(i, pow)| {
        if let Some(shift) = pow.checked_add(S::WIDTH - 1) {
            assert_eq!(
                i.round_to_multiple_of_power_of_2(shift, RoundingMode::Down),
                S::ZERO
            );
            assert_eq!(
                i.round_to_multiple_of_power_of_2(shift, RoundingMode::Floor),
                S::ZERO
            );
            if let Some(extra_shift) = shift.checked_add(1) {
                assert_eq!(
                    i.round_to_multiple_of_power_of_2(extra_shift, RoundingMode::Nearest),
                    S::ZERO
                );
            }
        }
    });

    signed_unsigned_pair_gen_var_17::<U, S, u64>().test_properties(|(i, pow)| {
        if let Some(shift) = pow.checked_add(S::WIDTH - 1) {
            assert_eq!(
                i.round_to_multiple_of_power_of_2(shift, RoundingMode::Down),
                S::ZERO
            );
            assert_eq!(
                i.round_to_multiple_of_power_of_2(shift, RoundingMode::Ceiling),
                S::ZERO
            );
            if let Some(extra_shift) = shift.checked_add(1) {
                assert_eq!(
                    i.round_to_multiple_of_power_of_2(extra_shift, RoundingMode::Nearest),
                    S::ZERO
                );
            }
        }
    });

    signed_rounding_mode_pair_gen::<S>().test_properties(|(n, rm)| {
        assert_eq!(n.round_to_multiple_of_power_of_2(0, rm), n);
    });

    unsigned_rounding_mode_pair_gen().test_properties(|(pow, rm)| {
        assert_eq!(S::ZERO.round_to_multiple_of_power_of_2(pow, rm), S::ZERO);
    });
}

#[test]
fn round_to_multiple_of_power_of_2_properties() {
    apply_fn_to_unsigneds!(round_to_multiple_of_power_of_2_properties_helper_unsigned);
    apply_fn_to_unsigned_signed_pairs!(round_to_multiple_of_power_of_2_properties_helper_signed);
}