mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-12-19 07:00:35 +08:00
feat: Add BitRotate utility for circular bit rotations (#7011)
* feat: Add BitRotate utility for circular bit rotations * feat: Add BitRotate utility for circular bit rotations * feat: Add BitRotate utility for circular bit rotations * fix: Remove trailing spaces and add newline at EOF --------- Co-authored-by: Yajunesh M R <yajunesh@Yajuneshs-MacBook-Pro.local>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package com.thealgorithms.bitmanipulation;
|
||||
|
||||
/**
|
||||
* Utility class for performing circular bit rotations on 32-bit integers.
|
||||
* Bit rotation is a circular shift operation where bits shifted out on one end
|
||||
* are reinserted on the opposite end.
|
||||
*
|
||||
* <p>This class provides methods for both left and right circular rotations,
|
||||
* supporting only 32-bit integer operations with proper shift normalization
|
||||
* and error handling.</p>
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Bit_rotation">Bit Rotation</a>
|
||||
*/
|
||||
public final class BitRotate {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
* This is a utility class with only static methods.
|
||||
*/
|
||||
private BitRotate() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a circular left rotation (left shift) on a 32-bit integer.
|
||||
* Bits shifted out from the left side are inserted on the right side.
|
||||
*
|
||||
* @param value the 32-bit integer value to rotate
|
||||
* @param shift the number of positions to rotate left (must be non-negative)
|
||||
* @return the result of left rotating the value by the specified shift amount
|
||||
* @throws IllegalArgumentException if shift is negative
|
||||
*
|
||||
* @example
|
||||
* // Binary: 10000000 00000000 00000000 00000001
|
||||
* rotateLeft(0x80000001, 1)
|
||||
* // Returns: 3 (binary: 00000000 00000000 00000000 00000011)
|
||||
*/
|
||||
public static int rotateLeft(int value, int shift) {
|
||||
if (shift < 0) {
|
||||
throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
|
||||
}
|
||||
|
||||
// Normalize shift to the range [0, 31] using modulo 32
|
||||
shift = shift % 32;
|
||||
|
||||
if (shift == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Left rotation: (value << shift) | (value >>> (32 - shift))
|
||||
return (value << shift) | (value >>> (32 - shift));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a circular right rotation (right shift) on a 32-bit integer.
|
||||
* Bits shifted out from the right side are inserted on the left side.
|
||||
*
|
||||
* @param value the 32-bit integer value to rotate
|
||||
* @param shift the number of positions to rotate right (must be non-negative)
|
||||
* @return the result of right rotating the value by the specified shift amount
|
||||
* @throws IllegalArgumentException if shift is negative
|
||||
*
|
||||
* @example
|
||||
* // Binary: 00000000 00000000 00000000 00000011
|
||||
* rotateRight(3, 1)
|
||||
* // Returns: -2147483647 (binary: 10000000 00000000 00000000 00000001)
|
||||
*/
|
||||
public static int rotateRight(int value, int shift) {
|
||||
if (shift < 0) {
|
||||
throw new IllegalArgumentException("Shift amount cannot be negative: " + shift);
|
||||
}
|
||||
|
||||
// Normalize shift to the range [0, 31] using modulo 32
|
||||
shift = shift % 32;
|
||||
|
||||
if (shift == 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Right rotation: (value >>> shift) | (value << (32 - shift))
|
||||
return (value >>> shift) | (value << (32 - shift));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.thealgorithms.bitmanipulation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for BitRotate class covering typical, boundary, and edge cases.
|
||||
* Tests verify correct behavior for 32-bit circular bit rotations.
|
||||
*
|
||||
* @author Yajunesh
|
||||
*/
|
||||
public class BitRotateTest {
|
||||
|
||||
// ===== rotateLeft Tests =====
|
||||
|
||||
@Test
|
||||
public void testRotateLeftBasic() {
|
||||
// Basic left rotation
|
||||
assertEquals(0b00000000_00000000_00000000_00000010, BitRotate.rotateLeft(1, 1));
|
||||
assertEquals(0b00000000_00000000_00000000_00000100, BitRotate.rotateLeft(1, 2));
|
||||
assertEquals(0b00000000_00000000_00000000_00001000, BitRotate.rotateLeft(1, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateLeftWithCarry() {
|
||||
// Test bits carrying from left to right
|
||||
// Binary: 10000000_00000000_00000000_00000001
|
||||
int value = 0x80000001;
|
||||
// After left rotate by 1: 00000000_00000000_00000000_00000011
|
||||
assertEquals(3, BitRotate.rotateLeft(value, 1));
|
||||
|
||||
// Binary: 11000000_00000000_00000000_00000000
|
||||
value = 0xC0000000;
|
||||
// After left rotate by 1: 10000000_00000000_00000000_00000001
|
||||
assertEquals(0x80000001, BitRotate.rotateLeft(value, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateLeftShift32() {
|
||||
// Shift of 32 should be same as shift of 0 (modulo behavior)
|
||||
int value = 0x12345678;
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 32));
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 64));
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 96));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateLeftShiftNormalization() {
|
||||
// Test that shifts > 32 are properly normalized
|
||||
int value = 1;
|
||||
assertEquals(BitRotate.rotateLeft(value, 1), BitRotate.rotateLeft(value, 33));
|
||||
assertEquals(BitRotate.rotateLeft(value, 5), BitRotate.rotateLeft(value, 37));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateLeftZeroShift() {
|
||||
// Zero shift should return original value
|
||||
int value = 0xABCD1234;
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 0));
|
||||
}
|
||||
|
||||
// ===== rotateRight Tests =====
|
||||
|
||||
@Test
|
||||
public void testRotateRightBasic() {
|
||||
// Basic right rotation
|
||||
assertEquals(0b10000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 1));
|
||||
assertEquals(0b01000000_00000000_00000000_00000000, BitRotate.rotateRight(1, 2));
|
||||
assertEquals(0b00100000_00000000_00000000_00000000, BitRotate.rotateRight(1, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightWithCarry() {
|
||||
// Test bits carrying from right to left
|
||||
// Binary: 00000000_00000000_00000000_00000011
|
||||
int value = 3;
|
||||
// After right rotate by 1: 10000000_00000000_00000000_00000001
|
||||
assertEquals(0x80000001, BitRotate.rotateRight(value, 1));
|
||||
|
||||
// Binary: 00000000_00000000_00000000_00000001
|
||||
value = 1;
|
||||
// After right rotate by 1: 10000000_00000000_00000000_00000000
|
||||
assertEquals(0x80000000, BitRotate.rotateRight(value, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightShift32() {
|
||||
// Shift of 32 should be same as shift of 0 (modulo behavior)
|
||||
int value = 0x9ABCDEF0;
|
||||
assertEquals(value, BitRotate.rotateRight(value, 32));
|
||||
assertEquals(value, BitRotate.rotateRight(value, 64));
|
||||
assertEquals(value, BitRotate.rotateRight(value, 96));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightShiftNormalization() {
|
||||
// Test that shifts > 32 are properly normalized
|
||||
int value = 1;
|
||||
assertEquals(BitRotate.rotateRight(value, 1), BitRotate.rotateRight(value, 33));
|
||||
assertEquals(BitRotate.rotateRight(value, 7), BitRotate.rotateRight(value, 39));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightZeroShift() {
|
||||
// Zero shift should return original value
|
||||
int value = 0xDEADBEEF;
|
||||
assertEquals(value, BitRotate.rotateRight(value, 0));
|
||||
}
|
||||
|
||||
// ===== Edge Case Tests =====
|
||||
|
||||
@Test
|
||||
public void testRotateLeftMaxValue() {
|
||||
// Test with maximum integer value
|
||||
int value = Integer.MAX_VALUE; // 0x7FFFFFFF
|
||||
int rotated = BitRotate.rotateLeft(value, 1);
|
||||
// MAX_VALUE << 1 should become 0xFFFFFFFE, but with rotation it becomes different
|
||||
assertEquals(0xFFFFFFFE, rotated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightMinValue() {
|
||||
// Test with minimum integer value (treated as unsigned)
|
||||
int value = Integer.MIN_VALUE; // 0x80000000
|
||||
int rotated = BitRotate.rotateRight(value, 1);
|
||||
// MIN_VALUE >>> 1 should become 0x40000000, but with rotation from left
|
||||
assertEquals(0x40000000, rotated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateAllOnes() {
|
||||
// Test with all bits set
|
||||
int value = 0xFFFFFFFF; // All ones
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 13));
|
||||
assertEquals(value, BitRotate.rotateRight(value, 27));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateAllZeros() {
|
||||
// Test with all bits zero
|
||||
int value = 0x00000000;
|
||||
assertEquals(value, BitRotate.rotateLeft(value, 15));
|
||||
assertEquals(value, BitRotate.rotateRight(value, 19));
|
||||
}
|
||||
|
||||
// ===== Exception Tests =====
|
||||
|
||||
@Test
|
||||
public void testRotateLeftNegativeShift() {
|
||||
// Negative shifts should throw IllegalArgumentException
|
||||
Exception exception = assertThrows(IllegalArgumentException.class, () -> BitRotate.rotateLeft(42, -1));
|
||||
assertTrue(exception.getMessage().contains("negative"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightNegativeShift() {
|
||||
// Negative shifts should throw IllegalArgumentException
|
||||
Exception exception = assertThrows(IllegalArgumentException.class, () -> BitRotate.rotateRight(42, -5));
|
||||
assertTrue(exception.getMessage().contains("negative"));
|
||||
}
|
||||
|
||||
// ===== Complementary Operations Test =====
|
||||
|
||||
@Test
|
||||
public void testRotateLeftRightComposition() {
|
||||
// Rotating left then right by same amount should return original value
|
||||
int original = 0x12345678;
|
||||
int shift = 7;
|
||||
|
||||
int leftRotated = BitRotate.rotateLeft(original, shift);
|
||||
int restored = BitRotate.rotateRight(leftRotated, shift);
|
||||
|
||||
assertEquals(original, restored);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRightLeftComposition() {
|
||||
// Rotating right then left by same amount should return original value
|
||||
int original = 0x9ABCDEF0;
|
||||
int shift = 13;
|
||||
|
||||
int rightRotated = BitRotate.rotateRight(original, shift);
|
||||
int restored = BitRotate.rotateLeft(rightRotated, shift);
|
||||
|
||||
assertEquals(original, restored);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateLeft31IsSameAsRotateRight1() {
|
||||
// Rotating left by 31 should be same as rotating right by 1
|
||||
int value = 0x55555555;
|
||||
assertEquals(BitRotate.rotateLeft(value, 31), BitRotate.rotateRight(value, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTraversals() {
|
||||
// Test that methods don't throw exceptions
|
||||
assertDoesNotThrow(() -> BitRotate.rotateLeft(1, 1));
|
||||
assertDoesNotThrow(() -> BitRotate.rotateRight(1, 1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user