Skip to content

Commit 1b3d80d

Browse files
feat: add rightmost_set_bit function (TheAlgorithms#975)
1 parent a486aea commit 1b3d80d

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* [N Bits Gray Code](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/n_bits_gray_code.rs)
2626
* [Previous Power of Two](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/find_previous_power_of_two.rs)
2727
* [Reverse Bits](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/reverse_bits.rs)
28+
* [Rightmost Set Bit](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/rightmost_set_bit.rs)
2829
* [Sum of Two Integers](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/sum_of_two_integers.rs)
2930
* [Swap Odd and Even Bits](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/swap_odd_even_bits.rs)
3031
* [Trailing Zeros](https://github.com/TheAlgorithms/Rust/blob/master/src/bit_manipulation/binary_count_trailing_zeros.rs)

src/bit_manipulation/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod highest_set_bit;
88
mod is_power_of_two;
99
mod n_bits_gray_code;
1010
mod reverse_bits;
11+
mod rightmost_set_bit;
1112
mod sum_of_two_integers;
1213
mod swap_odd_even_bits;
1314
mod twos_complement;
@@ -22,6 +23,7 @@ pub use self::highest_set_bit::find_highest_set_bit;
2223
pub use self::is_power_of_two::is_power_of_two;
2324
pub use self::n_bits_gray_code::generate_gray_code;
2425
pub use self::reverse_bits::reverse_bits;
26+
pub use self::rightmost_set_bit::{index_of_rightmost_set_bit, index_of_rightmost_set_bit_log};
2527
pub use self::sum_of_two_integers::add_two_integers;
2628
pub use self::swap_odd_even_bits::swap_odd_even_bits;
2729
pub use self::twos_complement::twos_complement;
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/// Finds the index (position) of the rightmost set bit in a number.
2+
///
3+
/// The index is 1-based, where position 1 is the least significant bit (rightmost).
4+
/// This function uses the bitwise trick `n & -n` to isolate the rightmost set bit,
5+
/// then calculates its position using logarithm base 2.
6+
///
7+
/// # Algorithm
8+
///
9+
/// 1. Use `n & -n` to isolate the rightmost set bit
10+
/// 2. Calculate log2 of the result to get the 0-based position
11+
/// 3. Add 1 to convert to 1-based indexing
12+
///
13+
/// # Arguments
14+
///
15+
/// * `num` - A positive integer
16+
///
17+
/// # Returns
18+
///
19+
/// * `Ok(u32)` - The 1-based position of the rightmost set bit
20+
/// * `Err(String)` - An error message if the input is invalid
21+
///
22+
/// # Examples
23+
///
24+
/// ```
25+
/// # use the_algorithms_rust::bit_manipulation::index_of_rightmost_set_bit;
26+
/// // 18 in binary: 10010, rightmost set bit is at position 2
27+
/// assert_eq!(index_of_rightmost_set_bit(18).unwrap(), 2);
28+
///
29+
/// // 12 in binary: 1100, rightmost set bit is at position 3
30+
/// assert_eq!(index_of_rightmost_set_bit(12).unwrap(), 3);
31+
///
32+
/// // 5 in binary: 101, rightmost set bit is at position 1
33+
/// assert_eq!(index_of_rightmost_set_bit(5).unwrap(), 1);
34+
///
35+
/// // 16 in binary: 10000, rightmost set bit is at position 5
36+
/// assert_eq!(index_of_rightmost_set_bit(16).unwrap(), 5);
37+
///
38+
/// // 0 has no set bits
39+
/// assert!(index_of_rightmost_set_bit(0).is_err());
40+
/// ```
41+
pub fn index_of_rightmost_set_bit(num: i32) -> Result<u32, String> {
42+
if num <= 0 {
43+
return Err("input must be a positive integer".to_string());
44+
}
45+
46+
// Isolate the rightmost set bit using n & -n
47+
let rightmost_bit = num & -num;
48+
49+
// Calculate position: log2(rightmost_bit) + 1
50+
// We use trailing_zeros which gives us the 0-based position
51+
// and add 1 to make it 1-based
52+
let position = rightmost_bit.trailing_zeros() + 1;
53+
54+
Ok(position)
55+
}
56+
57+
/// Alternative implementation using a different algorithm approach.
58+
///
59+
/// This version demonstrates the mathematical relationship between
60+
/// the rightmost set bit position and log2.
61+
///
62+
/// # Examples
63+
///
64+
/// ```
65+
/// # use the_algorithms_rust::bit_manipulation::index_of_rightmost_set_bit_log;
66+
/// assert_eq!(index_of_rightmost_set_bit_log(18).unwrap(), 2);
67+
/// assert_eq!(index_of_rightmost_set_bit_log(12).unwrap(), 3);
68+
/// ```
69+
pub fn index_of_rightmost_set_bit_log(num: i32) -> Result<u32, String> {
70+
if num <= 0 {
71+
return Err("input must be a positive integer".to_string());
72+
}
73+
74+
// Isolate the rightmost set bit
75+
let rightmost_bit = num & -num;
76+
77+
// Use f64 log2 and convert to position
78+
let position = (rightmost_bit as f64).log2() as u32 + 1;
79+
80+
Ok(position)
81+
}
82+
83+
#[cfg(test)]
84+
mod tests {
85+
use super::*;
86+
87+
#[test]
88+
fn test_basic_cases() {
89+
// 18 = 10010 in binary, rightmost set bit at position 2
90+
assert_eq!(index_of_rightmost_set_bit(18).unwrap(), 2);
91+
92+
// 12 = 1100 in binary, rightmost set bit at position 3
93+
assert_eq!(index_of_rightmost_set_bit(12).unwrap(), 3);
94+
95+
// 5 = 101 in binary, rightmost set bit at position 1
96+
assert_eq!(index_of_rightmost_set_bit(5).unwrap(), 1);
97+
}
98+
99+
#[test]
100+
fn test_powers_of_two() {
101+
// 1 = 1 in binary, position 1
102+
assert_eq!(index_of_rightmost_set_bit(1).unwrap(), 1);
103+
104+
// 2 = 10 in binary, position 2
105+
assert_eq!(index_of_rightmost_set_bit(2).unwrap(), 2);
106+
107+
// 4 = 100 in binary, position 3
108+
assert_eq!(index_of_rightmost_set_bit(4).unwrap(), 3);
109+
110+
// 8 = 1000 in binary, position 4
111+
assert_eq!(index_of_rightmost_set_bit(8).unwrap(), 4);
112+
113+
// 16 = 10000 in binary, position 5
114+
assert_eq!(index_of_rightmost_set_bit(16).unwrap(), 5);
115+
116+
// 32 = 100000 in binary, position 6
117+
assert_eq!(index_of_rightmost_set_bit(32).unwrap(), 6);
118+
}
119+
120+
#[test]
121+
fn test_odd_numbers() {
122+
// All odd numbers have rightmost set bit at position 1
123+
assert_eq!(index_of_rightmost_set_bit(1).unwrap(), 1);
124+
assert_eq!(index_of_rightmost_set_bit(3).unwrap(), 1);
125+
assert_eq!(index_of_rightmost_set_bit(7).unwrap(), 1);
126+
assert_eq!(index_of_rightmost_set_bit(15).unwrap(), 1);
127+
assert_eq!(index_of_rightmost_set_bit(31).unwrap(), 1);
128+
}
129+
130+
#[test]
131+
fn test_even_numbers() {
132+
// 6 = 110 in binary, rightmost set bit at position 2
133+
assert_eq!(index_of_rightmost_set_bit(6).unwrap(), 2);
134+
135+
// 10 = 1010 in binary, rightmost set bit at position 2
136+
assert_eq!(index_of_rightmost_set_bit(10).unwrap(), 2);
137+
138+
// 20 = 10100 in binary, rightmost set bit at position 3
139+
assert_eq!(index_of_rightmost_set_bit(20).unwrap(), 3);
140+
}
141+
142+
#[test]
143+
fn test_zero() {
144+
assert!(index_of_rightmost_set_bit(0).is_err());
145+
assert_eq!(
146+
index_of_rightmost_set_bit(0).unwrap_err(),
147+
"input must be a positive integer"
148+
);
149+
}
150+
151+
#[test]
152+
fn test_negative_numbers() {
153+
assert!(index_of_rightmost_set_bit(-1).is_err());
154+
assert!(index_of_rightmost_set_bit(-10).is_err());
155+
assert_eq!(
156+
index_of_rightmost_set_bit(-5).unwrap_err(),
157+
"input must be a positive integer"
158+
);
159+
}
160+
161+
#[test]
162+
fn test_large_numbers() {
163+
// 1024 = 10000000000 in binary, position 11
164+
assert_eq!(index_of_rightmost_set_bit(1024).unwrap(), 11);
165+
166+
// 1023 = 1111111111 in binary, position 1
167+
assert_eq!(index_of_rightmost_set_bit(1023).unwrap(), 1);
168+
169+
// 2048 = 100000000000 in binary, position 12
170+
assert_eq!(index_of_rightmost_set_bit(2048).unwrap(), 12);
171+
}
172+
173+
#[test]
174+
fn test_consecutive_numbers() {
175+
// Testing a range to ensure correctness
176+
assert_eq!(index_of_rightmost_set_bit(14).unwrap(), 2); // 1110
177+
assert_eq!(index_of_rightmost_set_bit(15).unwrap(), 1); // 1111
178+
assert_eq!(index_of_rightmost_set_bit(16).unwrap(), 5); // 10000
179+
assert_eq!(index_of_rightmost_set_bit(17).unwrap(), 1); // 10001
180+
}
181+
182+
#[test]
183+
fn test_log_version() {
184+
// Test the alternative log-based implementation
185+
assert_eq!(index_of_rightmost_set_bit_log(18).unwrap(), 2);
186+
assert_eq!(index_of_rightmost_set_bit_log(12).unwrap(), 3);
187+
assert_eq!(index_of_rightmost_set_bit_log(5).unwrap(), 1);
188+
assert_eq!(index_of_rightmost_set_bit_log(16).unwrap(), 5);
189+
}
190+
191+
#[test]
192+
fn test_both_implementations_match() {
193+
// Verify both implementations give the same results
194+
for i in 1..=100 {
195+
assert_eq!(
196+
index_of_rightmost_set_bit(i).unwrap(),
197+
index_of_rightmost_set_bit_log(i).unwrap()
198+
);
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)