Exact constructions: split and move

The current Python construction layer has two material operations:

Planes are ThreePointPlane((a,b,c)), with three ordered, noncollinear algebraic points. Both split children must be positive-volume and face-connected. Nonconvex pieces and cavities are supported; disconnected children are rejected. Handles are consumed by splits or moves, and the operation returns current handles.

See the design and Hafner verification and English recipe guide.

Start with Hafner step 1: split and move. Then continue through step 2: pentagonal pyramids. Its recipe starts with augmented_icosidodecahedron, dodecahedron and contains only the twelve pentagonal transfers. stages.py verifies the step-1 recipe, preserves its constituent pieces and history, and supplies the assembly input. The composed certificate still certifies both rhombic endpoint assemblies. The current recipe starts both solids centered, cycles through 19 splits and immediate moves, then explicitly moves the last residual. The shared Polycone subroutine rotates through base vertices to define the wedge sides. steps executes valid operations and returns the live pieces without issuing a certificate. Face-based moves do check their contacts locally. Finish closes the construction; Finish augmented_icosidodecahedron by icosidodecahedron, tetrahedra names its step-1 target and live constituents. Step 2 declares two targets on consecutive Finish ... by ... lines. Fixed piece arrays and assemblies expand to their members, and every live piece must occur exactly once in its correct target assembly. This does not fuse material or replace the geometric proof. Indentation closes Cycle blocks. The proof runner then certifies and independently replays the final assembly against the Hafner target. Each moved handle is written back into its fixed array slot.

python3 -m construction.language steps construction/examples/hafner_nineteen_splits.geom
python3 -m construction.language transpile construction/examples/hafner_nineteen_splits.geom --operations-only
python3 -m construction.language run construction/examples/split_move_brick.geom
python3 -m construction.examples.split_move_hafner --output-dir /tmp/split-move-hafner
python3 -m unittest tests.test_split_move tests.test_split_move_hafner -v

Run from the repository root with Python 3.11 or later. Only the standard library and the existing pure-Python exact kernel are required. No Rust, NumPy, or SciPy.

library/geometry.geom defines Polycone in the recipe language. subroutines.py reads Define blocks; the ordinary compiler translates them into Python functions with local variables and explicit returned outputs. Polycone is not a new kernel primitive or a text alias. Both Hafner stages call the same definition. Its fixed halfspace array is sized with size(base) at allocation. User definitions precede Start and can call earlier routines, with no recursion. definitions.py parses core expressions and inline comments; vocabulary.py lists the data operations. The current Hafner recipe explains each operation with comments and uses Let base_faces[20] be face of icosahedron, Cycle 19 into i:, and bracket indexing. Cycle triangle by j into a, b, c: visits cyclic vertex triples. Let sides[3] be halfspace allocates typed slots and Bound space by sides into wedge intersects the filled array. cycle_syntax.py handles indentation and source lines. Bound space by ab, bc, ca into wedge constructs an immutable Region of three halfspaces; Plane and Half define their geometry separately. Let triangular_faces[20] be face[3] of icosidodecahedron reads candidate faces; Match triangular_faces by base_faces, outward_docking into receiving_faces reorders all faces by exact outward direction and congruence, preserving their vertex order. Move tetrahedron by triangle, receiving_faces[i] into tetrahedron docks them and checks full face equality, opposite sides, and overlap with placed pieces. space is a constant, and face values have their own read-only Face type; Split residual by wedge ... changes the material. Fixed-size arrays live in arrays.py, and Region is defined in pieces.py. history.py records and replays contact histories as scissors-split-move-v2, while preserving v1 replay; contact.py checks exposed supporting convex polygons. pieces.py implements the nonconvex intersection/remainder and connectivity rules. goals.py defines initial assemblies and independent target regions.

The Hafner example now uses 19 binary splits and 20 proper moves, retaining a stationary icosidodecahedron. The generic verifier checks all final physical-piece pairs and exact target coverage. It does not invoke a fan-specific proof rule. The step 2 example adds 12 pentagonal transfers to its verified prerequisite: 31 total splits, 32 moves, 34 physical pieces in two separately checked rhombic assemblies. Let pentagonal_faces[12] be face[5] of icosidodecahedron filters by vertex count; Let vertices[12] be point of icosahedron reads canonical reference coordinates without introducing additional material; Match vertices by pentagonal_faces, outward_ray into apices reorders them in face order on the outward-normal rays from their centroid. The dodecahedron uses the dual orientation of the icosahedron, scaled to the same edge length.

relations.py defines typed immutable relations separately from the generic Match T[n] by U[n], relation(T,U) into T[n] permutation operation. same_position also supports exact point-to-point matching. Missing or ambiguous matches, duplicate candidates, and candidate reuse fail. Both inputs must be fixed arrays of equal length; every candidate is used once. Subset selection will use Select if needed; that keyword is not implemented yet. Move chooses the vertex correspondence and records it for exact replay.

python3 -m construction.examples.hafner_pentagonal_transfer --output-dir /tmp/hafner-step2
python3 -m unittest tests.test_hafner_pentagonal_transfer -v

Continuous collision-free paths and fabrication are separate obligations.

The earlier Dissection, PartitionConstruction, and special transfer APIs are retained for historical reproduction. Old .geom examples use python3 -m construction.legacy_language, with legacy_aliases.json. Their special certificate formats retain their original checkers. Existing saved-eight and search workflows are unchanged. New work should use Construction, PhysicalPiece, ThreePointPlane, Motion, and verify_history.