# indexin · the range–stride–sieve composition law

**Lars Rocha's law** · `sitoolz/indexin.py` ↔ `js/indexin.js` · fixtures `fixtures/indexin-law.json` (the selection core, written by the Python reference: 1,960 laws) and `fixtures/indexin.json` (the courtyard adapter, written by the page itself: 8 recipes) · the law as Lars states it: `INDEXIN_RANGE_STRIDE_SIEVE_LAW.md` (version 0.1, 2026-10-03, kept beside this card)

> Address a world element, select it through explicit integer rules, then construct it under shared boundary constraints. The
> same recipe and address must reproduce the same element, regardless of the order in which regions are requested.

## The jobs, kept apart

Address (which element is this), range (which are eligible), stride (which regularly spaced ones take part), stripe (which bands),
sieve (which are removed or given another role), ruler (which scale class), group and stack (what belongs together and where
the copies go), edge contract (how neighbours meet), realization (mesh, collision, navigation). Selection describes membership;
realization supplies shape. This engine is the selection and the records; a page is one realization of them.

## The arithmetic

**Address.** The square-shell index of the Foundry: `q = max(x, z)`, `I(x, z) = q² + (z if x ≥ z else 2q − x)`; its inverse
`square_address(n)`: `q = isqrt(n)`, `r = n − q²`, `(q, r)` when `r ≤ q`, else `(2q − r, q)`. A shell covers `q²..(q+1)²−1`, so a
24 × 24 block has addresses 0..575. Signed coordinates go through `zigzag` first when one nonnegative integer is wanted, and
that changes the spatial order, so it is versioned.

**Selection.** For a nonnegative address `n`, with `[·]` the indicator:

| | rule | knobs |
|---|---|---|
| range | `R(n) = [a ≤ n ≤ b]` | `start`, `end` |
| stride | `T(n) = [(n − α) mod s = 0]` | `stride` s ≥ 1, `origin` α, independent of the range's ends |
| stripe | `B(n) = [((n − φ) mod λ) < w]`, 1 ≤ w ≤ λ | `stripe_period` λ, `stripe_width` w, `stripe_phase` φ |
| sieve | `Q(n) = [((n ⊕ m) mod p) ∈ D]`, keep `K = 1 − Q` | `mask` m ≥ 0, `sieve_period` p ≥ 2, `drop_residues` D |

`S(n) = G · R · T · B · K`, Boolean AND, a filter left out being 1. `Q` marks removal and `K` retention, both named so that no
caller mistakes one for the other. The stride's origin stays apart from the clipped range so that a chunk beginning at 100
narrows the window and does not restart the pattern.

**What follows.** The filters commute on an immutable address (order-independent selection). The first stride candidate at
or above `a` is `f = a + ((α − a) mod s)` and the candidates number `1 + ⌊(b − f)/s⌋` (0 when `f > b`): `addresses()`
enumerates the stride directly and filters the rest. With `h = bit_length(m)` (`2^0 = 1` for `m = 0`), the unbounded
stride–stripe–sieve pattern has the valid period `L = lcm(s, λ, p, 2^h)`: adding `L` keeps the stride and stripe residues,
and being divisible by `2^h` keeps every bit the mask touches, so `(n + L) ⊕ m = (n ⊕ m) + L` and the sieve residue follows.
`survivor_offsets()` is the pattern over one period, the cached table the law proposes.

## Both signatures

| Python | JavaScript |
|---|---|
| `SelectionLaw(start, end, stride=1, origin=0, stripe_period=1, stripe_width=1, stripe_phase=0, mask=0, sieve_period=2, drop_residues=())`, `law(**kw)` | `law({start, end, stride, origin, stripe_period, stripe_width, stripe_phase, mask, sieve_period, drop_residues})` |
| `.in_range(n)`, `.on_stride(n)`, `.on_stripe(n)`, `.dropped(n)`, `.pattern(n)`, `.contains(n)` | `inRange`, `onStride`, `onStripe`, `dropped`, `pattern`, `contains` |
| `.first()`, `.candidates()`, `.addresses()`, `.period()`, `.survivor_offsets()`, `.explain(n)`, `.as_dict()` | `first`, `candidates`, `addresses`, `period`, `survivorOffsets`, `explain`, `params` |
| `square_address(n)`, `zigzag(k)`, `unzigzag(n)` (and `foundry.square_index`) | `squareAddress`, `zigzag`, `unzigzag` |

`explain(n)` returns the structured account the law asks for, big integers as decimal strings: `address`, `in_range`,
`stride_origin`, `stride_remainder`, `stripe_pass`, `xor_value`, `sieve_remainder`, `dropped`, `selected`. Every parameter must
be an integer.

