Skip to content

⚡️ Speed up function lcm by 29% #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: update-examples
Choose a base branch
from

Conversation

codeflash-ai[bot]
Copy link

@codeflash-ai codeflash-ai bot commented Jul 24, 2025

📄 29% (0.29x) speedup for lcm in src/math/computation.py

⏱️ Runtime : 1.07 milliseconds 827 microseconds (best of 224 runs)

📝 Explanation and details

Certainly! Here are the quick wins to make the program faster.

  • Move gcd function out of the lcm (it gets redefined every call, which is slow).
  • Use math.gcd from the standard library, which is implemented in C and thus much faster than a Python version.

Here’s the optimized program.

Notes:

  • The result will be identical to your version in all cases.
  • If you must use your own gcd, moving it global (outside) still gives you speedup.

But math.gcd is the fastest and simplest choice.

Correctness verification report:

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

# unit tests

# --------------------------
# 1. Basic Test Cases
# --------------------------

def test_lcm_simple_coprime():
    # Coprime numbers: lcm(4, 9) = 36
    codeflash_output = lcm(4, 9) # 750ns -> 667ns (12.4% faster)

def test_lcm_simple_non_coprime():
    # Non-coprime numbers: lcm(6, 8) = 24
    codeflash_output = lcm(6, 8) # 792ns -> 667ns (18.7% faster)

def test_lcm_equal_numbers():
    # Same numbers: lcm(7, 7) = 7
    codeflash_output = lcm(7, 7) # 667ns -> 542ns (23.1% faster)

def test_lcm_one_is_multiple_of_other():
    # One is a multiple of the other: lcm(5, 15) = 15
    codeflash_output = lcm(5, 15) # 709ns -> 625ns (13.4% faster)
    # Reverse order
    codeflash_output = lcm(15, 5) # 375ns -> 333ns (12.6% faster)

def test_lcm_with_one():
    # lcm with 1: lcm(1, 13) = 13
    codeflash_output = lcm(1, 13) # 750ns -> 625ns (20.0% faster)
    codeflash_output = lcm(13, 1) # 375ns -> 292ns (28.4% faster)

def test_lcm_with_zero():
    # lcm with zero: lcm(0, n) = 0 for any n
    codeflash_output = lcm(0, 5) # 292ns -> 167ns (74.9% faster)
    codeflash_output = lcm(7, 0) # 250ns -> 166ns (50.6% faster)
    codeflash_output = lcm(0, 0) # 208ns -> 125ns (66.4% faster)

# --------------------------
# 2. Edge Test Cases
# --------------------------

def test_lcm_negative_numbers():
    # Negative numbers: lcm(-3, 7) = 21
    codeflash_output = lcm(-3, 7) # 875ns -> 709ns (23.4% faster)
    codeflash_output = lcm(3, -7) # 708ns -> 583ns (21.4% faster)
    codeflash_output = lcm(-3, -7) # 458ns -> 417ns (9.83% faster)

def test_lcm_with_large_and_small():
    # Large and small: lcm(1, 999999937) = 999999937
    codeflash_output = lcm(1, 999999937) # 834ns -> 792ns (5.30% faster)

def test_lcm_prime_numbers():
    # Two large primes: lcm(101, 103) = 10403
    codeflash_output = lcm(101, 103) # 834ns -> 750ns (11.2% faster)

def test_lcm_negative_and_zero():
    # Negative and zero: lcm(-5, 0) = 0
    codeflash_output = lcm(-5, 0) # 333ns -> 208ns (60.1% faster)
    codeflash_output = lcm(0, -5) # 208ns -> 125ns (66.4% faster)

def test_lcm_one_negative_one_positive():
    # One negative, one positive: lcm(-8, 12) = 24
    codeflash_output = lcm(-8, 12) # 875ns -> 792ns (10.5% faster)
    codeflash_output = lcm(8, -12) # 583ns -> 500ns (16.6% faster)

def test_lcm_extreme_small_numbers():
    # Smallest nonzero values: lcm(1, -1) = 1
    codeflash_output = lcm(1, -1) # 667ns -> 542ns (23.1% faster)

def test_lcm_large_negative_numbers():
    # Large negative numbers: lcm(-1000000, -999999) = 999999000000
    codeflash_output = lcm(-1000000, -999999) # 1.00μs -> 875ns (14.3% faster)

def test_lcm_with_min_and_max_int():
    # Python ints are unbounded, but let's try large values
    a = 2**63 - 1  # large positive int
    b = 2**62      # another large int
    # Since they are coprime, lcm = a * b
    codeflash_output = lcm(a, b) # 1.33μs -> 1.25μs (6.64% faster)

def test_lcm_with_zero_and_negative_zero():
    # Zero and negative zero (should be treated the same)
    codeflash_output = lcm(0, -0) # 333ns -> 167ns (99.4% faster)
    codeflash_output = lcm(-0, 0) # 208ns -> 125ns (66.4% faster)

# --------------------------
# 3. Large Scale Test Cases
# --------------------------

