✓
Passing This code compiles and runs correctly.
Code
// TEST: Multi-step nbody — kernel:step wrapping pairwise + self
//
// Full fusion test with real solar system data.
// 10 steps of gravitational simulation.
// Verifies that step(0..N) wrapping pairwise + self produces correct output.
~import std/kernel
~import std/io
const PI: f64 = 3.141592653589793;
const SOLAR_MASS: f64 = 4.0 * PI * PI;
const DAYS_PER_YEAR: f64 = 365.24;
const DT: f64 = 0.01;
~std/kernel:shape(Body) {
x: f64,
y: f64,
z: f64,
vx: f64,
vy: f64,
vz: f64,
mass: f64,
}
~std/kernel:init(Body) {
{ x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0, mass: SOLAR_MASS },
{ x: 4.84143144246472090e+00, y: -1.16032004402742839e+00, z: -1.03622044471123109e-01, vx: 1.66007664274403694e-03 * DAYS_PER_YEAR, vy: 7.69901118419740425e-03 * DAYS_PER_YEAR, vz: -6.90460016972063023e-05 * DAYS_PER_YEAR, mass: 9.54791938424326609e-04 * SOLAR_MASS },
{ x: 8.34336671824457987e+00, y: 4.12479856412430479e+00, z: -4.03523417114321381e-01, vx: -2.76742510726862411e-03 * DAYS_PER_YEAR, vy: 4.99852801234917238e-03 * DAYS_PER_YEAR, vz: 2.30417297573763929e-05 * DAYS_PER_YEAR, mass: 2.85885980666130812e-04 * SOLAR_MASS },
{ x: 1.28943695621391310e+01, y: -1.51111514016986312e+01, z: -2.23307578892655734e-01, vx: 2.96460137564761618e-03 * DAYS_PER_YEAR, vy: 2.37847173959480950e-03 * DAYS_PER_YEAR, vz: -2.96589568540237556e-05 * DAYS_PER_YEAR, mass: 4.36624404335156298e-05 * SOLAR_MASS },
{ x: 1.53796971148509165e+01, y: -2.59193146099879641e+01, z: 1.79258772950371181e-01, vx: 2.68067772490389322e-03 * DAYS_PER_YEAR, vy: 1.62824170038242295e-03 * DAYS_PER_YEAR, vz: -9.51592254519715870e-05 * DAYS_PER_YEAR, mass: 5.15138902046611451e-05 * SOLAR_MASS },
}
| kernel k |> std/kernel:step(0..10)
| step |> std/kernel:pairwise {
const dx = k.x - k.other.x;
const dy = k.y - k.other.y;
const dz = k.z - k.other.z;
const dsq = dx*dx + dy*dy + dz*dz;
const mag = DT / (dsq * @sqrt(dsq));
k.vx -= dx * k.other.mass * mag;
k.vy -= dy * k.other.mass * mag;
k.vz -= dz * k.other.mass * mag;
k.other.vx += dx * k.mass * mag;
k.other.vy += dy * k.mass * mag;
k.other.vz += dz * k.mass * mag;
} |> std/kernel:self {
k.x += DT * k.vx;
k.y += DT * k.vy;
k.z += DT * k.vz;
}
| computed c |> std/io:print.blk {
vx[0]={{ c[0].vx:f }}
}
Actual
vx[0]=0.00016083321448098135
Expected output
vx[0]=0.00016083321448098135
Test Configuration
MUST_RUN
Post-validation Script:
#!/bin/bash
# Pins the kernel-fusion DEAD-BRANCH bug.
#
# After kernel:init fuses step/pairwise/self into its own proc and returns
# `.computed`, the CONSUMED `| kernel` continuation must NOT survive in the
# calling flow. Currently the fusion path copies every original continuation
# verbatim (koru_std/kernel.kz:868) and keeps the `kernel` union branch
# (kernel.kz:1006), so flow0 ends up with a dead `.kernel =>` switch arm that
# holds a SECOND, un-fused copy of the pairwise+self loops. Output is still
# correct (expected.txt passes), so only this structural check catches it.
#
# RED until the fusion path strips the consumed kernel continuation + branch.
set -u
fail=0
# 1. The fused pairwise body must be emitted exactly ONCE (inside the init
# proc), not duplicated into a dead `.kernel` switch arm.
pairwise_copies=$(grep -c 'const dsq = dx\*dx' output_emitted.zig)
if [ "$pairwise_copies" -ne 1 ]; then
echo "FAIL: pairwise kernel body emitted ${pairwise_copies}x (expected 1) — fusion left a dead duplicate"
fail=1
fi
# 2. No dead `.kernel =>` switch arm should remain once init returns `.computed`.
if grep -q '\.kernel =>' output_emitted.zig; then
echo "FAIL: dead '.kernel =>' switch arm present — consumed kernel continuation not stripped"
fail=1
fi
exit $fail