**The safe domain, exactly.** Python's integers are exact, so the Python twin is the reference. JavaScript holds integers to
2^53 − 1, and a difference may overflow that even when both operands are inside it: at address 2^53 − 2 with origin −(2^53 − 1)
the exact stride remainder is 1, while a plain `n - origin` says 0. The JavaScript twin therefore takes the XOR through BigInt
past 2^31 (so an address keeps its bits) and takes `n − origin` and `n − phase` through BigInt whenever `|n| + |origin|` passes
the safe domain, and `addresses()` walks by BigInt there rather than by `n += stride`, which would stand still. Addresses and
parameters are themselves safe integers; a period or a candidate count may exceed that, so read those as what they are. Four
fixture laws sit on that corner on purpose. (Found by spindexin's IX-02 review, 2026-10-03, against the imported copy of this
core; fixed here at the source so the next import starts correct.)

## The courtyard adapter

The Square Indexin courtyard is the first realization, and the engine carries it as an adapter: `normalize`, `select`
(stride anchored at the range's start, the page's convention today), `sieve` (one residue, kept rather than dropped, which
opens a window), `path` (a coordinate stripe of real width at coordinate 12), `ring`/`court` (the spatial reservation),
`portals()` (`{center: 12, width: 2, headroom: 2.4}` on every facade of every level), `walls`, `columns`, `cell`, `label`,
the Foundry ground (`recipe_of`, `raw_height`, `ground`), and `compile(input)` → `{params, columns, columnIds, windowIds,
walls, portals, spawn, stats}` with `stats.triangles` the count the page draws. The page (`square-indexin/dist/courtyard.js`)
turns these records into triangles and collision solids and nothing else.

## The stairs and the walker

One straight flight per level, in the room band: twelve steps rising 3.4 over a run of four cells, a flight 1.5 wide, landing on
the upper slab, which has no cells over the flight (the hole), so a body can rise. The band a flight stands in is
`(mask + level) mod 4`, west, north, east, south, each turned about the centre (`turn`), so a stack's stairs wind round the
courtyard and the recipe says where. `stairs(model)` gives the step boxes with their rule in the label, `holes(model)` the cells
without a slab; both twins, in the fixture through every box and the triangle count.

The walker is the law's too, so the page and Wander cannot disagree about what stops you: a body is a disc of radius 0.22 and
height 1.7; it stands on the highest support under any part of the disc within 0.3 of its feet (the ground, a slab of the ring at
a level, or the top of a solid box), may not overlap a solid above its feet, and may not leave the block (`support_at`,
`blocked_at`, `walk(model, solids, body, dx, dz)`). The ledge guard is the same 0.3: a body does not step off a slab into the
courtyard, and comes down a flight the way it went up.

## The 3 × 3 neighbourhood and the shared edge

`neighbourhood(g, seed, rank, kind)` compiles nine blocks round a settlement's, block (i, j) at origin (24i, 24j) from the address
`g + 24i + 576j`, so neighbours differ and the same block always gets the same recipe from this settlement. The street at local 12
runs on through every block and the nine streets form one grid; the outer facades' portals at 12 face it. The ground is one field
for the nine (the centre's recipe, sampled at the neighbourhood's coordinates) with one fade frame (the neighbourhood's outer
edge), so a shared edge's samples are the same from either side by construction, and a block never asks its neighbour for
anything: `edge_contract` is derived from the canonical key `version:seed:g:axis:boundary:segment`, with the 25 boundary
samples as each side computes them, `agree`, the street opening (centre 12 + segment, the stripe's width) and `traversal`.
`walk_hood` carries a body across the blocks on the same rule as `walk`; `hood_where` says which block, which cell and why.
In Wander the doorway opens the neighbourhood; on the canvas page `?g=…&hood=1` draws and walks it.

**What the agreement does and does not prove (measured 2026-10-03).** The twelve edges agree *inside one neighbourhood*, and they agree
by construction: the nine blocks share one ground field and one fade frame, both taken from the centre block and the neighbourhood's own
coordinates. That is a frame-dependent guarantee. Two neighbourhoods recentred on different settlements do **not** agree about a block
they share: the same block address, read from its own neighbourhood and from its neighbour's, differs by up to 0.125 blocks over its
cells, because `ground_origin` and `ground_recipe` follow the frame rather than the global address. So this is a finite, self-consistent
nine, not a streamed world. Making it global needs the ground to be a function of the signed global address alone, with a fade that is
itself global, and a boundary key that two independently recentred windows both derive. That work is spindexin's IX-07.

## Hunts as selection laws, villages lit by the law

`prize_hunt(key, m)`: a hunt over region m's prizes is a `SelectionLaw` on the prize columns whose knobs come from the key (a
date, a word), so the quest card can state it: "(g − origin) mod stride = 0, and (g XOR mask) mod period ≠ dropped". The
prizes that qualify, each with its explain record, are the clue, and the pattern's period is on the card. In Wander the Warp
panel shows today's law hunt and stars its prizes; the Rule panel gives the verdict on a prize column; `?hunt=word` sets the key.

`village_law(g, seed, rank)`: one law per settlement from its own address (stride and stripe from the rank, origin from the
square remainder, mask from the seed word, the sieve from the column); window i of the settlement is lit at tick t when the
pattern holds at i + t, so the lights move with the clock and repeat with the law's period, and the stripe's slot is always on
the stride and never the dropped residue, so no village is ever dark (the fixture checks that over every tick of the period).
In Wander it is the third state of Graphics → Town lights ("Indexin"), beside the Foundry law.

