"use client"; import { useState, useCallback } from "react"; import { Card } from "@/components/ui/card"; import { ClientOnly } from "@/components/ui/client-only"; import { StepControls } from "./step-controls"; import { MathDisplay } from "@/components/math/math-display"; type Mode = "expand" | "index"; type IndexOp = "multiply" | "divide"; interface Step { label: string; math: string; } function repeat(base: number, count: number): string { return Array.from({ length: count }, () => `${base}`).join(" \\times "); } function buildExpandSteps(base: number, exp: number): Step[] { if (exp === 0) { return [ { label: "The power (index) is 0.", math: `${base}^{0}` }, { label: "Any number raised to the power 0 is equal to 1.", math: `${base}^{0} = 1` }, ]; } if (exp === 1) { return [ { label: "The power (index) is 1.", math: `${base}^{1}` }, { label: "Any number raised to the power 1 is itself.", math: `${base}^{1} = ${base}` }, ]; } const steps: Step[] = [ { label: `The base is ${base} and the power is ${exp}: multiply the base by itself ${exp} times.`, math: `${base}^{${exp}}`, }, ]; let running = base; for (let k = 2; k <= exp; k++) { running *= base; steps.push({ label: `Multiply on another ${base}.`, math: `${repeat(base, k)} = ${running}`, }); } steps.push({ label: "So the power is worked out.", math: `${base}^{${exp}} = ${running}` }); return steps; } function buildIndexSteps(base: number, m: number, n: number, op: IndexOp): Step[] { if (op === "multiply") { const total = m + n; const value = Math.pow(base, total); const steps: Step[] = [ { label: "Multiplying powers of the SAME base.", math: `${base}^{${m}} \\times ${base}^{${n}}` }, { label: "Write each power out in full.", math: `(${repeat(base, m)}) \\times (${repeat(base, n)})` }, { label: `Count the factors: ${m} + ${n} = ${total}. When multiplying, ADD the powers.`, math: `${base}^{${m}} \\times ${base}^{${n}} = ${base}^{${m}+${n}} = ${base}^{${total}}` }, ]; if (value <= 1e7) steps.push({ label: "Work out the value.", math: `${base}^{${total}} = ${value}` }); return steps; } // divide, m >= n const diff = m - n; const value = Math.pow(base, diff); const steps: Step[] = [ { label: "Dividing powers of the SAME base.", math: `\\frac{${base}^{${m}}}{${base}^{${n}}}` }, { label: "Write each power out in full.", math: `\\frac{${repeat(base, m)}}{${repeat(base, n)}}` }, { label: `Cancel the ${n} matching factor${n === 1 ? "" : "s"} from top and bottom.`, math: `${base}^{${m}} \\div ${base}^{${n}} = ${base}^{${m}-${n}} = ${base}^{${diff}}` }, ]; if (value <= 1e7) steps.push({ label: "Work out the value.", math: `${base}^{${diff}} = ${value}` }); return steps; } // ── Quick practice ─────────────────────────────────────────────────────────── interface PracticeQ { prompt: string; answer: number; } function randInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } const SUPERSCRIPT: Record = { "0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", "5": "⁵", "6": "⁶", "7": "⁷", "8": "⁸", "9": "⁹", }; function sup(n: number): string { return String(n).split("").map((c) => SUPERSCRIPT[c] ?? c).join(""); } function makeQuestion(): PracticeQ { const roll = Math.floor(Math.random() * 5); if (roll === 0) { const base = randInt(2, 7); const exp = randInt(2, 3); return { prompt: `What is ${base}${sup(exp)}?`, answer: Math.pow(base, exp) }; } if (roll === 1) { const base = randInt(2, 9); return { prompt: `What is ${base}${sup(0)}?`, answer: 1 }; } if (roll === 2) { const base = randInt(2, 4); const a = randInt(2, 3); const b = 1; return { prompt: `What is ${base}${sup(a)} − ${base}${sup(b)}?`, answer: Math.pow(base, a) - Math.pow(base, b) }; } if (roll === 3) { const base = randInt(2, 5); const m = randInt(3, 6); const n = randInt(1, m - 1); return { prompt: `Simplify ${base}${sup(m)} ÷ ${base}${sup(n)}. The answer is ${base} to what power?`, answer: m - n }; } const base = randInt(2, 6); const m = randInt(1, 4); const n = randInt(1, 4); return { prompt: `Simplify ${base}${sup(m)} × ${base}${sup(n)}. The answer is ${base} to what power?`, answer: m + n }; } function QuickPractice() { const [q, setQ] = useState(() => makeQuestion()); const [val, setVal] = useState(""); const [feedback, setFeedback] = useState<"correct" | "incorrect" | null>(null); const [score, setScore] = useState({ correct: 0, total: 0 }); function check() { const parsed = parseInt(val, 10); if (isNaN(parsed)) return; const correct = parsed === q.answer; setFeedback(correct ? "correct" : "incorrect"); setScore((s) => ({ correct: s.correct + (correct ? 1 : 0), total: s.total + 1 })); } function next() { setQ(makeQuestion()); setVal(""); setFeedback(null); } return (

Quick Practice

{score.correct}/{score.total}

{q.prompt}

{ setVal(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-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-8" }`} placeholder="?" aria-label="Your answer" /> {feedback === null ? ( ) : ( )}
{feedback === "correct" &&

Correct!

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

Not quite — the answer is {q.answer}.

)}
); } // ── Main explorer ──────────────────────────────────────────────────────────── export function ExponentsExplorer() { const [mode, setMode] = useState("expand"); const [indexOp, setIndexOp] = useState("multiply"); const [base, setBase] = useState("2"); const [power, setPower] = useState("4"); const [m, setM] = useState("4"); const [n, setN] = useState("3"); 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; const reset = useCallback(() => { setSteps(null); setCurrentStep(0); setIsPlaying(false); }, []); function switchMode(md: Mode) { setMode(md); setError(""); reset(); } function handleGo() { setError(""); const b = parseInt(base, 10); if (isNaN(b) || b < 2 || b > 12) return setError("Use a base between 2 and 12."); if (mode === "expand") { const e = parseInt(power, 10); if (isNaN(e) || e < 0 || e > 6) return setError("Use a power between 0 and 6."); if (Math.pow(b, e) > 1_000_000) return setError("That gets too big. Try a smaller base or power."); setSteps(buildExpandSteps(b, e)); } else { const mm = parseInt(m, 10); const nn = parseInt(n, 10); if (isNaN(mm) || isNaN(nn) || mm < 1 || nn < 1 || mm > 8 || nn > 8) return setError("Use powers between 1 and 8."); if (indexOp === "divide" && mm < nn) return setError("For division, the top power must be at least the bottom power."); setSteps(buildIndexSteps(b, mm, nn, indexOp)); } setCurrentStep(0); setIsPlaying(false); } 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 step = steps ? steps[currentStep] : null; return (
{/* Mode toggle */}
{(["expand", "index"] as Mode[]).map((md) => ( ))}
{/* Index op tabs */} {mode === "index" && (
{(["multiply", "divide"] as IndexOp[]).map((o) => ( ))}
)} {/* Input card */} {mode === "expand" ? ( <>

Enter a base and a power

setBase(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-8" aria-label="Base" /> to the power setPower(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-8" aria-label="Power" />
) : ( <>

Same base, two powers {indexOp === "multiply" ? "to multiply" : "to divide"}

base setBase(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-8" aria-label="Base" /> powers setM(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-8" aria-label="First power" /> {indexOp === "multiply" ? "×" : "÷"} setN(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-8" aria-label="Second power" />
)} {error &&

{error}

}
{/* Display card */} {!step ? (

Enter values above and click Go

) : ( <>

{step.label}

)}
{/* Controls */} 0} /> {/* Quick practice */} }>
); }