FA-72396 / Check-digit algorithms / Open access
Verhoeff returns the final state instead of its inverse · case 01
Payloads ending in states 1-4 get a check digit that does not validate.
ROOT CAUSE
The generator returns c directly; in D5 the elements 1..4 are not self-inverse.
VERIFIED REPAIR
Return INV[c], the dihedral inverse, so the completed number reduces to 0.
Unsuccessful approach: Using the cyclic inverse (10 - c) % 10 treats the dihedral group as if it were Z10.
Case contract
Verhoeff dihedral-group scheme. mode "generate" returns the check digit for a nonempty ASCII digit payload; mode "validate" returns whether a nonempty digit string ending in its check digit is correct. Digits are processed from the right with permutation P[(i + offset) % 8], offset 1 when generating and 0 when validating; the check digit is the dihedral inverse of the final state. Empty or non-digit input returns None.
Why this case matters
National identity and ticketing numbers use Verhoeff to catch all single-digit and adjacent transposition errors.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s, mode):
D = [[0,1,2,3,4,5,6,7,8,9],[1,2,3,4,0,6,7,8,9,5],[2,3,4,0,1,7,8,9,5,6],[3,4,0,1,2,8,9,5,6,7],[4,0,1,2,3,9,5,6,7,8],[5,9,8,7,6,0,4,3,2,1],[6,5,9,8,7,1,0,4,3,2],[7,6,5,9,8,2,1,0,4,3],[8,7,6,5,9,3,2,1,0,4],[9,8,7,6,5,4,3,2,1,0]]
P = [[0,1,2,3,4,5,6,7,8,9],[1,5,7,6,2,8,3,0,9,4],[5,8,0,3,7,9,6,1,4,2],[8,9,1,6,0,4,3,5,2,7],[9,4,5,3,1,2,8,7,6,0],[4,2,8,6,5,7,3,9,0,1],[2,7,9,3,8,0,6,4,1,5],[7,0,4,6,9,1,3,2,5,8]]
INV = [0,4,3,2,1,5,6,7,8,9]
if not s or not s.isascii() or not s.isdigit():
return None
offset = 1 if mode == 'generate' else 0
c = 0
for i, ch in enumerate(reversed(s)):
c = D[c][P[(i + offset) % 8][int(ch)]]
if mode == 'generate':
return c
return c == 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["12", "generate"]', ['12', 'generate'], 1], ['regression ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False]], [['regression ["236", "generate"]', ['236', 'generate'], 3], ['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["286425756", "generate"]', ['286425756', 'generate'], 2], ['control ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False]], [['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['regression ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["651645631340669", "generate"]', ['651645631340669', 'generate'], 8], ['control ["2363", "validate"]', ['2363', 'validate'], True], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["123451", "validate"]', ['123451', 'validate'], True], ['control ["", "generate"]', ['', 'generate'], None]], [['regression ["286425756", "generate"]', ['286425756', 'generate'], 2], ['regression ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["0", "generate"]', ['0', 'generate'], 4], ['control ["12a", "generate"]', ['12a', 'generate'], None], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["142857", "generate"]', ['142857', 'generate'], 0], ['control ["1428570", "validate"]', ['1428570', 'validate'], True]], [['regression ["0", "generate"]', ['0', 'generate'], 4], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["12", "generate"] | 4 | 1 | Failed |
| regression ["07668886", "generate"] | 3 | 2 | Failed |
| partial-repair ["600", "generate"] | 8 | 8 | Passed |
| control ["7", "generate"] | 0 | 0 | Passed |
| control ["73", "validate"] | False | False | Passed |
| control ["123", "validate"] | False | False | Passed |
| control ["6001", "validate"] | False | False | Passed |
| control ["317230", "validate"] | False | False | Passed |
SHA-256 / 43bf08b44923be30c767fe71f9b38c4c8d2266b5d3283791cf7d620edb40a80e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s, mode):
D = [[0,1,2,3,4,5,6,7,8,9],[1,2,3,4,0,6,7,8,9,5],[2,3,4,0,1,7,8,9,5,6],[3,4,0,1,2,8,9,5,6,7],[4,0,1,2,3,9,5,6,7,8],[5,9,8,7,6,0,4,3,2,1],[6,5,9,8,7,1,0,4,3,2],[7,6,5,9,8,2,1,0,4,3],[8,7,6,5,9,3,2,1,0,4],[9,8,7,6,5,4,3,2,1,0]]
P = [[0,1,2,3,4,5,6,7,8,9],[1,5,7,6,2,8,3,0,9,4],[5,8,0,3,7,9,6,1,4,2],[8,9,1,6,0,4,3,5,2,7],[9,4,5,3,1,2,8,7,6,0],[4,2,8,6,5,7,3,9,0,1],[2,7,9,3,8,0,6,4,1,5],[7,0,4,6,9,1,3,2,5,8]]
INV = [0,4,3,2,1,5,6,7,8,9]
if not s or not s.isascii() or not s.isdigit():
return None
offset = 1 if mode == 'generate' else 0
c = 0
for i, ch in enumerate(reversed(s)):
c = D[c][P[(i + offset) % 8][int(ch)]]
if mode == 'generate':
return (10 - c) % 10
return c == 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["12", "generate"]', ['12', 'generate'], 1], ['regression ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False]], [['regression ["236", "generate"]', ['236', 'generate'], 3], ['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["286425756", "generate"]', ['286425756', 'generate'], 2], ['control ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False]], [['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['regression ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["651645631340669", "generate"]', ['651645631340669', 'generate'], 8], ['control ["2363", "validate"]', ['2363', 'validate'], True], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["123451", "validate"]', ['123451', 'validate'], True], ['control ["", "generate"]', ['', 'generate'], None]], [['regression ["286425756", "generate"]', ['286425756', 'generate'], 2], ['regression ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["0", "generate"]', ['0', 'generate'], 4], ['control ["12a", "generate"]', ['12a', 'generate'], None], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["142857", "generate"]', ['142857', 'generate'], 0], ['control ["1428570", "validate"]', ['1428570', 'validate'], True]], [['regression ["0", "generate"]', ['0', 'generate'], 4], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["12", "generate"] | 6 | 1 | Failed |
| regression ["07668886", "generate"] | 7 | 2 | Failed |
| partial-repair ["600", "generate"] | 2 | 8 | Failed |
| control ["7", "generate"] | 0 | 0 | Passed |
| control ["73", "validate"] | False | False | Passed |
| control ["123", "validate"] | False | False | Passed |
| control ["6001", "validate"] | False | False | Passed |
| control ["317230", "validate"] | False | False | Passed |
SHA-256 / 2862e66ff0ba5fc144974d53e5218fc9503c5874382e04c90f58119f8ebe8f53
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s, mode):
D = [[0,1,2,3,4,5,6,7,8,9],[1,2,3,4,0,6,7,8,9,5],[2,3,4,0,1,7,8,9,5,6],[3,4,0,1,2,8,9,5,6,7],[4,0,1,2,3,9,5,6,7,8],[5,9,8,7,6,0,4,3,2,1],[6,5,9,8,7,1,0,4,3,2],[7,6,5,9,8,2,1,0,4,3],[8,7,6,5,9,3,2,1,0,4],[9,8,7,6,5,4,3,2,1,0]]
P = [[0,1,2,3,4,5,6,7,8,9],[1,5,7,6,2,8,3,0,9,4],[5,8,0,3,7,9,6,1,4,2],[8,9,1,6,0,4,3,5,2,7],[9,4,5,3,1,2,8,7,6,0],[4,2,8,6,5,7,3,9,0,1],[2,7,9,3,8,0,6,4,1,5],[7,0,4,6,9,1,3,2,5,8]]
INV = [0,4,3,2,1,5,6,7,8,9]
if not s or not s.isascii() or not s.isdigit():
return None
offset = 1 if mode == 'generate' else 0
c = 0
for i, ch in enumerate(reversed(s)):
c = D[c][P[(i + offset) % 8][int(ch)]]
if mode == 'generate':
return INV[c]
return c == 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["12", "generate"]', ['12', 'generate'], 1], ['regression ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False]], [['regression ["236", "generate"]', ['236', 'generate'], 3], ['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["286425756", "generate"]', ['286425756', 'generate'], 2], ['control ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False]], [['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['regression ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["651645631340669", "generate"]', ['651645631340669', 'generate'], 8], ['control ["2363", "validate"]', ['2363', 'validate'], True], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["123451", "validate"]', ['123451', 'validate'], True], ['control ["", "generate"]', ['', 'generate'], None]], [['regression ["286425756", "generate"]', ['286425756', 'generate'], 2], ['regression ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['partial-repair ["0", "generate"]', ['0', 'generate'], 4], ['control ["12a", "generate"]', ['12a', 'generate'], None], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["142857", "generate"]', ['142857', 'generate'], 0], ['control ["1428570", "validate"]', ['1428570', 'validate'], True]], [['regression ["0", "generate"]', ['0', 'generate'], 4], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["12", "generate"]', ['12', 'generate'], 1], ['partial-repair ["600", "generate"]', ['600', 'generate'], 8], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False], ['control ["123", "validate"]', ['123', 'validate'], False], ['control ["6001", "validate"]', ['6001', 'validate'], False]]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["12", "generate"] | 1 | 1 | Passed |
| regression ["07668886", "generate"] | 2 | 2 | Passed |
| partial-repair ["600", "generate"] | 8 | 8 | Passed |
| control ["7", "generate"] | 0 | 0 | Passed |
| control ["73", "validate"] | False | False | Passed |
| control ["123", "validate"] | False | False | Passed |
| control ["6001", "validate"] | False | False | Passed |
| control ["317230", "validate"] | False | False | Passed |
SHA-256 / 2aaf87edb057e1ca5161402c41dd50a820be676a6d2f5776254f96cffa69ba2a
Verification & scope
A deterministic, bounded teaching model of the named scheme under the stated contract; not a certified validator. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:38.100565+00:00.
Case digest / 37e5a34f9dffd0b706bd6637bd10e038527e35514acb4fa611a43e0e3e8ccf08