"use client"; import { motion } from "framer-motion"; import { useEffect, useState } from "react"; import { COLORS, VizFrame } from "./common"; function rng(seed: number) { let s = seed; return () => { s = (s * 1664525 + 1013904223) % 4294967296; return s / 4294967296 - 0.5; }; } function genPoints(N: number, slope: number, intercept: number, noise: number, seed: number) { const r = rng(seed); return Array.from({ length: N }, () => { const x = (r() + 0.5) * 9 + 0.5; const y = slope * x + intercept + r() * noise; return { x, y }; }); } function fitLine(pts: { x: number; y: number }[]) { const n = pts.length; const sx = pts.reduce((a, p) => a + p.x, 0) / n; const sy = pts.reduce((a, p) => a + p.y, 0) / n; const sxx = pts.reduce((a, p) => a + (p.x - sx) ** 2, 0); const sxy = pts.reduce((a, p) => a + (p.x - sx) * (p.y - sy), 0); const w = sxy / sxx; return { w, b: sy - w * sx }; } function Panel({ title, noise, width, height, seed, truth, }: { title: string; noise: number; width: number; height: number; seed: number; truth: { w: number; b: number }; }) { const padX = 36; const padY = 30; const xMin = 0; const xMax = 10; const yMin = 0; const yMax = 9; const sx = (x: number) => padX + ((x - xMin) / (xMax - xMin)) * (width - padX * 2); const sy = (y: number) => height - padY - ((y - yMin) / (yMax - yMin)) * (height - padY * 2); const [seedTick, setSeedTick] = useState(0); useEffect(() => { const id = setInterval(() => setSeedTick((t) => t + 1), 1800); return () => clearInterval(id); }, []); const pts = genPoints(40, truth.w, truth.b, noise, seed + seedTick * 1000); const fit = fitLine(pts); return ( {/* Frame */} {title} {/* Axes */} {/* Truth line */} {/* Points */} {pts.map((p, i) => ( ))} {/* Fit line */} {/* Stats */} ŵ {fit.w.toFixed(2)} · b̂ {fit.b.toFixed(2)} ); } export function NoiseVsClean({ width = 880, height = 380, }: { width?: number; height?: number; }) { const panelW = (width - 30) / 2; const panelH = height - 30; const truth = { w: 0.6, b: 1.2 }; return ( ); }