"use client"; import * as React from "react"; import { useForm, useFieldArray } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { LagrangeFormSchema, type LagrangeFormValues, type DataPoint } from "@/lib/schemas"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { PlusCircle, MinusCircle, Calculator, Sigma, Target, AlertTriangle, ListChecks, FunctionSquare, Spline } from "lucide-react"; import { calculateLagrangeInterpolation, type CalculationStep } from "@/app/actions"; import { useToast } from "@/hooks/use-toast"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"; import { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, } from "@/components/ui/chart"; import { LineChart as RechartsLineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, Legend as RechartsLegend, Scatter, ReferenceDot, Label as RechartsLabel, ReferenceLine } from "recharts"; import { cn } from "@/lib/utils"; // Helper component to render fractions const SymbolicFraction: React.FC<{ numerator: string | React.ReactNode; denominator: string | React.ReactNode; className?: string }> = ({ numerator, denominator, className }) => { return (
{numerator}
{denominator}
); }; export function LagrangeSolver() { const { toast } = useToast(); const [isLoading, setIsLoading] = React.useState(false); const [result, setResult] = React.useState<{ interpolatedValue: number | null; polynomialTermsDisplay: string[]; calculationSteps: CalculationStep[]; plotData?: Array<{ x: number; original?: number; interpolated?: number; target?: number }>; interpolationPoint?: number; } | null>(null); const [error, setError] = React.useState(null); const form = useForm({ resolver: zodResolver(LagrangeFormSchema), defaultValues: { dataPoints: [{ x: "", y: "" }, { x: "", y: "" }], interpolationX: "", }, }); const { fields, append, remove } = useFieldArray({ control: form.control, name: "dataPoints", }); const onSubmit = async (values: LagrangeFormValues) => { setIsLoading(true); setResult(null); setError(null); const numericDataPoints: DataPoint[] = values.dataPoints.map(dp => ({ x: parseFloat(dp.x), y: parseFloat(dp.y), })); const numericInterpolationX = parseFloat(values.interpolationX); try { const response = await calculateLagrangeInterpolation(numericDataPoints, numericInterpolationX); if (response.error) { setError(response.error); toast({ title: "Calculation Error", description: response.error, variant: "destructive", }); } else { setResult(response); } } catch (e) { const errorMessage = e instanceof Error ? e.message : "An unknown error occurred."; setError(errorMessage); toast({ title: "Unhandled Error", description: errorMessage, variant: "destructive", }); } finally { setIsLoading(false); } }; const chartConfig = { original: { label: "Original Data", color: "hsl(var(--primary))" }, interpolated: { label: "Lagrange Polynomial", color: "hsl(var(--accent))" }, target: { label: `Interpolated L(${result?.interpolationPoint})`, color: "hsl(var(--destructive))" }, }; return (
Data Points (x, y) Enter the known data points. At least one point is required. X values must be unique. {fields.map((field, index) => (
( X{index} )} /> ( Y{index} )} /> {fields.length > 1 && ( )}
))} {form.formState.errors.dataPoints && typeof form.formState.errors.dataPoints.message === 'string' && (

{form.formState.errors.dataPoints.message}

)} {form.formState.errors.dataPoints?.root?.message && (

{form.formState.errors.dataPoints.root.message}

)}
Interpolation Point (X) Specify the X value at which to estimate the function. ( X value for interpolation )} /> {isLoading && (

Calculating, please wait...

)} {error && !isLoading && ( Error {error} )} {result && !isLoading && !error && ( <> Results Summary

Interpolated Value:

L({result.interpolationPoint?.toString()}) =  {result.interpolatedValue?.toFixed(6)}

Lagrange Polynomial L(x):

The polynomial is formed by summing these terms:


                      {result.polynomialTermsDisplay.map((term, index) => (
                        
                          {term}
                          {index < result.polynomialTermsDisplay.length - 1 ? " + " : ""}
                        
                      ))}
                    
{result.calculationSteps && result.calculationSteps.length > 0 && ( Step-by-Step Calculation {result.calculationSteps.map((step, index) => ( Term {index + 1}: Contribution of y{step.termIndex} = {step.yValue.toFixed(4)} {/* Section 1: Basis Polynomial Definition */}

Basis Polynomial L{step.termIndex}(x)

General form: L{step.termIndex}(x) = ∏k≠{step.termIndex} (x - xk) / (x{step.termIndex} - xk)

Actual L{step.termIndex}(x) = 
{/* Section 2: Evaluation */}

Evaluating L{step.termIndex}(x) at x = {result.interpolationPoint?.toString()}

L{step.termIndex}({result.interpolationPoint?.toString()}) = 
 = {step.basisPolynomialValueAtX.toFixed(6)}
{/* Section 3: Term Contribution */}

Term Contribution: y{step.termIndex} · L{step.termIndex}({result.interpolationPoint?.toString()})

{ form.getValues("dataPoints").length > 1 && (step.basisNumeratorSymbolic !== "1" || step.basisDenominatorSymbolic !== "1" || step.basisDenominatorValue !== 1.0) &&
Symbolic Term{step.termIndex}{step.yValue.toFixed(4)} · ({step.basisNumeratorSymbolic || "1"}) } denominator={step.basisDenominatorValue.toFixed(6)} />
} { (form.getValues("dataPoints").length === 1 || (step.basisNumeratorSymbolic === "1" && step.basisDenominatorSymbolic ==="1" && step.basisDenominatorValue === 1.0)) &&

Symbolic Term{step.termIndex} = {step.yValue.toFixed(4)}

}

Numerical Term{step.termIndex} = {step.yValue.toFixed(4)} · {step.basisPolynomialValueAtX.toFixed(6)}

{step.termValueAtX.toFixed(6)}

))}

Sum of Term Contributions (Final Interpolated Value):

{/* Removed font-code */} L({result.interpolationPoint?.toString()}) = {result.calculationSteps.map(s => s.termValueAtX.toFixed(6)).join(" + ")} = {result.interpolatedValue?.toFixed(6)}

)} {result.plotData && result.plotData.length > 0 && ( Interpolation Plot typeof tick === 'number' ? tick.toFixed(2) : tick} stroke="hsl(var(--muted-foreground))" /> typeof tick === 'number' ? tick.toFixed(2) : tick} stroke="hsl(var(--muted-foreground))" /> { if (props.payload?.target !== undefined && name === 'target') return [`L(${props.payload.x.toFixed(2)}) = ${props.payload.target.toFixed(4)}`, null]; if (name === 'original') return [value.toFixed(4), 'Original Point']; if (name === 'interpolated') return [value.toFixed(4), 'Polynomial P(x)']; return [value, name]; }} />} cursor={{ stroke: "hsl(var(--accent))", strokeDasharray: '3 3' }} wrapperStyle={{ outline: 'none', border: '1px solid hsl(var(--border))', borderRadius: 'var(--radius)', boxShadow: 'var(--shadow-md)'}} /> } wrapperStyle={{paddingTop: '20px'}} /> {result.interpolationPoint !== undefined && result.interpolatedValue !== null && ( )} {result.interpolationPoint !== undefined && result.interpolatedValue !== null && ( <> )} )} )}
); }