"use client"; import { useState, useCallback } from "react"; import { Card } from "@/components/ui/card"; import { StepControls } from "./step-controls"; import { MathDisplay } from "@/components/math/math-display"; type Tab = "multiply" | "divide" | "thermometer"; interface Step { label: string; math: string; signVisual?: { signA: "+" | "-"; signB: "+" | "-"; resultSign: "+" | "-" }; thermometer?: { startTemp: number; change: number; endTemp: number }; } function getSign(n: number): "+" | "-" { return n >= 0 ? "+" : "-"; } function buildMultiplySteps(a: number, b: number): Step[] { const steps: Step[] = []; const result = a * b; const signA = getSign(a); const signB = getSign(b); const signR = getSign(result); steps.push({ label: "Start with the multiplication", math: `${a < 0 ? `(${a})` : a} \\times ${b < 0 ? `(${b})` : b}`, }); // Step: Multiply the absolute values steps.push({ label: `First, multiply the absolute values: ${Math.abs(a)} × ${Math.abs(b)}`, math: `|${a}| \\times |${b}| = ${Math.abs(a)} \\times ${Math.abs(b)} = ${Math.abs(result)}`, }); // Step: Determine the sign const sameSign = (a >= 0 && b >= 0) || (a < 0 && b < 0); if (sameSign) { steps.push({ label: `Same signs (${signA} × ${signB}): the result is POSITIVE`, math: `(${signA}) \\times (${signB}) = (+)`, signVisual: { signA, signB, resultSign: "+" }, }); } else { steps.push({ label: `Different signs (${signA} × ${signB}): the result is NEGATIVE`, math: `(${signA}) \\times (${signB}) = (-)`, signVisual: { signA, signB, resultSign: "-" }, }); } // Step: Combine steps.push({ label: "Combine the magnitude and sign", math: `${a < 0 ? `(${a})` : a} \\times ${b < 0 ? `(${b})` : b} = ${result}`, signVisual: { signA, signB, resultSign: signR }, }); // Story time for negative × negative if (a < 0 && b < 0) { steps.push({ label: "Why does negative × negative = positive? Think of it like a double-negative in English!", math: `\\text{\"Do NOT not eat!\"} = \\text{\"Eat!\"} \\quad (-) \\times (-) = (+)`, signVisual: { signA: "-", signB: "-", resultSign: "+" }, }); } return steps; } function buildDivideSteps(a: number, b: number): Step[] { const steps: Step[] = []; const result = a / b; const signA = getSign(a); const signB = getSign(b); const signR = getSign(result); const isWhole = Number.isInteger(result); const displayResult = isWhole ? result.toString() : result.toFixed(2); steps.push({ label: "Start with the division", math: `${a < 0 ? `(${a})` : a} \\div ${b < 0 ? `(${b})` : b}`, }); steps.push({ label: `First, divide the absolute values: ${Math.abs(a)} ÷ ${Math.abs(b)}`, math: `|${a}| \\div |${b}| = ${Math.abs(a)} \\div ${Math.abs(b)} = ${isWhole ? Math.abs(result) : Math.abs(result).toFixed(2)}`, }); const sameSign = (a >= 0 && b >= 0) || (a < 0 && b < 0); if (sameSign) { steps.push({ label: `Same signs (${signA} ÷ ${signB}): the result is POSITIVE`, math: `(${signA}) \\div (${signB}) = (+)`, signVisual: { signA, signB, resultSign: "+" }, }); } else { steps.push({ label: `Different signs (${signA} ÷ ${signB}): the result is NEGATIVE`, math: `(${signA}) \\div (${signB}) = (-)`, signVisual: { signA, signB, resultSign: "-" }, }); } steps.push({ label: "Combine the magnitude and sign", math: `${a < 0 ? `(${a})` : a} \\div ${b < 0 ? `(${b})` : b} = ${displayResult}`, signVisual: { signA, signB, resultSign: signR }, }); return steps; } function buildThermometerSteps(startTemp: number, change: number): Step[] { const steps: Step[] = []; const endTemp = startTemp + change; const direction = change >= 0 ? "rose" : "dropped"; const absChange = Math.abs(change); steps.push({ label: "Read the starting temperature on the thermometer", math: `\\text{Starting temperature: } ${startTemp}°C`, thermometer: { startTemp, change: 0, endTemp: startTemp }, }); steps.push({ label: `The temperature ${direction} by ${absChange}°C`, math: change >= 0 ? `${startTemp}°C + ${absChange}°C` : `${startTemp}°C - ${absChange}°C`, thermometer: { startTemp, change: 0, endTemp: startTemp }, }); steps.push({ label: change >= 0 ? "Rising temperature means we ADD" : "Dropping temperature means we SUBTRACT", math: `${startTemp} + (${change}) = ${endTemp}`, thermometer: { startTemp, change, endTemp }, }); steps.push({ label: `The new temperature is ${endTemp}°C`, math: `\\text{New temperature: } ${endTemp}°C`, thermometer: { startTemp, change, endTemp }, }); return steps; } function SignRuleGrid({ visual, operation }: { visual?: Step["signVisual"]; operation: "×" | "÷" }) { const rules = [ { a: "+", b: "+", result: "+", example: operation === "×" ? "3 × 2 = 6" : "6 ÷ 2 = 3" }, { a: "-", b: "-", result: "+", example: operation === "×" ? "(-3) × (-2) = 6" : "(-6) ÷ (-2) = 3" }, { a: "+", b: "-", result: "-", example: operation === "×" ? "3 × (-2) = -6" : "6 ÷ (-2) = -3" }, { a: "-", b: "+", result: "-", example: operation === "×" ? "(-3) × 2 = -6" : "(-6) ÷ 2 = -3" }, ]; return (
{rules.map((r) => { const isActive = visual && visual.signA === r.a && visual.signB === r.b; return (

({r.a}) {operation} ({r.b}) = ({r.result})

{r.example}

); })}
); } function Thermometer({ data }: { data?: Step["thermometer"] }) { const minTemp = -20; const maxTemp = 30; const range = maxTemp - minTemp; const startTemp = data?.startTemp ?? 0; const endTemp = data?.endTemp ?? 0; const startY = ((startTemp - minTemp) / range) * 100; const endY = ((endTemp - minTemp) / range) * 100; const fillY = ((endTemp - minTemp) / range) * 100; const ticks = []; for (let t = minTemp; t <= maxTemp; t += 5) { ticks.push(t); } return (
{/* Thermometer */}
{/* Mercury fill */}
= 0 ? `linear-gradient(to top, #dc2626, #f97316)` : `linear-gradient(to top, #2563eb, #60a5fa)`, }} /> {/* Tick marks */} {ticks.map((t) => { const y = ((t - minTemp) / range) * 100; return (
); })}
{/* Bulb */}
°C
{/* Scale labels */}
{ticks.map((t) => { const y = ((t - minTemp) / range) * 100; const isStart = t === startTemp; const isEnd = data && data.change !== 0 && t === endTemp; return (
{t}°C {isStart && ( START )} {isEnd && ( END )}
); })}
); } function ThermometerPractice() { const scenarios = [ { city: "London", startTemp: -3, change: 7, question: "The temperature rose by 7°C. What is the new temperature?" }, { city: "Moscow", startTemp: -12, change: -5, question: "The temperature dropped by 5°C. What is the new temperature?" }, { city: "Kingston", startTemp: 28, change: -10, question: "The temperature dropped by 10°C. What is the new temperature?" }, { city: "Ottawa", startTemp: -8, change: 15, question: "The temperature rose by 15°C. What is the new temperature?" }, { city: "Reykjavik", startTemp: -1, change: -6, question: "The temperature dropped by 6°C. What is the new temperature?" }, ]; const [scenarioIdx, setScenarioIdx] = useState(0); const [userAnswer, setUserAnswer] = useState(""); const [feedback, setFeedback] = useState<"correct" | "incorrect" | null>(null); const scenario = scenarios[scenarioIdx]; const correctAnswer = scenario.startTemp + scenario.change; function check() { const parsed = parseInt(userAnswer); if (isNaN(parsed)) return; setFeedback(parsed === correctAnswer ? "correct" : "incorrect"); } function next() { setScenarioIdx((i) => (i + 1) % scenarios.length); setUserAnswer(""); setFeedback(null); } return (

Real-World Challenge

In {scenario.city}, the temperature is {scenario.startTemp}°C.

{scenario.question}

{ setUserAnswer(e.target.value); setFeedback(null); }} onKeyDown={(e) => { if (e.key === "Enter") { if (feedback !== null) next(); else check(); } }} className={`w-24 rounded-lg border-2 px-3 py-2 text-center text-lg font-bold outline-none transition-colors ${ feedback === "correct" ? "border-correct bg-correct-light" : feedback === "incorrect" ? "border-incorrect bg-incorrect-light" : "border-border focus:border-unit-5" }`} placeholder="°C" aria-label="Temperature answer" /> °C {feedback === null ? ( ) : ( )}
{feedback === "correct" && (

Correct! {scenario.startTemp} + ({scenario.change}) = {correctAnswer}°C

)} {feedback === "incorrect" && (

Not quite. {scenario.startTemp} + ({scenario.change}) = {correctAnswer}°C

)}
); } function MultiplyDividePractice() { const [problem, setProblem] = useState(() => generateProblem()); const [userAnswer, setUserAnswer] = useState(""); const [feedback, setFeedback] = useState<"correct" | "incorrect" | null>(null); const [score, setScore] = useState({ correct: 0, total: 0 }); function generateProblem() { const ops = ["×", "÷"] as const; const op = ops[Math.floor(Math.random() * ops.length)]; if (op === "×") { const a = Math.floor(Math.random() * 25) - 12; const b = Math.floor(Math.random() * 25) - 12; return { a, b, op, answer: a * b }; } else { // Generate division with clean integer results const b = Math.floor(Math.random() * 11) - 5; const nonZeroB = b === 0 ? 3 : b; const answer = Math.floor(Math.random() * 21) - 10; const a = answer * nonZeroB; return { a, b: nonZeroB, op, answer }; } } function check() { const parsed = parseInt(userAnswer); if (isNaN(parsed)) return; const isCorrect = parsed === problem.answer; setFeedback(isCorrect ? "correct" : "incorrect"); setScore((prev) => ({ correct: prev.correct + (isCorrect ? 1 : 0), total: prev.total + 1, })); } function next() { setProblem(generateProblem()); setUserAnswer(""); setFeedback(null); } const displayA = problem.a < 0 ? `(${problem.a})` : `${problem.a}`; const displayB = problem.b < 0 ? `(${problem.b})` : `${problem.b}`; return (

Quick Practice

{score.correct}/{score.total}
{displayA} {problem.op} {displayB} = { setUserAnswer(e.target.value); setFeedback(null); }} onKeyDown={(e) => { if (e.key === "Enter") { if (feedback !== null) next(); else check(); } }} className={`w-20 rounded-lg border-2 px-3 py-2 text-center text-xl font-bold outline-none transition-colors ${ feedback === "correct" ? "border-correct bg-correct-light" : feedback === "incorrect" ? "border-incorrect bg-incorrect-light" : "border-border focus:border-unit-5" }`} placeholder="?" aria-label="Your answer" /> {feedback === null ? ( ) : ( )}
{feedback === "correct" && (

Correct!

)} {feedback === "incorrect" && (

Not quite. The answer is {problem.answer}.

)}
); } export function IntegerMultiplyDivideExplorer() { const [tab, setTab] = useState("multiply"); const [inputA, setInputA] = useState("-3"); const [inputB, setInputB] = useState("5"); const [startTemp, setStartTemp] = useState("-3"); const [tempChange, setTempChange] = useState("7"); const [error, setError] = useState(""); const [steps, setSteps] = useState(null); const [currentStep, setCurrentStep] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const done = steps ? currentStep >= steps.length - 1 : false; function handleGo() { setError(""); if (tab === "thermometer") { const st = parseInt(startTemp); const ch = parseInt(tempChange); if (isNaN(st) || isNaN(ch)) { setError("Enter valid integers."); return; } if (Math.abs(st) > 20 || Math.abs(ch) > 30) { setError("Keep temperatures reasonable (-20 to 30)."); return; } const s = buildThermometerSteps(st, ch); setSteps(s); setCurrentStep(0); setIsPlaying(false); return; } const a = parseInt(inputA); const b = parseInt(inputB); if (isNaN(a) || isNaN(b)) { setError("Enter valid integers."); return; } if (tab === "divide" && b === 0) { setError("Cannot divide by zero!"); return; } if (Math.abs(a) > 99 || Math.abs(b) > 99) { setError("Keep numbers under 100."); return; } if (tab === "divide" && !Number.isInteger(a / b)) { setError("Choose numbers that divide evenly for now."); return; } try { const s = tab === "multiply" ? buildMultiplySteps(a, b) : buildDivideSteps(a, b); setSteps(s); setCurrentStep(0); setIsPlaying(false); } catch { setError("Could not compute. Check your inputs."); } } const stepForward = useCallback(() => { if (!steps || currentStep >= steps.length - 1) return; setCurrentStep((s) => s + 1); if (currentStep + 1 >= steps.length - 1) setIsPlaying(false); }, [steps, currentStep]); const stepBack = useCallback(() => { if (currentStep <= 0) return; setCurrentStep((s) => s - 1); }, [currentStep]); const togglePlay = useCallback(() => setIsPlaying((p) => !p), []); const reset = useCallback(() => { setSteps(null); setCurrentStep(0); setIsPlaying(false); }, []); const step = steps ? steps[currentStep] : null; return (
{/* Tab selector */}
{(["multiply", "divide", "thermometer"] as Tab[]).map((t) => ( ))}
{/* Input Card */}

{tab === "thermometer" ? "Enter temperature scenario" : "Enter two integers"}

{tab === "thermometer" ? (
setStartTemp(e.target.value)} className="w-24 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5" aria-label="Starting temperature" />
setTempChange(e.target.value)} className="w-24 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5" aria-label="Temperature change" />
) : (
setInputA(e.target.value)} className="w-20 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5" aria-label="First integer" /> {tab === "multiply" ? "×" : "÷"} setInputB(e.target.value)} className="w-20 rounded-lg border-2 border-border bg-surface px-3 py-2 text-center text-lg font-bold outline-none focus:border-unit-5" aria-label="Second integer" />
)} {error &&

{error}

}
{/* Sign Rules Grid (for multiply/divide) */} {tab !== "thermometer" && ( )} {/* Display Card */} {!step ? (

Enter values above and click Go

) : ( <>

{step.label}

{step.thermometer && } )}
{/* Controls */} 0} /> {/* Result */} {!done || !steps ? (

Result will appear here when steps are complete

) : ( <>

Answer

)}
{/* Practice sections */} {tab === "thermometer" ? : }
); }