import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts'; type ChartTheme = 'dark' | 'light'; type ComplexityProfile = { key: string; label: string; color: string; inputSizes: number[]; getGrowth: (n: number) => number; getAuxiliaryLabel?: (n: number) => string; inputLabel?: string; note?: string; }; type ComplexityPoint = { n: number; estimatedGrowth: number; normalizedGrowth: number; auxiliaryLabel?: string; }; type ComplexityGrowthChartProps = { complexity?: string | null; theme?: ChartTheme; className?: string; }; const STANDARD_INPUT_SIZES = [1, 2, 4, 8, 16, 32, 64, 128]; const EXPONENTIAL_INPUT_SIZES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const normalizeComplexity = (value?: string | null) => (value ?? '').replace(/\s+/g, '').toLowerCase(); const getComplexityProfile = (complexity?: string | null): ComplexityProfile | null => { const normalized = normalizeComplexity(complexity); if (!normalized) { return null; } if (/^o\(1\)$/.test(normalized)) { return { key: 'constant', label: 'O(1)', color: '#22c55e', inputSizes: STANDARD_INPUT_SIZES, getGrowth: () => 1, }; } if (/nlogn/.test(normalized)) { return { key: 'linearithmic', label: 'O(n log n)', color: '#a855f7', inputSizes: STANDARD_INPUT_SIZES, getGrowth: (n) => n * Math.log2(Math.max(2, n)), }; } if (/nlogk/.test(normalized)) { return { key: 'k-way-heap', label: 'O(N log k)', color: '#8b5cf6', inputSizes: STANDARD_INPUT_SIZES, inputLabel: 'Total elements (N)', getGrowth: (n) => { const k = Math.max(2, Math.round(Math.sqrt(n))); return n * Math.log2(k); }, getAuxiliaryLabel: (n) => `representative k = ${Math.max(2, Math.round(Math.sqrt(n)))}`, note: 'Representative multi-list plot: k grows with sqrt(N)', }; } if (/2\^n/.test(normalized)) { return { key: 'exponential', label: 'O(2^n)', color: '#e11d48', inputSizes: EXPONENTIAL_INPUT_SIZES, getGrowth: (n) => 2 ** n, }; } if (/n\^3/.test(normalized)) { return { key: 'cubic', label: 'O(n^3)', color: '#f97316', inputSizes: STANDARD_INPUT_SIZES, getGrowth: (n) => n ** 3, }; } if (/n\^2|m\*n|mn/.test(normalized)) { return { key: 'quadratic', label: normalized.includes('m') ? 'O(m * n)' : 'O(n^2)', color: '#ef4444', inputSizes: STANDARD_INPUT_SIZES, getGrowth: (n) => n ** 2, note: normalized.includes('m') ? 'Representative grid case: m = n' : undefined, }; } if (/^o\(n\)$/.test(normalized)) { return { key: 'linear', label: 'O(n)', color: '#60a5fa', inputSizes: STANDARD_INPUT_SIZES, getGrowth: (n) => n, }; } if (/logn/.test(normalized)) { return { key: 'logarithmic', label: 'O(log n)', color: '#eab308', inputSizes: STANDARD_INPUT_SIZES, getGrowth: (n) => Math.log2(n), }; } return null; }; const buildComplexityData = (profile: ComplexityProfile): ComplexityPoint[] => { const rawPoints = profile.inputSizes.map((n) => ({ n, estimatedGrowth: profile.getGrowth(n), auxiliaryLabel: profile.getAuxiliaryLabel?.(n), })); const maxGrowth = Math.max(...rawPoints.map((point) => point.estimatedGrowth)); const minGrowth = Math.min(...rawPoints.map((point) => point.estimatedGrowth)); const hasFlatGrowth = maxGrowth === minGrowth; return rawPoints.map((point) => ({ ...point, normalizedGrowth: hasFlatGrowth ? 48 : (point.estimatedGrowth / maxGrowth) * 100, })); }; const formatGrowth = (value: number) => { if (!Number.isFinite(value)) { return 'very high'; } if (Math.abs(value) < 1000) { return Number.isInteger(value) ? String(value) : value.toFixed(value < 10 ? 2 : 1); } if (Math.abs(value) < 1_000_000) { return value.toLocaleString(undefined, { maximumFractionDigits: 0 }); } return value.toExponential(2); }; function ComplexityTooltip({ active, payload, label, complexityLabel, inputLabel, theme, }: { active?: boolean; payload?: Array<{ payload?: ComplexityPoint }>; label?: number | string; complexityLabel: string; inputLabel: string; theme: ChartTheme; }) { if (!active || !payload?.length) { return null; } const point = payload[0]?.payload; const isDark = theme === 'dark'; return (