j-js commited on
Commit
fc53c42
·
verified ·
1 Parent(s): f5edc09

Create solver_percent.py

Browse files
Files changed (1) hide show
  1. solver_percent.py +189 -0
solver_percent.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Optional, List
5
+
6
+ from models import SolverResult
7
+
8
+
9
+ def _nums(text: str) -> List[float]:
10
+ return [float(x) for x in re.findall(r"-?\d+(?:\.\d+)?", text)]
11
+
12
+
13
+ def solve_percent(text: str) -> Optional[SolverResult]:
14
+ lower = (text or "").lower()
15
+
16
+ if "%" not in lower and "percent" not in lower:
17
+ return None
18
+
19
+ nums = _nums(lower)
20
+
21
+ # Pattern 1: "40% of a number is 24"
22
+ m = re.search(
23
+ r"(\d+(?:\.\d+)?)\s*%?\s*(?:percent)?\s*of\s+(?:a\s+number|number|x|an?\s+amount).*?\bis\b\s*(-?\d+(?:\.\d+)?)",
24
+ lower,
25
+ )
26
+ if m:
27
+ pct = float(m.group(1)) / 100.0
28
+ value = float(m.group(2))
29
+ if pct == 0:
30
+ return None
31
+ result = value / pct
32
+ return SolverResult(
33
+ domain="quant",
34
+ solved=True,
35
+ topic="percent",
36
+ answer_value=f"{result:g}",
37
+ internal_answer=f"{result:g}",
38
+ steps=[
39
+ "Convert the percent to a decimal.",
40
+ "Set up percent × whole = part.",
41
+ "Divide the part by the decimal percent to get the whole.",
42
+ ],
43
+ )
44
+
45
+ # Pattern 2: "What is 25% of 80?"
46
+ m = re.search(
47
+ r"what\s+is\s+(\d+(?:\.\d+)?)\s*%?\s*(?:percent)?\s*of\s*(-?\d+(?:\.\d+)?)",
48
+ lower,
49
+ )
50
+ if m:
51
+ pct = float(m.group(1)) / 100.0
52
+ whole = float(m.group(2))
53
+ result = pct * whole
54
+ return SolverResult(
55
+ domain="quant",
56
+ solved=True,
57
+ topic="percent",
58
+ answer_value=f"{result:g}",
59
+ internal_answer=f"{result:g}",
60
+ steps=[
61
+ "Convert the percent to a decimal.",
62
+ "Multiply the whole by the decimal percent.",
63
+ ],
64
+ )
65
+
66
+ # Pattern 3: "24 is what percent of 60?"
67
+ m = re.search(
68
+ r"(-?\d+(?:\.\d+)?)\s+is\s+what\s+percent\s+of\s+(-?\d+(?:\.\d+)?)",
69
+ lower,
70
+ )
71
+ if m:
72
+ part = float(m.group(1))
73
+ whole = float(m.group(2))
74
+ if whole == 0:
75
+ return None
76
+ result = (part / whole) * 100
77
+ return SolverResult(
78
+ domain="quant",
79
+ solved=True,
80
+ topic="percent",
81
+ answer_value=f"{result:g}",
82
+ internal_answer=f"{result:g}",
83
+ steps=[
84
+ "Use percent = part ÷ whole × 100.",
85
+ "Divide the part by the whole.",
86
+ "Convert the decimal to a percent.",
87
+ ],
88
+ )
89
+
90
+ # Pattern 4: "What percent of 60 is 24?"
91
+ m = re.search(
92
+ r"what\s+percent\s+of\s+(-?\d+(?:\.\d+)?)\s+is\s+(-?\d+(?:\.\d+)?)",
93
+ lower,
94
+ )
95
+ if m:
96
+ whole = float(m.group(1))
97
+ part = float(m.group(2))
98
+ if whole == 0:
99
+ return None
100
+ result = (part / whole) * 100
101
+ return SolverResult(
102
+ domain="quant",
103
+ solved=True,
104
+ topic="percent",
105
+ answer_value=f"{result:g}",
106
+ internal_answer=f"{result:g}",
107
+ steps=[
108
+ "Use percent = part ÷ whole × 100.",
109
+ "Divide the part by the whole.",
110
+ "Convert to percent form.",
111
+ ],
112
+ )
113
+
114
+ # Pattern 5: percent increase/decrease
115
+ m = re.search(
116
+ r"(-?\d+(?:\.\d+)?)\s*%?\s*(?:percent)?\s*(increase|decrease)[sd]?\s+from\s+(-?\d+(?:\.\d+)?)\s+to\s+(-?\d+(?:\.\d+)?)",
117
+ lower,
118
+ )
119
+ if m:
120
+ # rare wording, not ideal
121
+ pass
122
+
123
+ m = re.search(
124
+ r"(?:increase|decrease)[sd]?\s+from\s+(-?\d+(?:\.\d+)?)\s+to\s+(-?\d+(?:\.\d+)?)",
125
+ lower,
126
+ )
127
+ if m and ("percent" in lower or "%" in lower):
128
+ old = float(m.group(1))
129
+ new = float(m.group(2))
130
+ if old == 0:
131
+ return None
132
+ result = ((new - old) / old) * 100
133
+ return SolverResult(
134
+ domain="quant",
135
+ solved=True,
136
+ topic="percent_change",
137
+ answer_value=f"{result:g}",
138
+ internal_answer=f"{result:g}",
139
+ steps=[
140
+ "Find the change: new − old.",
141
+ "Divide by the original value.",
142
+ "Multiply by 100 to convert to a percent.",
143
+ ],
144
+ )
145
+
146
+ # Pattern 6: "increased by 20%" / "decreased by 20%"
147
+ m = re.search(
148
+ r"(-?\d+(?:\.\d+)?)\s+(?:is\s+)?(increased|decreased)\s+by\s+(\d+(?:\.\d+)?)\s*%",
149
+ lower,
150
+ )
151
+ if m:
152
+ value = float(m.group(1))
153
+ direction = m.group(2)
154
+ pct = float(m.group(3)) / 100.0
155
+ result = value * (1 + pct if direction == "increased" else 1 - pct)
156
+ return SolverResult(
157
+ domain="quant",
158
+ solved=True,
159
+ topic="percent_change",
160
+ answer_value=f"{result:g}",
161
+ internal_answer=f"{result:g}",
162
+ steps=[
163
+ "Convert the percent to a decimal.",
164
+ "Use multiplier 1 + p for increase or 1 − p for decrease.",
165
+ "Multiply the original value by that multiplier.",
166
+ ],
167
+ )
168
+
169
+ # Pattern 7: percent error / generic part-whole if 2 nums and clue words
170
+ if len(nums) >= 2 and ("of" in lower or "is" in lower):
171
+ # conservative fallback
172
+ pct = nums[0]
173
+ whole = nums[1]
174
+ if pct <= 100:
175
+ result = (pct / 100.0) * whole
176
+ return SolverResult(
177
+ domain="quant",
178
+ solved=True,
179
+ topic="percent",
180
+ answer_value=f"{result:g}",
181
+ internal_answer=f"{result:g}",
182
+ steps=[
183
+ "Interpret the first number as a percent.",
184
+ "Convert it to a decimal.",
185
+ "Multiply by the base quantity.",
186
+ ],
187
+ )
188
+
189
+ return None