From 4cd7ea7c404f5b25cb42f800d15c224934b2c47d Mon Sep 17 00:00:00 2001 From: pralav-25 <174412353+pralav-25@users.noreply.github.com> Date: Sun, 27 Sep 2026 13:51:39 +0530 Subject: [PATCH 1/2] fix(math): decode negative values emitted by int_to_base --- algorithms/math/base_conversion.py | 10 ++++++++-- tests/test_base_conversion_signed.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/test_base_conversion_signed.py diff --git a/algorithms/math/base_conversion.py b/algorithms/math/base_conversion.py index f332dc6f6..2d1c17390 100644 --- a/algorithms/math/base_conversion.py +++ b/algorithms/math/base_conversion.py @@ -54,7 +54,8 @@ def base_to_int(str_to_convert: str, base: int) -> int: """Convert a string in a given base to a base-10 integer. Args: - str_to_convert: The string representation of the number. + str_to_convert: The string representation of the number, optionally + prefixed with a minus sign as produced by int_to_base. base: The base of the input string (2-36). Returns: @@ -65,7 +66,12 @@ def base_to_int(str_to_convert: str, base: int) -> int: 5 >>> base_to_int('FF', 16) 255 + >>> base_to_int('-FF', 16) + -255 """ + is_negative = str_to_convert.startswith("-") + if is_negative: + str_to_convert = str_to_convert[1:] digit = {} for ind, char in enumerate(string.digits + string.ascii_uppercase): digit[char] = ind @@ -74,4 +80,4 @@ def base_to_int(str_to_convert: str, base: int) -> int: for char in str_to_convert[::-1]: res += digit[char] * multiplier multiplier *= base - return res + return -res if is_negative else res diff --git a/tests/test_base_conversion_signed.py b/tests/test_base_conversion_signed.py new file mode 100644 index 000000000..e1b81a3a0 --- /dev/null +++ b/tests/test_base_conversion_signed.py @@ -0,0 +1,23 @@ +"""Check that signed encodings can be decoded in every supported base.""" + +import pytest + +from algorithms.math.base_conversion import base_to_int, int_to_base + + +@pytest.mark.parametrize("base", range(2, 37)) +def test_signed_conversion_matches_builtin(base): + """Use int as an independent oracle for positive and negative outputs.""" + for number in [-2**128, -255, -31, -1, 0, 1, 31, 255, 2**128]: + encoded = int_to_base(number, base) + assert int(encoded, base) == number + assert base_to_int(encoded, base) == number + + +@pytest.mark.parametrize( + "encoded,base,expected", + [("-101", 2, -5), ("-FF", 16, -255), ("-Z", 36, -35), ("-0", 10, 0)], +) +def test_decode_negative_literal(encoded, base, expected): + """Decoding also works on literals not produced by the encoder.""" + assert base_to_int(encoded, base) == expected From e364e5887f4c7f37eb4c68e9cf6f63764c0f3efd Mon Sep 17 00:00:00 2001 From: pralav-25 <174412353+pralav-25@users.noreply.github.com> Date: Sun, 27 Sep 2026 13:57:27 +0530 Subject: [PATCH 2/2] fix(math): reject signed conversion inputs without digits --- algorithms/math/base_conversion.py | 5 +++++ tests/test_base_conversion_signed.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/algorithms/math/base_conversion.py b/algorithms/math/base_conversion.py index 2d1c17390..3ba3a2014 100644 --- a/algorithms/math/base_conversion.py +++ b/algorithms/math/base_conversion.py @@ -61,6 +61,9 @@ def base_to_int(str_to_convert: str, base: int) -> int: Returns: The base-10 integer value. + Raises: + ValueError: If the input contains no digits after removing its sign. + Examples: >>> base_to_int('101', 2) 5 @@ -72,6 +75,8 @@ def base_to_int(str_to_convert: str, base: int) -> int: is_negative = str_to_convert.startswith("-") if is_negative: str_to_convert = str_to_convert[1:] + if not str_to_convert: + raise ValueError("number must contain at least one digit") digit = {} for ind, char in enumerate(string.digits + string.ascii_uppercase): digit[char] = ind diff --git a/tests/test_base_conversion_signed.py b/tests/test_base_conversion_signed.py index e1b81a3a0..0916ddedf 100644 --- a/tests/test_base_conversion_signed.py +++ b/tests/test_base_conversion_signed.py @@ -21,3 +21,10 @@ def test_signed_conversion_matches_builtin(base): def test_decode_negative_literal(encoded, base, expected): """Decoding also works on literals not produced by the encoder.""" assert base_to_int(encoded, base) == expected + + +@pytest.mark.parametrize("encoded", ["", "-"]) +def test_missing_digits_are_rejected(encoded): + """Neither an empty string nor a sign alone represents zero.""" + with pytest.raises(ValueError, match="at least one digit"): + base_to_int(encoded, 16)