{"spec_id":"histogram-cumulative","library":"muix","language":"javascript","code":"// anyplot.ai\n// histogram-cumulative: Cumulative Histogram\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-09-05\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"histogram-cumulative · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\n\n// --- Data (in-memory, deterministic): call-center wait times before an agent\n// answers. Deterministic LCG so the sampled distribution is stable across\n// renders — the browser has no seeded Math.random().\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return function next() {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = makeLcg(42);\nfunction gaussianSample() {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\n// Log-normal draw: most callers wait only a couple of minutes, with a modest\n// tail of calls stuck in queue during peak load.\nconst CALL_COUNT = 1200;\nconst LOG_MU = Math.log(2.2);\nconst LOG_SIGMA = 0.45;\nconst waitMinutes = Array.from({ length: CALL_COUNT }, () => {\n  const raw = Math.exp(LOG_MU + LOG_SIGMA * gaussianSample());\n  return Math.min(12, raw);\n});\n\n// Bin into 1-minute buckets covering the observed range, then accumulate.\nconst BIN_WIDTH = 1;\nconst maxWait = Math.max(...waitMinutes);\nconst binCount = Math.ceil(maxWait / BIN_WIDTH) + 1;\nconst counts = new Array(binCount).fill(0);\nwaitMinutes.forEach((minutes) => {\n  const idx = Math.min(binCount - 1, Math.floor(minutes / BIN_WIDTH));\n  counts[idx] += 1;\n});\n\n// Running total up to each bin's right edge, expressed as a percentage of\n// all calls — the monotonically non-decreasing ogive.\nconst binEdges = Array.from({ length: binCount + 1 }, (_, i) => i * BIN_WIDTH);\nconst cumulativePct = [0];\nlet running = 0;\ncounts.forEach((count) => {\n  running += count;\n  cumulativePct.push((running / CALL_COUNT) * 100);\n});\n\n// Median wait time, used as a reference-line focal point for the curve.\nconst sortedWaits = [...waitMinutes].sort((a, b) => a - b);\nconst medianWait = sortedWaits[Math.floor(sortedWaits.length / 2)];\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const chartHeight = height - TITLE_HEIGHT;\n\n  return (\n    <div style={{ width, height }}>\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          lineHeight: `${TITLE_HEIGHT}px`,\n          textAlign: \"center\",\n          fontSize: 27,\n          fontWeight: 500,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <LineChart\n        width={width}\n        height={chartHeight}\n        skipAnimation\n        series={[\n          {\n            data: cumulativePct,\n            label: \"Calls Answered\",\n            color: t.palette[0],\n            curve: \"stepAfter\",\n            area: true,\n            showMark: false,\n            valueFormatter: (v) => `${v.toFixed(1)}% of calls`,\n          },\n        ]}\n        xAxis={[\n          {\n            data: binEdges,\n            scaleType: \"linear\",\n            label: \"Wait Time Before Answer (minutes)\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14 },\n            min: 0,\n            max: binEdges[binEdges.length - 1],\n            // Force whole-minute ticks (binEdges) instead of the auto-generated\n            // 0.5-minute increments, which crowd once scaled down for mobile.\n            tickInterval: binEdges,\n          },\n        ]}\n        yAxis={[\n          {\n            label: \"Cumulative Calls Answered (%)\",\n            labelStyle: { fontSize: 16, fontWeight: 500 },\n            tickLabelStyle: { fontSize: 14 },\n            min: 0,\n            max: 100,\n          },\n        ]}\n        grid={{ horizontal: true }}\n        margin={{ left: 96, right: 32, top: 24, bottom: 76 }}\n        slotProps={{ legend: { hidden: true } }}\n        sx={{\n          \"& .MuiAreaElement-root\": { fillOpacity: 0.18 },\n          \"& .MuiLineElement-root\": { strokeWidth: 3 },\n          \"& .MuiChartsAxis-tickLabel\": { fontSize: \"14px\" },\n        }}\n      >\n        <ChartsReferenceLine\n          x={medianWait}\n          label={`Median: ${medianWait.toFixed(1)} min`}\n          labelAlign=\"end\"\n          labelStyle={{ fontSize: 13, fill: t.inkSoft }}\n          lineStyle={{ stroke: t.inkSoft, strokeDasharray: \"6 4\" }}\n        />\n      </LineChart>\n    </div>\n  );\n}\n"}