def test_lcm_large_numbers():
    # Test with two large numbers
    a = 123456789012345678
    b = 987654321098765432
    # Compute expected lcm using the same logic
    def gcd(x, y):
        while y:
            x, y = y, x % y
        return x
    expected = abs(a * b) // gcd(a, b)
    codeflash_output = lcm(a, b) # 1.33μs -> 1.25μs (6.64% faster)

def test_lcm_large_prime_and_composite():
    # Large prime and large composite
    prime = 999999937  # large prime
    composite = 999999937 * 2
    codeflash_output = lcm(prime, composite) # 958ns -> 833ns (15.0% faster)

def test_lcm_many_pairs_performance():
    # Test many pairs to check performance and correctness
    # (but under 1000 iterations)
    for i in range(1, 1001):
        # lcm(i, i+1) = i*(i+1) since consecutive numbers are coprime
        codeflash_output = lcm(i, i+1) # 332μs -> 256μs (29.9% faster)

def test_lcm_large_negative_and_positive():
    # Large negative and large positive
    a = -987654321012345678
    b = 123456789098765432
    # Compute expected lcm
    def gcd(x, y):
        while y:
            x, y = y, x % y
        return x
    expected = abs(a * b) // gcd(a, b)
    codeflash_output = lcm(a, b) # 1.38μs -> 1.29μs (6.42% faster)

def test_lcm_large_power_of_two_and_odd():
    # Large power of two and odd number
    a = 2**50
    b = 999999999999
    # lcm should be a * b since they're coprime
    codeflash_output = lcm(a, b) # 2.17μs -> 2.17μs (0.046% faster)

def test_lcm_large_scale_with_ones():
    # lcm of 1 with many large numbers should be the number itself
    for n in [10**10, 10**12, 2**60, 999999999999]:
        codeflash_output = lcm(1, n) # 2.50μs -> 2.17μs (15.3% faster)
        codeflash_output = lcm(n, 1)

# --------------------------
# 4. Additional Robustness Tests
# --------------------------

@pytest.mark.parametrize("a,b,expected", [
    (0, 0, 0),
    (0, 5, 0),
    (5, 0, 0),
    (2, 3, 6),
    (-2, 3, 6),
    (2, -3, 6),
    (-2, -3, 6),
    (12, 15, 60),
    (15, 12, 60),
    (100, 10, 100),
    (10, 100, 100),
])
def test_lcm_various_cases(a, b, expected):
    # Parametrized test for various scenarios
    codeflash_output = lcm(a, b) # 7.46μs -> 6.00μs (24.3% 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 lcm

# unit tests

# === Basic Test Cases ===

def test_lcm_basic_positive_coprime():
    # Coprime numbers: LCM should be their product
    codeflash_output = lcm(3, 5) # 833ns -> 708ns (17.7% faster)
    codeflash_output = lcm(7, 11) # 542ns -> 459ns (18.1% faster)

def test_lcm_basic_positive_non_coprime():
    # Non-coprime numbers: LCM should be their least common multiple
    codeflash_output = lcm(4, 6) # 750ns -> 625ns (20.0% faster)
    codeflash_output = lcm(8, 12) # 417ns -> 375ns (11.2% faster)

def test_lcm_basic_one_is_multiple_of_other():
    # One number is a multiple of the other: LCM should be the larger
    codeflash_output = lcm(5, 15) # 709ns -> 625ns (13.4% faster)
    codeflash_output = lcm(21, 7) # 416ns -> 292ns (42.5% faster)

def test_lcm_basic_equal_numbers():
    # Equal numbers: LCM should be the number itself
    codeflash_output = lcm(9, 9) # 667ns -> 542ns (23.1% faster)
    codeflash_output = lcm(100, 100) # 458ns -> 375ns (22.1% faster)

def test_lcm_basic_with_one():
    # LCM with 1: should be the other number
    codeflash_output = lcm(1, 13) # 709ns -> 625ns (13.4% faster)
    codeflash_output = lcm(1, 1) # 375ns -> 292ns (28.4% faster)
    codeflash_output = lcm(17, 1) # 333ns -> 250ns (33.2% faster)

# === Edge Test Cases ===

def test_lcm_with_zero():
    # LCM with zero: should always be zero
    codeflash_output = lcm(0, 5) # 333ns -> 167ns (99.4% faster)
    codeflash_output = lcm(8, 0) # 250ns -> 166ns (50.6% faster)
    codeflash_output = lcm(0, 0) # 208ns -> 125ns (66.4% faster)

def test_lcm_with_negative_numbers():
    # LCM with negative numbers: should be positive
    codeflash_output = lcm(-3, 5) # 833ns -> 708ns (17.7% faster)
    codeflash_output = lcm(3, -5) # 500ns -> 458ns (9.17% faster)
    codeflash_output = lcm(-7, -11) # 750ns -> 666ns (12.6% faster)

def test_lcm_with_large_and_small():
    # Large and small numbers
    codeflash_output = lcm(1_000_000, 2) # 833ns -> 667ns (24.9% faster)
    codeflash_output = lcm(999_983, 2) # 542ns -> 458ns (18.3% faster)

