{
  "schemaVersion": 1,
  "slug": "party-invitation-count-calculator",
  "category": "party",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "ea23232d96ce2021e07f94d2fc753e7afca0e13e79f2f9d44b53c61484856aea",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/party-invitation-count-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": "I = ceil(T ÷ (a (1 + p ÷ 100) (1 − n ÷ 100))); E = I a (1 + p ÷ 100) (1 − n ÷ 100)",
    "vars": [
      [
        "T",
        "Target attendees"
      ],
      [
        "a",
        "RSVP acceptance rate (%)"
      ],
      [
        "p",
        "Plus-one percentage"
      ],
      [
        "n",
        "No-show percentage"
      ],
      [
        "I",
        "Invitations to send"
      ],
      [
        "E",
        "Expected actual attendees"
      ]
    ],
    "note": "Invites = ceil(target attendance / (acceptance rate × (1 + plus-one rate) × (1 − no-show rate))). Expected acceptances include estimated plus-ones; attendees then apply the no-show rate. Seat buffer is a separate illustrative 5% of the target, not an optimized venue capacity."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const accept = rsvpAcceptPct/100; const show = 1 - noShowPct/100; const perInvite = accept*(1 + plusOnePct/100)*show; const invites = perInvite > 0 ? Math.ceil(targetAttendees/perInvite) : targetAttendees; const yes = invites*accept*(1 + plusOnePct/100); return { invitesToSend: invites, expectedYes: Math.round(yes), expectedAttendees: Math.round(yes*show), expectedNoShows: Math.round(yes*noShowPct/100), headcountBuffer: Math.round(targetAttendees*0.05) };",
    "inputs": [
      {
        "name": "targetAttendees",
        "type": "number",
        "default": 60,
        "min": 1,
        "max": 2000,
        "step": 1,
        "slider": true
      },
      {
        "name": "rsvpAcceptPct",
        "type": "number",
        "default": 70,
        "min": 1,
        "max": 100,
        "step": 1,
        "slider": true
      },
      {
        "name": "noShowPct",
        "type": "number",
        "default": 10,
        "min": 0,
        "max": 100,
        "step": 1,
        "slider": true
      },
      {
        "name": "plusOnePct",
        "type": "number",
        "default": 20,
        "min": 0,
        "max": 100,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "invitesToSend",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "expectedYes",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "expectedAttendees",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "expectedNoShows",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "headcountBuffer",
        "precision": 0,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
