| import re |
| from .explainer_types import ExplainerResult, ExplainerScaffold |
|
|
|
|
| _PERCENT_PATTERNS = [ |
| r"\bpercent\b", |
| r"\bpercentage\b", |
| r"\bwhat percent\b", |
| r"\bpercent of\b", |
| r"\bpercent greater than\b", |
| r"\bpercent less than\b", |
| r"\bincrease(?:d)? by \d+ ?%\b", |
| r"\bdecrease(?:d)? by \d+ ?%\b", |
| r"\bdiscount\b", |
| r"\bmarkup\b", |
| r"\binterest\b", |
| r"\btax\b", |
| r"\btip\b", |
| r"\bprofit margin\b", |
| r"\bcommission\b", |
| r"\bpart of\b", |
| r"\bof a number\b", |
| ] |
|
|
|
|
| def _looks_like_percent_question(text: str) -> bool: |
| low = (text or "").lower() |
| if "%" in low: |
| return True |
| return any(re.search(p, low) for p in _PERCENT_PATTERNS) |
|
|
|
|
| def _infer_percent_subtype(text: str) -> str: |
| low = (text or "").lower() |
|
|
| if any(k in low for k in ["increase", "decrease", "increased", "decreased", "percent change", "changed by"]): |
| return "percent_change" |
| if any(k in low for k in ["discount", "sale", "off", "markdown"]): |
| return "discount" |
| if any(k in low for k in ["interest", "simple interest", "compound interest"]): |
| return "interest" |
| if any(k in low for k in ["tax", "vat", "tip", "gratuity"]): |
| return "tax_tip" |
| if any(k in low for k in ["what percent", "is what percent of", "as a percent of"]): |
| return "percent_of_percent" |
| if any(k in low for k in ["percent of", "of a number", "% of"]): |
| return "basic_percent" |
| return "generic_percent" |
|
|
|
|
| def explain_percent_question(text: str): |
| if not _looks_like_percent_question(text): |
| return None |
|
|
| subtype = _infer_percent_subtype(text) |
|
|
| result = ExplainerResult( |
| understood=True, |
| topic="percent", |
| summary="This is a percent problem. The key is to identify which quantity is the base, which is the compared amount, and what role the percent is playing.", |
| asks_for="the missing value, the percent itself, or the final changed total", |
| plain_english="Percent means per hundred, so the safest route is to identify the base first and then match the wording to the correct percent template.", |
| ) |
|
|
| scaffold = ExplainerScaffold( |
| concept="Percent problems are built around a base amount, a compared amount, and a rate per hundred.", |
| ask="Decide which quantity is the whole/base, which quantity is the part or changed amount, and whether the question is asking for a percent, a value, or a new total.", |
| target="Translate the wording into the correct percent relationship before doing any arithmetic.", |
| answer_hidden=True, |
| solution_path_type=subtype, |
| ) |
|
|
| result.teaching_points = [ |
| "Percent means per hundred, so it should usually be converted to a decimal or fraction before setting up the relationship.", |
| "Most percent questions reduce to one of a small number of templates, so identifying the template matters more than rushing into calculation.", |
| "The biggest source of error is choosing the wrong base quantity.", |
| ] |
| result.givens = [ |
| "A percent, base, part, or changed amount is implied by the wording.", |
| ] |
| result.relationships = [ |
| "Percent relationships depend on correctly matching part, whole, and rate.", |
| ] |
| result.needed_concepts = [ |
| "base vs part", |
| "percent as a decimal or fraction", |
| "choosing the correct percent template", |
| ] |
| result.trap_notes = [ |
| "Choosing the wrong base quantity.", |
| "Using 40 instead of 0.40.", |
| "Confusing a basic percent-of problem with a percent-change problem.", |
| ] |
| result.strategy_hint = "Start by asking: percent of what?" |
|
|
| if subtype == "basic_percent": |
| scaffold.setup_actions = [ |
| "Label the known quantities as part, whole, and percent.", |
| "Convert the percent to a decimal or fraction.", |
| "Match the statement to the relationship: part = percent × whole.", |
| ] |
| scaffold.intermediate_steps = [ |
| "If the whole is unknown, assign it a variable.", |
| "Substitute the known values into the relationship.", |
| "Solve only after the structure is correct.", |
| ] |
| scaffold.first_move = "Start by identifying the whole and the part." |
| scaffold.next_hint = "Once those roles are clear, write one equation connecting part, percent, and whole." |
| scaffold.variables_to_define = [ |
| "Let x represent the unknown quantity if either the part or whole is missing.", |
| ] |
| scaffold.equations_to_form = [ |
| "part = percent × whole", |
| ] |
| scaffold.key_operations = [ |
| "identify the base", |
| "convert percent to decimal", |
| "build one equation", |
| ] |
| scaffold.hint_ladder = [ |
| "Which quantity is the whole?", |
| "What is the percent as a decimal?", |
| "Can you write part = percent × whole?", |
| ] |
|
|
| elif subtype == "percent_of_percent": |
| scaffold.setup_actions = [ |
| "Identify the comparison being made.", |
| "Place the compared quantity over the base quantity.", |
| "Convert the resulting fraction to a percent.", |
| ] |
| scaffold.intermediate_steps = [ |
| "Make sure the denominator is the reference/base amount.", |
| "Simplify the fraction if helpful.", |
| "Only convert to percent at the end.", |
| ] |
| scaffold.first_move = "Ask: percent of what?" |
| scaffold.next_hint = "Use the base quantity as the denominator in the fraction before converting." |
| scaffold.equations_to_form = [ |
| "percent = (part / whole) × 100", |
| ] |
| scaffold.key_operations = [ |
| "form the fraction", |
| "use the base as denominator", |
| "convert to percent", |
| ] |
| scaffold.hint_ladder = [ |
| "What is the reference amount?", |
| "Which number belongs in the denominator?", |
| "Multiply by 100 only after the fraction is correct.", |
| ] |
|
|
| elif subtype == "percent_change": |
| scaffold.setup_actions = [ |
| "Identify the original value and the new value.", |
| "Find the amount of change first.", |
| "Use the original value as the base in the percent-change formula.", |
| ] |
| scaffold.intermediate_steps = [ |
| "Compute change = new − original.", |
| "Place that change over the original value.", |
| "Convert the result to a percent.", |
| ] |
| scaffold.first_move = "Find the amount of increase or decrease before thinking about the percent." |
| scaffold.next_hint = "After that, divide by the original amount, not the new amount." |
| scaffold.equations_to_form = [ |
| "percent change = (new − original) / original × 100", |
| ] |
| scaffold.key_operations = [ |
| "find the change", |
| "divide by the original", |
| "convert to percent", |
| ] |
| scaffold.hint_ladder = [ |
| "Which value is original?", |
| "What is the amount of change?", |
| "Which quantity belongs in the denominator?", |
| ] |
|
|
| elif subtype == "discount": |
| scaffold.setup_actions = [ |
| "Identify the original price and the discount rate.", |
| "Find the discount amount from the original price.", |
| "Subtract the discount from the original price if the question asks for the sale price.", |
| ] |
| scaffold.intermediate_steps = [ |
| "Treat the listed price as the base.", |
| "Compute the discount amount separately from the final price.", |
| "Check whether the question asks for discount amount or discounted price.", |
| ] |
| scaffold.first_move = "Use the original price as the percent base." |
| scaffold.next_hint = "Find the discount amount first, then decide whether to stop or subtract." |
| scaffold.equations_to_form = [ |
| "discount = rate × original price", |
| "sale price = original price − discount", |
| ] |
| scaffold.key_operations = [ |
| "identify the original price", |
| "find discount amount", |
| "subtract if needed", |
| ] |
| scaffold.hint_ladder = [ |
| "What is the original price?", |
| "What percent of that price is the discount?", |
| "Does the question want the discount or the final price?", |
| ] |
|
|
| elif subtype == "interest": |
| scaffold.setup_actions = [ |
| "Identify principal, rate, and time.", |
| "Determine whether the question is simple interest or compound interest.", |
| "Set up the matching formula structure.", |
| ] |
| scaffold.intermediate_steps = [ |
| "For simple interest, keep the principal fixed.", |
| "For compound interest, recognize that the base changes from period to period.", |
| "Check whether the question asks for interest earned or final amount.", |
| ] |
| scaffold.first_move = "Work out whether the interest is simple or compound." |
| scaffold.next_hint = "Then identify which quantity stays fixed and which quantity grows." |
| scaffold.key_operations = [ |
| "identify principal, rate, and time", |
| "match simple vs compound", |
| "check whether the target is interest or total amount", |
| ] |
| scaffold.hint_ladder = [ |
| "Is the base changing each period?", |
| "Which quantity is the principal?", |
| "Does the question want interest earned or the total balance?", |
| ] |
|
|
| elif subtype == "tax_tip": |
| scaffold.setup_actions = [ |
| "Identify the pre-tax or pre-tip amount.", |
| "Use that original amount as the base.", |
| "Compute the tax or tip amount before adding if a total is required.", |
| ] |
| scaffold.intermediate_steps = [ |
| "Separate the added charge from the final amount.", |
| "Check whether multiple percentages are applied independently or sequentially.", |
| "Make sure the question wants the fee amount or the full total.", |
| ] |
| scaffold.first_move = "Use the original listed amount as the base unless the wording clearly says otherwise." |
| scaffold.next_hint = "Find the added percent amount first, then combine only if the question asks for the total." |
| scaffold.equations_to_form = [ |
| "added amount = rate × base amount", |
| "total = base amount + added amount", |
| ] |
| scaffold.key_operations = [ |
| "identify the original amount", |
| "find the added charge", |
| "add only if the question asks for the total", |
| ] |
| scaffold.hint_ladder = [ |
| "What is the amount before tax or tip?", |
| "How much is the added percent of that base?", |
| "Does the question stop at the fee or ask for the total bill?", |
| ] |
|
|
| else: |
| scaffold.setup_actions = [ |
| "Identify the base quantity.", |
| "Identify the compared quantity or changed amount.", |
| "Match the wording to a standard percent relationship.", |
| ] |
| scaffold.intermediate_steps = [ |
| "Convert the percent appropriately.", |
| "Build one clear relationship before solving.", |
| "Check whether the question asks for a value, a rate, or a final amount.", |
| ] |
| scaffold.first_move = "Work out what the percent is being taken of." |
| scaffold.next_hint = "Once the base quantity is clear, write the relationship before calculating." |
| scaffold.key_operations = [ |
| "identify the base", |
| "convert the percent", |
| "match the correct template", |
| ] |
| scaffold.hint_ladder = [ |
| "What quantity is the base?", |
| "What role does the percent play here?", |
| "Which standard percent relationship matches this wording?", |
| ] |
|
|
| result.scaffold = scaffold |
| result.meta = { |
| "intent": "explain_question", |
| "bridge_ready": True, |
| "hint_style": "step_ready", |
| "subtype": subtype, |
| } |
| return result |