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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#![allow(dead_code, unused_variables)]

use std::fmt;

use serde::{Deserialize, Serialize};

/// A constant to describe the number of bits in one byte.
/// Defined just to improve readability.
const ONE_BYTE_BITS_COUNT: i8 = 8;

/// Struct represents a custom error that should be raised every time
/// a user tries to access or set the bit in the array that is out of this bit array size.
#[derive(Clone, Debug)]
pub struct OutOfRangeError {
    pub bitarray_size: i64,
    pub bitarray_position: i64,
}

impl OutOfRangeError {
    /// Constructor used to initialize a new OutOfRangeError with a given bitarray_size and bitarray_position.
    /// "bitarray_size" - The size in bits of the bitarray where the error was raised.
    /// "bitarray_position" - The wrong position caused an error.
    pub fn new(bitarray_size: i64, bitarray_position: i64) -> Self {
        Self {
            bitarray_size,
            bitarray_position,
        }
    }
}

impl fmt::Display for OutOfRangeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Given position: {} is out of the bitarray size {}.",
            self.bitarray_position, self.bitarray_size
        )
    }
}

/// A structure aimed to bring the bitarray functionality.
/// Structure is described within two fields.
/// "size" - The number of bits that will be allocated during the
///          struct instance initialization.
/// "bit_array" - The vector of 8 bit integer used to represent bits
///               where each 8 bits are packed in every 8 bit integer.
/// ```rust
/// use bitarray_naive::BitArray;
///
/// let bitarray_size: i64 = 9999;
///
/// let mut bitarray: BitArray = BitArray::new(bitarray_size);
///
/// bitarray.set(12, true).unwrap();
///
/// assert_eq!(bitarray.get(12).unwrap(), true);
/// ```
#[derive(Debug, Serialize, Deserialize)]
pub struct BitArray {
    pub size: i64,
    pub bit_array: Vec<u8>,
}

impl BitArray {
    /// Constructor used to initialize a new instance of the bitarray with a given size.
    /// "size" - The number of bits that will be allocated during the
    ///          struct instance initialization.
    pub fn new(size: i64) -> Self {
        // Calculates the number of elements should be allocated in vector per given size
        let _capacity: usize = (size / ONE_BYTE_BITS_COUNT as i64) as usize + 1;

        let mut bit_array: Vec<u8> = Vec::with_capacity(_capacity);

        // Initialize vector with default 0 values. Where each value equal to 8 false bits.
        for _ in 0.._capacity {
            bit_array.push(0);
        }

        Self { size, bit_array }
    }

    /// Calculates the element in bit array vector should be picked per given bit position.
    /// In other words, calculates the index in bitarray vector.
    fn calc_vec_position(position: i64) -> usize {
        (position / ONE_BYTE_BITS_COUNT as i64) as usize
    }

    /// Calculates the bit offset (position) in a given 8 bit integer.
    /// The position should be counted from right to left.
    fn calc_byte_offset(position: i64) -> u8 {
        let _pow: i64 = position % ONE_BYTE_BITS_COUNT as i64;

        2u64.pow(_pow as u32) as u8
    }

    /// Sets either true or false value in bit array at given position.
    pub fn set(&mut self, position: i64, flag: bool) -> Result<(), OutOfRangeError> {
        if position >= self.size {
            Err(OutOfRangeError::new(self.size, position as i64))
        } else {
            let vec_position: usize = Self::calc_vec_position(position);
            let byte_offset: u8 = Self::calc_byte_offset(position);

            if flag {
                self.bit_array[vec_position] |= byte_offset;
            } else {
                self.bit_array[vec_position] &= !byte_offset;
            }

            Ok(())
        }
    }

    /// Gets either true or false value in bit array at given position.
    pub fn get(&self, position: i64) -> Result<bool, OutOfRangeError> {
        if position >= self.size {
            Err(OutOfRangeError::new(self.size, position))
        } else {
            let vec_position: usize = Self::calc_vec_position(position);
            let byte_offset: u8 = Self::calc_byte_offset(position);

            Ok(self.bit_array[vec_position] == (self.bit_array[vec_position] | byte_offset))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{BitArray, ONE_BYTE_BITS_COUNT};

    #[test]
    fn test_init_bitarray() {
        // Bit array size is a number of bits that will be allocated
        // in a given bitarray.
        // Bit array based on the vector of u8 integers
        // Each u8 integer occupy 8 bits of memory.
        // So the bitarray's vector should be of bitarray_size size / 8 size.
        let bitarray_size: i64 = 10;

        let bitarray: BitArray = BitArray::new(bitarray_size);

        assert_eq!(
            bitarray.bit_array.len(),
            (bitarray_size / ONE_BYTE_BITS_COUNT as i64) as usize + 1
        );
        assert_eq!(bitarray.bit_array.len(), 2);
    }

    #[test]
    fn test_bitarray_set_true() {
        let bitarray_size: i64 = 10;
        let bitarray_position: i64 = 9;

        let mut bitarray: BitArray = BitArray::new(bitarray_size);

        let success: bool = match bitarray.set(bitarray_position, true) {
            Ok(_) => true,
            Err(_) => false,
        };

        assert!(success);
    }

    #[test]
    fn test_bitarray_set_with_error() {
        let bitarray_size: i64 = 10;
        let bitarray_position: i64 = 10;

        let mut bitarray: BitArray = BitArray::new(bitarray_size);

        let success: bool = match bitarray.set(bitarray_position, true) {
            Ok(_) => false,
            Err(err) => {
                err.bitarray_position == 10
                    && err.bitarray_size == 10
                    && format!("{}", err) == "Given position: 10 is out of the bitarray size 10."
            }
        };

        assert!(success);
    }

    #[test]
    fn test_bitarray_get_with_error() {
        let bitarray_size: i64 = 10;
        let bitarray_position: i64 = 10;

        let bitarray: BitArray = BitArray::new(bitarray_size);

        let success: bool = match bitarray.get(bitarray_position) {
            Ok(_) => false,
            Err(err) => {
                err.bitarray_position == 10
                    && err.bitarray_size == 10
                    && format!("{}", err) == "Given position: 10 is out of the bitarray size 10."
            }
        };

        assert!(success);
    }

    #[test]
    fn test_bit_array_get_set() {
        let bitarray_size: i64 = 74845;

        let mut bitarray: BitArray = BitArray::new(bitarray_size);

        for bitarray_position in 0..bitarray_size - 1 {
            assert!(!bitarray.get(bitarray_position).unwrap());
        }

        for bitarray_position in 0..bitarray_size {
            bitarray.set(bitarray_position, true).unwrap();

            assert!(bitarray.get(bitarray_position).unwrap());
        }

        for bitarray_position in 0..bitarray_size - 1 {
            assert!(bitarray.get(bitarray_position).unwrap());
        }

        for bitarray_position in 0..bitarray_size {
            bitarray.set(bitarray_position, false).unwrap();

            assert!(!bitarray.get(bitarray_position).unwrap());
        }

        for bitarray_position in 0..bitarray_size - 1 {
            assert!(!bitarray.get(bitarray_position).unwrap());
        }
    }
}