Files
LithosAnanake/experiments/std79-doe/std79-doe.fth
T
Robert Allan JamesandClaude Sonnet 5 c8ba8832c4
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Fix N-RUNS/START-REP hardcoded constants; N-REPS increase blocked on reservoir cost, not a bug (FABRIC-3.md §XXIII)
Two real bugs found and fixed while attempting to raise N-REPS from
3 (both harmless only by coincidence at N-REPS=3, since 3 happened
to equal the hardcoded/literal values):

- N-RUNS was `27 CONSTANT`, not derived -- now `N-ID N-REPS *
  CONSTANT N-RUNS`.
- START-REP's run_id decode used a literal `3 *` where it meant
  `N-REPS *` (confirmed against EXEC-STD79-DOE, the serialized
  baseline, which correctly uses N-REPS for the same decode).

Re-verified at N-REPS=3 (amd64): byte-identical to the already-
verified baseline -- 193s, 0 faults, 99/99 tokens every identity,
K conserved on all 656 rows.

Raising N-REPS to 6 was then tested and found to cause real, silent
data loss at full 8-identity scale (rajames 0/198, 00 66/198, 01/02
99/198, 03-06 fully complete) -- same failure class as §XXI defect 2,
just past the budget again since doubling N-REPS roughly doubles
total MSG-SEND volume (24->48).

Measured the reservoir's replenishment directly rather than assume
from source: 40 sends drained it from 21735 to 1; 300s of pure idle
time (zero further sends) brought it back to 2049 -- real, but only
~6.8 units/s, meaning a full refill would take on the order of 53
minutes against a campaign's few-hundred-second runtime. Raising
N-REPS further needs a cheaper per-send cost or an explicit top-up,
not reliance on ambient decay.

Reverted N-REPS to 3 (fixes kept, they're correctness fixes
independent of the value) rather than commit a silently-lossy result.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
2026-09-12 20:03:21 -04:00

450 lines
19 KiB
Forth

