FA-88416 / Mesh topology invariants / Open access
Vertex link tolerates a link vertex of degree three · case 01
Vertices on a three-sheet fin are classified as manifold.
ROOT CAUSE
The degree test rejects only degrees above 3.
VERIFIED REPAIR
Any link vertex of degree above 2 makes v non-manifold.
Unsuccessful approach: Rejecting degree 2 flags every interior vertex.
Case contract
Input: triangle faces. For each referenced vertex v, its link is the set of opposite edges of incident triangles. v is "nonmanifold" if a link vertex has degree above 2 or the link graph is disconnected, "interior" if every link vertex has degree 2, else "boundary". Return {vertex string: class}.
Why this case matters
Mesh processing pipelines (remeshing, export, simulation, printing) trust these topological counts and adjacency answers; a wrong invariant silently accepts broken meshes or rejects valid ones.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
faces=x
link={}
for f in faces:
for i in range(3):
v=f[i]
a=f[(i+1)%3]
b=f[(i+2)%3]
link.setdefault(v,[]).append((a,b))
out={}
for v in sorted(link):
es=link[v]
deg={}
for a,b in es:
deg[a]=deg.get(a,0)+1
deg[b]=deg.get(b,0)+1
if any(d>3 for d in deg.values()):
out[str(v)]='nonmanifold'
continue
adj={}
for a,b in es:
adj.setdefault(a,[]).append(b)
adj.setdefault(b,[]).append(a)
start=next(iter(adj))
stack=[start]
seen={start}
while stack:
u=stack.pop()
for w in adj.get(u,[]):
if w not in seen:
seen.add(w)
stack.append(w)
if len(seen)!=len(adj): out[str(v)]='nonmanifold'
elif all(d==2 for d in deg.values()): out[str(v)]='interior'
else: out[str(v)]='boundary'
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}]], [['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['single triangle', [[[0, 1, 2]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]]]
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 |
|---|---|---|---|
| tetrahedron | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'} | Passed |
| disk | {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'} | {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'} | Passed |
| fan | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| bowtie | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| book | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Failed |
| octahedron | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'} | Passed |
| torus | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'} | Passed |
SHA-256 / e750ee1de586c09a3a880e613b801190b8efb62dd38387ca70303f4bd3938281
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
faces=x
link={}
for f in faces:
for i in range(3):
v=f[i]
a=f[(i+1)%3]
b=f[(i+2)%3]
link.setdefault(v,[]).append((a,b))
out={}
for v in sorted(link):
es=link[v]
deg={}
for a,b in es:
deg[a]=deg.get(a,0)+1
deg[b]=deg.get(b,0)+1
if any(d>=2 for d in deg.values()):
out[str(v)]='nonmanifold'
continue
adj={}
for a,b in es:
adj.setdefault(a,[]).append(b)
adj.setdefault(b,[]).append(a)
start=next(iter(adj))
stack=[start]
seen={start}
while stack:
u=stack.pop()
for w in adj.get(u,[]):
if w not in seen:
seen.add(w)
stack.append(w)
if len(seen)!=len(adj): out[str(v)]='nonmanifold'
elif all(d==2 for d in deg.values()): out[str(v)]='interior'
else: out[str(v)]='boundary'
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}]], [['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['single triangle', [[[0, 1, 2]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]]]
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 |
|---|---|---|---|
| tetrahedron | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'nonmanifold', '3': 'nonmanifold'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'} | Failed |
| disk | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'nonmanifold', '3': 'nonmanifold', '4': 'nonmanifold', '5': 'nonmanifold'} | {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'} | Failed |
| fan | {'0': 'nonmanifold', '1': 'boundary', '2': 'nonmanifold', '3': 'nonmanifold', '4': 'boundary'} | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Failed |
| bowtie | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| book | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| octahedron | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'nonmanifold', '3': 'nonmanifold', '4': 'nonmanifold', '5': 'nonmanifold'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'} | Failed |
| torus | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'nonmanifold', '3': 'nonmanifold', '4': 'nonmanifold', '5': 'nonmanifold', '6': 'nonmanifold', '7': 'nonmanifold', '8': 'nonmanifold'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'} | Failed |
SHA-256 / 88e267eefc69b24360111d225a41ba37d5c24b4c538f02eca3248143f797fc35
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
faces=x
link={}
for f in faces:
for i in range(3):
v=f[i]
a=f[(i+1)%3]
b=f[(i+2)%3]
link.setdefault(v,[]).append((a,b))
out={}
for v in sorted(link):
es=link[v]
deg={}
for a,b in es:
deg[a]=deg.get(a,0)+1
deg[b]=deg.get(b,0)+1
if any(d>2 for d in deg.values()):
out[str(v)]='nonmanifold'
continue
adj={}
for a,b in es:
adj.setdefault(a,[]).append(b)
adj.setdefault(b,[]).append(a)
start=next(iter(adj))
stack=[start]
seen={start}
while stack:
u=stack.pop()
for w in adj.get(u,[]):
if w not in seen:
seen.add(w)
stack.append(w)
if len(seen)!=len(adj): out[str(v)]='nonmanifold'
elif all(d==2 for d in deg.values()): out[str(v)]='interior'
else: out[str(v)]='boundary'
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}]], [['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['double disk pinch', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1], [0, 6, 7], [0, 7, 8], [0, 8, 6]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}], ['single triangle', [[[0, 1, 2]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['tetrahedron', [[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'}], ['disk', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]]], {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'}], ['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['two triangles sharing edge', [[[0, 1, 2], [2, 1, 3]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary'}]], [['fan', [[[0, 1, 2], [0, 2, 3], [0, 3, 4]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['bowtie', [[[0, 1, 2], [0, 3, 4]]], {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['book', [[[0, 1, 2], [1, 0, 3], [0, 1, 4]]], {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'}], ['octahedron', [[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1], [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'}], ['torus', [[[0, 3, 4], [0, 4, 1], [1, 4, 5], [1, 5, 2], [2, 5, 3], [2, 3, 0], [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5], [5, 8, 6], [5, 6, 3], [6, 0, 1], [6, 1, 7], [7, 1, 2], [7, 2, 8], [8, 2, 0], [8, 0, 6]]], {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'}], ['open cylinder', [[[0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary', '6': 'boundary', '7': 'boundary'}], ['triangle grid', [[[0, 1, 4], [0, 4, 3], [1, 2, 5], [1, 5, 4], [3, 4, 7], [3, 7, 6], [4, 5, 8], [4, 8, 7]]], {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'interior', '5': 'boundary', '6': 'boundary', '7': 'boundary', '8': 'boundary'}]]]
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 |
|---|---|---|---|
| tetrahedron | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior'} | Passed |
| disk | {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'} | {'0': 'interior', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary', '5': 'boundary'} | Passed |
| fan | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'boundary', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| bowtie | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'boundary', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| book | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | {'0': 'nonmanifold', '1': 'nonmanifold', '2': 'boundary', '3': 'boundary', '4': 'boundary'} | Passed |
| octahedron | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior'} | Passed |
| torus | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'} | {'0': 'interior', '1': 'interior', '2': 'interior', '3': 'interior', '4': 'interior', '5': 'interior', '6': 'interior', '7': 'interior', '8': 'interior'} | Passed |
SHA-256 / dedff0333bf9cfc974f49f0ebb565bcdbd57c403454da1f28c49b2f9b2bab5cd
Verification & scope
Pure combinatorial teaching model over integer face lists with a stipulated contract; no geometry kernel or file format is implied. 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:51:07.786687+00:00.
Case digest / 062715b845d8bf1198b7d31123f7f2c6ec1cbf83a065a6597d4db560bc8d9319