# Exact constructions: split and move

The current Python construction layer has two material operations:

- `Construction.split(piece, planes)` extracts the intersection of a piece with
  all selected halfspaces and retains its entire remainder.
- `Construction.move(piece, motion)` applies one checked proper rigid motion to
  the whole piece, including every internal convex cell.

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](../docs/research/split_move_construction_language.md)
and [English recipe guide](../docs/research/english_construction_recipes.md).

Start with [Hafner step 1: split and move](../docs/research/hafner_nineteen_splits.md).
Then continue through [step 2: pentagonal pyramids](../docs/research/hafner_pentagonal_transfer.md).
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.

```sh
python3 -m construction.language steps construction/examples/hafner_nineteen_splits.geom
python3 -m construction.language transpile construction/examples/hafner_nineteen_splits.geom --operations-only
```

```sh
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.

```sh
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`.
