FA-96726 / Phone number formatting / Open access
Number list range compression: duplicates split a run · case 01
A repeated number produces a singleton followed by a run.
ROOT CAUSE
The input is sorted but not deduplicated.
VERIFIED REPAIR
Deduplicate before sorting.
Unsuccessful approach: Deduplicating in input order without sorting breaks unsorted lists.
Case contract
Input a list of digit strings. Deduplicate and sort by (length, text). Consecutive numbers of equal length (each one greater than the previous) form a run. A run of one is emitted as is; a longer run is emitted as START-TAIL where TAIL is END without the prefix it shares with START.
Why this case matters
Number inventories are displayed and exported as compressed ranges; a wrong run boundary or tail misstates which numbers are owned.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
nums = sorted(x, key=lambda v: (len(v), v))
out = []
i = 0
while i < len(nums):
j = i
while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
j += 1
a, b = nums[i], nums[j]
if a == b:
out.append(a)
else:
k = 0
while a[k] == b[k]:
k += 1
out.append(a + '-' + b[k:])
i = j + 1
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| run and singleton | ['5550110-2', '5550160'] | ['5550110-2', '5550160'] | Passed |
| duplicates inside a run | ['5550115', '5550115-6'] | ['5550115-6'] | Failed |
| unsorted input | ['5550110-2'] | ['5550110-2'] | Passed |
| run crossing a hundred | ['5550198-201'] | ['5550198-201'] | Passed |
| different lengths do not join | ['98-9', '100-1'] | ['98-9', '100-1'] | Passed |
| run crossing a thousand | ['1998-2000'] | ['1998-2000'] | Passed |
| shorter number sorts first | ['100', '0050'] | ['100', '0050'] | Passed |
| gap of two | ['5550130', '5550132'] | ['5550130', '5550132'] | Passed |
SHA-256 / 69f14c9bdc678d3b06633f1926efae4533de03c868abb38186d6d105beb0a931
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
nums = list(dict.fromkeys(x))
out = []
i = 0
while i < len(nums):
j = i
while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
j += 1
a, b = nums[i], nums[j]
if a == b:
out.append(a)
else:
k = 0
while a[k] == b[k]:
k += 1
out.append(a + '-' + b[k:])
i = j + 1
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| run and singleton | ['5550110-2', '5550160'] | ['5550110-2', '5550160'] | Passed |
| duplicates inside a run | ['5550115-6'] | ['5550115-6'] | Passed |
| unsorted input | ['5550112', '5550110-1'] | ['5550110-2'] | Failed |
| run crossing a hundred | ['5550198-201'] | ['5550198-201'] | Passed |
| different lengths do not join | ['98-9', '100-1'] | ['98-9', '100-1'] | Passed |
| run crossing a thousand | ['1998-2000'] | ['1998-2000'] | Passed |
| shorter number sorts first | ['0050', '100'] | ['100', '0050'] | Failed |
| gap of two | ['5550130', '5550132'] | ['5550130', '5550132'] | Passed |
SHA-256 / 254edaea26ddec9518971909c2e02cbcbd0e424c817aae02cc04983db165966e
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
nums = sorted(set(x), key=lambda v: (len(v), v))
out = []
i = 0
while i < len(nums):
j = i
while j + 1 < len(nums) and int(nums[j + 1]) == int(nums[j]) + 1 and len(nums[j + 1]) == len(nums[i]):
j += 1
a, b = nums[i], nums[j]
if a == b:
out.append(a)
else:
k = 0
while a[k] == b[k]:
k += 1
out.append(a + '-' + b[k:])
i = j + 1
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['run and singleton', ['5550110', '5550111', '5550112', '5550160'], ['5550110-2', '5550160']], ['duplicates inside a run', ['5550115', '5550115', '5550116'], ['5550115-6']], ['unsorted input', ['5550112', '5550110', '5550111'], ['5550110-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550130', '5550132'], ['5550130', '5550132']]], [['run and singleton', ['5550120', '5550121', '5550122', '5550170'], ['5550120-2', '5550170']], ['duplicates inside a run', ['5550125', '5550125', '5550126'], ['5550125-6']], ['unsorted input', ['5550122', '5550120', '5550121'], ['5550120-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550140', '5550142'], ['5550140', '5550142']]], [['run and singleton', ['5550130', '5550131', '5550132', '5550180'], ['5550130-2', '5550180']], ['duplicates inside a run', ['5550135', '5550135', '5550136'], ['5550135-6']], ['unsorted input', ['5550132', '5550130', '5550131'], ['5550130-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550150', '5550152'], ['5550150', '5550152']]], [['run and singleton', ['5550140', '5550141', '5550142', '5550190'], ['5550140-2', '5550190']], ['duplicates inside a run', ['5550145', '5550145', '5550146'], ['5550145-6']], ['unsorted input', ['5550142', '5550140', '5550141'], ['5550140-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550160', '5550162'], ['5550160', '5550162']]], [['run and singleton', ['5550150', '5550151', '5550152', '5550200'], ['5550150-2', '5550200']], ['duplicates inside a run', ['5550155', '5550155', '5550156'], ['5550155-6']], ['unsorted input', ['5550152', '5550150', '5550151'], ['5550150-2']], ['run crossing a hundred', ['5550198', '5550199', '5550200', '5550201'], ['5550198-201']], ['different lengths do not join', ['98', '99', '100', '101'], ['98-9', '100-1']], ['run crossing a thousand', ['1998', '1999', '2000'], ['1998-2000']], ['shorter number sorts first', ['0050', '100'], ['100', '0050']], ['gap of two', ['5550170', '5550172'], ['5550170', '5550172']]]]
for label, arg, expected in fixtures[N - 1]:
check(label, solve(arg), 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 |
|---|---|---|---|
| run and singleton | ['5550110-2', '5550160'] | ['5550110-2', '5550160'] | Passed |
| duplicates inside a run | ['5550115-6'] | ['5550115-6'] | Passed |
| unsorted input | ['5550110-2'] | ['5550110-2'] | Passed |
| run crossing a hundred | ['5550198-201'] | ['5550198-201'] | Passed |
| different lengths do not join | ['98-9', '100-1'] | ['98-9', '100-1'] | Passed |
| run crossing a thousand | ['1998-2000'] | ['1998-2000'] | Passed |
| shorter number sorts first | ['100', '0050'] | ['100', '0050'] | Passed |
| gap of two | ['5550130', '5550132'] | ['5550130', '5550132'] | Passed |
SHA-256 / 7718be816344333c3799284f36a47556ec1529b37d765452bd619981c869c99a
Verification & scope
A deterministic toy contract over synthetic, obviously fictitious telephone numbers and toy country tables. The rules are stipulated for this model only and make no claim of conformance with any numbering plan or telecommunications standard. 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:52:25.360517+00:00.
Case digest / 1b6b7361807024a708712b433f554f9ea07bb8543ef01910b61551d038624c0e