{
  "id": "P145",
  "slug": "username-registry",
  "key": "P145-username-registry",
  "title": "Username registry",
  "summary": "Names folded into keys four ways, from the name as typed to lower case without marks or trailing digits, set against who is who: the people each fold merges and the people it splits.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P145 - Username registry\n\nNames folded into keys that decide when two names are one account, set\nagainst who is who, written apart from every fold.\n\n- `main.eml` - the menu, the sign-ups, the keys each fold makes, a new\n  sign-up seen by every fold, and each fold's merges and splits\n- `folds.eml` - the four folds, the opening sign-ups, lower case written as\n  a table, the owner of a key, a person's first key, and the counts\n\nHow each part works:\n\n- Sixteen sign-ups by ten people: Alice, alice and ALICE are one person;\n  al.ice and al-ice another; alice1 and alice2 two more; carol7 and carol_7\n  one person, and carol another.\n- As in the corpus case `case-fold-collisions`, the folds go from the name\n  as typed, to lower case, to lower case without dots, dashes and\n  underscores, to that without trailing digits. Each is a function of the\n  one before, so it only ever joins keys.\n- Two failures, counted apart against who is who. Merged: a person's name\n  lands on another person's key, so the sign-up is refused or an account is\n  lost. Split: one person's names land on two keys, so one spelling no\n  longer reaches the account. Merged goes 0, 0, 2, 7 from fold to fold and\n  split 6, 3, 0, 0: no fold is right on both for these names.\n- A new sign-up is shown under every fold: a new account, one account of\n  the same person, a second account for someone who already has a key, or\n  a key that is another person's.\n\nWhat is checked: menu choices 1 to 5; a name of 1 to 16 letters, digits,\ndots, dashes or underscores; a person's number that exists, or n for\nsomeone new. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the sign-ups, the keys and the counts;\n- Bob.Smith as person 6, caro.l as someone new and ALICE2 as person 4; the\n  counts again.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- names: a cancel, one with a space and one of 17 characters;\n- Zed with a cancel, 0, 11, x and 1234 for the person; zed as someone new;\n  the counts.\n\nBuilt on the verified corpus case `case-fold-collisions` (three folds of\nincreasing reach, measured against a separately stated list of who is\nwho).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P145 username registry: sign-ups with who is who written beside them,\n# the key each fold makes of every name, a new sign-up seen by every fold,\n# and each fold's merges and splits.\nimport folds\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef q(s):\n    return \"'\" + s + \"'\"\n\ndef name_ok(t):\n    if len(t) == 0 or len(t) > 16:\n        return False\n    for c in t:\n        if not ((c >= \"a\" and c <= \"z\") or (c >= \"A\" and c <= \"Z\") or (c >= \"0\" and c <= \"9\") or c == \".\" or c == \"-\" or c == \"_\"):\n            return False\n    return True\n\ndef show_signups(rows):\n    \"The sign-ups, in order, with who is who:\" ^0\n    for r in rows:\n        (\"  \" + pad(r[0], 18) + \"person \" + str(r[1])) ^0\n\ndef show_keys(rows):\n    (\"  \" + pad(\"name\", 18) + pad(\"lower\", 18) + pad(\"strip\", 18) + \"strip+digits\") ^0\n    for r in rows:\n        (\"  \" + pad(r[0], 18) + pad(folds.fold(\"lower\", r[0]), 18) + pad(folds.fold(\"strip\", r[0]), 18) + folds.fold(\"strip+digits\", r[0])) ^0\n\ndef outcome(which, rows, name, who):\n    folds.fold(which, name) => k\n    folds.owner(which, rows, k) => o\n    folds.first_key(which, rows, who) => fk\n    if o == 0 and fk == \"\":\n        return \"new account \" + q(k)\n    if o == 0:\n        return \"new key \" + q(k) + \", though person \" + str(who) + \" already has \" + q(fk) + \": a second account (split)\"\n    if o == who:\n        return \"the account \" + q(k) + \" of the same person: one account, right\"\n    return \"key \" + q(k) + \" is person \" + str(o) + \"'s: a different person refused (merged)\"\n\ndef signup(rows):\n    plain(input(\"name> \")) => name\n    if name == \"\":\n        \"Cancelled.\" ^0\n        return rows\n    if not name_ok(name):\n        \"Type a name of 1 to 16 letters, digits, dots, dashes or underscores.\" ^0\n        return rows\n    folds.people(rows) => n\n    plain(input(\"who is this (1 to \" + str(n) + \", or n for someone new)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return rows\n    if t == \"n\":\n        n + 1 => who\n    else:\n        if len(t) > 3:\n            (\"Type a person's number from 1 to \" + str(n) + \", or n.\") ^0\n            return rows\n        for c in t:\n            if c < \"0\" or c > \"9\":\n                (\"Type a person's number from 1 to \" + str(n) + \", or n.\") ^0\n                return rows\n        int(t) => who\n        if who < 1 or who > n:\n            (\"Type a person's number from 1 to \" + str(n) + \", or n.\") ^0\n            return rows\n    (name + \", person \" + str(who) + \":\") ^0\n    for f in folds.folds():\n        (\"  \" + pad(f, 14) + outcome(f, rows, name, who)) ^0\n    return rows + [[name, who]]\n\ndef results(rows):\n    (\"Over \" + str(len(rows)) + \" sign-ups by \" + str(folds.people(rows)) + \" people:\") ^0\n    (\"  \" + pad(\"fold\", 14) + \"keys  merged  split\") ^0\n    [] => right\n    for f in folds.folds():\n        folds.measure(f, rows) => m\n        (\"  \" + pad(f, 14) + lpad(str(m[2]), 4) + lpad(str(m[0]), 8) + lpad(str(m[1]), 7)) ^0\n        if m[0] == 0 and m[1] == 0:\n            right + [f] => right\n    \"Merged: a person's name lands on another person's key. Split: one person's names land on two keys.\" ^0\n    for f in folds.folds():\n        for r in rows:\n            folds.fold(f, r[0]) => k\n            folds.owner(f, rows, k) => o\n            if o != r[1]:\n                (\"  \" + f + \": \" + r[0] + \" (person \" + str(r[1]) + \") lands on \" + q(k) + \", person \" + str(o) + \"'s key\") ^0\n    for f in folds.folds():\n        \"\" => ex\n        for r in rows:\n            folds.fold(f, r[0]) => k\n            folds.first_key(f, rows, r[1]) => fk\n            if fk != k and ex == \"\":\n                \"  \" + f + \" splits, first \" + r[0] + \" (person \" + str(r[1]) + \"): \" + q(k) + \", where the same person has \" + q(fk) => ex\n        if ex != \"\":\n            ex ^0\n    if len(right) == 0:\n        \"No fold keeps every person apart and every person's names together, for these names.\" ^0\n\nfolds.opening() => rows\n\n\"== Username registry ==\" ^0\n\"Names folded into keys, set against who is who: people merged, and people split.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_signups(rows)\n    elif choice == \"2\":\n        show_keys(rows)\n    elif choice == \"3\":\n        signup(rows) => rows\n    elif choice == \"4\":\n        results(rows)\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import folds\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef q(s):\n    return \"'\" + s + \"'\"\n\ndef name_ok(t):\n    if len(t) == 0 or len(t) > 16:\n        return False\n    for c in t:\n        if not (c >= \"a\" and c <= \"z\" or c >= \"A\" and c <= \"Z\" or c >= \"0\" and c <= \"9\" or c == \".\" or c == \"-\" or c == \"_\"):\n            return False\n    return True\n\ndef show_signups(rows):\n    print(\"The sign-ups, in order, with who is who:\")\n    for r in rows:\n        print(\"  \" + pad(r[0], 18) + \"person \" + str(r[1]))\n\ndef show_keys(rows):\n    print(\"  \" + pad(\"name\", 18) + pad(\"lower\", 18) + pad(\"strip\", 18) + \"strip+digits\")\n    for r in rows:\n        print(\"  \" + pad(r[0], 18) + pad(folds.fold(\"lower\", r[0]), 18) + pad(folds.fold(\"strip\", r[0]), 18) + folds.fold(\"strip+digits\", r[0]))\n\ndef outcome(which, rows, name, who):\n    k = folds.fold(which, name)\n    o = folds.owner(which, rows, k)\n    fk = folds.first_key(which, rows, who)\n    if o == 0 and fk == \"\":\n        return \"new account \" + q(k)\n    if o == 0:\n        return \"new key \" + q(k) + \", though person \" + str(who) + \" already has \" + q(fk) + \": a second account (split)\"\n    if o == who:\n        return \"the account \" + q(k) + \" of the same person: one account, right\"\n    return \"key \" + q(k) + \" is person \" + str(o) + \"'s: a different person refused (merged)\"\n\ndef signup(rows):\n    name = plain(input(\"name> \"))\n    if name == \"\":\n        print(\"Cancelled.\")\n        return rows\n    if not name_ok(name):\n        print(\"Type a name of 1 to 16 letters, digits, dots, dashes or underscores.\")\n        return rows\n    n = folds.people(rows)\n    t = plain(input(\"who is this (1 to \" + str(n) + \", or n for someone new)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return rows\n    if t == \"n\":\n        who = n + 1\n    else:\n        if len(t) > 3:\n            print(\"Type a person's number from 1 to \" + str(n) + \", or n.\")\n            return rows\n        for c in t:\n            if c < \"0\" or c > \"9\":\n                print(\"Type a person's number from 1 to \" + str(n) + \", or n.\")\n                return rows\n        who = int(t)\n        if who < 1 or who > n:\n            print(\"Type a person's number from 1 to \" + str(n) + \", or n.\")\n            return rows\n    print(name + \", person \" + str(who) + \":\")\n    for f in folds.folds():\n        print(\"  \" + pad(f, 14) + outcome(f, rows, name, who))\n    return rows + [[name, who]]\n\ndef results(rows):\n    print(\"Over \" + str(len(rows)) + \" sign-ups by \" + str(folds.people(rows)) + \" people:\")\n    print(\"  \" + pad(\"fold\", 14) + \"keys  merged  split\")\n    right = []\n    for f in folds.folds():\n        m = folds.measure(f, rows)\n        print(\"  \" + pad(f, 14) + lpad(str(m[2]), 4) + lpad(str(m[0]), 8) + lpad(str(m[1]), 7))\n        if m[0] == 0 and m[1] == 0:\n            right = right + [f]\n    print(\"Merged: a person's name lands on another person's key. Split: one person's names land on two keys.\")\n    for f in folds.folds():\n        for r in rows:\n            k = folds.fold(f, r[0])\n            o = folds.owner(f, rows, k)\n            if o != r[1]:\n                print(\"  \" + f + \": \" + r[0] + \" (person \" + str(r[1]) + \") lands on \" + q(k) + \", person \" + str(o) + \"'s key\")\n    for f in folds.folds():\n        ex = \"\"\n        for r in rows:\n            k = folds.fold(f, r[0])\n            fk = folds.first_key(f, rows, r[1])\n            if fk != k and ex == \"\":\n                ex = \"  \" + f + \" splits, first \" + r[0] + \" (person \" + str(r[1]) + \"): \" + q(k) + \", where the same person has \" + q(fk)\n        if ex != \"\":\n            print(ex)\n    if len(right) == 0:\n        print(\"No fold keeps every person apart and every person's names together, for these names.\")\n\nrows = folds.opening()\nprint(\"== Username registry ==\")\nprint(\"Names folded into keys, set against who is who: people merged, and people split.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_signups(rows)\n    elif choice == \"2\":\n        show_keys(rows)\n    elif choice == \"3\":\n        rows = signup(rows)\n    elif choice == \"4\":\n        results(rows)\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "folds.eml",
      "eml": "# P145 username registry - names registered under a fold that decides when\n# two names are the same account. As in the corpus case\n# case-fold-collisions, a fold is many-to-one, and the property that makes\n# it a uniqueness key - fold(a) == fold(b) only when a and b are the same\n# person - can only be checked against who is who, written apart from every\n# fold. Two failures, counted apart: MERGED, a person whose name lands on\n# another person's key (refused, or their account lost), and SPLIT, a\n# person whose names land on two keys (one spelling no longer reaches the\n# account). A sign-up is [name as typed, person].\n\ndef folds():\n    return [\"exact\", \"lower\", \"strip\", \"strip+digits\"]\n\ndef what():\n    return [\"the name as typed\", \"lower case\", \"lower case, without . - _\", \"lower case, without . - _ and trailing digits\"]\n\ndef opening():\n    return [[\"Alice\", 1], [\"alice\", 1], [\"ALICE\", 1], [\"al.ice\", 2], [\"al-ice\", 2], [\"alice1\", 3], [\"alice2\", 4], [\"Bob\", 5], [\"bob\", 5], [\"bob.smith\", 6], [\"bobsmith\", 6], [\"carol\", 7], [\"carol7\", 8], [\"carol_7\", 8], [\"dave\", 9], [\"Dave2024\", 10]]\n\ndef to_lower(s):\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => up\n    \"abcdefghijklmnopqrstuvwxyz\" => low\n    \"\" => out\n    for c in s:\n        c => m\n        0 => i\n        while i < 26:\n            if up[i] == c:\n                low[i] => m\n            i + 1 => i\n        out + m => out\n    return out\n\ndef fold(which, s):\n    if which == \"exact\":\n        return s\n    to_lower(s) => t\n    if which == \"lower\":\n        return t\n    \"\" => u\n    for c in t:\n        if c != \".\" and c != \"-\" and c != \"_\":\n            u + c => u\n    if which == \"strip\":\n        return u\n    len(u) => i\n    while i > 0 and u[i - 1] >= \"0\" and u[i - 1] <= \"9\":\n        i - 1 => i\n    return u[0:i]\n\ndef owner(which, rows, k):\n    # the person whose name first took key k, or 0\n    for r in rows:\n        if fold(which, r[0]) == k:\n            return r[1]\n    return 0\n\ndef first_key(which, rows, who):\n    # the first key person who has, or \"\"\n    for r in rows:\n        if r[1] == who:\n            return fold(which, r[0])\n    return \"\"\n\ndef measure(which, rows):\n    # [merged, split, keys] over the sign-ups in order\n    {} => key_to\n    {} => person_to\n    0 => merged\n    0 => split\n    for r in rows:\n        fold(which, r[0]) => k\n        if k in key_to:\n            if key_to[k] != r[1]:\n                merged + 1 => merged\n        else:\n            r[1] => key_to[k]\n        if r[1] in person_to:\n            if person_to[r[1]] != k:\n                split + 1 => split\n        else:\n            k => person_to[r[1]]\n    return [merged, split, len(key_to)]\n\ndef people(rows):\n    0 => n\n    for r in rows:\n        if r[1] > n:\n            r[1] => n\n    return n\n",
      "python": "def folds():\n    return [\"exact\", \"lower\", \"strip\", \"strip+digits\"]\n\ndef what():\n    return [\"the name as typed\", \"lower case\", \"lower case, without . - _\", \"lower case, without . - _ and trailing digits\"]\n\ndef opening():\n    return [[\"Alice\", 1], [\"alice\", 1], [\"ALICE\", 1], [\"al.ice\", 2], [\"al-ice\", 2], [\"alice1\", 3], [\"alice2\", 4], [\"Bob\", 5], [\"bob\", 5], [\"bob.smith\", 6], [\"bobsmith\", 6], [\"carol\", 7], [\"carol7\", 8], [\"carol_7\", 8], [\"dave\", 9], [\"Dave2024\", 10]]\n\ndef to_lower(s):\n    up = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    low = \"abcdefghijklmnopqrstuvwxyz\"\n    out = \"\"\n    for c in s:\n        m = c\n        i = 0\n        while i < 26:\n            if up[i] == c:\n                m = low[i]\n            i = i + 1\n        out = out + m\n    return out\n\ndef fold(which, s):\n    if which == \"exact\":\n        return s\n    t = to_lower(s)\n    if which == \"lower\":\n        return t\n    u = \"\"\n    for c in t:\n        if c != \".\" and c != \"-\" and c != \"_\":\n            u = u + c\n    if which == \"strip\":\n        return u\n    i = len(u)\n    while i > 0 and u[i - 1] >= \"0\" and u[i - 1] <= \"9\":\n        i = i - 1\n    return u[0:i]\n\ndef owner(which, rows, k):\n    for r in rows:\n        if fold(which, r[0]) == k:\n            return r[1]\n    return 0\n\ndef first_key(which, rows, who):\n    for r in rows:\n        if r[1] == who:\n            return fold(which, r[0])\n    return \"\"\n\ndef measure(which, rows):\n    key_to = {}\n    person_to = {}\n    merged = 0\n    split = 0\n    for r in rows:\n        k = fold(which, r[0])\n        if k in key_to:\n            if key_to[k] != r[1]:\n                merged = merged + 1\n        else:\n            key_to[k] = r[1]\n        if r[1] in person_to:\n            if person_to[r[1]] != k:\n                split = split + 1\n        else:\n            person_to[r[1]] = k\n    return [merged, split, len(key_to)]\n\ndef people(rows):\n    n = 0\n    for r in rows:\n        if r[1] > n:\n            n = r[1]\n    return n\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n3\n\n3\nbad name!\n3\nseventeen_chars_x\n3\nZed\n\n3\nZed\n0\n3\nZed\n11\n3\nZed\nx\n3\nZed\n1234\n3\nzed\nn\n4\n5\n",
      "screen": "== Username registry ==\nNames folded into keys, set against who is who: people merged, and people split.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> \nCancelled.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> bad name!\nType a name of 1 to 16 letters, digits, dots, dashes or underscores.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> seventeen_chars_x\nType a name of 1 to 16 letters, digits, dots, dashes or underscores.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Zed\nwho is this (1 to 10, or n for someone new)> \nCancelled.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Zed\nwho is this (1 to 10, or n for someone new)> 0\nType a person's number from 1 to 10, or n.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Zed\nwho is this (1 to 10, or n for someone new)> 11\nType a person's number from 1 to 10, or n.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Zed\nwho is this (1 to 10, or n for someone new)> x\nType a person's number from 1 to 10, or n.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Zed\nwho is this (1 to 10, or n for someone new)> 1234\nType a person's number from 1 to 10, or n.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> zed\nwho is this (1 to 10, or n for someone new)> n\nzed, person 11:\n  exact         new account 'zed'\n  lower         new account 'zed'\n  strip         new account 'zed'\n  strip+digits  new account 'zed'\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 4\nOver 17 sign-ups by 11 people:\n  fold          keys  merged  split\n  exact           17       0      6\n  lower           14       0      3\n  strip           10       2      0\n  strip+digits     6       7      0\nMerged: a person's name lands on another person's key. Split: one person's names land on two keys.\n  strip: al.ice (person 2) lands on 'alice', person 1's key\n  strip: al-ice (person 2) lands on 'alice', person 1's key\n  strip+digits: al.ice (person 2) lands on 'alice', person 1's key\n  strip+digits: al-ice (person 2) lands on 'alice', person 1's key\n  strip+digits: alice1 (person 3) lands on 'alice', person 1's key\n  strip+digits: alice2 (person 4) lands on 'alice', person 1's key\n  strip+digits: carol7 (person 8) lands on 'carol', person 7's key\n  strip+digits: carol_7 (person 8) lands on 'carol', person 7's key\n  strip+digits: Dave2024 (person 10) lands on 'dave', person 9's key\n  exact splits, first alice (person 1): 'alice', where the same person has 'Alice'\n  lower splits, first al-ice (person 2): 'al-ice', where the same person has 'al.ice'\nNo fold keeps every person apart and every person's names together, for these names.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n4\n3\nBob.Smith\n6\n3\ncaro.l\nn\n3\nALICE2\n4\n4\n5\n",
      "screen": "== Username registry ==\nNames folded into keys, set against who is who: people merged, and people split.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 1\nThe sign-ups, in order, with who is who:\n  Alice             person 1\n  alice             person 1\n  ALICE             person 1\n  al.ice            person 2\n  al-ice            person 2\n  alice1            person 3\n  alice2            person 4\n  Bob               person 5\n  bob               person 5\n  bob.smith         person 6\n  bobsmith          person 6\n  carol             person 7\n  carol7            person 8\n  carol_7           person 8\n  dave              person 9\n  Dave2024          person 10\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 2\n  name              lower             strip             strip+digits\n  Alice             alice             alice             alice\n  alice             alice             alice             alice\n  ALICE             alice             alice             alice\n  al.ice            al.ice            alice             alice\n  al-ice            al-ice            alice             alice\n  alice1            alice1            alice1            alice\n  alice2            alice2            alice2            alice\n  Bob               bob               bob               bob\n  bob               bob               bob               bob\n  bob.smith         bob.smith         bobsmith          bobsmith\n  bobsmith          bobsmith          bobsmith          bobsmith\n  carol             carol             carol             carol\n  carol7            carol7            carol7            carol\n  carol_7           carol_7           carol7            carol\n  dave              dave              dave              dave\n  Dave2024          dave2024          dave2024          dave\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 4\nOver 16 sign-ups by 10 people:\n  fold          keys  merged  split\n  exact           16       0      6\n  lower           13       0      3\n  strip            9       2      0\n  strip+digits     5       7      0\nMerged: a person's name lands on another person's key. Split: one person's names land on two keys.\n  strip: al.ice (person 2) lands on 'alice', person 1's key\n  strip: al-ice (person 2) lands on 'alice', person 1's key\n  strip+digits: al.ice (person 2) lands on 'alice', person 1's key\n  strip+digits: al-ice (person 2) lands on 'alice', person 1's key\n  strip+digits: alice1 (person 3) lands on 'alice', person 1's key\n  strip+digits: alice2 (person 4) lands on 'alice', person 1's key\n  strip+digits: carol7 (person 8) lands on 'carol', person 7's key\n  strip+digits: carol_7 (person 8) lands on 'carol', person 7's key\n  strip+digits: Dave2024 (person 10) lands on 'dave', person 9's key\n  exact splits, first alice (person 1): 'alice', where the same person has 'Alice'\n  lower splits, first al-ice (person 2): 'al-ice', where the same person has 'al.ice'\nNo fold keeps every person apart and every person's names together, for these names.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> Bob.Smith\nwho is this (1 to 10, or n for someone new)> 6\nBob.Smith, person 6:\n  exact         new key 'Bob.Smith', though person 6 already has 'bob.smith': a second account (split)\n  lower         the account 'bob.smith' of the same person: one account, right\n  strip         the account 'bobsmith' of the same person: one account, right\n  strip+digits  the account 'bobsmith' of the same person: one account, right\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> caro.l\nwho is this (1 to 10, or n for someone new)> n\ncaro.l, person 11:\n  exact         new account 'caro.l'\n  lower         new account 'caro.l'\n  strip         key 'carol' is person 7's: a different person refused (merged)\n  strip+digits  key 'carol' is person 7's: a different person refused (merged)\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 3\nname> ALICE2\nwho is this (1 to 11, or n for someone new)> 4\nALICE2, person 4:\n  exact         new key 'ALICE2', though person 4 already has 'alice2': a second account (split)\n  lower         the account 'alice2' of the same person: one account, right\n  strip         the account 'alice2' of the same person: one account, right\n  strip+digits  key 'alice' is person 1's: a different person refused (merged)\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 4\nOver 19 sign-ups by 11 people:\n  fold          keys  merged  split\n  exact           19       0      8\n  lower           14       0      3\n  strip            9       3      0\n  strip+digits     5       9      0\nMerged: a person's name lands on another person's key. Split: one person's names land on two keys.\n  strip: al.ice (person 2) lands on 'alice', person 1's key\n  strip: al-ice (person 2) lands on 'alice', person 1's key\n  strip: caro.l (person 11) lands on 'carol', person 7's key\n  strip+digits: al.ice (person 2) lands on 'alice', person 1's key\n  strip+digits: al-ice (person 2) lands on 'alice', person 1's key\n  strip+digits: alice1 (person 3) lands on 'alice', person 1's key\n  strip+digits: alice2 (person 4) lands on 'alice', person 1's key\n  strip+digits: carol7 (person 8) lands on 'carol', person 7's key\n  strip+digits: carol_7 (person 8) lands on 'carol', person 7's key\n  strip+digits: Dave2024 (person 10) lands on 'dave', person 9's key\n  strip+digits: caro.l (person 11) lands on 'carol', person 7's key\n  strip+digits: ALICE2 (person 4) lands on 'alice', person 1's key\n  exact splits, first alice (person 1): 'alice', where the same person has 'Alice'\n  lower splits, first al-ice (person 2): 'al-ice', where the same person has 'al.ice'\nNo fold keeps every person apart and every person's names together, for these names.\n\n1) sign-ups  2) keys  3) sign up  4) merged and split  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "case-fold-collisions",
      "caseId": "218-case-fold-collisions",
      "title": "Two ways to lose a user, one of them silent"
    }
  ],
  "updated": "2026-10-18"
}