( std79-doe.4th - 3(arch, fixed per boot) x 9(identity) x 3(rep) )
( randomized full-factorial DoE. Same 24-case content as )
( std79-exerciser.fth, driven via VM-EXEC into each identity's own )
( live VM (all 9 pre-attached before this runs); "zuse" dispatches )
( into "Hera" herself since her power is a session flag on Hera, )
( not a separate registered VM. Fisher-Yates shuffle matches )
( doe.4th's own algorithm/style; seed fixed for cross-architecture )
( reproducibility (same permutation applied on every arch). )
9 CONSTANT N-ID
3 CONSTANT N-REPS
N-ID N-REPS * CONSTANT N-RUNS
24 CONSTANT N-TESTS
: ID-NAME ( idx -- c-addr u )
CASE
0 OF S" Hera" ENDOF
1 OF S" rajames" ENDOF
2 OF S" 00" ENDOF
3 OF S" 01" ENDOF
4 OF S" 02" ENDOF
5 OF S" 03" ENDOF
6 OF S" 04" ENDOF
7 OF S" 05" ENDOF
8 OF S" 06" ENDOF
ENDCASE ;
: ID-LABEL ( idx -- c-addr u )
CASE
0 OF S" zuse" ENDOF
1 OF S" rajames" ENDOF
2 OF S" 00" ENDOF
3 OF S" 01" ENDOF
4 OF S" 02" ENDOF
5 OF S" 03" ENDOF
6 OF S" 04" ENDOF
7 OF S" 05" ENDOF
8 OF S" 06" ENDOF
ENDCASE ;
( idx 0 (zuse) runs natively on Hera -- never via VM-EXEC targeting )
( "Hera" herself: VM-EXEC's own vm_state_push/pop only saves )
( rsp/exit_colon/ecw_nesting, not input_buffer/input_pos, so a )
( self-targeting call while THIS capsule's own vm_interpret call is )
( still mid-line would risk clobbering the outer parse -- the exact )
( class of bug the idle-tick reentrancy guards (repl.c) exist for, )
( just not covered by VM-EXEC's own narrower state stack. Sidestepped )
( entirely by never dispatching a string back into the caller's own )
( VM; the same 24 cases are just directly-compiled code below. )
: RUN-TEST-NATIVE ( n -- )
CASE
0 OF 7 3 /MOD SWAP . . ENDOF
1 OF -7 3 /MOD SWAP . . ENDOF
2 OF 7 -3 /MOD SWAP . . ENDOF
3 OF -7 -3 /MOD SWAP . . ENDOF
4 OF 2147483647 1 + . ENDOF
5 OF -2147483648 NEGATE . ENDOF
6 OF -2147483648 ABS . ENDOF
7 OF -1 1 RSHIFT . ENDOF
8 OF -8 2 RSHIFT . ENDOF
9 OF 1 31 LSHIFT . ENDOF
10 OF -1 0 < . ENDOF
11 OF -1 0 U< . ENDOF
12 OF 123 456 M* SWAP D. ENDOF
13 OF -123 456 M* SWAP D. ENDOF
14 OF 100000 S>D SWAP 7 M/MOD . . ENDOF
15 OF 7 11 3 */ . ENDOF
16 OF 7 11 3 */MOD SWAP . . ENDOF
17 OF -5 S>D SWAP D. ENDOF
18 OF -5 S>D 3 S>D D+ SWAP D. ENDOF
19 OF -5 S>D DNEGATE SWAP D. ENDOF
20 OF -5 3 MIN . -5 3 MAX . ENDOF
21 OF 5 0 10 WITHIN . ENDOF
22 OF -1 0 AND . -1 0 OR . 5 3 XOR . ENDOF
23 OF -2147483648 2/ . ENDOF
ENDCASE ;
: TEST-CMD ( n -- c-addr u )
CASE
0 OF S" 7 3 /MOD SWAP . ." ENDOF
1 OF S" -7 3 /MOD SWAP . ." ENDOF
2 OF S" 7 -3 /MOD SWAP . ." ENDOF
3 OF S" -7 -3 /MOD SWAP . ." ENDOF
4 OF S" 2147483647 1 + ." ENDOF
5 OF S" -2147483648 NEGATE ." ENDOF
6 OF S" -2147483648 ABS ." ENDOF
7 OF S" -1 1 RSHIFT ." ENDOF
8 OF S" -8 2 RSHIFT ." ENDOF
9 OF S" 1 31 LSHIFT ." ENDOF
10 OF S" -1 0 < ." ENDOF
11 OF S" -1 0 U< ." ENDOF
12 OF S" 123 456 M* SWAP D." ENDOF
13 OF S" -123 456 M* SWAP D." ENDOF
14 OF S" 100000 S>D SWAP 7 M/MOD . ." ENDOF
15 OF S" 7 11 3 */ ." ENDOF
16 OF S" 7 11 3 */MOD SWAP . ." ENDOF
17 OF S" -5 S>D SWAP D." ENDOF
18 OF S" -5 S>D 3 S>D D+ SWAP D." ENDOF
19 OF S" -5 S>D DNEGATE SWAP D." ENDOF
20 OF S" -5 3 MIN . -5 3 MAX ." ENDOF
21 OF S" 5 0 10 WITHIN ." ENDOF
22 OF S" -1 0 AND . -1 0 OR . 5 3 XOR ." ENDOF
23 OF S" -2147483648 2/ ." ENDOF
ENDCASE ;
CREATE RUN-MATRIX N-RUNS CELLS ALLOT
: MATRIX! ( val idx -- ) CELLS RUN-MATRIX + ! ;
: MATRIX@ ( idx -- val ) CELLS RUN-MATRIX + @ ;
VARIABLE SW-I VARIABLE SW-J VARIABLE SW-VI VARIABLE SW-VJ
: SWAP-MTX ( i j -- )
SW-J ! SW-I !
SW-I @ MATRIX@ SW-VI !
SW-J @ MATRIX@ SW-VJ !
SW-VJ @ SW-I @ MATRIX!
SW-VI @ SW-J @ MATRIX! ;
: INIT-MATRIX ( -- ) N-RUNS 0 DO I I MATRIX! LOOP ;
: SHUFFLE-MATRIX ( -- )
N-RUNS 1 - 0 DO
I N-RUNS 1 - RANDOM
I SWAP-MTX
LOOP ;
VARIABLE CURR-ID
VARIABLE CURR-REP
( Boot-batching support, added 2026-09-11 (FABRIC-3.md SXV). Originally a )
( workaround for a real aarch64 Stadium/COOL scaling bug -- root-caused and )
( fixed 2026-09-11/12 (FABRIC-3.md SXVI, src/starkernel/vm/stadium.c): )
( stadium_grant_quota() halves the granting VM's own free list on every )
( birth with no floor, and stadium_admit()'s eviction fallback used to scan )
( the ENTIRE global cell array (stadium_ncells, in the hundreds of )
( thousands on aarch64's larger RAM-scaled Stadium) filtered by owner, )
( instead of walking just the calling VM's own resident cells as its own )
( doc comment already claimed -- fixed by threading a real per-VM resident )
( list. A single boot with all 9 identities simultaneously live now works )
( cleanly on all three architectures (see results-20260911-stadium-fix/), )
( so ACTIVE-LO/ACTIVE-HI is no longer load-bearing -- kept only as general )
( flexibility: every boot shares the SAME seed so INIT-MATRIX/SHUFFLE-MATRIX )
( produce the identical master 27-slot permutation regardless of how many )
( boots this runs across; only the ACTIVE-LO/ACTIVE-HI filter differs if )
( split. The recorded run_id is always I itself (the slot's true position )
( in the master shuffle), never a separately-incremented counter, so row )
( order stays meaningful and comparable across any number of boots. )
VARIABLE ACTIVE-LO
VARIABLE ACTIVE-HI
: ACTIVE? ( idx -- flag )
DUP ACTIVE-LO @ >=
SWAP ACTIVE-HI @ <=
AND ;
: RUN-TEST ( n -- )
CURR-ID @ 0= IF
RUN-TEST-NATIVE
ELSE
TEST-CMD CURR-ID @ ID-NAME VM-EXEC
THEN ;
VARIABLE CURR-RUN-ID
: RUN-TRIAL ( id-idx rep run-id -- )
CURR-RUN-ID !
CURR-REP ! CURR-ID !
." DOE-RUN," CURR-RUN-ID @ . ." ," CURR-ID @ . ." ,"
CURR-ID @ ID-LABEL TYPE ." ," CURR-REP @ . CR
N-TESTS 0 DO I RUN-TEST LOOP ;
( HB-ON/HB-OFF (doe_log.c) bracket the trial loop below so the per-tick )
( DoE-tagged physics/heartbeat CSV -- 20 columns: tick_number, elapsed_ns, )
( hot_word_count, avg_word_heat_q48, window_width, apic_ticks, )
( hera/hermes/artemis heat_q48, fleet_k_q48, fleet_conserved, etc. -- )
( covers exactly this run's window, not the whole boot. )
( g_doe_log_enabled defaults OFF (doe_log.c), so )
( every prior campaign run's extracted CSV (scripts/extract_doe.sh, run )
( automatically at the end of every `make qemu`) was silently empty; )
( without this bracket there is no per-tick data to analyze the 3x9x3 )
( factorial's physics behavior by, only the DOE-RUN/DOE-HEADER correctness )
( lines already printed below. NOTE: this comment deliberately never )
( spells out the literal tag string doe_log.c prefixes each row with -- )
( writing it here once already corrupted a real extracted CSV live )
( 2026-09-12, since scripts/extract_doe.sh greps the log for exactly that )
( substring and can't tell a real telemetry row from this comment's own )
( compile-time echo saying the same characters. )
: EXEC-STD79-DOE ( seed lo hi -- )
ACTIVE-HI ! ACTIVE-LO !
SEED
INIT-MATRIX SHUFFLE-MATRIX
." DOE-HEADER,run_id,id_idx,id_label,rep" CR
HB-ON
N-RUNS 0 DO
I MATRIX@
DUP N-REPS /
DUP ACTIVE? IF
SWAP N-REPS MOD I RUN-TRIAL
ELSE
DROP DROP
THEN
LOOP
HB-OFF
." STD79-DOE: complete" CR ;
( ============================================================ )
( Compudynamic turn-attractor (FABRIC-3.md SXIX, 2026-09-12). )
( EXEC-STD79-DOE above is strictly serialized: each of the 27 )
( shuffled (identity,rep) cells runs its full 24-test block to )
( completion before the next starts -- confirmed live that this is )
( the real null hypothesis behind SXVIII's K result, not "real )
( concurrent multi-VM load": VM-EXEC's vm_interpret() call blocks )
( the caller until the target returns (mama_forth_words.c), and )
( vm_tick() -- hence vm_physics_heartbeat_tick() -- is driven by )
( each VM's OWN word dispatch (vm_runtime.c), so an idle identity )
( accrues zero ticks between its own turns, not just "not currently )
( executing a word." )
( )
( EXEC-STD79-DOE-CD replaces the fixed shuffle-order sequencing )
( with a turn-attractor: at every step, whichever LIVE identity )
( has the lowest VM-HEAT / tests-remaining-in-its-current-rep gets )
( the next single test dispatched -- the same least-dense-candidate )
( judgment stadium_admit() already trusts for eviction (heat/mass), )
( applied to "whose turn is next" instead of "who gets evicted," )
( with tests-remaining standing in for mass (Stadium's own mass )
( field is hardcoded to 1 for every patron -- see FABRIC-3.md SXIX -- )
( so this computes its own local ratio rather than hijacking that )
( field). Equal-priority candidates are broken by TIE-BREAK, a )
( seeded coin toss (swappable for inference later, not built yet). )
( No fixed round-robin, no priority levels, no preemption -- )
( deliberately not a scheduler. )
( )
( FABRIC-3.md SXX/SXXI (2026-09-12): rebuilt on real messaging. This )
( draft's first pass (still visible in RUN-TEST/EXEC-STD79-DOE's own )
( turn dispatch above, untouched, kept as the historical serialized )
( baseline) and this word's own first cut both dispatched turns via )
( blocking VM-EXEC -- no different from the null hypothesis this )
( whole mechanism exists to move past. Hera can now genuinely message )
( every identity (SXX closed the "Hera can't message" gap), so )
( RUN-REP-CD below queues each turn's whole rep as a real message )
( (MSG-SEND, one per rep -- SXXII cut this from one-message-per-test )
( after a live reservoir probe showed that granularity silently )
( dropped work, see RUN-REP-CD's own comment) and pumps it (MSG-TICK) )
( instead of calling VM-EXEC directly. MSG-DELIVER still calls VM-EXEC under the hood )
( (messaging.4th), so actual instruction execution stays serialized )
( at delivery time -- that's a real constraint of a single-threaded )
( VM, not something this mechanism claims to remove. What changes is )
( who pays and how turns are queued: MSG-ALLOC's STADIUM-RES-PULL )
( pulls Q.SLOT admission heat from the CALLING VM's own reservoir )
( (mama_forth_words.c -- confirmed, not assumed), so queuing a turn )
( costs Hera, the orchestrator, not the identity receiving the turn -- )
( "sender pays," not "winner pays." zuse's (idx 0) tests still run )
( natively on Hera, message-free, for the same reason as before: )
( MSG-DELIVER's own VM-EXEC would be self-targeting into Hera's own )
( input buffer mid-parse, the same reentrancy class the idle-loop )
( pump guards against. The 27-slot run_id/(id_idx,rep) LABEL mapping )
( from SHUFFLE-MATRIX is preserved exactly (REV-MATRIX inverts it) )
( so run_id means the same thing as every prior campaign, even though )
( real execution order is now priority-driven, not shuffle-position- )
( driven. )
CREATE REV-MATRIX N-RUNS CELLS ALLOT
: REV! ( val idx -- ) CELLS REV-MATRIX + ! ;
: REV@ ( idx -- val ) CELLS REV-MATRIX + @ ;
: BUILD-REV-MATRIX ( -- ) N-RUNS 0 DO I I MATRIX@ REV! LOOP ;
CREATE REP-IDX N-ID CELLS ALLOT ( each identity's current/next rep, 0..N-REPS-1; N-REPS = done )
: REP-IDX! ( val idx -- ) CELLS REP-IDX + ! ;
: REP-IDX@ ( idx -- val ) CELLS REP-IDX + @ ;
: TURN-HEAT ( idx -- heat )
DUP 0= IF DROP S" Hera" VM-HEAT
ELSE ID-NAME VM-HEAT THEN ;
( BASE-HEAT: each identity's TURN-HEAT captured once, at campaign )
( start (CAPTURE-BASE-HEAT, called from EXEC-STD79-DOE-CD before the )
( turn loop begins). Live-probed before trusting this design (2026- )
( 09-12, amd64): right after attach, before a single test has run, )
( S" Hera" VM-HEAT . -> 62672, S" rajames" VM-HEAT . -> 0 -- a ~63000x )
( gap. Absolute VM-HEAT mostly encodes how long a VM has existed and )
( how much boot/capsule-load work it already did, not how recently )
( it took a DoE turn: Hera's dictionary alone (fabric.4th, ACL.4th, )
( messaging.4th, etc.) generates heat no identity VM ever will. )
( Dividing that gap by a 1..24 tests-remaining divisor cannot close )
( 4-5 orders of magnitude -- confirmed live: an early two-identity )
( smoke test with the undivided-baseline formula produced zuse )
( running one rep, then rajames sweeping all 3 of its reps back to )
( back before zuse got another turn, not fine-grained alternation. )
( Subtracting each identity's own BASE-HEAT zeroes out the pre- )
( existing disparity: every identity's priority starts at 0 for its )
( very first candidate turn (a genuine N-way tie -- see TIE-BREAK )
( below), and diverges afterward strictly from heat gained during )
( THIS campaign, which is the comparison that was actually intended. )
CREATE BASE-HEAT N-ID CELLS ALLOT
: BASE-HEAT! ( val idx -- ) CELLS BASE-HEAT + ! ;
: BASE-HEAT@ ( idx -- val ) CELLS BASE-HEAT + @ ;
: CAPTURE-BASE-HEAT ( -- )
N-ID 0 DO I REP-IDX@ N-REPS < IF I DUP TURN-HEAT SWAP BASE-HEAT! THEN LOOP ;
( reps left for idx, ranges 1..N-REPS whenever this is called -- )
( PICK-NEXT-TURN only ever evaluates idx's still REP-IDX@ < N-REPS. )
( SXXII (2026-09-12): reps, not tests, are this campaign's real turn )
( granularity -- see the RUN-REP-CD comment below for why a whole )
( rep is dispatched as one message instead of 24 single-test )
( messages, confirmed live via a direct reservoir probe. )
: REPS-REMAINING@ ( idx -- n ) REP-IDX@ N-REPS SWAP - ;
( priority = (heat - base-heat) / reps-remaining -- heat gained )
( THIS campaign so far, per rep still owed -- same "coolest wins" )
( direction as stadium_admit()'s heat/mass density, with reps- )
( remaining standing in for mass (Stadium's own mass field is )
( hardcoded to 1 for every patron -- see FABRIC-3.md SXIX -- so this )
( computes its own local ratio rather than hijacking that field). )
( Plain integer "/" is correct even though heat is Q48.16 fixed- )
( point: dividing a Q48.16 value by a plain integer divisor )
( preserves the Q48.16 scale, no Q./ needed. )
: TURN-PRIORITY ( idx -- priority )
DUP TURN-HEAT OVER BASE-HEAT@ - SWAP REPS-REMAINING@ / ;
( seeded coin toss (RANDOM draws off the same SEED'd stream )
( EXEC-STD79-DOE-CD's SHUFFLE-MATRIX already consumes, so the whole )
( run stays reproducible run-to-run) -- swappable later for a )
( rolling-window-of-truth inference, per Bob: "start with a coin )
( toss or go straight to inference?" -- coin toss first. )
: TIE-BREAK ( idx1 idx2 -- idx )
0 1 RANDOM IF SWAP THEN DROP ;
VARIABLE BEST-IDX VARIABLE BEST-PRI
: PICK-NEXT-TURN ( -- idx )
-1 BEST-IDX !
N-ID 0 DO
I REP-IDX@ N-REPS < IF
I TURN-PRIORITY
BEST-IDX @ -1 = IF
BEST-PRI ! I BEST-IDX !
ELSE
DUP BEST-PRI @ = IF
DROP BEST-IDX @ I TIE-BREAK BEST-IDX !
ELSE
DUP BEST-PRI @ < IF BEST-PRI ! I BEST-IDX ! ELSE DROP THEN
THEN
THEN
THEN
LOOP
BEST-IDX @ ;
VARIABLE SR-IDX VARIABLE SR-REP VARIABLE SR-RUNID
: START-REP ( idx -- )
SR-IDX !
SR-IDX @ REP-IDX@ SR-REP !
SR-IDX @ N-REPS * SR-REP @ + REV@ SR-RUNID !
." DOE-RUN," SR-RUNID @ . ." ," SR-IDX @ . ." ,"
SR-IDX @ ID-LABEL TYPE ." ," SR-REP @ . CR ;
( message-type tag for a queued turn's rep command. Past the )
( highest constant messaging.4th itself defines (BLK-ATTACH-EVENT=9); )
( kept local to this file rather than added to the shared common )
( capsule, since it's specific to this campaign's own dispatch, not )
( general messaging infrastructure. )
10 CONSTANT DOE-TEST-EVENT
( REP-CMD-BUF: scratch buffer holding one whole rep's 24 test )
( commands concatenated with spaces (BUILD-REP-CMD). 442 bytes for )
( the real 24-test set, measured; 1024 leaves ample headroom and )
( stays well under mama_word_vm_exec's own INPUT_BUFFER_SIZE (1025) )
( refusal threshold (checked in the C source, not assumed -- it )
( prints "VM-EXEC: command too long" and returns cleanly rather than )
( truncating, so an oversized rep would fail loudly, not silently). )
CREATE REP-CMD-BUF 1024 ALLOT
VARIABLE REP-CMD-LEN
: REP-CMD-RESET ( -- ) 0 REP-CMD-LEN ! ;
: REP-CMD-APPEND ( addr u -- )
>R
REP-CMD-BUF REP-CMD-LEN @ + R@ CMOVE
REP-CMD-LEN @ R> + REP-CMD-LEN !
REP-CMD-BUF REP-CMD-LEN @ + 32 SWAP C!
1 REP-CMD-LEN +! ;
: BUILD-REP-CMD ( -- )
REP-CMD-RESET
N-TESTS 0 DO I TEST-CMD REP-CMD-APPEND LOOP ;
( RUN-REP-CD ( idx -- ): the messaging-based, whole-rep sibling of )
( RUN-TRIAL -- RUN-TEST/RUN-TRIAL/EXEC-STD79-DOE (the serialized )
( baseline above) are deliberately left untouched, so that campaign )
( stays a byte-for-byte-reproducible historical baseline, not )
( silently migrated onto messaging along with this one. )
( )
( SXXII (2026-09-12): this dispatches a WHOLE REP (all 24 tests, one )
( concatenated command string) per message, not one message per )
( single test as the first cut of this word did. Confirmed live, )
( directly, why that had to change: a probe of STADIUM-RES@ (Hera's )
( own reservoir, the thing MSG-ALLOC's STADIUM-RES-PULL draws every )
( MSG-SEND from) showed it draining by roughly Q.SLOT-scale per )
( send/tick cycle with no observed replenishment during the run -- )
( 21760 -> 13811 over just 11 send+tick cycles in one live probe. A )
( full 9-identity x 3-rep x 24-test campaign at single-test )
( granularity is 648 sends, which exhausts that budget almost )
( immediately: an actual two-identity smoke run at that granularity )
( silently dropped roughly half of every rep's tests (MSG-SEND no- )
( ops on MSG-ALLOC failure, and STEP-TURN's old per-test bookkeeping )
( advanced regardless, so the row looked complete when it wasn't). )
( Batching to one message per rep cuts a full campaign to 27 sends )
( total -- comfortably inside the reservoir budget the probe )
( measured -- while still keeping genuine attractor behavior: )
( PICK-NEXT-TURN still re-evaluates priority between every rep, so )
( which identity goes next is still heat-driven, not shuffle-order- )
( driven, just at rep granularity instead of single-test granularity. )
( zuse (CURR-ID = 0) still runs natively, unchanged, same )
( reentrancy reasoning as RUN-TEST's. )
: RUN-REP-NATIVE ( -- ) N-TESTS 0 DO I RUN-TEST-NATIVE LOOP ;
: RUN-REP-CD ( idx -- )
DUP CURR-ID !
0= IF
RUN-REP-NATIVE
ELSE
BUILD-REP-CMD
DOE-TEST-EVENT 0 CURR-ID @ DOE-IDX>MSG-IDX
REP-CMD-BUF REP-CMD-LEN @
0
MSG-SEND
MSG-TICK
THEN ;
: STEP-TURN ( idx -- )
DUP RUN-REP-CD
DUP REP-IDX@ 1+ OVER REP-IDX!
DUP REP-IDX@ N-REPS < IF DUP START-REP THEN
DROP ;
: INIT-TURN-STATE ( -- )
N-ID 0 DO
I ACTIVE? IF 0 I REP-IDX! ELSE N-REPS I REP-IDX! THEN
LOOP ;
: PRINT-INITIAL-HEADERS ( -- )
N-ID 0 DO I REP-IDX@ N-REPS < IF I START-REP THEN LOOP ;
: EXEC-STD79-DOE-CD ( seed lo hi -- )
ACTIVE-HI ! ACTIVE-LO !
SEED
INIT-MATRIX SHUFFLE-MATRIX
BUILD-REV-MATRIX
INIT-TURN-STATE
CAPTURE-BASE-HEAT
." DOE-HEADER,run_id,id_idx,id_label,rep" CR
HB-ON
PRINT-INITIAL-HEADERS
BEGIN
PICK-NEXT-TURN DUP -1 <>
WHILE
STEP-TURN
REPEAT
DROP
HB-OFF
." STD79-DOE: complete" CR ;