FA-72441 / Check-digit algorithms / Open access
ISBN-13 conversion weights the prefix digit by three · case 01
Converted ISBN-13 numbers fail barcode scanners.
ROOT CAUSE
The EAN weights start with 3 at the leftmost digit instead of 1.
VERIFIED REPAIR
Weight even 0-based positions from the left by 1 and odd positions by 3.
Unsuccessful approach: Weighting from the right of the 12-digit body by 1,3 is the same wrong phase in disguise.
Case contract
Convert a hyphenated or plain ISBN-10 to ISBN-13. The input must be a valid ISBN-10 (mod 11, X allowed as the final character) or None is returned. The result is "978" + the nine ISBN-10 payload digits + a freshly computed EAN-13 check digit (weights 1,3 alternating from the left).
Why this case matters
Library and retail systems migrate legacy ISBN-10 records to 13-digit EAN identifiers.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s):
t = s.replace('-', '')
if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
return None
if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
return None
body = '978' + t[:9]
total = sum(int(ch) * (3 if i % 2 == 0 else 1) for i, ch in enumerate(body))
return body + str((10 - total % 10) % 10)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['regression ["3905987139"]', ['3905987139'], '9783905987133'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["2266685686"]', ['2266685686'], '9782266685689']], [['regression ["3407375115"]', ['3407375115'], '9783407375117'], ['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["9999999999"]', ['9999999999'], '9789999999991'], ['regression ["097522980X"]', ['097522980X'], '9780975229804'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]]]
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 ["5892872939"] | 9785892872937 | 9785892872935 | Failed |
| regression ["2399553950"] | 9782399553950 | 9782399553954 | Failed |
| control ["2266685686"] | 9782266685689 | 9782266685689 | Passed |
| control ["1475433131"] | 9781475433135 | 9781475433135 | Passed |
| control ["0436178273"] | 9780436178276 | 9780436178276 | Passed |
| control ["0306406152"] | 9780306406157 | 9780306406157 | Passed |
| control ["0000000000"] | 9780000000002 | 9780000000002 | Passed |
| control ["123456789X"] | 9781234567897 | 9781234567897 | Passed |
SHA-256 / 1758875ab130b9dd0e03d4d73d4dad1117c912696222666bc172c0fe0c0397ba
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s):
t = s.replace('-', '')
if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
return None
if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
return None
body = '978' + t[:9]
total = sum(int(ch) * (1 if i % 2 == 0 else 3) for i, ch in enumerate(reversed(body)))
return body + str((10 - total % 10) % 10)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['regression ["3905987139"]', ['3905987139'], '9783905987133'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["2266685686"]', ['2266685686'], '9782266685689']], [['regression ["3407375115"]', ['3407375115'], '9783407375117'], ['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["9999999999"]', ['9999999999'], '9789999999991'], ['regression ["097522980X"]', ['097522980X'], '9780975229804'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]]]
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 ["5892872939"] | 9785892872937 | 9785892872935 | Failed |
| regression ["2399553950"] | 9782399553950 | 9782399553954 | Failed |
| control ["2266685686"] | 9782266685689 | 9782266685689 | Passed |
| control ["1475433131"] | 9781475433135 | 9781475433135 | Passed |
| control ["0436178273"] | 9780436178276 | 9780436178276 | Passed |
| control ["0306406152"] | 9780306406157 | 9780306406157 | Passed |
| control ["0000000000"] | 9780000000002 | 9780000000002 | Passed |
| control ["123456789X"] | 9781234567897 | 9781234567897 | Passed |
SHA-256 / 84274641d8f1d7bb1b07f9eab319a7c89b2ccc728bb981406f81324ed9410891
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(s):
t = s.replace('-', '')
if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
return None
if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
return None
body = '978' + t[:9]
total = sum(int(ch) * (1 if i % 2 == 0 else 3) for i, ch in enumerate(body))
return body + str((10 - total % 10) % 10)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['regression ["3905987139"]', ['3905987139'], '9783905987133'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["2266685686"]', ['2266685686'], '9782266685689']], [['regression ["3407375115"]', ['3407375115'], '9783407375117'], ['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['control ["2266685686"]', ['2266685686'], '9782266685689'], ['control ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["0306406152"]', ['0306406152'], '9780306406157'], ['control ["0000000000"]', ['0000000000'], '9780000000002'], ['control ["123456789X"]', ['123456789X'], '9781234567897']], [['regression ["9999999999"]', ['9999999999'], '9789999999991'], ['regression ["097522980X"]', ['097522980X'], '9780975229804'], ['control ["123456789X"]', ['123456789X'], '9781234567897'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]]]
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 ["5892872939"] | 9785892872935 | 9785892872935 | Passed |
| regression ["2399553950"] | 9782399553954 | 9782399553954 | Passed |
| control ["2266685686"] | 9782266685689 | 9782266685689 | Passed |
| control ["1475433131"] | 9781475433135 | 9781475433135 | Passed |
| control ["0436178273"] | 9780436178276 | 9780436178276 | Passed |
| control ["0306406152"] | 9780306406157 | 9780306406157 | Passed |
| control ["0000000000"] | 9780000000002 | 9780000000002 | Passed |
| control ["123456789X"] | 9781234567897 | 9781234567897 | Passed |
SHA-256 / 98eaecb59fb1d3e0ddcf577b9b85da43c08a84c6d9d161d3987c43379ab087a8
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.555500+00:00.
Case digest / 7d757cc62ef23854ddd5bd68b80546387f28d4e91fa51e0eb0c13e8eb726f694