FAILURE MAP
← Case archive

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.

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

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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