def test_lcm_with_min_max_int():
    # Python ints are unbounded, but test large values
    big = 2**60
    small = 2**5
    codeflash_output = lcm(big, small) # 958ns -> 875ns (9.49% faster)
    # Test with negative big
    codeflash_output = lcm(-big, small) # 541ns -> 417ns (29.7% faster)
    # Test with both negative
    codeflash_output = lcm(-big, -small) # 583ns -> 458ns (27.3% faster)

def test_lcm_with_minimal_values():
    # Minimal non-zero values
    codeflash_output = lcm(1, 0) # 333ns -> 167ns (99.4% faster)
    codeflash_output = lcm(-1, 0) # 250ns -> 125ns (100% faster)
    codeflash_output = lcm(1, -1) # 583ns -> 500ns (16.6% faster)

def test_lcm_commutative_property():
    # LCM should be commutative: lcm(a, b) == lcm(b, a)
    for a, b in [(3, 5), (0, 7), (-4, 6), (123, 456)]:
        codeflash_output = lcm(a, b) # 2.29μs -> 1.96μs (17.1% faster)

def test_lcm_with_prime_numbers():
    # LCM of two distinct primes is their product
    codeflash_output = lcm(13, 17) # 792ns -> 667ns (18.7% faster)
    codeflash_output = lcm(2, 17) # 417ns -> 333ns (25.2% faster)

def test_lcm_with_negative_and_zero():
    # Negative and zero: should always be zero
    codeflash_output = lcm(-5, 0) # 333ns -> 208ns (60.1% faster)
    codeflash_output = lcm(0, -7) # 250ns -> 125ns (100% faster)

# === Large Scale Test Cases ===

def test_lcm_large_numbers():
    # Test with two large coprime numbers
    a = 999_999_937  # large prime
    b = 999_999_929  # another large prime
    expected = a * b
    codeflash_output = lcm(a, b) # 1.04μs -> 875ns (19.1% faster)

def test_lcm_large_numbers_with_common_factors():
    # Large numbers with a common factor
    a = 2**20 * 3**10
    b = 2**15 * 3**5 * 5**2
    # Their LCM should be the product of the highest powers of all primes
    expected = 2**20 * 3**10 * 5**2
    codeflash_output = lcm(a, b) # 1.04μs -> 917ns (13.6% faster)

def test_lcm_performance_many_pairs():
    # Test many pairs for performance and correctness
    for i in range(1, 1001):
        codeflash_output = lcm(i, i+1) # 337μs -> 264μs (27.7% faster)
        codeflash_output = lcm(i, 2*i)  # one is a multiple of the other

def test_lcm_large_negative_numbers():
    # Large negative numbers
    a = -2**30
    b = -2**28
    expected = 2**30
    codeflash_output = lcm(a, b) # 917ns -> 833ns (10.1% faster)

def test_lcm_large_and_one():
    # Large number and 1
    a = 10**18
    b = 1
    codeflash_output = lcm(a, b) # 917ns -> 833ns (10.1% faster)

def test_lcm_large_and_zero():
    # Large number and zero
    a = 10**18
    b = 0
    codeflash_output = lcm(a, b) # 333ns -> 208ns (60.1% faster)

# === Property-based and Special Case Tests ===

@pytest.mark.parametrize("a,b", [
    (0, 0), (0, 1), (1, 0), (-1, 0), (0, -1),
    (999, 0), (0, 999)
])
def test_lcm_zero_property(a, b):
    # LCM with zero should always be zero
    codeflash_output = lcm(a, b) # 2.37μs -> 1.46μs (62.9% faster)

@pytest.mark.parametrize("a,b,expected", [
    (2, 4, 4),
    (6, 8, 24),
    (21, 6, 42),
    (18, 24, 72),
    (100, 25, 100),
    (25, 100, 100),
    (17, 19, 323),
    (-2, 4, 4),
    (2, -4, 4),
    (-2, -4, 4),
])
def test_lcm_various_known_values(a, b, expected):
    # Test a variety of known values, including negatives
    codeflash_output = lcm(a, b) # 8.13μs -> 6.83μs (18.9% faster)

def test_lcm_idempotent():
    # lcm(a, a) == abs(a)
    for a in [-100, -1, 0, 1, 100, 999999]:
        codeflash_output = lcm(a, a) # 2.79μs -> 2.25μs (24.1% 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 lcm

def test_lcm():
    lcm(-39, -4)

def test_lcm_2():
    lcm(-1, 0)

To edit these changes git checkout codeflash/optimize-lcm-mdgw6cav and push.

Codeflash

Certainly! Here are the quick wins to make the program faster.

- **Move `gcd` function out** of the `lcm` (it gets redefined every call, which is slow).
- Use `math.gcd` from the standard library, which is implemented in C and thus much faster than a Python version.

Here’s the optimized program.



**Notes:**
- The result will be **identical** to your version in all cases.
- If you must use your own `gcd`, moving it global (outside) still gives you speedup.



But `math.gcd` is the fastest and simplest choice.
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Jul 24, 2025
@codeflash-ai codeflash-ai bot requested a review from KRRT7 July 24, 2025 04:28
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.

0 participants