Spaces:
Paused
Paused
Download arithmetic_check.py from TechAvenger/MathMate: direct link, hf CLI and curl.
- Browser
- Download file 3.46 kB
-
https://huggingface.co/spaces/TechAvenger/MathMate/resolve/main/arithmetic_check.py
- Command line
-
hf download hf://spaces/TechAvenger/MathMate/arithmetic_check.py
-
curl -L -o arithmetic_check.py https://huggingface.co/spaces/TechAvenger/MathMate/resolve/main/arithmetic_check.py
3.46 kB
| """ | |
| Deterministic arithmetic checker for student working steps. | |
| Uses sympy for safe expression evaluation. | |
| """ | |
| import re | |
| from typing import List, Dict, Optional | |
| from sympy import sympify, SympifyError, N | |
| def _preprocess_expression(expr: str) -> str: | |
| """Convert common math notation to sympy-compatible.""" | |
| expr = expr.strip() | |
| # Replace multiplication signs | |
| expr = expr.replace('×', '*').replace('·', '*') | |
| # Replace division signs | |
| expr = expr.replace('÷', '/') | |
| # Handle "of" as multiplication (e.g., "20% of 100") | |
| expr = re.sub(r'\bof\b', '*', expr, flags=re.IGNORECASE) | |
| # Handle percent | |
| expr = re.sub(r'(\d+(?:\.\d+)?)\s*%', r'(\1/100)', expr) | |
| # Handle implicit multiplication (e.g., "2x" -> "2*x") | |
| expr = re.sub(r'(\d)([a-zA-Z])', r'\1*\2', expr) | |
| # Handle spaces as multiplication between number and variable/paren | |
| expr = re.sub(r'(\d)\s+([a-zA-Z\(])', r'\1*\2', expr) | |
| return expr | |
| def check_working(working_text: str) -> List[Dict[str, any]]: | |
| """ | |
| Check each equation line in student working. | |
| Returns list of {line, correct, expected} for each line. | |
| """ | |
| results = [] | |
| # Split on newlines, semicolons, commas (but not inside numbers) | |
| # First, normalize line breaks | |
| text = working_text.replace(';', '\n').replace(',', '\n') | |
| lines = [line.strip() for line in text.split('\n') if line.strip()] | |
| for line in lines: | |
| # Check if line looks like an equation with = | |
| if '=' not in line: | |
| results.append({ | |
| "line": line, | |
| "correct": None, | |
| "expected": None, | |
| "reason": "Not an equation" | |
| }) | |
| continue | |
| parts = line.split('=') | |
| if len(parts) != 2: | |
| results.append({ | |
| "line": line, | |
| "correct": None, | |
| "expected": None, | |
| "reason": "Multiple equals signs" | |
| }) | |
| continue | |
| lhs = parts[0].strip() | |
| rhs = parts[1].strip() | |
| # Try to evaluate both sides | |
| try: | |
| lhs_processed = _preprocess_expression(lhs) | |
| rhs_processed = _preprocess_expression(rhs) | |
| lhs_val = sympify(lhs_processed, evaluate=True) | |
| rhs_val = sympify(rhs_processed, evaluate=True) | |
| # Check if both are numeric | |
| if lhs_val.is_number and rhs_val.is_number: | |
| lhs_float = float(N(lhs_val)) | |
| rhs_float = float(N(rhs_val)) | |
| correct = abs(lhs_float - rhs_float) < 1e-6 | |
| expected = str(rhs_float) if not correct else None | |
| results.append({ | |
| "line": line, | |
| "correct": correct, | |
| "expected": expected, | |
| "reason": "Numeric mismatch" if not correct else "OK" | |
| }) | |
| else: | |
| # Non-numeric (has variables) - can't verify | |
| results.append({ | |
| "line": line, | |
| "correct": None, | |
| "expected": None, | |
| "reason": "Contains variables" | |
| }) | |
| except (SympifyError, TypeError, ValueError, ZeroDivisionError) as e: | |
| results.append({ | |
| "line": line, | |
| "correct": None, | |
| "expected": None, | |
| "reason": f"Parse error: {str(e)}" | |
| }) | |
| return results |