From da82699a4c7e245fe35b82a49b4683eb9f7bbc54 Mon Sep 17 00:00:00 2001 From: warringtond Date: Mon, 18 May 2026 21:15:33 -0400 Subject: [PATCH] denary to binary --- .claude/settings.local.json | 7 +- .../unit-6-number-system/binary/page.tsx | 45 ++- .../explorers/denary-to-binary-explorer.tsx | 368 ++++++++++++++++++ 3 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 components/explorers/denary-to-binary-explorer.tsx diff --git a/.claude/settings.local.json b/.claude/settings.local.json index de7759c..30b46fd 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,12 @@ { "permissions": { "allow": [ - "Bash(cd \"C:/Users/earl_/source/repos/CabritsMath/cabrits-math\" && npx next build 2>&1 | tail -80)" + "Bash(cd \"C:/Users/earl_/source/repos/CabritsMath/cabrits-math\" && npx next build 2>&1 | tail -80)", + "Bash(npx tsc *)", + "Bash(echo \"TSC_EXIT=$?\")", + "Bash(npx eslint *)", + "Bash(echo \"LINT_EXIT=$?\")", + "Bash(npx next *)" ] } } diff --git a/app/lessons/unit-6-number-system/binary/page.tsx b/app/lessons/unit-6-number-system/binary/page.tsx index 8966e97..005804d 100644 --- a/app/lessons/unit-6-number-system/binary/page.tsx +++ b/app/lessons/unit-6-number-system/binary/page.tsx @@ -1,9 +1,20 @@ "use client"; +import { useState } from "react"; import { Breadcrumbs } from "@/components/layout/breadcrumbs"; import { BinaryConverterExplorer } from "@/components/explorers/binary-converter-explorer"; +import { DenaryToBinaryExplorer } from "@/components/explorers/denary-to-binary-explorer"; + +type Mode = "binary-to-denary" | "denary-to-binary"; + +const MODES: { key: Mode; label: string }[] = [ + { key: "binary-to-denary", label: "Binary → Denary" }, + { key: "denary-to-binary", label: "Denary → Binary" }, +]; export default function BinaryPage() { + const [mode, setMode] = useState("binary-to-denary"); + return (
-

Binary → Decimal Converter

-

Same shape, different base — explore place value in powers of 2.

+

+ {mode === "binary-to-denary" + ? "Binary → Denary Converter" + : "Denary → Binary Converter"} +

+

+ {mode === "binary-to-denary" + ? "Same shape, different base — explore place value in powers of 2." + : "Divide by 2. Track the remainder. Read bottom-up."} +

- +
+ {MODES.map((m) => ( + + ))} +
+ + {mode === "binary-to-denary" ? ( + + ) : ( + + )}
); } diff --git a/components/explorers/denary-to-binary-explorer.tsx b/components/explorers/denary-to-binary-explorer.tsx new file mode 100644 index 0000000..5fa78f4 --- /dev/null +++ b/components/explorers/denary-to-binary-explorer.tsx @@ -0,0 +1,368 @@ +"use client"; + +import { useState, useMemo, useCallback } from "react"; +import { Card } from "@/components/ui/card"; + +const COLUMNS = [32, 16, 8, 4, 2, 1]; +const PRESETS = [3, 5, 11, 13, 22, 25, 29, 31, 45, 58]; + +interface DivisionRow { + before: number; + quotient: number; + remainder: number; +} + +interface ConversionResult { + rows: DivisionRow[]; + bitsLowToHigh: number[]; +} + +function runDivision(n: number): ConversionResult { + const rows: DivisionRow[] = []; + if (n === 0) { + rows.push({ before: 0, quotient: 0, remainder: 0 }); + return { rows, bitsLowToHigh: [0] }; + } + let current = n; + while (current > 0) { + const q = Math.floor(current / 2); + const r = current % 2; + rows.push({ before: current, quotient: q, remainder: r }); + current = q; + } + return { rows, bitsLowToHigh: rows.map((r) => r.remainder) }; +} + +function bitsForChart(bitsLowToHigh: number[]): (number | null)[] { + const out: (number | null)[] = Array(COLUMNS.length).fill(null); + for (let i = 0; i < bitsLowToHigh.length && i < COLUMNS.length; i++) { + out[COLUMNS.length - 1 - i] = bitsLowToHigh[i]; + } + return out; +} + +export function DenaryToBinaryExplorer() { + const [input, setInput] = useState(""); + const [submitted, setSubmitted] = useState(null); + const [error, setError] = useState(""); + const [answerHidden, setAnswerHidden] = useState(false); + const [chartHidden, setChartHidden] = useState(false); + + const handleInputChange = (value: string) => { + setInput(value.replace(/[^0-9]/g, "").slice(0, 3)); + setError(""); + }; + + const convert = useCallback((raw?: string) => { + const source = (raw ?? input).trim(); + if (source === "") { + setError("Type a denary number first (whole number from 1 to 63)."); + setSubmitted(null); + return; + } + if (!/^\d+$/.test(source)) { + setError("Use whole digits only (0-9)."); + return; + } + const n = parseInt(source, 10); + if (n < 0 || n > 63) { + setError("Pick a whole number between 1 and 63. The chart in this lesson is six columns: 32, 16, 8, 4, 2, 1."); + return; + } + setError(""); + setSubmitted(n); + }, [input]); + + const applyPreset = (value: number) => { + setInput(String(value)); + convert(String(value)); + }; + + const handleClear = () => { + setInput(""); + setSubmitted(null); + setError(""); + }; + + const handleRandom = () => { + const n = 1 + Math.floor(Math.random() * 63); + setInput(String(n)); + convert(String(n)); + }; + + const result = useMemo(() => { + if (submitted === null) return null; + return runDivision(submitted); + }, [submitted]); + + const chartBits = useMemo(() => { + if (!result) return Array(COLUMNS.length).fill(null) as (number | null)[]; + return bitsForChart(result.bitsLowToHigh); + }, [result]); + + const binaryString = useMemo(() => { + if (!result) return ""; + const reversed = result.bitsLowToHigh.slice().reverse().join(""); + return reversed.replace(/^0+/, "") || "0"; + }, [result]); + + return ( +
+ {/* Input Card */} + +

+ Type a Denary Number (1 to 63) +

+
+ + handleInputChange(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") convert(); + }} + className="w-32 rounded-lg border-2 border-unit-6 bg-surface px-3 py-2 text-center font-mono text-2xl font-bold tracking-[0.3em] text-unit-6 outline-none focus:border-unit-6-dark" + aria-label="Denary number input" + /> + + + +
+ +
+ {PRESETS.map((p) => ( + + ))} +
+ + {error && ( +

+ {error} +

+ )} +
+ + {/* Division steps */} + +

+ Repeated Division by 2 +

+ +
+ {!result ? ( +

+ Type a denary number above and press Convert to see the division table. +

+ ) : submitted === 0 ? ( +

+ 0 in binary is 0. +

+ ) : ( +
+ + + + {["Step", "n ÷ 2", "Quotient", "Remainder", ""].map((h, i) => ( + + ))} + + + + {result.rows.map((row, idx) => ( + + + + + + + + ))} + + + + +
+ {h} +
+ {idx + 1} + + {row.before} ÷ 2 + + {row.quotient} + + {row.remainder} + + {idx === 0 + ? "↓ start" + : idx === result.rows.length - 1 + ? "↑ read up" + : ""} +
+ Quotient is 0 — stop. Read remainders from bottom to top. +
+
+ )} +
+ +

+ Read the remainders column from bottom to top. That string of 0s and 1s is your binary number. +

+
+ + {/* Place value chart */} + +
+

+ Place Value Chart (powers of 2) +

+ +
+ + {!chartHidden && ( +
+ + + + {COLUMNS.map((_, idx) => { + const exp = COLUMNS.length - 1 - idx; + return ( + + ); + })} + + + {COLUMNS.map((v) => ( + + ))} + + + {chartBits.map((b, i) => ( + + ))} + + + {chartBits.map((b, i) => ( + + ))} + + +
+ 2{exp} +
+ {v} +
+ {b === null ? "-" : b} +
+ {b === null ? "OFF" : b === 1 ? "ON" : "OFF"} +
+
+ )} +
+ + {/* Answer banner */} + + {submitted === null ? ( +

+ Type a denary number above and press Convert. +

+ ) : answerHidden ? ( +

+ {submitted} = ?2 (press Show Answer) +

+ ) : ( +

+ {submitted} = {binaryString}2 in binary +

+ )} + +
+ + {/* Footer note */} + +

+ For students: solve on paper first using the division table in your worksheet. Type the denary number here only to check. The lesson is in the division and the bottom-up read, not the click. +

+
+
+ ); +}