FAILURE MAP
← Case archive

FA-72791 / Check-digit algorithms / Open access

NRIC reads the check table from the other end · case 01

Almost every check letter is wrong.

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

ROOT CAUSE

The remainder indexes the table as table[10 - r].

VERIFIED REPAIR

Index the table directly with r.

Unsuccessful approach: An off-by-one index table[r - 1] still shifts every letter.

Case contract

Singapore-style NRIC/FIN check letter for the S, T, F and G series: nine characters, series letter, seven digits, check letter (else "malformed"). Weights 2,7,6,5,4,3,2; add 4 for T and G; r = sum mod 11; the letter is "JZIHGFEDCBA"[r] for S/T and "XWUTRQPNMLK"[r] for F/G. Return [expected letter, match].

Why this case matters

Identity verification forms validate NRIC/FIN numbers before submission.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    if len(s) != 9 or not s.isascii() or s[0] not in 'STFG' or not s[1:8].isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(s[1:8], [2, 7, 6, 5, 4, 3, 2]))
    if s[0] in 'TG':
        total += 4
    r = total % 11
    table = 'JZIHGFEDCBA' if s[0] in 'ST' else 'XWUTRQPNMLK'
    c = table[10 - r]
    return [c, s[8] == c]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["S6621805O"]', ['S6621805O'], ['H', False]], ['regression ["G1651468G"]', ['G1651468G'], ['W', False]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["G0138893V"]', ['G0138893V'], ['U', False]], ['control ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["F4116515A"]', ['F4116515A'], ['N', False]]], [['regression ["G0138893V"]', ['G0138893V'], ['U', False]], ['regression ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["T4006687X"]', ['T4006687X'], ['F', False]], ['control ["S5101399J"]', ['S5101399J'], ['I', False]], ['control ["S0965290F"]', ['S0965290F'], ['F', True]]], [['regression ["G1812101N"]', ['G1812101N'], ['N', True]], ['regression ["G9259714Y"]', ['G9259714Y'], ['N', False]], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["F9230492U"]', ['F9230492U'], ['L', False]], ['control ["F4160346W"]', ['F4160346W'], ['K', False]]], [['regression ["F3876611S"]', ['F3876611S'], ['L', False]], ['regression ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["S5101399J"]', ['S5101399J'], ['I', False]], ['partial-repair ["S0965290F"]', ['S0965290F'], ['F', True]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["T2474569W"]', ['T2474569W'], ['J', False]]], [['regression ["F9230492U"]', ['F9230492U'], ['L', False]], ['regression ["F4160346W"]', ['F4160346W'], ['K', False]], ['partial-repair ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["F5183813A"]', ['F5183813A'], ['X', 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 ["S6621805O"]['D', False]['H', False]Failed
regression ["G1651468G"]['L', False]['W', False]Failed
control ["S123456D"]malformedmalformedPassed
control ["M1234567K"]malformedmalformedPassed
control ["SA234567D"]malformedmalformedPassed
control ["G0138893V"]['M', False]['U', False]Failed
control ["S3757569X"]['A', False]['J', False]Failed
control ["F4116515A"]['T', False]['N', False]Failed

SHA-256 / d42cbc750d23cd59408e9dd69675372bc0757ef40f62afc9ce9b6b066da94ec9

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    if len(s) != 9 or not s.isascii() or s[0] not in 'STFG' or not s[1:8].isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(s[1:8], [2, 7, 6, 5, 4, 3, 2]))
    if s[0] in 'TG':
        total += 4
    r = total % 11
    table = 'JZIHGFEDCBA' if s[0] in 'ST' else 'XWUTRQPNMLK'
    c = table[r - 1]
    return [c, s[8] == c]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["S6621805O"]', ['S6621805O'], ['H', False]], ['regression ["G1651468G"]', ['G1651468G'], ['W', False]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["G0138893V"]', ['G0138893V'], ['U', False]], ['control ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["F4116515A"]', ['F4116515A'], ['N', False]]], [['regression ["G0138893V"]', ['G0138893V'], ['U', False]], ['regression ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["T4006687X"]', ['T4006687X'], ['F', False]], ['control ["S5101399J"]', ['S5101399J'], ['I', False]], ['control ["S0965290F"]', ['S0965290F'], ['F', True]]], [['regression ["G1812101N"]', ['G1812101N'], ['N', True]], ['regression ["G9259714Y"]', ['G9259714Y'], ['N', False]], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["F9230492U"]', ['F9230492U'], ['L', False]], ['control ["F4160346W"]', ['F4160346W'], ['K', False]]], [['regression ["F3876611S"]', ['F3876611S'], ['L', False]], ['regression ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["S5101399J"]', ['S5101399J'], ['I', False]], ['partial-repair ["S0965290F"]', ['S0965290F'], ['F', True]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["T2474569W"]', ['T2474569W'], ['J', False]]], [['regression ["F9230492U"]', ['F9230492U'], ['L', False]], ['regression ["F4160346W"]', ['F4160346W'], ['K', False]], ['partial-repair ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["F5183813A"]', ['F5183813A'], ['X', 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 ["S6621805O"]['I', False]['H', False]Failed
regression ["G1651468G"]['X', False]['W', False]Failed
control ["S123456D"]malformedmalformedPassed
control ["M1234567K"]malformedmalformedPassed
control ["SA234567D"]malformedmalformedPassed
control ["G0138893V"]['W', False]['U', False]Failed
control ["S3757569X"]['A', False]['J', False]Failed
control ["F4116515A"]['P', False]['N', False]Failed

SHA-256 / b6baeb41e1bb0bbdf7f377fbf1024fb276256363122728982ed6193d4e501c18

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    if len(s) != 9 or not s.isascii() or s[0] not in 'STFG' or not s[1:8].isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(s[1:8], [2, 7, 6, 5, 4, 3, 2]))
    if s[0] in 'TG':
        total += 4
    r = total % 11
    table = 'JZIHGFEDCBA' if s[0] in 'ST' else 'XWUTRQPNMLK'
    c = table[r]
    return [c, s[8] == c]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["S6621805O"]', ['S6621805O'], ['H', False]], ['regression ["G1651468G"]', ['G1651468G'], ['W', False]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["G0138893V"]', ['G0138893V'], ['U', False]], ['control ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["F4116515A"]', ['F4116515A'], ['N', False]]], [['regression ["G0138893V"]', ['G0138893V'], ['U', False]], ['regression ["S3757569X"]', ['S3757569X'], ['J', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["T4006687X"]', ['T4006687X'], ['F', False]], ['control ["S5101399J"]', ['S5101399J'], ['I', False]], ['control ["S0965290F"]', ['S0965290F'], ['F', True]]], [['regression ["G1812101N"]', ['G1812101N'], ['N', True]], ['regression ["G9259714Y"]', ['G9259714Y'], ['N', False]], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["F9230492U"]', ['F9230492U'], ['L', False]], ['control ["F4160346W"]', ['F4160346W'], ['K', False]]], [['regression ["F3876611S"]', ['F3876611S'], ['L', False]], ['regression ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["S5101399J"]', ['S5101399J'], ['I', False]], ['partial-repair ["S0965290F"]', ['S0965290F'], ['F', True]], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["T2474569W"]', ['T2474569W'], ['J', False]]], [['regression ["F9230492U"]', ['F9230492U'], ['L', False]], ['regression ["F4160346W"]', ['F4160346W'], ['K', False]], ['partial-repair ["T7996809P"]', ['T7996809P'], ['E', False]], ['partial-repair ["G4191951W"]', ['G4191951W'], ['K', False]], ['control ["SA234567D"]', ['SA234567D'], 'malformed'], ['control ["S123456D"]', ['S123456D'], 'malformed'], ['control ["M1234567K"]', ['M1234567K'], 'malformed'], ['control ["F5183813A"]', ['F5183813A'], ['X', 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 ["S6621805O"]['H', False]['H', False]Passed
regression ["G1651468G"]['W', False]['W', False]Passed
control ["S123456D"]malformedmalformedPassed
control ["M1234567K"]malformedmalformedPassed
control ["SA234567D"]malformedmalformedPassed
control ["G0138893V"]['U', False]['U', False]Passed
control ["S3757569X"]['J', False]['J', False]Passed
control ["F4116515A"]['N', False]['N', False]Passed

SHA-256 / bb2a0cc453315c60431d6101326f038b9c4cc83d7d50b71673635aaf4ca814ea

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:41.628494+00:00.

Case digest / a098911284c9dc34d653d3af453f03eec97bfd2b26f996228b006280623adebe