"use client"; import { useState, useCallback } from "react"; import { Card } from "@/components/ui/card"; import { StepControls } from "./step-controls"; type SortDirection = "ascending" | "descending"; interface Step { label: string; values: { value: number; str: string; sorted: boolean; comparing: boolean }[]; comparingPair?: [number, number]; } function padDecimal(s: string, maxInt: number, maxDec: number): string { const parts = s.split("."); const intPart = (parts[0] || "0").padStart(maxInt, "\u00A0"); // nbsp padding const decPart = (parts[1] || "").padEnd(maxDec, "0"); return maxDec > 0 ? `${intPart}.${decPart}` : intPart; } function buildSortSteps(values: number[], direction: SortDirection): Step[] { const steps: Step[] = []; const strs = values.map((v) => v.toString()); // Find max integer and decimal lengths for alignment const maxInt = Math.max(...strs.map((s) => s.split(".")[0].length)); const maxDec = Math.max(...strs.map((s) => (s.split(".")[1] || "").length)); // Step 0: Show unsorted steps.push({ label: `Sort ${values.length} decimals in ${direction} order`, values: values.map((v, i) => ({ value: v, str: padDecimal(strs[i], maxInt, maxDec), sorted: false, comparing: false, })), }); // Step 1: Show aligned place values steps.push({ label: "Align decimal points to compare place values", values: values.map((v, i) => ({ value: v, str: padDecimal(strs[i], maxInt, maxDec), sorted: false, comparing: false, })), }); // Bubble sort with steps const arr = [...values]; const arrStrs = [...strs]; const cmp = direction === "ascending" ? (a: number, b: number) => a > b : (a: number, b: number) => a < b; for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { // Show comparison steps.push({ label: `Compare ${arrStrs[j]} and ${arrStrs[j + 1]}`, values: arr.map((v, k) => ({ value: v, str: padDecimal(arrStrs[k], maxInt, maxDec), sorted: k >= arr.length - i, comparing: k === j || k === j + 1, })), comparingPair: [j, j + 1], }); if (cmp(arr[j], arr[j + 1])) { // Swap [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; [arrStrs[j], arrStrs[j + 1]] = [arrStrs[j + 1], arrStrs[j]]; steps.push({ label: `Swap: ${arrStrs[j]} ${direction === "ascending" ? "<" : ">"} ${arrStrs[j + 1]}`, values: arr.map((v, k) => ({ value: v, str: padDecimal(arrStrs[k], maxInt, maxDec), sorted: k >= arr.length - i, comparing: k === j || k === j + 1, })), }); } } } // Final step: Show sorted steps.push({ label: `Sorted (${direction}): ${arrStrs.join(direction === "ascending" ? " < " : " > ")}`, values: arr.map((v, k) => ({ value: v, str: padDecimal(arrStrs[k], maxInt, maxDec), sorted: true, comparing: false, })), }); return steps; } function NumberLine({ values, min, max }: { values: { value: number; sorted: boolean; comparing: boolean }[]; min: number; max: number }) { const range = max - min || 1; return (
{values.map((v, i) => { const pct = ((v.value - min) / range) * 100; return (
{i + 1}
); })}
{min} {max}
); } export function DecimalOrderExplorer() { const [input, setInput] = useState("3.14, 3.1, 3.141, 3.04, 3.4"); const [direction, setDirection] = useState("ascending"); 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(""); const parts = input .split(/[,\s]+/) .map((s) => s.trim()) .filter(Boolean); if (parts.length < 2) { setError("Enter at least 2 decimals separated by commas."); return; } if (parts.length > 6) { setError("Maximum 6 decimals."); return; } const values = parts.map(Number); if (values.some(isNaN)) { setError("All values must be valid numbers."); return; } try { const s = buildSortSteps(values, direction); setSteps(s); setCurrentStep(0); setIsPlaying(false); } catch { setError("Could not process. Check your input."); } } 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 (
{/* Direction tabs */}
{(["ascending", "descending"] as SortDirection[]).map((d) => ( ))}
{/* Input */}

Enter 2-6 decimal numbers (comma separated)

setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleGo()} placeholder="e.g. 3.14, 3.1, 3.141" autoComplete="off" className="max-w-[320px] rounded-lg border-2 border-border bg-surface px-3.5 py-2.5 text-lg font-medium outline-none transition-colors focus:border-unit-2" />
{error &&

{error}

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

Enter decimals above and click Sort

) : ( <>

{step.label}

{/* Place value columns */}
{step.values.map((v, i) => (
{v.str}
))}
{/* Number line */} v.value)) - 0.1} max={Math.max(...step.values.map((v) => v.value)) + 0.1} /> )}
{/* Controls */} 0} /> {/* Result */} {!done || !steps ? (

Result will appear here when steps are complete

) : ( <>

Sorted ({direction})

{step!.values.map((v) => v.value).join(direction === "ascending" ? " < " : " > ")}

)}
); }