116 lines
5.7 KiB
TypeScript
116 lines
5.7 KiB
TypeScript
import React, { useState } from 'react';
|
||
|
||
const FactoringWidget: React.FC = () => {
|
||
// ax^2 + bx + c
|
||
const [a, setA] = useState(1);
|
||
const [b, setB] = useState(5);
|
||
const [c, setC] = useState(6);
|
||
|
||
const product = a * c;
|
||
const sum = b;
|
||
|
||
// We won't solve it for them immediately, let them guess or think
|
||
// But we will show if it's factorable over integers
|
||
// Simple check for nice numbers
|
||
const getFactors = () => {
|
||
// Find two numbers p, q such that p*q = product and p+q = sum
|
||
// Brute force range reasonable for typical SAT (up to +/- 100)
|
||
for (let i = -100; i <= 100; i++) {
|
||
if (i === 0) continue;
|
||
const q = product / i;
|
||
if (Math.abs(q - Math.round(q)) < 0.001) { // is integer
|
||
if (i + q === sum) return [i, q].sort((x,y) => x-y);
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
const solution = getFactors();
|
||
|
||
return (
|
||
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
|
||
<div className="flex flex-col md:flex-row gap-8 items-center">
|
||
{/* Input Side */}
|
||
<div className="w-full md:w-1/2 space-y-4">
|
||
<h4 className="font-bold text-violet-900 mb-2">Polynomial: <span className="font-mono text-lg">{a === 1 ? '' : a}x² {b >= 0 ? '+' : ''}{b}x {c >= 0 ? '+' : ''}{c}</span></h4>
|
||
|
||
<div className="grid grid-cols-3 gap-2">
|
||
<div>
|
||
<label className="text-xs font-bold text-slate-400">a</label>
|
||
<input type="number" value={a} onChange={e => setA(parseInt(e.target.value) || 0)} className="w-full p-2 border rounded font-mono font-bold text-center" />
|
||
</div>
|
||
<div>
|
||
<label className="text-xs font-bold text-slate-400">b (Sum)</label>
|
||
<input type="number" value={b} onChange={e => setB(parseInt(e.target.value) || 0)} className="w-full p-2 border rounded font-mono font-bold text-center" />
|
||
</div>
|
||
<div>
|
||
<label className="text-xs font-bold text-slate-400">c</label>
|
||
<input type="number" value={c} onChange={e => setC(parseInt(e.target.value) || 0)} className="w-full p-2 border rounded font-mono font-bold text-center" />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="p-4 bg-slate-50 rounded-lg text-sm text-slate-600">
|
||
<p><strong>AC Method (Diamond):</strong></p>
|
||
<p>Find two numbers that multiply to <strong>a·c</strong> and add to <strong>b</strong>.</p>
|
||
<p className="mt-2 font-mono text-center">
|
||
Product (ac) = {a} × {c} = <strong>{product}</strong> <br/>
|
||
Sum (b) = <strong>{sum}</strong>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Visual Side */}
|
||
<div className="flex-1 flex flex-col items-center justify-center">
|
||
<div className="relative w-48 h-48">
|
||
{/* X Shape */}
|
||
<div className="absolute top-0 left-0 w-full h-full">
|
||
<svg width="100%" height="100%" viewBox="0 0 200 200">
|
||
<line x1="20" y1="20" x2="180" y2="180" stroke="#cbd5e1" strokeWidth="4" />
|
||
<line x1="180" y1="20" x2="20" y2="180" stroke="#cbd5e1" strokeWidth="4" />
|
||
</svg>
|
||
</div>
|
||
|
||
{/* Top (Product) */}
|
||
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-4 bg-violet-100 px-3 py-1 rounded border border-violet-300 text-violet-800 font-bold shadow-sm">
|
||
{product}
|
||
</div>
|
||
|
||
{/* Bottom (Sum) */}
|
||
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-4 bg-indigo-100 px-3 py-1 rounded border border-indigo-300 text-indigo-800 font-bold shadow-sm">
|
||
{sum}
|
||
</div>
|
||
|
||
{/* Left (Factor 1) */}
|
||
<div className="absolute left-0 top-1/2 -translate-x-6 -translate-y-1/2 bg-white px-3 py-2 rounded border-2 border-emerald-400 text-emerald-700 font-bold shadow-md min-w-[3rem] text-center">
|
||
{solution ? solution[0] : "?"}
|
||
</div>
|
||
|
||
{/* Right (Factor 2) */}
|
||
<div className="absolute right-0 top-1/2 translate-x-6 -translate-y-1/2 bg-white px-3 py-2 rounded border-2 border-emerald-400 text-emerald-700 font-bold shadow-md min-w-[3rem] text-center">
|
||
{solution ? solution[1] : "?"}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-8 text-center">
|
||
{solution ? (
|
||
<div className="text-emerald-700 font-bold bg-emerald-50 px-4 py-2 rounded-lg border border-emerald-200">
|
||
Factors Found: {solution[0]} and {solution[1]}
|
||
{a === 1 && (
|
||
<div className="text-sm mt-1 font-mono text-slate-600">
|
||
(x {solution[0] >= 0 ? '+' : ''}{solution[0]})(x {solution[1] >= 0 ? '+' : ''}{solution[1]})
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="text-rose-600 font-bold text-sm bg-rose-50 px-4 py-2 rounded-lg border border-rose-200">
|
||
No integer factors found. (Prime)
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default FactoringWidget; |