reasoning-gym/reasoning_gym/exercises/algorithmic/base_conversion.py
2025-02-09 08:44:22 +00:00

104 lines
3.9 KiB
Python

"""Base conversion exercise that converts numbers between different bases."""
from typing import Dict, Any
class BaseConversionExercise:
"""Exercise generator for base conversion problems."""
def __init__(self):
self.curriculum = None
def generate(self, curriculum: Any) -> Dict[str, Any]:
"""
Generate a base conversion problem using the curriculum.
Returns:
Dict containing:
- question: str (e.g. "Convert the binary number 1010 to hexadecimal")
- answer: str (the converted number in target base)
- metadata: dict with details (value, source_base, target_base, etc.)
"""
self.curriculum = curriculum
template = curriculum.get_template(curriculum.rng)
return template.eval(self, curriculum.rng)
def _parse_expression(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""
Parse the template metadata into structured data.
The metadata structure from the curriculum:
{
"source_value": {"val": str}, # e.g. "1010" or "a5"
"source_base": {"base": str}, # e.g. "binary" or "base-3"
"target_base": {"base": str, "hint": str}, # e.g. "hexadecimal" or "base-8" with optional hint
}
Returns:
Dictionary containing:
- source_value: str (value to convert)
- source_base: int (base to convert from)
- target_base: int (base to convert to)
"""
def parse_base_name(name: str) -> int:
"""Convert base name to numeric value."""
name = name.lower()
if name == "binary":
return 2
elif name == "octal":
return 8
elif name == "decimal":
return 10
elif name == "hexadecimal":
return 16
elif name.startswith("base-"):
return int(name[5:])
raise ValueError(f"Unknown base name: {name}")
return {
"source_value": metadata["source_value"]["val"],
"source_base": parse_base_name(metadata["source_base"]["base"]),
"target_base": parse_base_name(metadata["target_base"]["base"])
}
def _evaluate_expression(self, parsed: Dict[str, Any]) -> str:
"""
Convert the number between bases.
Args:
parsed: Dictionary containing:
- source_base: int (base to convert from)
- target_base: int (base to convert to)
- source_value: str (value to convert)
Returns:
String representation of the number in target base
"""
try:
# Convert source value to decimal, handling letter digits
source_value = parsed["source_value"].lower()
decimal_value = 0
for digit in source_value:
if digit.isdigit():
digit_val = int(digit)
else:
digit_val = ord(digit) - ord('a') + 10
if digit_val >= parsed["source_base"]:
raise ValueError(f"Digit {digit} is invalid for base {parsed['source_base']}")
decimal_value = decimal_value * parsed["source_base"] + digit_val
# Convert decimal to target base
if decimal_value == 0:
return "0"
# Manual conversion for all bases
digits = []
n = decimal_value
while n:
digits.append(int(n % parsed["target_base"]))
n //= parsed["target_base"]
# Convert to string with letters for digits > 9
result = "".join(str(d) if d < 10 else chr(ord("a") + d - 10)
for d in reversed(digits))
return result
except ValueError as e:
return f"Error converting number: {str(e)}"