FAILURE MAP
← Case archive

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.

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

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

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

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

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