## Bounded queries and IX1 recipe codes

Two more of Lars's tools over this law, written to his direction in the `~/spindexin` workspace (implementation with Codex) and
brought here by `tools/pull-spindexin.py`, which records the source and its hashes in `SPINDEXIN-MANIFEST.json` (`--verify` says
whether the working copy has moved on):

- `indexin_query.QueryLaw` ↔ `js/indexin-query.js` (`SIQuery`): the same selection, read in bounded pages with a cursor, a
  work budget, a clipped view that keeps the origin and phase fixed, and an opt-in period table. Periods and candidate counts
  come back as decimal strings, because either can exceed what a JavaScript number holds exactly.
- `indexin_recipe` ↔ `js/indexin-recipe.js` (`SIRecipe`): `IX1.<base64url payload>.<crc32>`, a law as a portable code.
  `encode_recipe`, `decode_recipe`, `recipe_document`. The checksum catches a code that got mangled in transit; it is not a
  signature and proves nothing about who wrote it.

The law is the one on this card, and these are a reader and a serialization of it: nothing in them changes what an address decides. `fixtures/indexin-recipe.json` holds the published compatibility vector and codes for the laws this library actually
makes (the hunts, the village lights), so a code written in Python decodes in JavaScript and back.

## The Indexin Terraces: a landscape made of the selection

`land_height(g, z, args)` ↔ `landHeight`, registered with terrain as the kind `indexin` and the world `indexin-terraces`. For a
column `g` and a lane `z`, count how many of the last `reach` addresses the law keeps; that count times `unit` (and the lane's,
times `unitz`) is the tier the ground stands on, ramped by the ladder's rule over a grid of `cell`. The lane's address is
`zigzag(z)`, so a signed lane becomes a nonnegative one and the range is symmetric about the centre line. `land_law(args)`
returns the SelectionLaw a world is written in; `land_lane` puts prizes and settlements on the ladder's shelves.

**How far a range runs before it repeats** is `land_repeat(args) = lcm(the law's period, the ramp's cell)`. The ramp has a grid of
its own, so the law's period alone is not the answer. A valid repeat length, not necessarily the smallest, the same caution the
law's own period carries. The fixture walks four terraced worlds and checks the ground really does repeat at that number.

Three ranges in Wander are written this way: **The Indexin Terraces** (stride 7, mask 5, modulo 4, repeat 56: short enough to
walk and see), **The Sieve Range** (stride 7, stripe 11, sieve 13, mask 255, repeat 256,256, with rivers and settlements whose
doorways open courtyards), and **The Long Count** (stride 9, stripe 25, sieve 49, mask 4,095, repeat 45,158,400). Choosing how
long a world runs before it comes round is choosing four small numbers and taking their least common multiple.

## The fixtures

`tests/write_indexin_law_fixture.py` writes the core's fixture from the Python: the document's worked selection (range
[10, 30], stride 3 from 10, mask 2, modulus 4, drop 0 → 13, 16, 19, 25, 28), 1,960 laws over seven clipped windows with their
addresses, period, survivor offsets, candidate count, first candidate and explain records, three laws past 2^31 and 2^52, 10,000
square-address round trips, zigzag pairs and eight invalid laws. The tests also run the law's integration gates live: stride
enumeration against a predicate scan on every law, the period against the selector over two periods, empty and all-drop
sieves, clipped windows that keep the origin's phase. `tests/write_indexin_fixture.cjs` writes the adapter's fixture from the
page's own `compile` (`tests/oracle/square-indexin/`). `python3 tests/test_indexin.py` and `node tests/twins-indexin.test.cjs`.

## Provenance

This law is Lars Rocha's: he invented the Indexin addressing, the ruler, the range, the stride, the stripe, the sieve and the
grouping, designed the courtyard, and directed every step of the work. ChatGPT wrote notation and drafts under his instruction,
as a typist writes what is dictated; it discovered nothing here, and his document's own provenance section says the same.
The ground is the Foundry's (`foundry.md`), also his. Written into sitoolz on 2026-10-03.

## Known issues for the next round

- **Feature identity is emission order.** `boxes`, `slabs` and `stairs` return arrays, and a feature's identity today is its index in
  them plus its `info` text. The law document warns against exactly this: change the sieve and a wall becomes a window, every later
  index shifts, and anything keyed to one lands on the wrong feature. Each record needs an id derived from the law
  (`world / version / block address / cell / level / role / part`), not from the order it was drawn. That is spindexin's IX-05.
- **The neighbourhood is frame-dependent**, as measured above: spindexin's IX-07.
- **The courtyard adapter's stride is anchored at the range's start.** The selection core has had an independent `origin` since the law
  document arrived; the adapter has not been given the knob. Legacy default must stay `origin = start`.

## What is next, in the law's order

The adapter's stride origin as a knob of its own (the engine has it; the page's control does not yet); protected sets `P_open`
and `P_support` with geometry clipped at their edges rather than dropped by centre; 
 recipe codes and edits keyed by the stable identity
`(world, recipe_version, chunk, local_cell, layer, feature_role, local_part)`.
