{"spec_id":"qq-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// qq-basic: Basic Q-Q Plot\n// Library: chartjs 4.4.7 | JavaScript 22.23.1\n// Quality: 92/100 | Created: 2026-07-24\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Deterministic PRNG (Park-Miller LCG) + Box-Muller normal sampler ------\nlet seed = 42;\nfunction nextUniform() {\n  seed = (seed * 16807) % 2147483647;\n  return seed / 2147483647;\n}\nfunction nextStandardNormal() {\n  const u1 = Math.max(nextUniform(), 1e-12);\n  const u2 = nextUniform();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// --- Inverse standard-normal CDF (Acklam's rational approximation) --------\nfunction normInv(p) {\n  const a = [-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2,\n    1.383577518672690e2, -3.066479806614716e1, 2.506628277459239e0];\n  const b = [-5.447609879822406e1, 1.615858368580409e2, -1.556989798598866e2,\n    6.680131188771972e1, -1.328068155288572e1];\n  const c = [-7.784894002430293e-3, -3.223964580411365e-1, -2.400758277161838e0,\n    -2.549732539343734e0, 4.374664141464968e0, 2.938163982698783e0];\n  const d = [7.784695709041462e-3, 3.224671290700398e-1, 2.445134137142996e0,\n    3.754408661907416e0];\n  const pLow = 0.02425;\n  const pHigh = 1 - pLow;\n  if (p < pLow) {\n    const q = Math.sqrt(-2 * Math.log(p));\n    return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /\n      ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);\n  }\n  if (p <= pHigh) {\n    const q = p - 0.5;\n    const r = q * q;\n    return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /\n      (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);\n  }\n  const q = Math.sqrt(-2 * Math.log(1 - p));\n  return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /\n    ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);\n}\n\n// --- Data: standardized residuals from a precision gauge calibration run ---\n// (z-scores; a Q-Q plot against the standard normal tests whether the\n// instrument's error distribution supports the normality assumption behind\n// its control-chart limits)\nconst n = 100;\nconst residuals = [];\nfor (let i = 0; i < n; i++) residuals.push(nextStandardNormal());\nresiduals.sort((x, y) => x - y);\n\nconst qqPoints = residuals.map((sampleQuantile, i) => ({\n  x: normInv((i + 0.5) / n),\n  y: sampleQuantile,\n}));\n\n// The point with the largest departure from the reference line — the visual\n// signature of the heavy-tail/outlier behavior this plot is meant to surface.\nconst maxDevPoint = qqPoints.reduce((worst, p) =>\n  Math.abs(p.y - p.x) > Math.abs(worst.y - worst.x) ? p : worst\n);\n\n// 45-degree reference line spanning the data range, with a little padding.\n// The axis min/max below are set explicitly to this same [lo - pad, hi + pad]\n// range, so the dashed line always reaches the plot corners instead of\n// stopping short of Chart.js's auto-scaled (and independently rounded) extent.\nconst allCoords = qqPoints.flatMap((p) => [p.x, p.y]);\nconst lo = Math.min(...allCoords);\nconst hi = Math.max(...allCoords);\nconst pad = (hi - lo) * 0.08;\nconst axisMin = lo - pad;\nconst axisMax = hi + pad;\nconst referenceLine = [\n  { x: axisMin, y: axisMin },\n  { x: axisMax, y: axisMax },\n];\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement('canvas');\ndocument.getElementById('container').appendChild(canvas);\n\n// --- Title — scale font size for length (baseline: 22px at 67 chars) -------\nconst titleText = 'Calibration Residuals · qq-basic · javascript · chartjs · anyplot.ai';\nconst titleSize = Math.max(14, Math.round(22 * 67 / titleText.length));\n\n// --- Custom plugin: callout labeling the largest tail deviation ------------\n// A native Chart.js plugin (afterDatasetsDraw hook) rather than a bundled\n// datalabels plugin — draws a leader line + text pointing at the point that\n// diverges most from the reference line, reinforcing the outlier/heavy-tail\n// story the spec calls for.\nconst tailCalloutPlugin = {\n  id: 'tailCallout',\n  afterDatasetsDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const px = scales.x.getPixelForValue(maxDevPoint.x);\n    const py = scales.y.getPixelForValue(maxDevPoint.y);\n    const cx = (chartArea.left + chartArea.right) / 2;\n    const cy = (chartArea.top + chartArea.bottom) / 2;\n    const dirX = px >= cx ? 1 : -1;\n    const dirY = py >= cy ? 1 : -1;\n    const labelX = px + dirX * 90;\n    const labelY = py + dirY * 30;\n\n    ctx.save();\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(px, py);\n    ctx.lineTo(labelX, labelY);\n    ctx.stroke();\n\n    ctx.fillStyle = t.inkSoft;\n    ctx.font = '13px sans-serif';\n    ctx.textAlign = dirX > 0 ? 'left' : 'right';\n    ctx.textBaseline = 'middle';\n    ctx.fillText('Largest tail deviation', labelX + dirX * 4, labelY);\n    ctx.restore();\n  },\n};\n\n// --- Chart -------------------------------------------------------------------\nnew Chart(canvas, {\n  type: 'scatter',\n  data: {\n    datasets: [\n      {\n        type: 'line',\n        label: 'Reference Line (y = x)',\n        data: referenceLine,\n        borderColor: t.ink,\n        borderDash: [10, 6],\n        borderWidth: 2,\n        pointRadius: 0,\n        fill: false,\n        order: 2,\n      },\n      {\n        type: 'scatter',\n        label: 'Sample vs. Theoretical',\n        data: qqPoints,\n        backgroundColor: `${t.palette[0]}B3`,\n        borderColor: t.pageBg,\n        borderWidth: 1,\n        pointRadius: 5,\n        pointHoverRadius: 7,\n        order: 1,\n      },\n    ],\n  },\n  plugins: [tailCalloutPlugin],\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: titleText,\n        color: t.ink,\n        font: { size: titleSize, weight: '500' },\n        padding: { top: 8, bottom: 16 },\n      },\n      legend: {\n        labels: { color: t.ink, font: { size: 15 }, boxWidth: 22, padding: 14 },\n      },\n    },\n    scales: {\n      x: {\n        min: axisMin,\n        max: axisMax,\n        title: { display: true, text: 'Theoretical Quantiles', color: t.ink, font: { size: 16 } },\n        ticks: { color: t.inkSoft, font: { size: 13 } },\n        grid: { color: t.grid },\n        border: { color: t.inkSoft },\n      },\n      y: {\n        min: axisMin,\n        max: axisMax,\n        title: { display: true, text: 'Sample Quantiles', color: t.ink, font: { size: 16 } },\n        ticks: { color: t.inkSoft, font: { size: 13 } },\n        grid: { color: t.grid },\n        border: { color: t.inkSoft },\n      },\n    },\n  },\n});\n"}