arbi/builtin_int_methods/
is_power_of_two.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
Copyright 2024 Owain Davies
SPDX-License-Identifier: Apache-2.0 OR MIT
*/

use crate::{Arbi, BitCount};

impl Arbi {
    /// Returns `true` if and only if `|self| == 2^k` for some `k`.
    ///
    /// That is, return `true` iff the absolute value of this integer is a power
    /// of 2.
    ///
    /// # Examples
    /// ```
    /// use arbi::Arbi;
    ///
    /// let one = Arbi::one();
    /// assert!(one.is_power_of_two());
    /// assert!(1u32.is_power_of_two());
    ///
    /// let two = Arbi::from(2);
    /// assert!(two.is_power_of_two());
    /// assert!(2u32.is_power_of_two());
    ///
    /// let a = Arbi::from(32);
    /// assert!(a.is_power_of_two());
    /// assert!(32u32.is_power_of_two());
    ///
    /// let b = Arbi::from(30);
    /// assert!(!b.is_power_of_two());
    /// assert!(!(30u32.is_power_of_two()));
    ///
    /// let base = Arbi::from(Arbi::BASE);
    /// assert!(base.is_power_of_two());
    /// ```
    pub fn is_power_of_two(&self) -> bool {
        // Integer `k > 0` is a power of two if and only if `k & (k - 1) == 0`.
        // Only one bit should be set in its binary representation.
        let mut count: BitCount = 0;
        for digit in self.vec.iter().rev() {
            count += (*digit).count_ones() as BitCount;
            if count > 1 {
                // > one bit
                return false;
            }
        }
        count == 1
    }
}

#[cfg(test)]
mod tests {
    use crate::util::test::{get_seedable_rng, get_uniform_die, Distribution};
    use crate::{Arbi, DDigit, Digit, QDigit, SDDigit, SQDigit};

    #[test]
    fn test_is_power_of_two_digit_boundaries() {
        let a = Arbi::from(Digit::MAX);
        assert!(!a.is_power_of_two());
        let a = Arbi::from(Digit::MAX as DDigit + 1);
        assert!(a.is_power_of_two());

        let a = Arbi::from(-(Digit::MAX as SDDigit));
        assert!(!a.is_power_of_two());
        let a = Arbi::from(-(Digit::MAX as SDDigit + 1));
        assert!(a.is_power_of_two());

        let a = Arbi::from(DDigit::MAX);
        assert!(!a.is_power_of_two());
        let a = Arbi::from(DDigit::MAX as QDigit + 1);
        assert!(a.is_power_of_two());

        let a = Arbi::from(-(DDigit::MAX as SQDigit));
        assert!(!a.is_power_of_two());
        let a = Arbi::from(-(DDigit::MAX as SQDigit + 1));
        assert!(a.is_power_of_two());
    }

    #[test]
    fn test_is_power_of_two_zero() {
        let a = Arbi::zero();
        assert!(!a.is_power_of_two());
    }

    #[test]
    fn test_is_power_of_two_negative() {
        let a = Arbi::from(-4);
        assert!(a.is_power_of_two());
    }

    #[test]
    fn smoke() {
        let (mut rng, _) = get_seedable_rng();
        let die_digit = get_uniform_die(Digit::MIN, Digit::MAX);
        let die_ddigit = get_uniform_die(Digit::MAX as DDigit + 1, DDigit::MAX);
        let die_qdigit =
            get_uniform_die(DDigit::MAX as QDigit + 1, QDigit::MAX);
        let die_sddigit = get_uniform_die(SDDigit::MIN, SDDigit::MAX);

        for _ in 0..i16::MAX {
            let r = die_digit.sample(&mut rng);
            let a = Arbi::from(r);
            assert_eq!(a.is_power_of_two(), r.is_power_of_two());

            let r = die_ddigit.sample(&mut rng);
            let a = Arbi::from(r);
            assert_eq!(a.is_power_of_two(), r.is_power_of_two());

            let r = die_qdigit.sample(&mut rng);
            let a = Arbi::from(r);
            assert_eq!(a.is_power_of_two(), r.is_power_of_two());

            let r = die_sddigit.sample(&mut rng);
            let a = Arbi::from(r);
            assert_eq!(a.is_power_of_two(), r.unsigned_abs().is_power_of_two());
        }
    }
}