Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fb12971

Browse files
Added BitwiseGCD.java and BitwiseGCDTest.java (#6545)
1 parent 09cacae commit fb12971

File tree

2 files changed

+257
-0
lines changed

2 files changed

+257
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.thealgorithms.bitmanipulation;
2+
3+
import java.math.BigInteger;
4+
5+
/**
6+
* Bitwise GCD implementation with full-range support utilities.
7+
*
8+
* <p>This class provides a fast binary (Stein's) GCD implementation for {@code long}
9+
* inputs and a BigInteger-backed API for full 2's-complement range support (including
10+
* {@code Long.MIN_VALUE}). The {@code long} implementation is efficient and avoids
11+
* division/modulo operations. For edge-cases that overflow signed-64-bit ranges
12+
* (e.g., gcd(Long.MIN_VALUE, 0) = 2^63), use the BigInteger API {@code gcdBig}.
13+
*
14+
* <p>Behaviour:
15+
* <ul>
16+
* <li>{@code gcd(long,long)} : returns non-negative {@code long} gcd for inputs whose
17+
* absolute values fit in signed {@code long} (i.e., not causing an unsigned 2^63 result).
18+
* If the true gcd does not fit in a signed {@code long} (for example gcd(Long.MIN_VALUE,0) = 2^63)
19+
* this method will delegate to BigInteger and throw {@link ArithmeticException} if the
20+
* BigInteger result does not fit into a signed {@code long}.</li>
21+
* <li>{@code gcdBig(BigInteger, BigInteger)} : returns the exact gcd as a {@link BigInteger}
22+
* and works for the full signed-64-bit range and beyond.</li>
23+
* </ul>
24+
*/
25+
public final class BitwiseGCD {
26+
27+
private BitwiseGCD() {
28+
}
29+
30+
/**
31+
* Computes GCD of two long values using Stein's algorithm (binary GCD).
32+
* <p>Handles negative inputs. If either input is {@code Long.MIN_VALUE} the
33+
* method delegates to the BigInteger implementation and will throw {@link ArithmeticException}
34+
* if the result cannot be represented as a signed {@code long}.
35+
*
36+
* @param a first value (may be negative)
37+
* @param b second value (may be negative)
38+
* @return non-negative gcd as a {@code long}
39+
* @throws ArithmeticException when the exact gcd does not fit into a signed {@code long}
40+
*/
41+
public static long gcd(long a, long b) {
42+
// Trivial cases
43+
if (a == 0L) {
44+
return absOrThrowIfOverflow(b);
45+
}
46+
if (b == 0L) {
47+
return absOrThrowIfOverflow(a);
48+
}
49+
50+
// If either is Long.MIN_VALUE, absolute value doesn't fit into signed long.
51+
if (a == Long.MIN_VALUE || b == Long.MIN_VALUE) {
52+
// Delegate to BigInteger and try to return a long if it fits
53+
BigInteger g = gcdBig(BigInteger.valueOf(a), BigInteger.valueOf(b));
54+
return g.longValueExact();
55+
}
56+
57+
// Work with non-negative long values now (safe because we excluded Long.MIN_VALUE)
58+
a = (a < 0) ? -a : a;
59+
b = (b < 0) ? -b : b;
60+
61+
// Count common factors of 2
62+
int commonTwos = Long.numberOfTrailingZeros(a | b);
63+
64+
// Remove all factors of 2 from a
65+
a >>= Long.numberOfTrailingZeros(a);
66+
67+
while (b != 0L) {
68+
// Remove all factors of 2 from b
69+
b >>= Long.numberOfTrailingZeros(b);
70+
71+
// Now both a and b are odd. Ensure a <= b
72+
if (a > b) {
73+
long tmp = a;
74+
a = b;
75+
b = tmp;
76+
}
77+
78+
// b >= a; subtract a from b (result is even)
79+
b = b - a;
80+
}
81+
82+
// Restore common powers of two
83+
return a << commonTwos;
84+
}
85+
86+
/**
87+
* Helper to return absolute value of x unless x == Long.MIN_VALUE, in which
88+
* case we delegate to BigInteger and throw to indicate overflow.
89+
*/
90+
private static long absOrThrowIfOverflow(long x) {
91+
if (x == Long.MIN_VALUE) {
92+
// |Long.MIN_VALUE| = 2^63 which does not fit into signed long
93+
throw new ArithmeticException("Absolute value of Long.MIN_VALUE does not fit into signed long. Use gcdBig() for full-range support.");
94+
}
95+
return (x < 0) ? -x : x;
96+
}
97+
98+
/**
99+
* Computes GCD for an array of {@code long} values. Returns 0 for empty/null arrays.
100+
* If any intermediate gcd cannot be represented in signed long (rare), an ArithmeticException
101+
* will be thrown.
102+
*/
103+
public static long gcd(long... values) {
104+
105+
if (values == null || values.length == 0) {
106+
return 0L;
107+
}
108+
long result = values[0];
109+
for (int i = 1; i < values.length; i++) {
110+
result = gcd(result, values[i]);
111+
if (result == 1L) {
112+
return 1L; // early exit
113+
}
114+
}
115+
return result;
116+
}
117+
118+
/**
119+
* BigInteger-backed gcd that works for the full integer range (and beyond).
120+
* This is the recommended method when inputs may be Long.MIN_VALUE or when you
121+
* need an exact result even if it is greater than Long.MAX_VALUE.
122+
* @param a first value (may be negative)
123+
* @param b second value (may be negative)
124+
* @return non-negative gcd as a {@link BigInteger}
125+
*/
126+
public static BigInteger gcdBig(BigInteger a, BigInteger b) {
127+
128+
if (a == null || b == null) {
129+
throw new NullPointerException("Arguments must not be null");
130+
}
131+
return a.abs().gcd(b.abs());
132+
}
133+
134+
/**
135+
* Convenience overload that accepts signed-64 inputs and returns BigInteger gcd.
136+
*/
137+
public static BigInteger gcdBig(long a, long b) {
138+
return gcdBig(BigInteger.valueOf(a), BigInteger.valueOf(b));
139+
}
140+
141+
/**
142+
* int overload for convenience.
143+
*/
144+
public static int gcd(int a, int b) {
145+
return (int) gcd((long) a, (long) b);
146+
}
147+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.thealgorithms.bitmanipulation;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.math.BigInteger;
7+
import org.junit.jupiter.api.Test;
8+
9+
public class BitwiseGCDTest {
10+
11+
@Test
12+
public void testGcdBasic() {
13+
assertEquals(6L, BitwiseGCD.gcd(48L, 18L));
14+
}
15+
16+
@Test
17+
public void testGcdZeroAndNonZero() {
18+
assertEquals(5L, BitwiseGCD.gcd(0L, 5L));
19+
assertEquals(5L, BitwiseGCD.gcd(5L, 0L));
20+
}
21+
22+
@Test
23+
public void testGcdBothZero() {
24+
assertEquals(0L, BitwiseGCD.gcd(0L, 0L));
25+
}
26+
27+
@Test
28+
public void testGcdNegativeInputs() {
29+
assertEquals(6L, BitwiseGCD.gcd(-48L, 18L));
30+
assertEquals(6L, BitwiseGCD.gcd(48L, -18L));
31+
assertEquals(6L, BitwiseGCD.gcd(-48L, -18L));
32+
}
33+
34+
@Test
35+
public void testGcdIntOverload() {
36+
assertEquals(6, BitwiseGCD.gcd(48, 18));
37+
}
38+
39+
@Test
40+
public void testGcdArray() {
41+
long[] values = {48L, 18L, 6L};
42+
assertEquals(6L, BitwiseGCD.gcd(values));
43+
}
44+
45+
@Test
46+
public void testGcdEmptyArray() {
47+
long[] empty = {};
48+
assertEquals(0L, BitwiseGCD.gcd(empty));
49+
}
50+
51+
@Test
52+
public void testGcdCoprime() {
53+
assertEquals(1L, BitwiseGCD.gcd(17L, 13L));
54+
}
55+
56+
@Test
57+
public void testGcdPowersOfTwo() {
58+
assertEquals(1024L, BitwiseGCD.gcd(1L << 20, 1L << 10));
59+
}
60+
61+
@Test
62+
public void testGcdLargeNumbers() {
63+
assertEquals(6L, BitwiseGCD.gcd(270L, 192L));
64+
}
65+
66+
@Test
67+
public void testGcdEarlyExitArray() {
68+
long[] manyCoprimes = {7L, 11L, 13L, 17L, 19L, 23L, 29L};
69+
assertEquals(1L, BitwiseGCD.gcd(manyCoprimes));
70+
}
71+
72+
@Test
73+
public void testGcdSameNumbers() {
74+
assertEquals(42L, BitwiseGCD.gcd(42L, 42L));
75+
}
76+
77+
@Test
78+
public void testGcdLongMinValueBigInteger() {
79+
// gcd(Long.MIN_VALUE, 0) = |Long.MIN_VALUE| = 2^63; must use BigInteger to represent it
80+
BigInteger expected = BigInteger.ONE.shiftLeft(63); // 2^63
81+
assertEquals(expected, BitwiseGCD.gcdBig(Long.MIN_VALUE, 0L));
82+
}
83+
84+
@Test
85+
public void testGcdLongMinValueLongOverloadThrows() {
86+
// The long overload cannot return 2^63 as a positive signed long, so it must throw
87+
assertThrows(ArithmeticException.class, () -> BitwiseGCD.gcd(Long.MIN_VALUE, 0L));
88+
}
89+
90+
@Test
91+
public void testGcdWithLongMinAndOther() {
92+
// gcd(Long.MIN_VALUE, 2^10) should be 2^10
93+
long p = 1L << 10;
94+
BigInteger expected = BigInteger.valueOf(p);
95+
assertEquals(expected, BitwiseGCD.gcdBig(Long.MIN_VALUE, p));
96+
}
97+
98+
@Test
99+
public void testGcdWithBothLongMin() {
100+
// gcd(Long.MIN_VALUE, Long.MIN_VALUE) = 2^63
101+
BigInteger expected = BigInteger.ONE.shiftLeft(63);
102+
assertEquals(expected, BitwiseGCD.gcdBig(Long.MIN_VALUE, Long.MIN_VALUE));
103+
}
104+
105+
@Test
106+
public void testGcdEdgeCasesMixed() {
107+
assertEquals(1L, BitwiseGCD.gcd(1L, Long.MAX_VALUE));
108+
assertEquals(1L, BitwiseGCD.gcd(Long.MAX_VALUE, 1L));
109+
}
110+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /