Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Oct 21, 2025

📄 16% (0.16x) speedup for gcd_recursive in src/math/computation.py

⏱️ Runtime : 341 microseconds 293 microseconds (best of 336 runs)

📝 Explanation and details

The optimization replaces recursion with iteration, eliminating Python's function call overhead which is the primary bottleneck in recursive algorithms.

Key changes:

  • if b == 0: return a + return gcd_recursive(b, a % b)while b: a, b = b, a % b + return a
  • Removes 4,318 recursive function calls that were consuming 68.1% of the original runtime
  • Uses tuple unpacking for efficient variable swapping

Why it's faster:
Python function calls are expensive due to stack frame creation, parameter passing, and return value handling. The line profiler shows the recursive call consumed 1.991 microseconds (68.1% of total time). The iterative version eliminates this overhead entirely, executing the same mathematical operations in a tight loop.

Performance characteristics:

  • Best gains (25-50% faster): Cases requiring multiple iterations like coprime numbers, large number pairs, and Fibonacci-like sequences
  • Modest gains (15-25% faster): Simple cases with fewer iterations
  • Minimal/no gains: Trivial cases that terminate immediately (e.g., gcd(9, 0))

The 16% overall speedup comes from removing Python's recursive overhead while preserving identical mathematical behavior.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 3101 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 1 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
from src.math.computation import gcd_recursive

# unit tests

# ---------------------------
# Basic Test Cases
# ---------------------------

def test_gcd_basic_coprime():
    # Coprime numbers: gcd(8, 15) == 1
    codeflash_output = gcd_recursive(8, 15) # 417ns -> 333ns (25.2% faster)

def test_gcd_basic_common_divisor():
    # Simple case: gcd(12, 18) == 6
    codeflash_output = gcd_recursive(12, 18) # 375ns -> 292ns (28.4% faster)

def test_gcd_basic_equal_numbers():
    # Both numbers are the same: gcd(7, 7) == 7
    codeflash_output = gcd_recursive(7, 7) # 291ns -> 250ns (16.4% faster)

def test_gcd_basic_one_is_multiple_of_other():
    # One is a multiple of the other: gcd(21, 7) == 7
    codeflash_output = gcd_recursive(21, 7) # 291ns -> 250ns (16.4% faster)
    codeflash_output = gcd_recursive(7, 21) # 208ns -> 208ns (0.000% faster)

def test_gcd_basic_one_is_zero():
    # One argument is zero: gcd(0, 9) == 9, gcd(9, 0) == 9
    codeflash_output = gcd_recursive(0, 9) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(9, 0) # 125ns -> 125ns (0.000% faster)

# ---------------------------
# Edge Test Cases
# ---------------------------

def test_gcd_edge_both_zero():
    # Both arguments are zero: gcd(0, 0) is mathematically undefined, but should return 0 for practical purposes
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 166ns (24.7% slower)

def test_gcd_edge_negative_numbers():
    # Negative numbers should return positive gcd
    codeflash_output = gcd_recursive(-12, 18) # 416ns -> 333ns (24.9% faster)
    codeflash_output = gcd_recursive(12, -18) # 333ns -> 292ns (14.0% faster)
    codeflash_output = gcd_recursive(-12, -18) # 250ns -> 250ns (0.000% faster)

def test_gcd_edge_one_is_one():
    # One argument is 1 or -1: gcd(1, n) == 1, gcd(-1, n) == 1
    for n in [1, 2, 100, -5, -100]:
        codeflash_output = gcd_recursive(1, n) # 1.04μs -> 834ns (24.7% faster)
        codeflash_output = gcd_recursive(n, 1)
        codeflash_output = gcd_recursive(-1, n) # 540ns -> 499ns (8.22% faster)
        codeflash_output = gcd_recursive(n, -1)

def test_gcd_edge_large_and_small_mix():
    # Large and small numbers: gcd(1_000_000_000, 2) == 2
    codeflash_output = gcd_recursive(1_000_000_000, 2) # 292ns -> 250ns (16.8% faster)

def test_gcd_edge_prime_and_composite():
    # Prime and composite: gcd(13, 26) == 13
    codeflash_output = gcd_recursive(13, 26) # 333ns -> 292ns (14.0% faster)

# ---------------------------
# Large Scale Test Cases
# ---------------------------

def test_gcd_large_numbers():
    # Large numbers: gcd(1234567890, 987654321) == 9
    codeflash_output = gcd_recursive(1234567890, 987654321) # 583ns -> 541ns (7.76% faster)

def test_gcd_large_coprime_numbers():
    # Large coprime numbers: gcd(999983, 999979) == 1 (both are large primes)
    codeflash_output = gcd_recursive(999983, 999979) # 458ns -> 375ns (22.1% faster)

def test_gcd_large_power_of_two():
    # Power of two and a multiple: gcd(2**30, 2**20) == 2**20
    codeflash_output = gcd_recursive(2**30, 2**20) # 333ns -> 292ns (14.0% faster)

def test_gcd_large_with_zero():
    # Large number and zero: gcd(10**12, 0) == 10**12
    codeflash_output = gcd_recursive(10**12, 0) # 166ns -> 167ns (0.599% slower)

def test_gcd_large_negative_numbers():
    # Large negative numbers: gcd(-10**12, -10**6) == 10**6
    codeflash_output = gcd_recursive(-10**12, -10**6) # 292ns -> 291ns (0.344% faster)

# ---------------------------
# Additional Edge Cases
# ---------------------------

@pytest.mark.parametrize("a,b,expected", [
    (0, 1, 1),
    (1, 0, 1),
    (0, -1, 1),
    (-1, 0, 1),
    (0, 0, 0),
    (123, 0, 123),
    (0, 123, 123),
    (0, -123, 123),
    (-123, 0, 123),
])
def test_gcd_zero_and_one_variants(a, b, expected):
    # Various combinations of zeros and ones, positive and negative
    codeflash_output = gcd_recursive(a, b) # 1.96μs -> 1.92μs (2.19% faster)

def test_gcd_commutativity():
    # GCD should be commutative: gcd(a, b) == gcd(b, a)
    pairs = [
        (15, 8),
        (100, 25),
        (7, 13),
        (0, 5),
        (-12, 18),
        (2**20, 2**15),
        (999983, 999979),
    ]
    for a, b in pairs:
        codeflash_output = gcd_recursive(a, b) # 1.71μs -> 1.33μs (28.2% faster)

def test_gcd_recursive_depth():
    # Test that the function does not hit recursion limit for reasonable large inputs
    # (Euclidean algorithm is logarithmic in steps)
    a = 987654321
    b = 123456789
    # Should complete without RecursionError
    codeflash_output = gcd_recursive(a, b); result = codeflash_output # 333ns -> 333ns (0.000% faster)

# ---------------------------
# Mutation Testing Guards
# ---------------------------

def test_gcd_mutation_guard():
    # If the implementation is changed to return min(a, b) or max(a, b), these cases will fail
    codeflash_output = gcd_recursive(14, 21) # 375ns -> 333ns (12.6% faster)
    codeflash_output = gcd_recursive(14, 28) # 167ns -> 166ns (0.602% faster)
    codeflash_output = gcd_recursive(17, 19) # 250ns -> 208ns (20.2% faster)
    codeflash_output = gcd_recursive(100, 10) # 125ns -> 83ns (50.6% faster)
    codeflash_output = gcd_recursive(10, 100) # 125ns -> 83ns (50.6% faster)
    codeflash_output = gcd_recursive(0, 0) # 83ns -> 83ns (0.000% faster)
    codeflash_output = gcd_recursive(-7, 21) # 208ns -> 166ns (25.3% faster)
    codeflash_output = gcd_recursive(21, -7) # 167ns -> 125ns (33.6% faster)
    codeflash_output = gcd_recursive(-21, -7) # 125ns -> 83ns (50.6% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import pytest  # used for our unit tests
from src.math.computation import gcd_recursive

# unit tests

# -------------------------
# Basic Test Cases
# -------------------------

def test_gcd_basic_coprime():
    # Coprime numbers (should return 1)
    codeflash_output = gcd_recursive(13, 17) # 417ns -> 292ns (42.8% faster)
    codeflash_output = gcd_recursive(101, 10) # 167ns -> 166ns (0.602% faster)

def test_gcd_basic_non_coprime():
    # Non-coprime numbers (should return their common divisor)
    codeflash_output = gcd_recursive(12, 18) # 375ns -> 292ns (28.4% faster)
    codeflash_output = gcd_recursive(100, 80) # 167ns -> 166ns (0.602% faster)

def test_gcd_basic_equal_numbers():
    # Equal numbers (should return the number itself)
    codeflash_output = gcd_recursive(7, 7) # 291ns -> 250ns (16.4% faster)
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_basic_one_zero():
    # One argument is zero (should return the absolute value of the other)
    codeflash_output = gcd_recursive(0, 5) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(9, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_basic_one_one():
    # Both arguments are one (should return 1)
    codeflash_output = gcd_recursive(1, 1) # 292ns -> 250ns (16.8% faster)

# -------------------------
# Edge Test Cases
# -------------------------

def test_gcd_edge_negative_numbers():
    # Negative numbers (should return positive GCD)
    codeflash_output = gcd_recursive(-12, 18) # 416ns -> 333ns (24.9% faster)
    codeflash_output = gcd_recursive(12, -18) # 333ns -> 291ns (14.4% faster)
    codeflash_output = gcd_recursive(-12, -18) # 292ns -> 250ns (16.8% faster)

def test_gcd_edge_zero_and_negative():
    # Zero and negative number
    codeflash_output = gcd_recursive(0, -7) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(-7, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_edge_large_prime_and_one():
    # Large prime and 1 (should return 1)
    codeflash_output = gcd_recursive(982451653, 1) # 292ns -> 250ns (16.8% faster)

def test_gcd_edge_large_prime_and_zero():
    # Large prime and 0 (should return the prime)
    codeflash_output = gcd_recursive(982451653, 0) # 166ns -> 166ns (0.000% faster)

def test_gcd_edge_zero_and_zero():
    # Both zero (should return 0)
    codeflash_output = gcd_recursive(0, 0) # 166ns -> 125ns (32.8% faster)

def test_gcd_edge_one_and_zero():
    # One and zero (should return 1)
    codeflash_output = gcd_recursive(1, 0) # 125ns -> 167ns (25.1% slower)
    codeflash_output = gcd_recursive(0, 1) # 250ns -> 208ns (20.2% faster)

def test_gcd_edge_one_and_negative_one():
    # One and negative one (should return 1)
    codeflash_output = gcd_recursive(1, -1) # 291ns -> 208ns (39.9% faster)
    codeflash_output = gcd_recursive(-1, 1) # 125ns -> 125ns (0.000% faster)
    codeflash_output = gcd_recursive(-1, -1) # 125ns -> 125ns (0.000% faster)

def test_gcd_edge_large_and_small():
    # Large and small number
    codeflash_output = gcd_recursive(12345678901234567890, 10) # 334ns -> 333ns (0.300% faster)
    codeflash_output = gcd_recursive(10, 12345678901234567890) # 416ns -> 291ns (43.0% faster)

def test_gcd_edge_small_and_large_prime():
    # Small and large prime
    codeflash_output = gcd_recursive(2, 982451653) # 458ns -> 375ns (22.1% faster)

# -------------------------
# Large Scale Test Cases
# -------------------------

def test_gcd_large_scale_large_numbers():
    # Very large numbers
    a = 98765432123456789
    b = 123456789123456789
    # Compute expected GCD using Euclidean algorithm
    # For these numbers, GCD is 9
    codeflash_output = gcd_recursive(a, b) # 1.92μs -> 1.46μs (31.4% faster)

def test_gcd_large_scale_multiple_steps():
    # Numbers that require many recursive steps
    # These are consecutive Fibonacci numbers, whose GCD is always 1
    fibs = [
        (832040, 1346269),
        (267914296, 433494437),
        (701408733, 1134903170)
    ]
    for a, b in fibs:
        codeflash_output = gcd_recursive(a, b) # 4.83μs -> 3.29μs (46.8% faster)

def test_gcd_large_scale_large_composite():
    # Large composite numbers with large common divisors
    a = 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29
    b = 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31
    # Common divisor is product of shared primes: 3*5*7*11*13*17*19*23*29
    expected_gcd = 3*5*7*11*13*17*19*23*29
    codeflash_output = gcd_recursive(a, b) # 708ns -> 583ns (21.4% faster)

def test_gcd_large_scale_many_zeros():
    # Test a batch of zeros with a nonzero number
    for i in range(1, 1001):
        codeflash_output = gcd_recursive(i, 0) # 60.6μs -> 66.4μs (8.83% slower)
        codeflash_output = gcd_recursive(0, i)

def test_gcd_large_scale_negative_large_numbers():
    # Large negative numbers
    a = -98765432123456789
    b = -123456789123456789
    codeflash_output = gcd_recursive(a, b) # 1.75μs -> 1.38μs (27.3% faster)

def test_gcd_large_scale_stress():
    # Stress test with many calls
    for i in range(1, 1001):
        codeflash_output = gcd_recursive(i, i+1) # 161μs -> 119μs (35.1% faster)

# -------------------------
# Mutation-sensitive test cases
# -------------------------

def test_gcd_mutation_sensitive():
    # Ensure swapping arguments does not affect result
    codeflash_output = gcd_recursive(18, 12) # 334ns -> 292ns (14.4% faster)
    codeflash_output = gcd_recursive(12, 18) # 209ns -> 167ns (25.1% faster)
    # Ensure order does not matter for negatives
    codeflash_output = gcd_recursive(-18, 12) # 250ns -> 167ns (49.7% faster)
    codeflash_output = gcd_recursive(12, -18) # 250ns -> 208ns (20.2% faster)
    # Ensure that gcd(a, 0) == abs(a)
    codeflash_output = gcd_recursive(-15, 0) # 83ns -> 42ns (97.6% faster)
    codeflash_output = gcd_recursive(0, -15) # 125ns -> 83ns (50.6% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from src.math.computation import gcd_recursive

def test_gcd_recursive():
    gcd_recursive(272, -169)
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_6ele6a58/tmpq5jp_dbz/test_concolic_coverage.py::test_gcd_recursive 750ns 666ns 12.6%✅

To edit these changes git checkout codeflash/optimize-gcd_recursive-mh13vxx8 and push.

Codeflash

The optimization replaces **recursion with iteration**, eliminating Python's function call overhead which is the primary bottleneck in recursive algorithms.

**Key changes:**
- `if b == 0: return a` + `return gcd_recursive(b, a % b)` → `while b: a, b = b, a % b` + `return a`
- Removes 4,318 recursive function calls that were consuming 68.1% of the original runtime
- Uses tuple unpacking for efficient variable swapping

**Why it's faster:**
Python function calls are expensive due to stack frame creation, parameter passing, and return value handling. The line profiler shows the recursive call consumed 1.991 microseconds (68.1% of total time). The iterative version eliminates this overhead entirely, executing the same mathematical operations in a tight loop.

**Performance characteristics:**
- **Best gains (25-50% faster)**: Cases requiring multiple iterations like coprime numbers, large number pairs, and Fibonacci-like sequences
- **Modest gains (15-25% faster)**: Simple cases with fewer iterations 
- **Minimal/no gains**: Trivial cases that terminate immediately (e.g., `gcd(9, 0)`)

The 16% overall speedup comes from removing Python's recursive overhead while preserving identical mathematical behavior.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 October 21, 2025 21:58
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 21, 2025
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-gcd_recursive-mh13vxx8 branch October 21, 2025 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant