Skip to content

Commit 3976ad2

Browse files
Yajunesh M RYajunesh M R
authored andcommitted
feat: Add BitRotate utility for circular bit rotations
1 parent dfd8d69 commit 3976ad2

File tree

2 files changed

+277
-0
lines changed

2 files changed

+277
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.thealgorithms.bitmanipulation;
2+
3+
/**
4+
* Utility class for performing circular bit rotations on 32-bit integers.
5+
* Bit rotation is a circular shift operation where bits shifted out on one end
6+
* are reinserted on the opposite end.
7+
*
8+
* <p>This class provides methods for both left and right circular rotations,
9+
* supporting only 32-bit integer operations with proper shift normalization
10+
* and error handling.</p>
11+
*
12+
* @see <a href="https://en.wikipedia.org/wiki/Bit_rotation">Bit Rotation</a>
13+
*/
14+
public final class BitRotate {
15+
16+
/**
17+
* Private constructor to prevent instantiation.
18+
* This is a utility class with only static methods.
19+
*/
20+
private BitRotate() {
21+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
22+
}
23+
24+
/**
25+
* Performs a circular left rotation (left shift) on a 32-bit integer.
26+
* Bits shifted out from the left side are inserted on the right side.
27+
*
28+
* @param value the 32-bit integer value to rotate
29+
* @param shift the number of positions to rotate left (must be non-negative)
30+
* @return the result of left rotating the value by the specified shift amount
31+
* @throws IllegalArgumentException if shift is negative
32+
*
33+
* @example
34+
* // Binary: 10000000 00000000 00000000 00000001
35+
* rotateLeft(0x80000001, 1)
36+
* // Returns: 3 (binary: 00000000 00000000 00000000 00000011)
37+
*/
38+
public static int rotateLeft(int value, int shift) {
39+
if (shift < 0) {
40+
throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
41+
}
42+
43+
// Normalize shift to the range [0, 31] using modulo 32
44+
shift = shift % 32;
45+
46+
if (shift == 0) {
47+
return value;
48+
}
49+
50+
// Left rotation: (value << shift) | (value >>> (32 - shift))
51+
return (value << shift) | (value >>> (32 - shift));
52+
}
53+
54+
/**
55+
* Performs a circular right rotation (right shift) on a 32-bit integer.
56+
* Bits shifted out from the right side are inserted on the left side.
57+
*
58+
* @param value the 32-bit integer value to rotate
59+
* @param shift the number of positions to rotate right (must be non-negative)
60+
* @return the result of right rotating the value by the specified shift amount
61+
* @throws IllegalArgumentException if shift is negative
62+
*
63+
* @example
64+
* // Binary: 00000000 00000000 00000000 00000011
65+
* rotateRight(3, 1)
66+
* // Returns: -2147483647 (binary: 10000000 00000000 00000000 00000001)
67+
*/
68+
public static int rotateRight(int value, int shift) {
69+
if (shift < 0) {
70+
throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
71+
}
72+
73+
// Normalize shift to the range [0, 31] using modulo 32
74+
shift = shift % 32;
75+
76+
if (shift == 0) {
77+
return value;
78+
}
79+
80+
// Right rotation: (value >>> shift) | (value << (32 - shift))
81+
return (value >>> shift) | (value << (32 - shift));
82+
}
83+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package com.thealgorithms.bitmanipulation;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
import org.junit.jupiter.api.Test;
5+
6+
/**
7+
* Unit tests for BitRotate class covering typical, boundary, and edge cases.
8+
* Tests verify correct behavior for 32-bit circular bit rotations.
9+
*/
10+
public class BitRotateTest {
11+
12+
// ===== rotateLeft Tests =====
13+
14+
@Test
15+
void testRotateLeftBasic() {
16+
// Basic left rotation
17+
assertEquals(0b00000000_00000000_00000000_00000010, BitRotate.rotateLeft(1, 1));
18+
assertEquals(0b00000000_00000000_00000000_00000100, BitRotate.rotateLeft(1, 2));
19+
assertEquals(0b00000000_00000000_00000000_00001000, BitRotate.rotateLeft(1, 3));
20+
}
21+
22+
@Test
23+
void testRotateLeftWithCarry() {
24+
// Test bits carrying from left to right
25+
// Binary: 10000000_00000000_00000000_00000001
26+
int value = 0x80000001;
27+
// After left rotate by 1: 00000000_00000000_00000000_00000011
28+
assertEquals(3, BitRotate.rotateLeft(value, 1));
29+
30+
// Binary: 11000000_00000000_00000000_00000000
31+
value = 0xC0000000;
32+
// After left rotate by 1: 10000000_00000000_00000000_00000001
33+
assertEquals(0x80000001, BitRotate.rotateLeft(value, 1));
34+
}
35+
36+
@Test
37+
void testRotateLeftShift32() {
38+
// Shift of 32 should be same as shift of 0 (modulo behavior)
39+
int value = 0x12345678;
40+
assertEquals(value, BitRotate.rotateLeft(value, 32));
41+
assertEquals(value, BitRotate.rotateLeft(value, 64));
42+
assertEquals(value, BitRotate.rotateLeft(value, 96));
43+
}
44+
45+
@Test
46+
void testRotateLeftShiftNormalization() {
47+
// Test that shifts > 32 are properly normalized
48+
int value = 1;
49+
assertEquals(BitRotate.rotateLeft(value, 1), BitRotate.rotateLeft(value, 33));
50+
assertEquals(BitRotate.rotateLeft(value, 5), BitRotate.rotateLeft(value, 37));
51+
}
52+
53+
@Test
54+
void testRotateLeftZeroShift() {
55+
// Zero shift should return original value
56+
int value = 0xABCD1234;
57+
assertEquals(value, BitRotate.rotateLeft(value, 0));
58+
}
59+
60+
// ===== rotateRight Tests =====
61+
62+
@Test
63+
void testRotateRightBasic() {
64+
// Basic right rotation
65+
assertEquals(0b10000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 1));
66+
assertEquals(0b01000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 2));
67+
assertEquals(0b00100000_00000000_00000000_00000000, BitRotate.rotateRight(1, 3));
68+
}
69+
70+
@Test
71+
void testRotateRightWithCarry() {
72+
// Test bits carrying from right to left
73+
// Binary: 00000000_00000000_00000000_00000011
74+
int value = 3;
75+
// After right rotate by 1: 10000000_00000000_00000000_00000001
76+
assertEquals(0x80000001, BitRotate.rotateRight(value, 1));
77+
78+
// Binary: 00000000_00000000_00000000_00000001
79+
value = 1;
80+
// After right rotate by 1: 10000000_00000000_00000000_00000000
81+
assertEquals(0x80000000, BitRotate.rotateRight(value, 1));
82+
}
83+
84+
@Test
85+
void testRotateRightShift32() {
86+
// Shift of 32 should be same as shift of 0 (modulo behavior)
87+
int value = 0x9ABCDEF0;
88+
assertEquals(value, BitRotate.rotateRight(value, 32));
89+
assertEquals(value, BitRotate.rotateRight(value, 64));
90+
assertEquals(value, BitRotate.rotateRight(value, 96));
91+
}
92+
93+
@Test
94+
void testRotateRightShiftNormalization() {
95+
// Test that shifts > 32 are properly normalized
96+
int value = 1;
97+
assertEquals(BitRotate.rotateRight(value, 1), BitRotate.rotateRight(value, 33));
98+
assertEquals(BitRotate.rotateRight(value, 7), BitRotate.rotateRight(value, 39));
99+
}
100+
101+
@Test
102+
void testRotateRightZeroShift() {
103+
// Zero shift should return original value
104+
int value = 0xDEADBEEF;
105+
assertEquals(value, BitRotate.rotateRight(value, 0));
106+
}
107+
108+
// ===== Edge Case Tests =====
109+
110+
@Test
111+
void testRotateLeftMaxValue() {
112+
// Test with maximum integer value
113+
int value = Integer.MAX_VALUE; // 0x7FFFFFFF
114+
int rotated = BitRotate.rotateLeft(value, 1);
115+
// MAX_VALUE << 1 should become 0xFFFFFFFE, but with rotation it becomes different
116+
assertEquals(0xFFFFFFFE, rotated);
117+
}
118+
119+
@Test
120+
void testRotateRightMinValue() {
121+
// Test with minimum integer value (treated as unsigned)
122+
int value = Integer.MIN_VALUE; // 0x80000000
123+
int rotated = BitRotate.rotateRight(value, 1);
124+
// MIN_VALUE >>> 1 should become 0x40000000, but with rotation from left
125+
assertEquals(0x40000000, rotated);
126+
}
127+
128+
@Test
129+
void testRotateAllOnes() {
130+
// Test with all bits set
131+
int value = 0xFFFFFFFF; // All ones
132+
assertEquals(value, BitRotate.rotateLeft(value, 13));
133+
assertEquals(value, BitRotate.rotateRight(value, 27));
134+
}
135+
136+
@Test
137+
void testRotateAllZeros() {
138+
// Test with all bits zero
139+
int value = 0x00000000;
140+
assertEquals(value, BitRotate.rotateLeft(value, 15));
141+
assertEquals(value, BitRotate.rotateRight(value, 19));
142+
}
143+
144+
// ===== Exception Tests =====
145+
146+
@Test
147+
void testRotateLeftNegativeShift() {
148+
// Negative shifts should throw IllegalArgumentException
149+
Exception exception = assertThrows(IllegalArgumentException.class,
150+
() -> BitRotate.rotateLeft(42, -1));
151+
assertTrue(exception.getMessage().contains("negative"));
152+
}
153+
154+
@Test
155+
void testRotateRightNegativeShift() {
156+
// Negative shifts should throw IllegalArgumentException
157+
Exception exception = assertThrows(IllegalArgumentException.class,
158+
() -> BitRotate.rotateRight(42, -5));
159+
assertTrue(exception.getMessage().contains("negative"));
160+
}
161+
162+
// ===== Complementary Operations Test =====
163+
164+
@Test
165+
void testRotateLeftRightComposition() {
166+
// Rotating left then right by same amount should return original value
167+
int original = 0x12345678;
168+
int shift = 7;
169+
170+
int leftRotated = BitRotate.rotateLeft(original, shift);
171+
int restored = BitRotate.rotateRight(leftRotated, shift);
172+
173+
assertEquals(original, restored);
174+
}
175+
176+
@Test
177+
void testRotateRightLeftComposition() {
178+
// Rotating right then left by same amount should return original value
179+
int original = 0x9ABCDEF0;
180+
int shift = 13;
181+
182+
int rightRotated = BitRotate.rotateRight(original, shift);
183+
int restored = BitRotate.rotateLeft(rightRotated, shift);
184+
185+
assertEquals(original, restored);
186+
}
187+
188+
@Test
189+
void testRotateLeft31IsSameAsRotateRight1() {
190+
// Rotating left by 31 should be same as rotating right by 1
191+
int value = 0x55555555;
192+
assertEquals(BitRotate.rotateLeft(value, 31), BitRotate.rotateRight(value, 1));
193+
}
194+
}

0 commit comments

Comments
 (0)