FA-88771 / Mesh topology invariants / Open access
Split vertex id reuses an existing index · case 01
With index gaps the inserted vertex aliases a vertex that already exists.
ROOT CAUSE
The new id is the count of referenced vertices instead of max index + 1.
VERIFIED REPAIR
Use max referenced index + 1.
Unsuccessful approach: Using max index without +1 always aliases the largest vertex.
Case contract
Input [faces, a, b] for a triangle mesh (any non-triangle returns "non-triangle"). Insert vertex m = max index + 1 on edge {a,b}: each triangle (u,w,c) with {u,w}={a,b} along its boundary becomes [u,m,c] then [m,w,c] at its original position. No such triangle returns "no-edge". Return [m, faces, number of split triangles].
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,a,b=x
m=len({v for f in faces for v in f})
out=[]
hit=0
for f in faces:
n=len(f)
if n!=3: return 'non-triangle'
done=False
for j in range(3):
u,w=f[j],f[(j+1)%3]
if {u,w}=={a,b}:
c=f[(j+2)%3]
out.append([u,m,c])
out.append([m,w,c])
hit+=1
done=True
break
if not done: out.append(list(f))
if hit==0: return 'no-edge'
return [m,out,hit]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle'], ['book spine', [[[[0, 1, 2], [1, 0, 3], [0, 1, 4]], 0, 1]], [5, [[0, 5, 2], [5, 1, 2], [1, 5, 3], [5, 0, 3], [0, 5, 4], [5, 1, 4]], 3]]], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']]]
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 |
|---|---|---|---|
| shared diagonal | [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2] | [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2] | Passed |
| rim edge | [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1] | [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1] | Passed |
| missing edge | no-edge | no-edge | Passed |
| gapped indices | [4, [[1, 4, 0], [4, 5, 0], [5, 4, 7], [4, 1, 7]], 2] | [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2] | Failed |
| tetrahedron edge | [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2] | [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2] | Passed |
| quad input | non-triangle | non-triangle | Passed |
| disk spoke | [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | Passed |
SHA-256 / aa52ddcda21030bd4f7c76f260c4d29e6d77a20c946faab155ff819889f5857a
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,a,b=x
m=max(v for f in faces for v in f)
out=[]
hit=0
for f in faces:
n=len(f)
if n!=3: return 'non-triangle'
done=False
for j in range(3):
u,w=f[j],f[(j+1)%3]
if {u,w}=={a,b}:
c=f[(j+2)%3]
out.append([u,m,c])
out.append([m,w,c])
hit+=1
done=True
break
if not done: out.append(list(f))
if hit==0: return 'no-edge'
return [m,out,hit]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle'], ['book spine', [[[[0, 1, 2], [1, 0, 3], [0, 1, 4]], 0, 1]], [5, [[0, 5, 2], [5, 1, 2], [1, 5, 3], [5, 0, 3], [0, 5, 4], [5, 1, 4]], 3]]], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']]]
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 |
|---|---|---|---|
| shared diagonal | [3, [[1, 3, 0], [3, 2, 0], [2, 3, 3], [3, 1, 3]], 2] | [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2] | Failed |
| rim edge | [3, [[0, 3, 2], [3, 1, 2], [2, 1, 3]], 1] | [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1] | Failed |
| missing edge | no-edge | no-edge | Passed |
| gapped indices | [7, [[1, 7, 0], [7, 5, 0], [5, 7, 7], [7, 1, 7]], 2] | [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2] | Failed |
| tetrahedron edge | [3, [[0, 2, 1], [0, 1, 3], [2, 3, 1], [3, 3, 1], [3, 3, 0], [3, 2, 0]], 2] | [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2] | Failed |
| quad input | non-triangle | non-triangle | Passed |
| disk spoke | [5, [[0, 1, 2], [3, 5, 2], [5, 0, 2], [0, 5, 4], [5, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | Failed |
SHA-256 / 1b7aff5db5133ed0666c94badd759560bbe4045df1ecd0354812c46d80771450
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,a,b=x
m=1+max(v for f in faces for v in f)
out=[]
hit=0
for f in faces:
n=len(f)
if n!=3: return 'non-triangle'
done=False
for j in range(3):
u,w=f[j],f[(j+1)%3]
if {u,w}=={a,b}:
c=f[(j+2)%3]
out.append([u,m,c])
out.append([m,w,c])
hit+=1
done=True
break
if not done: out.append(list(f))
if hit==0: return 'no-edge'
return [m,out,hit]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle'], ['book spine', [[[[0, 1, 2], [1, 0, 3], [0, 1, 4]], 0, 1]], [5, [[0, 5, 2], [5, 1, 2], [1, 5, 3], [5, 0, 3], [0, 5, 4], [5, 1, 4]], 3]]], [['shared diagonal', [[[[0, 1, 2], [2, 1, 3]], 1, 2]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['rim edge', [[[[0, 1, 2], [2, 1, 3]], 0, 1]], [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1]], ['missing edge', [[[[0, 1, 2], [2, 1, 3]], 0, 3]], 'no-edge'], ['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]]], [['gapped indices', [[[[0, 1, 5], [1, 7, 5]], 1, 5]], [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2]], ['tetrahedron edge', [[[[0, 2, 1], [0, 1, 3], [1, 2, 3], [0, 3, 2]], 2, 3]], [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2]], ['quad input', [[[[0, 1, 2, 3]], 0, 1]], 'non-triangle'], ['disk spoke', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], 0, 3]], [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2]], ['reversed request', [[[[0, 1, 2], [2, 1, 3]], 2, 1]], [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2]], ['fan rim', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4]], 3, 4]], [5, [[0, 1, 2], [0, 2, 3], [3, 5, 0], [5, 4, 0]], 1]], ['triangle then quad', [[[[0, 1, 2], [2, 1, 3, 4]], 0, 1]], 'non-triangle']]]
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 |
|---|---|---|---|
| shared diagonal | [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2] | [4, [[1, 4, 0], [4, 2, 0], [2, 4, 3], [4, 1, 3]], 2] | Passed |
| rim edge | [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1] | [4, [[0, 4, 2], [4, 1, 2], [2, 1, 3]], 1] | Passed |
| missing edge | no-edge | no-edge | Passed |
| gapped indices | [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2] | [8, [[1, 8, 0], [8, 5, 0], [5, 8, 7], [8, 1, 7]], 2] | Passed |
| tetrahedron edge | [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2] | [4, [[0, 2, 1], [0, 1, 3], [2, 4, 1], [4, 3, 1], [3, 4, 0], [4, 2, 0]], 2] | Passed |
| quad input | non-triangle | non-triangle | Passed |
| disk spoke | [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | [6, [[0, 1, 2], [3, 6, 2], [6, 0, 2], [0, 6, 4], [6, 3, 4], [0, 4, 5], [0, 5, 1]], 2] | Passed |
SHA-256 / 2c65a8759bded9059ffd44105c1a9aab1473983034b699d0aab3a27102631002
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:11.249690+00:00.
Case digest / a9b96e85227f499ac82e3f58c37ddf9ffad633d51624c9025369ba1c5fee0ef0