FA-72401 / Check-digit algorithms / Open access
Verhoeff composes the dihedral product in the wrong order · case 01
Numbers containing reflections produce wrong check digits.
ROOT CAUSE
The step computes D[P(...)][c] instead of D[c][P(...)]; the dihedral group is not commutative.
VERIFIED REPAIR
Multiply the running state on the left: c = D[c][P[pos][digit]].
Unsuccessful approach: Transposing the permutation lookup (P[digit % 8][pos % 10]) keeps the product order but uses the wrong permutation.
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[P[(i + offset) % 8][int(ch)]][c]
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 ["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], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False]], [['regression ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['regression ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False], ['control ["6516456313406690", "validate"]', ['6516456313406690', 'validate'], False]], [['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['regression ["123451", "validate"]', ['123451', 'validate'], True], ['partial-repair ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["", "generate"]', ['', 'generate'], None], ['control ["", "validate"]', ['', 'validate'], None], ['control ["12a", "generate"]', ['12a', 'generate'], None]], [['regression ["1428570", "validate"]', ['1428570', 'validate'], True], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["1428572", "validate"]', ['1428572', 'validate'], False], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False]], [['regression ["600", "generate"]', ['600', 'generate'], 8], ['regression ["31723", "generate"]', ['31723', 'generate'], 6], ['partial-repair ["142857", "generate"]', ['142857', 'generate'], 0], ['partial-repair ["1428570", "validate"]', ['1428570', 'validate'], True], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', '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 ["600", "generate"] | 7 | 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 |
| control ["2864257569", "validate"] | False | False | Passed |
SHA-256 / ff742e016f5b81183d88cb057818505cee809f9cf1a594c3f6da28a7f3c736bb
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[int(ch) % 8][(i + offset) % 10]]
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 ["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], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False]], [['regression ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['regression ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False], ['control ["6516456313406690", "validate"]', ['6516456313406690', 'validate'], False]], [['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['regression ["123451", "validate"]', ['123451', 'validate'], True], ['partial-repair ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["", "generate"]', ['', 'generate'], None], ['control ["", "validate"]', ['', 'validate'], None], ['control ["12a", "generate"]', ['12a', 'generate'], None]], [['regression ["1428570", "validate"]', ['1428570', 'validate'], True], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["1428572", "validate"]', ['1428572', 'validate'], False], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False]], [['regression ["600", "generate"]', ['600', 'generate'], 8], ['regression ["31723", "generate"]', ['31723', 'generate'], 6], ['partial-repair ["142857", "generate"]', ['142857', 'generate'], 0], ['partial-repair ["1428570", "validate"]', ['1428570', 'validate'], True], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', '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 ["600", "generate"] | 4 | 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 |
| control ["2864257569", "validate"] | False | False | Passed |
SHA-256 / 489c64112599b9951df2c560b41ea154b67e33c25a97ca606bee241a82dc989b
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 ["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], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False]], [['regression ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['regression ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['partial-repair ["07668886", "generate"]', ['07668886', 'generate'], 2], ['partial-repair ["076688867", "validate"]', ['076688867', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', 'validate'], False], ['control ["8115669551647", "validate"]', ['8115669551647', 'validate'], False], ['control ["6516456313406690", "validate"]', ['6516456313406690', 'validate'], False]], [['regression ["12345", "generate"]', ['12345', 'generate'], 1], ['regression ["123451", "validate"]', ['123451', 'validate'], True], ['partial-repair ["42668879332", "generate"]', ['42668879332', 'generate'], 9], ['partial-repair ["811566955164", "generate"]', ['811566955164', 'generate'], 9], ['control ["2364", "validate"]', ['2364', 'validate'], False], ['control ["", "generate"]', ['', 'generate'], None], ['control ["", "validate"]', ['', 'validate'], None], ['control ["12a", "generate"]', ['12a', 'generate'], None]], [['regression ["1428570", "validate"]', ['1428570', 'validate'], True], ['regression ["84736430954837284567892", "generate"]', ['84736430954837284567892', 'generate'], 2], ['partial-repair ["236", "generate"]', ['236', 'generate'], 3], ['partial-repair ["12345", "generate"]', ['12345', 'generate'], 1], ['control ["0", "validate"]', ['0', 'validate'], True], ['control ["1428572", "validate"]', ['1428572', 'validate'], False], ['control ["7", "generate"]', ['7', 'generate'], 0], ['control ["73", "validate"]', ['73', 'validate'], False]], [['regression ["600", "generate"]', ['600', 'generate'], 8], ['regression ["31723", "generate"]', ['31723', 'generate'], 6], ['partial-repair ["142857", "generate"]', ['142857', 'generate'], 0], ['partial-repair ["1428570", "validate"]', ['1428570', 'validate'], True], ['control ["6001", "validate"]', ['6001', 'validate'], False], ['control ["317230", "validate"]', ['317230', 'validate'], False], ['control ["2864257569", "validate"]', ['2864257569', 'validate'], False], ['control ["426688793324", "validate"]', ['426688793324', '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 ["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 |
| control ["2864257569", "validate"] | False | False | Passed |
SHA-256 / b4479882c226cce6423fd47f9e931be37aab38bd8350e3781bcabc32dddebfa9
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.187010+00:00.
Case digest / b62df265c76b27aebc536cfde6df5c8033398ea7a41a7b3bd4aefce29418ce1f