|
1 | 1 | /**
|
2 | 2 | * @file
|
3 |
| - * @brief Compute the greatest common denominator of two integers using |
4 |
| - * *recursive form* of |
5 |
| - * [Euclidean algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm) |
| 3 | + * @brief Implementation of the [Euclidean |
| 4 | + * algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm) to find the |
| 5 | + * Greatest Common Divisor (GCD) using a recursive approach. |
6 | 6 | *
|
7 |
| - * @see gcd_iterative_euclidean.cpp, gcd_of_n_numbers.cpp |
| 7 | + * @details |
| 8 | + * The Euclidean algorithm computes the GCD of two integers efficiently: |
| 9 | + * - Uses modulus instead of repeated subtraction (faster) |
| 10 | + * - Handles negative numbers and zero correctly |
| 11 | + * |
| 12 | + * ### Algorithm |
| 13 | + * For integers a and b: |
| 14 | + * 1. If b is 0, return |a| |
| 15 | + * 2. Otherwise, recursively compute GCD(b, a % b) |
| 16 | + * |
| 17 | + * ### Complexity |
| 18 | + * - Time Complexity: O(log(min(|a|, |b|))) |
| 19 | + * - Space Complexity: O(log(min(|a|, |b|))) due to recursion stack |
| 20 | + * |
| 21 | + * @author [Contributor](Optimize Recursive GCD: Use Modulus-Based Euclidean |
| 22 | + * Algorithm with Robust Edge Case Handling(https://github.com/kokatesaurabh)) |
8 | 23 | */
|
| 24 | + |
| 25 | +#include <cassert> |
| 26 | +#include <cmath> |
9 | 27 | #include <iostream>
|
| 28 | +#include <stdexcept> |
| 29 | + |
| 30 | +namespace math { |
| 31 | +namespace gcd_recursive { |
10 | 32 |
|
11 | 33 | /**
|
12 |
| - * algorithm |
| 34 | + * @brief Compute GCD of two integers using recursive Euclidean algorithm |
| 35 | + * @param a First integer |
| 36 | + * @param b Second integer |
| 37 | + * @returns GCD of a and b |
| 38 | + * @throws std::invalid_argument if both a and b are zero |
13 | 39 | */
|
14 |
| -int gcd(int num1, int num2) { |
15 |
| - if (num1 <= 0 | num2 <= 0) { |
16 |
| - throw std::domain_error("Euclidean algorithm domain is for ints > 0"); |
17 |
| - } |
| 40 | +int64_t gcd(int64_t a, int64_t b) { |
| 41 | + a = std::abs(a); |
| 42 | + b = std::abs(b); |
| 43 | + |
| 44 | + if (a == 0 && b == 0) |
| 45 | + throw std::invalid_argument("GCD(0,0) is undefined"); |
| 46 | + |
| 47 | + if (b == 0) |
| 48 | + return a; |
| 49 | + |
| 50 | + return gcd(b, a % b); |
| 51 | +} |
| 52 | + |
| 53 | +} // namespace gcd_recursive |
| 54 | +} // namespace math |
| 55 | + |
| 56 | +/** |
| 57 | + * @brief Run comprehensive test cases |
| 58 | + */ |
| 59 | +static void test() { |
| 60 | + using math::gcd_recursive::gcd; |
| 61 | + |
| 62 | + // Basic positive numbers |
| 63 | + assert(gcd(48, 18) == 6); |
| 64 | + assert(gcd(56, 42) == 14); |
| 65 | + assert(gcd(17, 13) == 1); |
18 | 66 |
|
19 |
| - if (num1 == num2) { |
20 |
| - return num1; |
| 67 | + // Zero cases |
| 68 | + assert(gcd(0, 5) == 5); |
| 69 | + assert(gcd(7, 0) == 7); |
| 70 | + |
| 71 | + // Both zero (should throw) |
| 72 | + try { |
| 73 | + gcd(0, 0); |
| 74 | + assert(false); // Should not reach here |
| 75 | + } catch (const std::invalid_argument&) { |
21 | 76 | }
|
22 | 77 |
|
23 |
| - // Everything divides 0 |
24 |
| - if (num1 == 0) |
25 |
| - return num2; |
26 |
| - if (num2 == 0) |
27 |
| - return num1; |
| 78 | + // Negative numbers |
| 79 | + assert(gcd(-48, 18) == 6); |
| 80 | + assert(gcd(48, -18) == 6); |
| 81 | + assert(gcd(-48, -18) == 6); |
| 82 | + |
| 83 | + // Equal numbers |
| 84 | + assert(gcd(15, 15) == 15); |
| 85 | + assert(gcd(100, 100) == 100); |
28 | 86 |
|
29 |
| - // base case |
30 |
| - if (num1 == num2) |
31 |
| - return num1; |
| 87 | + // Multiples |
| 88 | + assert(gcd(25, 100) == 25); |
| 89 | + assert(gcd(17, 51) == 17); |
32 | 90 |
|
33 |
| - // a is greater |
34 |
| - if (num1 > num2) |
35 |
| - return gcd(num1 - num2, num2); |
36 |
| - return gcd(num1, num2 - num1); |
| 91 | + // Large numbers |
| 92 | + assert(gcd(123456789, 987654321) == 9); |
| 93 | + assert(gcd(1000000007, 1000000009) == 1); |
| 94 | + |
| 95 | + std::cout << "All GCD tests passed successfully!\n"; |
37 | 96 | }
|
38 | 97 |
|
39 | 98 | /**
|
40 |
| - * Main function |
| 99 | + * @brief Demonstrate example usage |
41 | 100 | */
|
| 101 | +static void demonstrate_usage() { |
| 102 | + using math::gcd_recursive::gcd; |
| 103 | + |
| 104 | + std::cout << "GCD Examples:\n"; |
| 105 | + std::cout << "GCD(48, 18) = " << gcd(48, 18) << "\n"; |
| 106 | + std::cout << "GCD(17, 13) = " << gcd(17, 13) << " (co-prime)\n"; |
| 107 | + std::cout << "GCD(0, 15) = " << gcd(0, 15) << "\n"; |
| 108 | + std::cout << "GCD(-48, 18) = " << gcd(-48, 18) << "\n"; |
| 109 | + std::cout << "GCD(123456789, 987654321) = " << gcd(123456789, 987654321) |
| 110 | + << "\n"; |
| 111 | +} |
| 112 | + |
42 | 113 | int main() {
|
43 |
| - std::cout << "gcd of 120,7 is " << (gcd(120, 7)) << std::endl; |
44 |
| - try { |
45 |
| - std::cout << "gcd of -120,10 is " << gcd(-120, 10) << std::endl; |
46 |
| - } catch (const std::domain_error &e) { |
47 |
| - std::cout << "Error handling was successful" << std::endl; |
48 |
| - } |
49 |
| - std::cout << "gcd of 312,221 is " << (gcd(312, 221)) << std::endl; |
50 |
| - std::cout << "gcd of 289,204 is " << (gcd(289, 204)) << std::endl; |
| 114 | + test(); |
| 115 | + demonstrate_usage(); |
51 | 116 | return 0;
|
52 | 117 | }
|
0 commit comments