{
  "schemaVersion": 1,
  "slug": "flame-twin-calculator",
  "category": "party",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "8b491325b8e17741a85f5e60a3322b3550327a870ab895665eb6f689c7324660",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/flame-twin-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": "c = Σ_{x∈X} min(μ1(x), μ2(x)), X = {a,…,z}; k = |n1| + |n2| − 2c; S → S ∖ {S[idx]}, idx → (idx + k − 1) mod m, m → m − 1 (m = 6, 5, …, 2)",
    "vars": [
      [
        "n1",
        "letters of name 1 (a-z)"
      ],
      [
        "n2",
        "letters of name 2 (a-z)"
      ],
      [
        "μ1",
        "count μ1(x) of letter x in name 1"
      ],
      [
        "μ2",
        "count μ2(x) of letter x in name 2"
      ],
      [
        "X",
        "letter set {a,…,z}"
      ],
      [
        "c",
        "common pairs removed"
      ],
      [
        "k",
        "unmatched letters left"
      ],
      [
        "S",
        "ordered FLAMES letter pool"
      ],
      [
        "idx",
        "cursor position"
      ],
      [
        "m",
        "letters remaining in pool"
      ]
    ],
    "note": "Names are lowercased and stripped to a-z; shared pairs are cancelled by multiplicity, c = Σ_{x∈X} min(μ1(x), μ2(x)); elimination cycles over F,L,A,M,E,S (Friends, Lovers, Affection, Marriage, Enemies, Siblings) and stops at one survivor; k = 0 leaves F."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const validName=s=>/^[A-Za-z][A-Za-z\\s'’-]*$/.test(String(s).trim());if(!validName(name1)||!validName(name2))return{__error:'inputError'};const a = String(name1).toLowerCase().replace(/[^a-z]/g, '').split(''); const b = String(name2).toLowerCase().replace(/[^a-z]/g, '').split(''); const pool = b.slice(); let removed = 0; const rest = []; for (const ch of a) { const i = pool.indexOf(ch); if (i >= 0) { pool.splice(i, 1); removed++; } else rest.push(ch); } const count = rest.length + pool.length; const letters = ['F', 'L', 'A', 'M', 'E', 'S']; const names = { F: 'Friends', L: 'Lovers', A: 'Affection', M: 'Marriage', E: 'Enemies', S: 'Siblings' }; let arr = letters.slice(); let idx = 0; if (count > 0) { while (arr.length > 1) { idx = (idx + count - 1) % arr.length; arr.splice(idx, 1); } } const letter = arr[0]; return { result: letter, fullMeaning: names[letter], remainingLetters: count, commonLetters: removed };",
    "inputs": [
      {
        "name": "name1",
        "type": "text",
        "default": "Alex",
        "step": 1,
        "preserveEmpty": true
      },
      {
        "name": "name2",
        "type": "text",
        "default": "Jordan",
        "step": 1,
        "preserveEmpty": true
      }
    ],
    "outputs": [
      {
        "name": "fullMeaning",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "result",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "remainingLetters",
        "precision": 0,
        "unit": "",
        "trimTrailingZeros": true
      },
      {
        "name": "commonLetters",
        "precision": 0,
        "unit": "",
        "trimTrailingZeros": true
      }
    ],
    "interpret": null
  }
}
