FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
regression ["12", "generate"]41Failed
regression ["07668886", "generate"]32Failed
partial-repair ["600", "generate"]88Passed
control ["7", "generate"]00Passed
control ["73", "validate"]FalseFalsePassed
control ["123", "validate"]FalseFalsePassed
control ["6001", "validate"]FalseFalsePassed
control ["317230", "validate"]FalseFalsePassed

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 fixtureActualExpectedOutcome
regression ["12", "generate"]61Failed
regression ["07668886", "generate"]72Failed
partial-repair ["600", "generate"]28Failed
control ["7", "generate"]00Passed
control ["73", "validate"]FalseFalsePassed
control ["123", "validate"]FalseFalsePassed
control ["6001", "validate"]FalseFalsePassed
control ["317230", "validate"]FalseFalsePassed

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 fixtureActualExpectedOutcome
regression ["12", "generate"]11Passed
regression ["07668886", "generate"]22Passed
partial-repair ["600", "generate"]88Passed
control ["7", "generate"]00Passed
control ["73", "validate"]FalseFalsePassed
control ["123", "validate"]FalseFalsePassed
control ["6001", "validate"]FalseFalsePassed
control ["317230", "validate"]FalseFalsePassed

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