Create solver_ratio.py
Browse files- solver_ratio.py +157 -0
solver_ratio.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import re
|
| 4 |
+
from math import gcd
|
| 5 |
+
from typing import Optional, List
|
| 6 |
+
|
| 7 |
+
from models import SolverResult
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _nums(text: str) -> List[float]:
|
| 11 |
+
return [float(x) for x in re.findall(r"-?\d+(?:\.\d+)?", text)]
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _reduce_ratio(a: int, b: int) -> tuple[int, int]:
|
| 15 |
+
if a == 0 and b == 0:
|
| 16 |
+
return (0, 0)
|
| 17 |
+
g = gcd(abs(a), abs(b))
|
| 18 |
+
return (a // g, b // g)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def solve_ratio(text: str) -> Optional[SolverResult]:
|
| 22 |
+
lower = (text or "").lower()
|
| 23 |
+
|
| 24 |
+
if "ratio" not in lower and ":" not in lower:
|
| 25 |
+
return None
|
| 26 |
+
|
| 27 |
+
# Pattern 1: "ratio of x to y"
|
| 28 |
+
m = re.search(r"ratio\s+of\s+(-?\d+)\s+to\s+(-?\d+)", lower)
|
| 29 |
+
if m:
|
| 30 |
+
a = int(m.group(1))
|
| 31 |
+
b = int(m.group(2))
|
| 32 |
+
if b == 0 and a == 0:
|
| 33 |
+
return None
|
| 34 |
+
ra, rb = _reduce_ratio(a, b)
|
| 35 |
+
return SolverResult(
|
| 36 |
+
domain="quant",
|
| 37 |
+
solved=True,
|
| 38 |
+
topic="ratio",
|
| 39 |
+
answer_value=f"{ra}:{rb}",
|
| 40 |
+
internal_answer=f"{ra}:{rb}",
|
| 41 |
+
steps=[
|
| 42 |
+
"Write the ratio in order.",
|
| 43 |
+
"Reduce both parts by their greatest common factor.",
|
| 44 |
+
],
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# Pattern 2: "a:b = 2:3 and total is 40"
|
| 48 |
+
m = re.search(
|
| 49 |
+
r"(\d+)\s*:\s*(\d+).*?(?:sum|total).*?(\d+(?:\.\d+)?)",
|
| 50 |
+
lower,
|
| 51 |
+
)
|
| 52 |
+
if m:
|
| 53 |
+
a = float(m.group(1))
|
| 54 |
+
b = float(m.group(2))
|
| 55 |
+
total = float(m.group(3))
|
| 56 |
+
parts = a + b
|
| 57 |
+
if parts == 0:
|
| 58 |
+
return None
|
| 59 |
+
unit = total / parts
|
| 60 |
+
first = a * unit
|
| 61 |
+
second = b * unit
|
| 62 |
+
return SolverResult(
|
| 63 |
+
domain="quant",
|
| 64 |
+
solved=True,
|
| 65 |
+
topic="ratio_partition",
|
| 66 |
+
answer_value=f"{first:g}, {second:g}",
|
| 67 |
+
internal_answer=f"{first:g}, {second:g}",
|
| 68 |
+
steps=[
|
| 69 |
+
"Add the ratio parts.",
|
| 70 |
+
"Divide the total by the total number of parts to get one unit.",
|
| 71 |
+
"Multiply each ratio part by the unit value.",
|
| 72 |
+
],
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
# Pattern 3: "ratio is 2:3, one part is 10 more than the other"
|
| 76 |
+
m = re.search(
|
| 77 |
+
r"(\d+)\s*:\s*(\d+).*?(\d+(?:\.\d+)?)\s+more\s+than",
|
| 78 |
+
lower,
|
| 79 |
+
)
|
| 80 |
+
if m:
|
| 81 |
+
a = float(m.group(1))
|
| 82 |
+
b = float(m.group(2))
|
| 83 |
+
diff_val = float(m.group(3))
|
| 84 |
+
diff_parts = abs(a - b)
|
| 85 |
+
if diff_parts == 0:
|
| 86 |
+
return None
|
| 87 |
+
unit = diff_val / diff_parts
|
| 88 |
+
first = a * unit
|
| 89 |
+
second = b * unit
|
| 90 |
+
return SolverResult(
|
| 91 |
+
domain="quant",
|
| 92 |
+
solved=True,
|
| 93 |
+
topic="ratio_difference",
|
| 94 |
+
answer_value=f"{first:g}, {second:g}",
|
| 95 |
+
internal_answer=f"{first:g}, {second:g}",
|
| 96 |
+
steps=[
|
| 97 |
+
"Find the difference in ratio parts.",
|
| 98 |
+
"Match that to the actual difference to get one unit.",
|
| 99 |
+
"Multiply each ratio part by the unit value.",
|
| 100 |
+
],
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
# Pattern 4: "If x:y = 2:3 and y:z = 4:5, find x:z"
|
| 104 |
+
m = re.search(
|
| 105 |
+
r"(\w+)\s*:\s*(\w+)\s*=\s*(\d+)\s*:\s*(\d+).*?(\w+)\s*:\s*(\w+)\s*=\s*(\d+)\s*:\s*(\d+).*?find\s+(\w+)\s*:\s*(\w+)",
|
| 106 |
+
lower,
|
| 107 |
+
)
|
| 108 |
+
if m:
|
| 109 |
+
left1, right1, a, b = m.group(1), m.group(2), int(m.group(3)), int(m.group(4))
|
| 110 |
+
left2, right2, c, d = m.group(5), m.group(6), int(m.group(7)), int(m.group(8))
|
| 111 |
+
target1, target2 = m.group(9), m.group(10)
|
| 112 |
+
|
| 113 |
+
if right1 == left2:
|
| 114 |
+
lcm_base = b * c // gcd(b, c)
|
| 115 |
+
mul1 = lcm_base // b
|
| 116 |
+
mul2 = lcm_base // c
|
| 117 |
+
first = a * mul1
|
| 118 |
+
middle = lcm_base
|
| 119 |
+
third = d * mul2
|
| 120 |
+
|
| 121 |
+
mapping = {left1: first, right1: middle, right2: third}
|
| 122 |
+
if target1 in mapping and target2 in mapping:
|
| 123 |
+
x = mapping[target1]
|
| 124 |
+
y = mapping[target2]
|
| 125 |
+
rx, ry = _reduce_ratio(int(x), int(y))
|
| 126 |
+
return SolverResult(
|
| 127 |
+
domain="quant",
|
| 128 |
+
solved=True,
|
| 129 |
+
topic="ratio_chain",
|
| 130 |
+
answer_value=f"{rx}:{ry}",
|
| 131 |
+
internal_answer=f"{rx}:{ry}",
|
| 132 |
+
steps=[
|
| 133 |
+
"Match the common middle term across both ratios.",
|
| 134 |
+
"Scale the ratios so the shared term is equal.",
|
| 135 |
+
"Read off the requested ratio and simplify.",
|
| 136 |
+
],
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
# Pattern 5: direct colon simplification
|
| 140 |
+
m = re.search(r"(\d+)\s*:\s*(\d+)", lower)
|
| 141 |
+
if m and any(w in lower for w in ["simplify", "reduce", "ratio"]):
|
| 142 |
+
a = int(m.group(1))
|
| 143 |
+
b = int(m.group(2))
|
| 144 |
+
ra, rb = _reduce_ratio(a, b)
|
| 145 |
+
return SolverResult(
|
| 146 |
+
domain="quant",
|
| 147 |
+
solved=True,
|
| 148 |
+
topic="ratio",
|
| 149 |
+
answer_value=f"{ra}:{rb}",
|
| 150 |
+
internal_answer=f"{ra}:{rb}",
|
| 151 |
+
steps=[
|
| 152 |
+
"Find the greatest common factor of both terms.",
|
| 153 |
+
"Divide both sides of the ratio by that factor.",
|
| 154 |
+
],
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
return None
|