{
  "schemaVersion": 1,
  "slug": "marathon-finish-time-calculator",
  "category": "sports",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "921c331381bacbeb740c228bb030e50f8c039d8b49f848a88c71d48290216946",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/marathon-finish-time-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": "t₂ = t₁ × (D₂ ÷ D₁)^e",
    "vars": [
      [
        "t₂",
        "predicted time for target distance, min"
      ],
      [
        "t₁",
        "recent race time, min = 60h + m"
      ],
      [
        "D₁",
        "recent race distance, km"
      ],
      [
        "D₂",
        "target distance, km"
      ],
      [
        "e",
        "fatigue exponent (≈1.06)"
      ]
    ],
    "note": "Riegel estimate: t₂ = t₁ × (D₂ ÷ D₁)^e; the default exponent 1.06 is a model parameter, not an individual guarantee. Actual performance depends on preparation and conditions."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const T={'5k':5,'10k':10,half:21.0975,marathon:42.195,'50k':50},target=T[targetDistance];const shortMin=shortHours*60+shortMinutes;if(!target||!Number.isFinite(shortDistance)||shortDistance<=0||!Number.isFinite(shortMin)||shortMin<=0||!Number.isFinite(exponent)||exponent<=0)return{predictedTime:null,predictedMinutes:null,pacePerKm:null,pacePerMile:null,halfSplit:null,__error:'inputError'};const predictedMinutes=shortMin*Math.pow(target/shortDistance,exponent),fmt=min=>{const ts=Math.round(min*60),h=Math.floor(ts/3600),m=Math.floor(ts%3600/60),sec=ts%60;return(h>0?h+':'+String(m).padStart(2,'0'):m)+':'+String(sec).padStart(2,'0');};return{predictedTime:fmt(predictedMinutes),predictedMinutes,pacePerKm:fmt(predictedMinutes/target),pacePerMile:fmt(predictedMinutes/(target/1.609344)),halfSplit:fmt(predictedMinutes/2)};",
    "inputs": [
      {
        "name": "shortDistance",
        "type": "number",
        "default": 21.0975,
        "min": 0.4,
        "max": 100,
        "step": 0.1,
        "slider": true
      },
      {
        "name": "shortHours",
        "type": "number",
        "default": 1,
        "min": 0,
        "max": 24,
        "step": 1,
        "slider": true
      },
      {
        "name": "shortMinutes",
        "type": "number",
        "default": 45,
        "min": 0,
        "max": 59,
        "step": 1,
        "slider": true
      },
      {
        "name": "exponent",
        "type": "number",
        "default": 1.06,
        "min": 1,
        "max": 1.2,
        "step": 0.01,
        "slider": true
      },
      {
        "name": "targetDistance",
        "type": "select",
        "default": "marathon",
        "options": [
          {
            "value": "5k"
          },
          {
            "value": "10k"
          },
          {
            "value": "half"
          },
          {
            "value": "marathon"
          },
          {
            "value": "50k"
          }
        ]
      }
    ],
    "outputs": [
      {
        "name": "predictedTime",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "predictedMinutes",
        "precision": 1,
        "unit": "min"
      },
      {
        "name": "pacePerKm",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "pacePerMile",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "halfSplit",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
