{
  "schemaVersion": 1,
  "slug": "percentile-calculator",
  "category": "statistics",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "0479fcbaab27acef89bf6ed474e19a92b6d3e51326f4b34f9fb625d47d04fe20",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/percentile-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "Qp = x[lo] + (x[hi] − x[lo]) × (idx − lo); idx = (n − 1) × p ÷ 100, lo = ⌊idx⌋, hi = ⌈idx⌉",
    "vars": [
      [
        "Qp",
        "Value at percentile p"
      ],
      [
        "n",
        "Number of values"
      ],
      [
        "p",
        "Percentile (0–100)"
      ],
      [
        "x[]",
        "Sorted data (ascending)"
      ]
    ],
    "note": "Uses linear interpolation at index (n−1)×p/100 (the inclusive percentile method). Accepts up to 10,000 finite values and percentile p from 0 to 100; invalid or empty list entries are rejected."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const raw=String(values).trim(),tokens=raw?raw.split(/[\\s,]+/):[],validToken=t=>/^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?$/i.test(t),emptyItem=/^\\s*,|,\\s*(?:,|$)/.test(String(values)),xs=tokens.map(Number);if(!tokens.length||raw.length>20000||tokens.length>10000||emptyItem||tokens.some(t=>!validToken(t))||xs.some(n=>!Number.isFinite(n))||!Number.isFinite(percentile)||percentile<0||percentile>100)return{__error:'inputError',value:null,index:null,count:null,minimum:null,maximum:null};xs.sort((a,b)=>a-b);const idx=(xs.length-1)*percentile/100,lo=Math.floor(idx),hi=Math.ceil(idx),val=xs[lo]+(xs[hi]-xs[lo])*(idx-lo);return{value:val,index:idx,count:xs.length,minimum:xs[0],maximum:xs[xs.length-1]};",
    "inputs": [
      {
        "name": "values",
        "type": "text",
        "default": "12, 45, 67, 78, 88, 90, 92, 95, 99, 100",
        "step": 1
      },
      {
        "name": "percentile",
        "type": "number",
        "default": 90,
        "min": 0,
        "max": 100,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "value",
        "precision": 4,
        "unit": ""
      },
      {
        "name": "index",
        "precision": 2,
        "unit": ""
      },
      {
        "name": "count",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "minimum",
        "precision": 4,
        "unit": ""
      },
      {
        "name": "maximum",
        "precision": 4,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
