From 8ea3e492282f6a9baaab746bd8afe5a4313dcf7a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 12:35:13 +0700 Subject: [PATCH 1/5] t27o: tri t27o file.zig --check judges the spec by its own tests, --oracle says it does not apply (Closes #10823) --check on a .zig file used to run the Rust oracle and fail with "rustc refused the oracle harness". It now writes the converted spec to specs/t27o_check/.t27 and judges it as #10538 judges Rust specs (t27b test first, t27c test-report only when t27b's exit code is no verdict; t27b_outcome and needs_reference are reused, not copied). --oracle on a .zig file prints one line, that the file's own tests are the oracle, and exits 0. A spec with no converted test passes t27b test as 0 of 0, which is no verdict (#10497): has_evidence refuses it, exit 6 (EXIT_UNJUDGED, outside t27b's table). A Rust file keeps its oracle and is unchanged. ledger.t27 gains front_end (the extension rule `tri t27o` and the sweep both ask) and has_evidence, with tests and negative controls. zig_convert.t27 loses is_zig_name, which front_end replaces (one rule, not two). main.rs: +15 / -17 against the merge base (the own-language budget holds; rc=0). Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oLedger.json | 18 +++---- .trinity/seals/t27o_T27oZig.json | 16 +++--- cli/tri/src/main.rs | 26 +++++----- gen/rust/tools/t27o/ledger.rs | 29 ++++++++++- gen/rust/tools/t27o/zig_convert.rs | 14 +----- specs/tools/t27o/ledger.t27 | 76 +++++++++++++++++++++++++++++ specs/tools/t27o/zig_convert.t27 | 20 +------- 7 files changed, 135 insertions(+), 64 deletions(-) diff --git a/.trinity/seals/t27o_T27oLedger.json b/.trinity/seals/t27o_T27oLedger.json index 0d70e686d7..033c1749ad 100644 --- a/.trinity/seals/t27o_T27oLedger.json +++ b/.trinity/seals/t27o_T27oLedger.json @@ -1,5 +1,5 @@ { - "built_by": "t27c-bootstrap@0.5.2+9b556b7c3", + "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [ { "spec": "specs/tri/lab/receipt.t27", @@ -15,23 +15,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:428200eaf55079318d360ecec24a359a21320f1f64f94c50001bb93159d8f882", - "gen_hash_rust": "sha256:02f9f45cec23ed1149a09edd668dfc48aef3b4d83d0ad7a1f0709a564fd6d3c5", - "gen_hash_verilog": "sha256:7d461b24959b4ee95bcb3617f2a41e5eaac048bf7640f8a467f7e60b6db1c3b8", - "gen_hash_zig": "sha256:174c0e2cf57c8647ba48908f90aaaf5f87456ddbae3346f0db0550bbd8c57026", + "gen_hash_c": "sha256:5783f096926c94781513b0295add4dbfc70be51dcede5269c2ec4caa5fe327d4", + "gen_hash_rust": "sha256:396624fcfd4ebbebc29932f03abed33b2082319a15622db7b47fa561f68c61af", + "gen_hash_verilog": "sha256:1d2097caa4a792a9d940ace84cf66857ad5aefd3f6bcc5a4057489b82bc3b1ca", + "gen_hash_zig": "sha256:d7f52889ac3abba5fd0da4ffe8f439862710d42f230c7b9b7bb7d3e5d537b386", "module": "T27oLedger", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T21:16:15Z", + "sealed_at": "2026-10-19T05:32:28Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:98b04611fe2950faac8f137f1dbc7be83d595049988dbb13a520d840e4b09dd6", + "spec_hash": "sha256:03124fccff840617f9b993597e92264e56ddf58eb47cefbdb905caa210ff554d", "spec_path": "specs/tools/t27o/ledger.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 29, - "total": 29, + "passed": 34, + "total": 34, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json index 92ce0b6a27..6533b89261 100644 --- a/.trinity/seals/t27o_T27oZig.json +++ b/.trinity/seals/t27o_T27oZig.json @@ -7,23 +7,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:1902658b3fdd4234f5995a344bff8e880b6469635ea1fb0af023899d9cc6ead8", - "gen_hash_rust": "sha256:b804e5f4927cb4226bd6623c919e290f26a54d42d2c09ae9b949daeb1f00ee46", - "gen_hash_verilog": "sha256:1824c0407ae4c0cf97e7afb4b103ad6adcc20052636dbad081dd9330c1439e8e", - "gen_hash_zig": "sha256:c6e3f12811dd2eeaab6f29949789c444d9b65bdfa683d7a9d2ddb46d0359b906", + "gen_hash_c": "sha256:b0777efcba3b90f8301ef633ce6aed654e3a8d180ab0b91a0e102411ed13c6ec", + "gen_hash_rust": "sha256:259873cc878b01225a8e39c3b25d5f366da891e870c94860244ac86eba8c7c5b", + "gen_hash_verilog": "sha256:981a767c68f6b9a429a75e6c7918d7d8b593446faae93f61bd4b97ba0da071ab", + "gen_hash_zig": "sha256:f5e9c245382d426ecc79f1f61576677e2f2501335a5861126a941187b3533ddf", "module": "T27oZig", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T03:31:24Z", + "sealed_at": "2026-10-19T05:32:14Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:a21f02d266648e370fcaf0e2d9cc50f30b4b23277caa3dc4a625369a1b4c59e0", + "spec_hash": "sha256:46f003442a62ca78dd303c469c07f17539af55000686af5d3fe74b8452228c9b", "spec_path": "specs/tools/t27o/zig_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 45, - "total": 45, + "passed": 44, + "total": 44, "vacuous": 0 }, "toolchain": { diff --git a/cli/tri/src/main.rs b/cli/tri/src/main.rs index 2e8c36a72a..8cca8a5ce3 100644 --- a/cli/tri/src/main.rs +++ b/cli/tri/src/main.rs @@ -56,12 +56,10 @@ mod gen_cache; // the gen-hash cache's eviction limit, for `tri hot caches` (#89 mod bit_cache; // the bitstream cache's eviction limit, for `tri hot caches` (#8952) #[path = "../../../gen/rust/tri/hot/hot.rs"] #[allow(dead_code, unused_parens)] mod hot; // t27c gen-rust of specs/tri/hot/hot.t27 (#8870): every rule of `tri hot` -#[path = "../../../gen/rust/tools/t27o/ledger.rs"] #[allow(dead_code, unused_parens, clippy::all)] -mod t27o_ledger; // t27c gen-rust of specs/tools/t27o/ledger.t27: which runner's exit code is a verdict for `tri t27o --check` (#10525) +#[path = "../../../gen/rust/tools/t27o/ledger.rs"] #[allow(dead_code, unused_parens, clippy::all)] mod t27o_ledger; // t27c gen-rust of specs/tools/t27o/ledger.t27: which runner's exit code is a verdict for `tri t27o --check` (#10525), which front end reads a file, what a .zig file is judged by (#10823) #[path = "../../../gen/rust/tri/lanes/lanes.rs"] #[allow(dead_code, unused_parens)] mod lanes; // t27c gen-rust of specs/tri/lanes/lanes.t27: every rule of `tri lanes` -#[path = "../../../gen/rust/tools/t27o/rust_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] -mod t27o; // t27c gen-rust of specs/tools/t27o/rust_convert.t27: every rule of `tri t27o` (#8967) +#[path = "../../../gen/rust/tools/t27o/rust_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod t27o; // t27c gen-rust of specs/tools/t27o/rust_convert.t27: every rule of `tri t27o` (#8967) #[path = "../../../gen/rust/tools/t27o/spice_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod spice; // t27c gen-rust of specs/tools/t27o/spice_convert.t27: the SPICE front end of `tri t27o` (#10509) #[path = "../../../gen/rust/tools/t27o/zig_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod zig; // t27c gen-rust of specs/tools/t27o/zig_convert.t27: the Zig front end of `tri t27o` (#10765) mod renum; @@ -432,7 +430,7 @@ enum Commands { /// Print one JSON object instead of the table. #[arg(long)] json: bool, }, - /// t27o (Ouroboros): a Rust file as a t27 spec; --oracle prints the rustc harness, --check runs it and t27c test-report on the spec and its oracle tests (rules: specs/tools/t27o/rust_convert.t27). A .spice/.cir/.sp netlist prints its inventory or the refusal, and with --emit-cell the constants of the M1 linear cell folded from it (rules: specs/tools/t27o/spice_convert.t27). A .zig file prints the spec and its count line (rules: specs/tools/t27o/zig_convert.t27); its own tests are the oracle, so run `t27b test` on the spec (--oracle and --check are for Rust). + /// t27o (Ouroboros): a Rust file as a t27 spec; --oracle prints the rustc harness, --check runs it and t27c test-report on the spec and its oracle tests (rules: specs/tools/t27o/rust_convert.t27). A .spice/.cir/.sp netlist prints its inventory or the refusal, and with --emit-cell the constants of the M1 linear cell folded from it (rules: specs/tools/t27o/spice_convert.t27). A .zig file prints the spec and its count line (rules: specs/tools/t27o/zig_convert.t27); its own tests are the oracle, so --check writes the spec and judges it by them, and --oracle says it does not apply. T27o { file: String, /// The module name; default the file's stem. @@ -1364,22 +1362,22 @@ fn cmd_t27o(file: &str, name: Option<&str>, oracle: bool, check: bool, t27c: Opt return Ok(spice::exit_code(&st)); } let stem = name.map_or_else(|| Path::new(file).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()), str::to_string); + let zig = t27o_ledger::front_end(file.as_bytes()) == t27o_ledger::FE_ZIG; if zig && oracle { println!("{}", t27o_ledger::NOTE_ZIG_ORACLE); return Ok(0); } // a .zig file: its own tests are the oracle (ledger.t27, #10823) let (mut out, mut st, mut spec) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], vec![0u8; 1 << 24]); - let n = if zig::is_zig_name(file.as_bytes()) { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else if oracle || check { t27o::oracle(&src, &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; + let mut n = if zig { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else if oracle || check { t27o::oracle(&src, &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; if !check { std::io::Write::write_all(&mut std::io::stdout(), &out[..n.min(out.len())])?; return Ok(0); } // the harness sits next to the source, so its #[path] mods resolve; the spec under specs/, so its `use`s do let (h, exe, path) = (Path::new(file).with_file_name("zz_t27o_oracle.rs"), std::env::temp_dir().join("t27o_oracle"), PathBuf::from(format!("specs/t27o_check/{stem}.t27"))); - fs::write(&h, &out[..n.min(out.len())])?; - let built = Command::new("rustc").args(["--edition", "2021", "-o"]).arg(&exe).arg(&h).status(); - fs::remove_file(&h)?; - anyhow::ensure!(built?.success(), "rustc refused the oracle harness of {file}"); - let m = t27o::convert(&src, stem.as_bytes(), &mut spec, &mut st); - fs::create_dir_all("specs/t27o_check")?; - fs::write(&path, [&spec[..m.min(spec.len())], &Command::new(&exe).output()?.stdout[..]].concat())?; - println!("{}", path.display()); + if !zig { // a Rust file: the harness rustc builds is run and its samples follow the spec + fs::write(&h, &out[..n.min(out.len())])?; let built = Command::new("rustc").args(["--edition", "2021", "-o"]).arg(&exe).arg(&h).status(); fs::remove_file(&h)?; + anyhow::ensure!(built?.success(), "rustc refused the oracle harness of {file}"); + let m = t27o::convert(&src, stem.as_bytes(), &mut spec, &mut st); out = [&spec[..m.min(spec.len())], &Command::new(&exe).output()?.stdout[..]].concat(); n = out.len(); + } + fs::create_dir_all("specs/t27o_check")?; fs::write(&path, &out[..n.min(out.len())])?; println!("{}", path.display()); + if !t27o_ledger::has_evidence(zig, st[zig::Z_NTEST]) { eprintln!("{}", t27o_ledger::NOTE_ZIG_UNJUDGED); return Ok(t27o_ledger::EXIT_UNJUDGED as u8); } let b = ["target/release/t27b", "target/debug/t27b"].into_iter().find(|p| Path::new(p).exists()).unwrap_or("t27b"); // t27b judges first, t27c only when its exit is no verdict (specs/tools/t27o/ledger.t27, #10525) let c = Command::new(b).arg("test").arg(&path).status().ok().and_then(|s| s.code()).unwrap_or(-1); if !t27o_ledger::needs_reference(t27o_ledger::t27b_outcome(c)) { return Ok(c as u8); } eprintln!("t27o: {b} test exited {c} ({}), so t27c test-report judges instead", t27o_ledger::ORC_NAMES[t27o_ledger::t27b_outcome(c) as usize]); diff --git a/gen/rust/tools/t27o/ledger.rs b/gen/rust/tools/t27o/ledger.rs index 7bd930bc18..5f6e791fdb 100644 --- a/gen/rust/tools/t27o/ledger.rs +++ b/gen/rust/tools/t27o/ledger.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 29 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 34 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -78,6 +78,33 @@ pub const T27B_DISAGREE: i32 = 4; pub const T27B_CODEGEN_LIMIT: i32 = 5; +pub const FE_RUST: u32 = 0; + +pub const FE_ZIG: u32 = 1; + +pub const EXIT_UNJUDGED: i32 = 6; + +pub const NOTE_ZIG_ORACLE: &'static str = "t27o: --oracle does not apply to a .zig file: its own tests are the oracle, and --check runs them on the spec"; + +pub const NOTE_ZIG_UNJUDGED: &'static str = "t27o: no test of this .zig file converted, so nothing judges the spec; a spec that ran no test is not a pass"; + +pub fn front_end(path: &[u8]) -> u32 { + if (path.len() < 4) { + return FE_RUST; + } + if ((((path[((path.len() - 4)) as usize] == 46) && (path[((path.len() - 3)) as usize] == 122)) && (path[((path.len() - 2)) as usize] == 105)) && (path[((path.len() - 1)) as usize] == 103)) { + return FE_ZIG; + } + return FE_RUST; +} + +pub fn has_evidence(is_zig: bool, tests_converted: usize) -> bool { + if (is_zig == false) { + return true; + } + return (tests_converted > 0); +} + pub fn t27b_outcome(code: i32) -> u32 { if (code == T27B_PASS) { return ORC_PASS; diff --git a/gen/rust/tools/t27o/zig_convert.rs b/gen/rust/tools/t27o/zig_convert.rs index df27576097..e4138368bb 100644 --- a/gen/rust/tools/t27o/zig_convert.rs +++ b/gen/rust/tools/t27o/zig_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 45 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 44 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -72,10 +72,6 @@ pub const Z_B_TILDE: u8 = 126; pub const Z_B_P: u8 = 112; -pub const Z_B_I: u8 = 105; - -pub const Z_B_G: u8 = 103; - pub fn z_tok_end(src: &[u8], p: usize) -> usize { if (p >= src.len()) { return p; @@ -3321,14 +3317,6 @@ pub fn z_item(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { st[(P) as usize] = skip_ws(src, end); } -pub fn is_zig_name(name: &[u8]) -> bool { - if (name.len() < 4) { - return false; - } - let f: usize = (name.len() - 4); - return ((((name[(f) as usize] == B_DOT) && (name[((f + 1)) as usize] == B_LOWER_Z)) && (name[((f + 2)) as usize] == Z_B_I)) && (name[((f + 3)) as usize] == Z_B_G)); -} - pub fn z_ci_at(src: &[u8], i: usize, s: &[u8]) -> bool { if ((i + s.len()) > src.len()) { return false; diff --git a/specs/tools/t27o/ledger.t27 b/specs/tools/t27o/ledger.t27 index db1bf7667c..357bb4c7d4 100644 --- a/specs/tools/t27o/ledger.t27 +++ b/specs/tools/t27o/ledger.t27 @@ -97,6 +97,37 @@ pub const T27B_FRONT_END : i32 = 3; pub const T27B_DISAGREE : i32 = 4; pub const T27B_CODEGEN_LIMIT : i32 = 5; +; --- Which front end reads a file, and what `--check` and `--oracle` mean for a Zig file (#10823) ---------- +; By its extension: a .zig file goes to zig_convert.t27, any other file to rust_convert.t27 (a netlist is +; spice_convert.t27's, and `tri t27o` sends it there before it asks). `tri t27o` and `tri t27o-sweep` both ask +; front_end, so the two cannot disagree about a file. +; A Rust file is judged twice: by its translated tests and by an oracle, the original compiled by rustc and run on +; sampled inputs. A Zig file has no such harness: its own tests ARE the oracle, translated into the spec, and +; `t27b test` runs them (judged as above, t27b_outcome and needs_reference). So `--oracle` has nothing to run for a +; Zig file, and `--check` has evidence only when at least one test of the file converted: a spec with none passes +; `t27b test` as 0 of 0, and a pass that ran nothing is no verdict (#10497; oracle_outcome with nothing sampled is +; ORC_SKIPPED, never ORC_PASS). EXIT_UNJUDGED is outside t27b's table (0 .. 5), so no verdict of t27b is mistaken for it. + +pub const FE_RUST : u32 = 0; +pub const FE_ZIG : u32 = 1; +pub const EXIT_UNJUDGED : i32 = 6; +pub const NOTE_ZIG_ORACLE : str = "t27o: --oracle does not apply to a .zig file: its own tests are the oracle, and --check runs them on the spec"; +pub const NOTE_ZIG_UNJUDGED : str = "t27o: no test of this .zig file converted, so nothing judges the spec; a spec that ran no test is not a pass"; + +; The front end of a path, by its last four bytes: ".zig" is the Zig one, everything else the Rust one. +pub fn front_end(path: []const u8) -> u32 { + if (path.len < 4) { return FE_RUST; } + if (path[path.len - 4] == 46 and path[path.len - 3] == 122 and path[path.len - 2] == 105 and path[path.len - 1] == 103) { return FE_ZIG; } + return FE_RUST; +} + +; Does `--check` have anything to judge the spec by? A Rust file always has its oracle; a Zig file has the tests that +; converted and nothing else. +pub fn has_evidence(is_zig: bool, tests_converted: usize) -> bool { + if (is_zig == false) { return true; } + return tests_converted > 0; +} + ; --- Decisions ------------------------------------------------------------------------------- ; The oracle outcome of one `t27b test` run of a converted spec, from its exit code. A construct @@ -638,3 +669,48 @@ test the_seals_are_read_from_the_checkout_and_a_missing_one_says_so { assert(span_is(SEAL_HINT, 0, SEAL_HINT.len, "run from a t27 checkout")); assert(SEAL_CONVERTER.len != SEAL_STD.len); } + +; --- Tests: the front end of a file, and `--check` / `--oracle` of a Zig file (#10823) ----------------- + +test a_file_ending_in_dot_zig_goes_to_the_zig_front_end_and_any_other_to_the_rust_one { + assert(front_end("a.zig") == FE_ZIG); + assert(front_end("src/x/y.zig") == FE_ZIG); + assert(front_end("/srv/checkouts/trinity/src/vsa/ops.zig") == FE_ZIG); + assert(front_end("a.rs") == FE_RUST); + assert(front_end("cli/tri/src/main.rs") == FE_RUST); + assert(FE_ZIG != FE_RUST); +} + +test negative_control_a_name_that_only_contains_zig_is_not_a_zig_file { + assert(front_end("") == FE_RUST); + assert(front_end("zig") == FE_RUST); + assert(front_end(".zi") == FE_RUST); + assert(front_end("a.zig.rs") == FE_RUST); + assert(front_end("a.zigg") == FE_RUST); + assert(front_end("azig") == FE_RUST); + assert(front_end("a.ZIG") == FE_RUST); + assert(front_end("a.cir") == FE_RUST); + assert(front_end("zig/a.rs") == FE_RUST); +} + +test a_zig_file_is_judged_by_the_tests_that_converted_and_a_rust_file_by_its_oracle { + assert(has_evidence(true, 1)); + assert(has_evidence(true, 600)); + assert(has_evidence(false, 0)); + assert(has_evidence(false, 3)); +} + +test negative_control_a_zig_spec_with_no_converted_test_has_nothing_to_judge_it_by { + assert(has_evidence(true, 0) == false); + assert(t27b_outcome(T27B_PASS) == ORC_PASS); + assert(EXIT_UNJUDGED > T27B_CODEGEN_LIMIT); + assert(t27b_outcome(EXIT_UNJUDGED) == ORC_UNKNOWN); + assert(needs_reference(t27b_outcome(EXIT_UNJUDGED))); +} + +test the_notes_of_a_zig_file_say_what_does_not_apply_and_what_ran_nothing { + assert(starts_at(NOTE_ZIG_ORACLE, 0, "t27o: --oracle does not apply to a .zig file")); + assert(starts_at(NOTE_ZIG_UNJUDGED, 0, "t27o: no test of this .zig file converted")); + assert(NOTE_ZIG_ORACLE.len > 0); + assert(NOTE_ZIG_UNJUDGED.len > 0); +} diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 index 8c673a0eff..642f856a40 100644 --- a/specs/tools/t27o/zig_convert.t27 +++ b/specs/tools/t27o/zig_convert.t27 @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // specs/tools/t27o/zig_convert.t27 -- t27o (Ouroboros), its Zig front end (#10765, step 1 of #10597, option B of #10522): // hand-written Zig becomes a t27 spec, judged by the file's own `test` blocks. It sits beside rust_convert.t27 and -// spice_convert.t27 and is reached by file extension: `tri t27o file.zig` prints the spec and the count line. +// spice_convert.t27 and is reached by file extension (ledger.t27 front_end): `tri t27o file.zig` prints the spec and the count line. // -- // The contract is the Rust front end's: an item converts exactly or is refused with its reason, never approximated. A // refused item is a comment where it stood, `// t27o: refused fn NAME (line N): `` at line M`, and the last @@ -149,8 +149,6 @@ pub const Z_NAME_SLOTS : usize = 5; pub const Z_B_AT : u8 = 64; pub const Z_B_TILDE : u8 = 126; pub const Z_B_P : u8 = 112; -pub const Z_B_I : u8 = 105; -pub const Z_B_G : u8 = 103; // ---------------------------------------------------------------- tokens @@ -2874,13 +2872,6 @@ pub fn z_item(src: []const u8, st: []usize, out: []u8) -> void { // ---------------------------------------------------------------- the file -/// Does a file name end in .zig? `tri t27o` sends such a file here. -pub fn is_zig_name(name: []const u8) -> bool { - if (name.len < 4) { return false; } - const f : usize = name.len - 4; - return name[f] == B_DOT and name[f + 1] == B_LOWER_Z and name[f + 2] == Z_B_I and name[f + 3] == Z_B_G; -} - /// Is the text s at src[i], whatever the case? pub fn z_ci_at(src: []const u8, i: usize, s: []const u8) -> bool { if (i + s.len > src.len) { return false; } @@ -3286,15 +3277,6 @@ test the_tokenizer_reads_zig_operators_whole { assert(z_tok_end("\\\\line", 0) == 1); } -test a_file_name_ending_in_dot_zig_is_a_zig_file { - assert(is_zig_name("a.zig")); - assert(is_zig_name("src/x/y.zig")); - assert(!is_zig_name("a.zig.rs")); - assert(!is_zig_name("zig")); - assert(!is_zig_name("a.rs")); - assert(!is_zig_name("a.cir")); -} - test a_generator_mark_in_the_first_20_lines_marks_the_file { assert(z_generated("// Generated from specs/x.tri\nfn f() void {}\n")); assert(z_generated("// DO NOT EDIT\n")); From ba35877ebbb458bc5600f7c9756fcdd9d7399992 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 12:45:10 +0700 Subject: [PATCH 2/5] t27o: tri t27o-sweep lists .zig beside .rs, a Zig row is keyed by zig_convert.t27's seal too (Closes #10823) sweep_path (ledger.t27) takes a .zig file under the same rule as a .rs one (nothing under gen/ or target/). The glue picks the front end by extension through ledger.t27's front_end, the same rule `tri t27o file.zig` asks. A Zig row's converter key is the Zig seal's spec_hash joined to the Rust converter's (zig_convert.t27 uses rust_convert.t27's helpers), so a change to either makes every Zig row stale and no Rust row: checked by editing the Zig seal, 346 of 1105 trios rows went cold, 759 were reused. zig_convert.t27 numbers its five counters like rust_convert.t27's report (NFN, NOK, NTS, NTEST, NSTUB), so the sweep reads one set of slots for either front end; the output of all 2573 real .zig files is byte-identical to before the renumbering. Two tests pin the slots. kind_to now stops at a semicolon as well as a backtick: the cache row separates kinds with ';', and "generated; convert its source spec instead" was cut in two and counted as its second half. It is now one kind, "generated", with its own count. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oLedger.json | 16 ++++----- .trinity/seals/t27o_T27oZig.json | 18 +++++----- cli/tri/src/main.rs | 14 ++++---- gen/rust/tools/t27o/ledger.rs | 8 +++-- gen/rust/tools/t27o/zig_convert.rs | 26 +++++++++----- specs/tools/t27o/ledger.t27 | 55 ++++++++++++++++++++++++++--- specs/tools/t27o/zig_convert.t27 | 44 +++++++++++++++++++---- 7 files changed, 135 insertions(+), 46 deletions(-) diff --git a/.trinity/seals/t27o_T27oLedger.json b/.trinity/seals/t27o_T27oLedger.json index 033c1749ad..2e0bdbe576 100644 --- a/.trinity/seals/t27o_T27oLedger.json +++ b/.trinity/seals/t27o_T27oLedger.json @@ -15,23 +15,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:5783f096926c94781513b0295add4dbfc70be51dcede5269c2ec4caa5fe327d4", - "gen_hash_rust": "sha256:396624fcfd4ebbebc29932f03abed33b2082319a15622db7b47fa561f68c61af", - "gen_hash_verilog": "sha256:1d2097caa4a792a9d940ace84cf66857ad5aefd3f6bcc5a4057489b82bc3b1ca", - "gen_hash_zig": "sha256:d7f52889ac3abba5fd0da4ffe8f439862710d42f230c7b9b7bb7d3e5d537b386", + "gen_hash_c": "sha256:9d516eccbc6e4404953f1f5c85c3fcce8c3e0abe08bcca15cfa0e5abb70a5185", + "gen_hash_rust": "sha256:1b30b3432b64262d5c3e42e5fbec57937afbc0c0b31fbc2ccf27a11513431955", + "gen_hash_verilog": "sha256:744dab1dd12a361c179b15417e0feebebaef4cd8b3c7044c0452458c5ba43cba", + "gen_hash_zig": "sha256:274bbd97674fbb7f4d3e100a400e64e10861c1651dd961a31ad9335690a124c9", "module": "T27oLedger", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:32:28Z", + "sealed_at": "2026-10-19T05:41:02Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:03124fccff840617f9b993597e92264e56ddf58eb47cefbdb905caa210ff554d", + "spec_hash": "sha256:1ca252b0187eb7b996e2e1f0c5331fe53c0db65b3f4aa78f85a3eb5d0704b023", "spec_path": "specs/tools/t27o/ledger.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 34, - "total": 34, + "passed": 38, + "total": 38, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json index 6533b89261..fbd05ec4dc 100644 --- a/.trinity/seals/t27o_T27oZig.json +++ b/.trinity/seals/t27o_T27oZig.json @@ -7,24 +7,24 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:b0777efcba3b90f8301ef633ce6aed654e3a8d180ab0b91a0e102411ed13c6ec", - "gen_hash_rust": "sha256:259873cc878b01225a8e39c3b25d5f366da891e870c94860244ac86eba8c7c5b", - "gen_hash_verilog": "sha256:981a767c68f6b9a429a75e6c7918d7d8b593446faae93f61bd4b97ba0da071ab", - "gen_hash_zig": "sha256:f5e9c245382d426ecc79f1f61576677e2f2501335a5861126a941187b3533ddf", + "gen_hash_c": "sha256:a75be5c7a7ff3c9c6b8cf4a8b700de0a94048c66c503bf0a55e65e866684c80f", + "gen_hash_rust": "sha256:3da64c7f9a150b14e77e1cb3b33a7a134b0932f64604fe457fb200e14ed9ad19", + "gen_hash_verilog": "sha256:70a92c566efb6f1a50e9612de03eacaa72c0310702ec24887c61ffeedb5f0cfb", + "gen_hash_zig": "sha256:923147d74c971f64b7b93fe1de72233d5762c30292831a669a49a6605f14eb92", "module": "T27oZig", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:32:14Z", + "sealed_at": "2026-10-19T05:37:36Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:46f003442a62ca78dd303c469c07f17539af55000686af5d3fe74b8452228c9b", + "spec_hash": "sha256:ae824d39ad7d1df73ca1446a0b5b4447b3fc975021a6767be1e7b7c9aa946de6", "spec_path": "specs/tools/t27o/zig_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 44, - "total": 44, - "vacuous": 0 + "passed": 46, + "total": 46, + "vacuous": 1 }, "toolchain": { "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", diff --git a/cli/tri/src/main.rs b/cli/tri/src/main.rs index 8cca8a5ce3..01c8f4f8a7 100644 --- a/cli/tri/src/main.rs +++ b/cli/tri/src/main.rs @@ -443,7 +443,7 @@ enum Commands { /// A netlist: print the M1 cell constants (CELL_N, CELL_D, CELL_NV) folded from it, or the refusal, instead of the inventory. #[arg(long)] emit_cell: bool, }, - /// t27o over a tree (#10543): every tracked .rs file converted in-process; a file's row is reused while its key holds (its SHA-256 and the seals of rust_convert.t27 and rust_std.t27, read from the t27 checkout this runs in); `--repo ` sweeps another checkout, with a cache of its own (#10638); counts, coverage and the ranked first-refusal kinds (rules: specs/tools/t27o/ledger.t27). + /// t27o over a tree (#10543): every tracked .rs and .zig file converted in-process; a file's row is reused while its key holds (its SHA-256 and the seals of rust_convert.t27 and rust_std.t27, and of zig_convert.t27 for a .zig file, read from the t27 checkout this runs in); `--repo ` sweeps another checkout, with a cache of its own (#10638); counts, coverage and the ranked first-refusal kinds (rules: specs/tools/t27o/ledger.t27). T27oSweep { paths: Vec, #[arg(long, default_value = t27o_ledger::SWEEP_HERE)] repo: String, #[arg(long, default_value_t = t27o_ledger::SWEEP_MAX_BYTES)] max_bytes: usize, #[arg(long)] cache: Option, #[arg(long, default_value_t = t27o_ledger::SWEEP_TOP)] top: usize }, } @@ -1387,7 +1387,7 @@ fn cmd_t27o(file: &str, name: Option<&str>, oracle: bool, check: bool, t27c: Opt /// `tri t27o-sweep` (#10543): every rule is ledger.t27 (which files, whether a row is reusable, coverage, root vs cascade, the ranking); this lists, hashes, converts in-process and keeps the cache. fn cmd_t27o_sweep(paths: &[String], repo: &str, max: usize, cache: Option<&str>, top: usize) -> Result { let t0 = std::time::Instant::now(); let seal = |f: &str| -> Result { let p = std::env::current_dir()?.join(f); Ok(serde_json::from_slice::(&fs::read(&p).with_context(|| format!("{}: cannot read the seal; {}", p.display(), t27o_ledger::SEAL_HINT))?)?["spec_hash"].as_str().with_context(|| format!("{}: no spec_hash", p.display()))?.to_string()) }; - let (conv, std) = (seal(t27o_ledger::SEAL_CONVERTER)?, seal(t27o_ledger::SEAL_STD)?); let repo: &'static str = repo.to_string().leak(); // the keys come from this checkout; the listing, the files and the cache belong to `root` + let (conv, std, zs) = (seal(t27o_ledger::SEAL_CONVERTER)?, seal(t27o_ledger::SEAL_STD)?, seal(t27o_ledger::SEAL_ZIG)?); let zk = format!("{conv}+{zs}"); let repo: &'static str = repo.to_string().leak(); // the keys come from this checkout; the listing, the files and the cache belong to `root` let root: &'static str = if t27o_ledger::repo_is_here(repo) { t27o_ledger::SWEEP_HERE } else { fs::canonicalize(repo).with_context(|| format!("{repo}: cannot open the repository"))?.to_string_lossy().into_owned().leak() }; let cache = cache.map_or_else(|| { let mut b = vec![0u8; 4096]; let n = t27o_ledger::cache_path(root, &mut b); String::from_utf8_lossy(&b[..n]).into_owned() }, str::to_string); let old: &'static str = fs::read_to_string(&cache).unwrap_or_default().leak(); let rows: std::collections::HashMap<&'static str, Vec<&'static str>> = old.lines().map(|l| l.split('\t').collect::>()).filter(|f| f.len() == 10).map(|f| (f[0], f)).collect(); let ls = Command::new("git").args(["-C", root, "ls-files", "--"]).args(paths).output()?; anyhow::ensure!(ls.status.success(), "{root}: git ls-files failed: {}", String::from_utf8_lossy(&ls.stderr).trim()); let listing: &'static str = String::from_utf8(ls.stdout)?.leak(); @@ -1395,13 +1395,15 @@ fn cmd_t27o_sweep(paths: &[String], repo: &str, max: usize, cache: Option<&str>, for p in listing.lines().filter(|p| t27o_ledger::sweep_path(p)) { let Ok(src) = fs::read(Path::new(root).join(p)) else { continue }; if t27o_ledger::over_cap(src.len(), max) { n[2] += 1; println!("{p} {} bytes skipped", src.len()); continue; } + let z = t27o_ledger::front_end(p.as_bytes()) == t27o_ledger::FE_ZIG; let ck = if z { &zk } else { &conv }; // a .zig row is keyed by both converters' seals (#10823) let (sha, r) = (format!("{:x}", Sha256::digest(&src)), rows.get(p)); - let reuse = t27o_ledger::reuse_row(t27o_ledger::key_check(r.is_some(), r.is_some_and(|r| r[1] == sha), r.is_some_and(|r| r[2] == conv), r.is_some_and(|r| r[3] == std))); + let reuse = t27o_ledger::reuse_row(t27o_ledger::key_check(r.is_some(), r.is_some_and(|r| r[1] == sha), r.is_some_and(|r| r[2] == *ck), r.is_some_and(|r| r[3] == std))); let f: Vec<&'static str> = if reuse { r.unwrap().clone() } else { - let (mut out, mut st, mut k) = (vec![0u8; 1 << 24], vec![0usize; 1 << 16], std::collections::BTreeMap::<&'static str, usize>::new()); - let m = t27o::convert(&src, Path::new(p).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()).as_bytes(), &mut out, &mut st); + let (mut out, mut st, mut k) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], std::collections::BTreeMap::<&'static str, usize>::new()); + let stem = Path::new(p).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()); + let m = if z { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; let o: &'static str = String::from_utf8_lossy(&out[..m.min(out.len())]).into_owned().leak(); for l in o.lines().filter(|l| t27o_ledger::is_refusal(l)) { *k.entry(&l[t27o_ledger::kind_from(l)..t27o_ledger::kind_to(l)]).or_default() += 1; } - format!("{p}\t{sha}\t{conv}\t{std}\t{}\t{}\t{}\t{}\t{}\t{}", st[t27o::NFN], st[t27o::NOK], st[t27o::NTEST], st[t27o::NTS], st[t27o::NSTUB], k.iter().map(|(a, b)| format!("{a}={b}")).collect::>().join(";")).leak().split('\t').collect() + format!("{p}\t{sha}\t{ck}\t{std}\t{}\t{}\t{}\t{}\t{}\t{}", st[t27o::NFN], st[t27o::NOK], st[t27o::NTEST], st[t27o::NTS], st[t27o::NSTUB], k.iter().map(|(a, b)| format!("{a}={b}")).collect::>().join(";")).leak().split('\t').collect() }; n[reuse as usize] += 1; tab += &format!("{}\n", f.join("\t")); (0..5).for_each(|i| tot[i] += f[4 + i].parse::().unwrap_or(0)); diff --git a/gen/rust/tools/t27o/ledger.rs b/gen/rust/tools/t27o/ledger.rs index 5f6e791fdb..1f94ee3b14 100644 --- a/gen/rust/tools/t27o/ledger.rs +++ b/gen/rust/tools/t27o/ledger.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 34 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 38 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -263,6 +263,8 @@ pub const SEAL_CONVERTER: &'static str = ".trinity/seals/t27o_T27oRust.json"; pub const SEAL_STD: &'static str = ".trinity/seals/t27o_T27oRustStd.json"; +pub const SEAL_ZIG: &'static str = ".trinity/seals/t27o_T27oZig.json"; + pub const SEAL_HINT: &'static str = "run from a t27 checkout"; pub const COVERAGE_UNKNOWN: usize = 1001; @@ -277,7 +279,7 @@ pub fn sweep_path(path: &'static str) -> bool { if (path.len() < 3) { return false; } - if (span_is(path, (path.len() - 3), path.len(), ".rs") == false) { + if ((span_is(path, (path.len() - 3), path.len(), ".rs") == false) && (front_end(path.as_bytes()) != FE_ZIG)) { return false; } let mut from: usize = 0; @@ -382,7 +384,7 @@ pub fn kind_to(line: &'static str) -> usize { return line.len(); } let mut k: usize = reason_from(line); - while ((k < line.len()) && (line.as_bytes()[(k) as usize] != 96)) { + while (((k < line.len()) && (line.as_bytes()[(k) as usize] != 96)) && (line.as_bytes()[(k) as usize] != 59)) { k = (k + 1); } if ((k > reason_from(line)) && (line.as_bytes()[((k - 1)) as usize] == 32)) { diff --git a/gen/rust/tools/t27o/zig_convert.rs b/gen/rust/tools/t27o/zig_convert.rs index e4138368bb..4ec4a17434 100644 --- a/gen/rust/tools/t27o/zig_convert.rs +++ b/gen/rust/tools/t27o/zig_convert.rs @@ -1,20 +1,20 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 44 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 46 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. -pub const Z_NFN: usize = 8; +pub const Z_NFN: usize = NFN; -pub const Z_NOK: usize = 9; +pub const Z_NOK: usize = NOK; -pub const Z_NTS: usize = 10; +pub const Z_NTS: usize = NTS; -pub const Z_NTEST: usize = 11; +pub const Z_NTEST: usize = NTEST; -pub const Z_CLS: usize = 12; +pub const Z_CLS: usize = 8; -pub const Z_STMT: usize = 13; +pub const Z_STMT: usize = 9; pub const Z_TMP: usize = 14; @@ -28,7 +28,7 @@ pub const Z_ROUND: usize = 18; pub const Z_RET: usize = 19; -pub const Z_GREW: usize = 20; +pub const Z_GREW: usize = 100; pub const Z_HIT: usize = 21; @@ -3612,10 +3612,20 @@ pub const L_UNARY: usize = 13; pub const L_XOR: usize = 7; +pub const NFN: usize = 10; + pub const NHI: usize = 5; pub const NLO: usize = 4; +pub const NOK: usize = 11; + +pub const NSTUB: usize = 20; + +pub const NTEST: usize = 13; + +pub const NTS: usize = 12; + pub const P: usize = 0; pub const W: usize = 1; diff --git a/specs/tools/t27o/ledger.t27 b/specs/tools/t27o/ledger.t27 index 357bb4c7d4..278cabadd9 100644 --- a/specs/tools/t27o/ledger.t27 +++ b/specs/tools/t27o/ledger.t27 @@ -252,6 +252,9 @@ pub const SWEEP_HERE : str = "."; ; swept repository), and what a missing one is told to do. pub const SEAL_CONVERTER : str = ".trinity/seals/t27o_T27oRust.json"; pub const SEAL_STD : str = ".trinity/seals/t27o_T27oRustStd.json"; +; A .zig row is also made with zig_convert.t27, so its converter key is the Zig seal's spec_hash joined to the Rust one's +; (zig_convert.t27 uses rust_convert.t27's helpers): a change to either converter makes every Zig row stale (#10823). +pub const SEAL_ZIG : str = ".trinity/seals/t27o_T27oZig.json"; pub const SEAL_HINT : str = "run from a t27 checkout"; ; seen == 0 has no share: unknown, one above 100.0 percent, never a number inside the scale. pub const COVERAGE_UNKNOWN : usize = 1001; @@ -264,11 +267,11 @@ pub fn reuse_row(state: u32) -> bool { return state == K_FRESH; } -; Is this tracked path one the sweep converts: a .rs file with no gen/ or target/ directory above it -; (generated Rust is gen-rust of a .t27 spec, build output is neither). +; Is this tracked path one the sweep converts: a .rs or a .zig file (#10823) with no gen/ or target/ directory above it +; (generated Rust is gen-rust of a .t27 spec, build output is neither, and a generator's Zig sits under gen/ too). pub fn sweep_path(path: string) -> bool { if (path.len < 3) { return false; } - if (span_is(path, path.len - 3, path.len, ".rs") == false) { return false; } + if (span_is(path, path.len - 3, path.len, ".rs") == false and front_end(path) != FE_ZIG) { return false; } var from : usize = 0; var k : usize = 0; while (k < path.len) { @@ -350,7 +353,9 @@ pub fn reason_from(line: string) -> usize { } ; The kind of a refusal is the reason's words before the first backtick (the quoted text that follows -; them differs from one refusal to the next). A line with no reason is its own kind, whole. +; them differs from one refusal to the next) or the first semicolon: a row of the sweep's cache separates its +; kinds with ';', and the Zig front end's "generated; convert its source spec instead" was cut in two by it and +; counted as the second half (#10823). A line with no reason is its own kind, whole. pub fn kind_from(line: string) -> usize { if (reason_from(line) == line.len) { return 0; } return reason_from(line); @@ -359,7 +364,7 @@ pub fn kind_from(line: string) -> usize { pub fn kind_to(line: string) -> usize { if (reason_from(line) == line.len) { return line.len; } var k : usize = reason_from(line); - while (k < line.len and line[k] != 96) { k = k + 1; } + while (k < line.len and line[k] != 96 and line[k] != 59) { k = k + 1; } if (k > reason_from(line) and line[k - 1] == 32) { return k - 1; } return k; } @@ -666,6 +671,9 @@ test the_seals_are_read_from_the_checkout_and_a_missing_one_says_so { assert(span_is(SEAL_STD, 0, SEAL_STD.len, ".trinity/seals/t27o_T27oRustStd.json")); assert(starts_at(SEAL_CONVERTER, 0, ".trinity/seals/")); assert(starts_at(SEAL_STD, 0, ".trinity/seals/")); + assert(span_is(SEAL_ZIG, 0, SEAL_ZIG.len, ".trinity/seals/t27o_T27oZig.json")); + assert(starts_at(SEAL_ZIG, 0, ".trinity/seals/")); + assert(SEAL_ZIG.len != SEAL_CONVERTER.len); assert(span_is(SEAL_HINT, 0, SEAL_HINT.len, "run from a t27 checkout")); assert(SEAL_CONVERTER.len != SEAL_STD.len); } @@ -714,3 +722,40 @@ test the_notes_of_a_zig_file_say_what_does_not_apply_and_what_ran_nothing { assert(NOTE_ZIG_ORACLE.len > 0); assert(NOTE_ZIG_UNJUDGED.len > 0); } + +test the_sweep_takes_zig_files_beside_rust_ones { + assert(sweep_path("src/vsa/ops.zig")); + assert(sweep_path("crates/trios-x/src/lib.zig")); + assert(sweep_path("build.zig")); + assert(sweep_path("a.zig")); + assert(sweep_path("src/targets/x.zig")); + assert(sweep_path("src/generator/x.zig")); + assert(sweep_path("src/main.rs")); +} + +test negative_control_a_generated_or_target_zig_path_and_a_near_miss_are_not_swept { + assert(sweep_path("gen/zig/x.zig") == false); + assert(sweep_path("deploy/trinity-nexus/tools/src/gen/batch_gen.zig") == false); + assert(sweep_path("target/x.zig") == false); + assert(sweep_path("a/b/target/c.zig") == false); + assert(sweep_path("main.zig.bak") == false); + assert(sweep_path("zig") == false); + assert(sweep_path("x.zi") == false); + assert(sweep_path("x.zig/y.txt") == false); +} + +test a_refusal_kind_stops_at_a_semicolon_because_the_cache_row_separates_kinds_with_one { + const g : string = "// t27o: refused file (line 1): generated; convert its source spec instead"; + assert(is_refusal(g)); + assert(span_is(g, kind_from(g), kind_to(g), "generated")); + const h : string = "// t27o: refused fn f (line 3): pointer `*` at line 3; 2 fn inside"; + assert(span_is(h, kind_from(h), kind_to(h), "pointer")); +} + +test negative_control_a_kind_with_no_semicolon_or_backtick_is_still_the_whole_reason { + const k : string = "// t27o: refused fn f (line 3): name table full"; + assert(span_is(k, kind_from(k), kind_to(k), "name table full")); + const c : string = "// t27o: refused fn f (line 3): uses refused `g` at line 4; 1 fn inside"; + assert(span_is(c, kind_from(c), kind_to(c), "uses refused")); + assert(is_cascade("uses refused")); +} diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 index 642f856a40..6625764423 100644 --- a/specs/tools/t27o/zig_convert.t27 +++ b/specs/tools/t27o/zig_convert.t27 @@ -111,19 +111,22 @@ use tools::t27o::rust_convert::is_close; /// Slots 0 .. 6 are rust_convert's (its put, newline and refuse read them): P the reader's position, W how much /// is written, ERR the refusal, AT where it was found, NLO and NHI the source text it names, IND the indent. /// They are used here by their names and not declared again. -pub const Z_NFN : usize = 8; -pub const Z_NOK : usize = 9; -pub const Z_NTS : usize = 10; -pub const Z_NTEST : usize = 11; -pub const Z_CLS : usize = 12; -pub const Z_STMT : usize = 13; +/// The five counters (fn seen, converted, tests seen, tests converted, stubs) sit where rust_convert's report reads +/// them (NFN, NOK, NTS, NTEST, NSTUB): `tri t27o-sweep` reads one set of slots for either front end (#10823). No fn is +/// kept as a stub here, so NSTUB stays 0. +pub const Z_NFN : usize = NFN; +pub const Z_NOK : usize = NOK; +pub const Z_NTS : usize = NTS; +pub const Z_NTEST : usize = NTEST; +pub const Z_CLS : usize = 8; +pub const Z_STMT : usize = 9; pub const Z_TMP : usize = 14; pub const Z_INTEST : usize = 15; pub const Z_NNAMES : usize = 16; pub const Z_NFILE : usize = 17; pub const Z_ROUND : usize = 18; pub const Z_RET : usize = 19; -pub const Z_GREW : usize = 20; +pub const Z_GREW : usize = 100; pub const Z_HIT : usize = 21; pub const Z_NARGS : usize = 22; pub const Z_ARG0 : usize = 23; @@ -3357,3 +3360,30 @@ test a_source_with_crlf_lines_reads_like_one_with_lf { const n : usize = z_body_text(src[0..w], &out, &st); assert(out_is(n, &out, 0, "fn f(x: u32) -> u32 {\n return x;\n}\n\ntest f {\n assert(f(1) == 1);\n}\n\n")); } + +test the_counters_sit_where_rust_converts_report_reads_them_and_no_fn_is_a_stub { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn ok(x: u32) u32 {\n return x + 1;\n}\nfn bad(p: *u32) u32 {\n return p.*;\n}\ntest \"good\" {\n try std.testing.expect(ok(1) == 2);\n}\ntest \"calls bad\" {\n try std.testing.expect(bad(1) == 2);\n}\n", &out, &st); + assert(n > 0); + assert(st[NFN] == 2); + assert(st[NOK] == 1); + assert(st[NTS] == 2); + assert(st[NTEST] == 1); + assert(st[NSTUB] == 0); + assert(Z_NFN == NFN); + assert(Z_NTEST == NTEST); +} + +test negative_control_the_five_counters_are_five_different_slots_and_none_is_the_class_slot { + assert(NFN != NOK); + assert(NOK != NTS); + assert(NTS != NTEST); + assert(NTEST != NSTUB); + assert(Z_CLS != NFN); + assert(Z_CLS != NOK); + assert(Z_CLS != NTS); + assert(Z_CLS != NTEST); + assert(Z_STMT != NTEST); + assert(Z_GREW != NSTUB); +} From 0009f373abdb7f13c2497637c03ce2309dd3d3c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 12:56:22 +0700 Subject: [PATCH 3/5] t27o: a .zig const imported as @import("x.zig").C is written in as a scalar literal when x.zig is in the same tree (Closes #10823) `pub const C = @import("x.zig").C;` made C another file's, so every use of C was refused ("uses external"). zig_convert.t27 now has two pure fns: `imports` lists the paths to read (each once), and `inline_imports` writes `pub const C: u32 = 5;` in place of the import when the imported file holds a `pub const C` that is a scalar literal (integer, float, bool, a string with no escape, at most 64 bytes, under a scalar type or none). The glue (zig_source in main.rs) only reads the files, from the importing file's own directory tree: a path with `..`, a leading `/`, a backslash or a colon is never listed, and a symlink out of the tree is not followed. Anything else is left exactly as it was and stays refused with its reason: a struct, a fn, a const that is not pub, an expression, a non-scalar type, an escaped string, a literal over 64 bytes, a declaration over several lines (no line is added or taken, so every "line N" of a refusal stays true), a missing file. Tests for each, negative controls for all of them, and the bound (output <= 4 x source + 64 bytes) the glue sizes its buffer by. The sweep keys a Zig row by the source with the values written in, so changing an imported literal makes the importing file's row stale and changing a comment in the imported file does not (checked on a two-file repo). Measured on trinity 0770abb99 src/ and trios 483dd9ee crates/: 23 imports of a scalar literal resolve in the tree; 12 are in two files that convert now (src/tri/constants.zig, src/tri/error.zig, consts only: no new fn converts), 11 are in files refused whole as generated. The other 2571 outputs are byte-identical to before. main.rs: +31 / -31 against the merge base. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oZig.json | 16 +- cli/tri/src/main.rs | 28 +- gen/rust/tools/t27o/zig_convert.rs | 341 ++++++++++++++++++++++- specs/tools/t27o/zig_convert.t27 | 426 ++++++++++++++++++++++++++++- 4 files changed, 787 insertions(+), 24 deletions(-) diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json index fbd05ec4dc..da7e0d6651 100644 --- a/.trinity/seals/t27o_T27oZig.json +++ b/.trinity/seals/t27o_T27oZig.json @@ -7,23 +7,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:a75be5c7a7ff3c9c6b8cf4a8b700de0a94048c66c503bf0a55e65e866684c80f", - "gen_hash_rust": "sha256:3da64c7f9a150b14e77e1cb3b33a7a134b0932f64604fe457fb200e14ed9ad19", - "gen_hash_verilog": "sha256:70a92c566efb6f1a50e9612de03eacaa72c0310702ec24887c61ffeedb5f0cfb", - "gen_hash_zig": "sha256:923147d74c971f64b7b93fe1de72233d5762c30292831a669a49a6605f14eb92", + "gen_hash_c": "sha256:673010adda7df541762662c16140e3b23a0359623b1836f14e68744332f3f732", + "gen_hash_rust": "sha256:550c8f70e4bfc65cd0e8ca9dd4095f4abc7f11253d0c11ab31d860d73a3d8f13", + "gen_hash_verilog": "sha256:a4a33303f27f566873be7788e4f487c89ffa21e8a348be73aca214be9b3f2014", + "gen_hash_zig": "sha256:42c52299a562ffb982a30a8e7af775a086ed4477f1c9fc96f239be5388fb8e12", "module": "T27oZig", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:37:36Z", + "sealed_at": "2026-10-19T05:55:46Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:ae824d39ad7d1df73ca1446a0b5b4447b3fc975021a6767be1e7b7c9aa946de6", + "spec_hash": "sha256:0514a77d14353d2164d42d2eae4256293ff237e413f2379b1f636a08ff3a3d9f", "spec_path": "specs/tools/t27o/zig_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 46, - "total": 46, + "passed": 56, + "total": 56, "vacuous": 1 }, "toolchain": { diff --git a/cli/tri/src/main.rs b/cli/tri/src/main.rs index 01c8f4f8a7..d82c7e7e2a 100644 --- a/cli/tri/src/main.rs +++ b/cli/tri/src/main.rs @@ -1353,6 +1353,12 @@ fn main() -> Result<()> { } /// `tri t27o` (#8967): every rule is gen-rust of rust_convert.t27; this reads the file, runs rustc and t27c, and writes. +fn zig_source(file: &Path, src: &[u8]) -> Vec { // a .zig source with the consts it imports from files of its own directory tree written in: zig_convert.t27 `imports` names the files, `inline_imports` writes the values, this only reads (a symlink out of the tree is not followed; #10823) + let (dir, mut want, mut ext, mut flat, mut st) = (file.parent().and_then(|d| fs::canonicalize(d).ok()).unwrap_or_default(), vec![0u8; src.len() + 64], Vec::new(), vec![0u8; 4 * src.len() + 64], vec![0usize; zig::ZI_SLOTS]); + let w = zig::imports(src, &mut want, &mut st).min(want.len()); for rel in String::from_utf8_lossy(&want[..w]).lines() { if let Some(t) = fs::canonicalize(dir.join(rel)).ok().filter(|f| f.starts_with(&dir)).and_then(|f| fs::read(f).ok()) { ext.extend([&[zig::Z_B_REC][..], rel.as_bytes(), b"\n", &t].concat()); } } + let n = zig::inline_imports(src, &ext, &mut flat, &mut st).min(flat.len()); flat.truncate(n); flat +} + fn cmd_t27o(file: &str, name: Option<&str>, oracle: bool, check: bool, t27c: Option<&str>, emit_cell: bool) -> Result { let src = fs::read(file).with_context(|| format!("reading {file}"))?; if spice::is_spice_name(file.as_bytes()) { // a netlist: every rule is specs/tools/t27o/spice_convert.t27 (#10509) @@ -1363,23 +1369,18 @@ fn cmd_t27o(file: &str, name: Option<&str>, oracle: bool, check: bool, t27c: Opt } let stem = name.map_or_else(|| Path::new(file).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()), str::to_string); let zig = t27o_ledger::front_end(file.as_bytes()) == t27o_ledger::FE_ZIG; if zig && oracle { println!("{}", t27o_ledger::NOTE_ZIG_ORACLE); return Ok(0); } // a .zig file: its own tests are the oracle (ledger.t27, #10823) + let src = if zig { zig_source(Path::new(file), &src) } else { src }; let (mut out, mut st, mut spec) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], vec![0u8; 1 << 24]); let mut n = if zig { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else if oracle || check { t27o::oracle(&src, &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; - if !check { - std::io::Write::write_all(&mut std::io::stdout(), &out[..n.min(out.len())])?; - return Ok(0); - } - // the harness sits next to the source, so its #[path] mods resolve; the spec under specs/, so its `use`s do - let (h, exe, path) = (Path::new(file).with_file_name("zz_t27o_oracle.rs"), std::env::temp_dir().join("t27o_oracle"), PathBuf::from(format!("specs/t27o_check/{stem}.t27"))); + if !check { std::io::Write::write_all(&mut std::io::stdout(), &out[..n.min(out.len())])?; return Ok(0); } + let (h, exe, path) = (Path::new(file).with_file_name("zz_t27o_oracle.rs"), std::env::temp_dir().join("t27o_oracle"), PathBuf::from(format!("specs/t27o_check/{stem}.t27"))); // the harness sits next to the source, so its #[path] mods resolve; the spec under specs/, so its `use`s do if !zig { // a Rust file: the harness rustc builds is run and its samples follow the spec - fs::write(&h, &out[..n.min(out.len())])?; let built = Command::new("rustc").args(["--edition", "2021", "-o"]).arg(&exe).arg(&h).status(); fs::remove_file(&h)?; - anyhow::ensure!(built?.success(), "rustc refused the oracle harness of {file}"); + fs::write(&h, &out[..n.min(out.len())])?; let built = Command::new("rustc").args(["--edition", "2021", "-o"]).arg(&exe).arg(&h).status(); fs::remove_file(&h)?; anyhow::ensure!(built?.success(), "rustc refused the oracle harness of {file}"); let m = t27o::convert(&src, stem.as_bytes(), &mut spec, &mut st); out = [&spec[..m.min(spec.len())], &Command::new(&exe).output()?.stdout[..]].concat(); n = out.len(); } fs::create_dir_all("specs/t27o_check")?; fs::write(&path, &out[..n.min(out.len())])?; println!("{}", path.display()); if !t27o_ledger::has_evidence(zig, st[zig::Z_NTEST]) { eprintln!("{}", t27o_ledger::NOTE_ZIG_UNJUDGED); return Ok(t27o_ledger::EXIT_UNJUDGED as u8); } - let b = ["target/release/t27b", "target/debug/t27b"].into_iter().find(|p| Path::new(p).exists()).unwrap_or("t27b"); // t27b judges first, t27c only when its exit is no verdict (specs/tools/t27o/ledger.t27, #10525) - let c = Command::new(b).arg("test").arg(&path).status().ok().and_then(|s| s.code()).unwrap_or(-1); + let b = ["target/release/t27b", "target/debug/t27b"].into_iter().find(|p| Path::new(p).exists()).unwrap_or("t27b"); let c = Command::new(b).arg("test").arg(&path).status().ok().and_then(|s| s.code()).unwrap_or(-1); // t27b judges first, t27c only when its exit is no verdict (specs/tools/t27o/ledger.t27, #10525) if !t27o_ledger::needs_reference(t27o_ledger::t27b_outcome(c)) { return Ok(c as u8); } eprintln!("t27o: {b} test exited {c} ({}), so t27c test-report judges instead", t27o_ledger::ORC_NAMES[t27o_ledger::t27b_outcome(c) as usize]); Ok(Command::new(mutate::resolve_t27c(t27c)).arg("test-report").arg(&path).status()?.code().unwrap_or(1) as u8) } @@ -1395,12 +1396,11 @@ fn cmd_t27o_sweep(paths: &[String], repo: &str, max: usize, cache: Option<&str>, for p in listing.lines().filter(|p| t27o_ledger::sweep_path(p)) { let Ok(src) = fs::read(Path::new(root).join(p)) else { continue }; if t27o_ledger::over_cap(src.len(), max) { n[2] += 1; println!("{p} {} bytes skipped", src.len()); continue; } - let z = t27o_ledger::front_end(p.as_bytes()) == t27o_ledger::FE_ZIG; let ck = if z { &zk } else { &conv }; // a .zig row is keyed by both converters' seals (#10823) - let (sha, r) = (format!("{:x}", Sha256::digest(&src)), rows.get(p)); + let z = t27o_ledger::front_end(p.as_bytes()) == t27o_ledger::FE_ZIG; let ck = if z { &zk } else { &conv }; // a .zig row is keyed by both converters' seals, and by the source with its imported consts written in (#10823) + let src = if z { zig_source(&Path::new(root).join(p), &src) } else { src }; let (sha, r) = (format!("{:x}", Sha256::digest(&src)), rows.get(p)); let reuse = t27o_ledger::reuse_row(t27o_ledger::key_check(r.is_some(), r.is_some_and(|r| r[1] == sha), r.is_some_and(|r| r[2] == *ck), r.is_some_and(|r| r[3] == std))); let f: Vec<&'static str> = if reuse { r.unwrap().clone() } else { - let (mut out, mut st, mut k) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], std::collections::BTreeMap::<&'static str, usize>::new()); - let stem = Path::new(p).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()); + let (mut out, mut st, mut k) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], std::collections::BTreeMap::<&'static str, usize>::new()); let stem = Path::new(p).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()); let m = if z { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; let o: &'static str = String::from_utf8_lossy(&out[..m.min(out.len())]).into_owned().leak(); for l in o.lines().filter(|l| t27o_ledger::is_refusal(l)) { *k.entry(&l[t27o_ledger::kind_from(l)..t27o_ledger::kind_to(l)]).or_default() += 1; } format!("{p}\t{sha}\t{ck}\t{std}\t{}\t{}\t{}\t{}\t{}\t{}", st[t27o::NFN], st[t27o::NOK], st[t27o::NTEST], st[t27o::NTS], st[t27o::NSTUB], k.iter().map(|(a, b)| format!("{a}={b}")).collect::>().join(";")).leak().split('\t').collect() diff --git a/gen/rust/tools/t27o/zig_convert.rs b/gen/rust/tools/t27o/zig_convert.rs index 4ec4a17434..81a764ed67 100644 --- a/gen/rust/tools/t27o/zig_convert.rs +++ b/gen/rust/tools/t27o/zig_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 46 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 56 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -3317,6 +3317,324 @@ pub fn z_item(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { st[(P) as usize] = skip_ws(src, end); } +pub const Z_B_REC: u8 = 1; + +pub const Z_LIT_MAX: usize = 64; + +pub const ZI_NA: usize = 8; + +pub const ZI_NB: usize = 9; + +pub const ZI_TA: usize = 10; + +pub const ZI_TB: usize = 11; + +pub const ZI_PA: usize = 12; + +pub const ZI_PB: usize = 13; + +pub const ZI_MA: usize = 14; + +pub const ZI_MB: usize = 15; + +pub const ZI_SEMI: usize = 16; + +pub const ZI_XA: usize = 17; + +pub const ZI_XB: usize = 18; + +pub const ZI_LA: usize = 19; + +pub const ZI_LB: usize = 20; + +pub const ZI_SLOTS: usize = 21; + +pub fn z_import_path_ok(src: &[u8], a: usize, b: usize) -> bool { + if ((b < (a + 5)) || (b > src.len())) { + return false; + } + if !(word_is(src, (b - 4), b, b".zig")) { + return false; + } + if (src[(a) as usize] == B_SLASH) { + return false; + } + let mut comp: usize = a; + let mut k: usize = a; + while (k < b) { + let c: u8 = src[(k) as usize]; + if ((((c == B_BACKSLASH) || (c == B_COLON)) || (c < B_SPACE)) || (c == B_DEL)) { + return false; + } + if (c == B_SLASH) { + if ((((k - comp) == 2) && (src[(comp) as usize] == B_DOT)) && (src[((comp + 1)) as usize] == B_DOT)) { + return false; + } + comp = (k + 1); + } + k = (k + 1); + } + return true; +} + +pub fn z_lit_end(buf: &[u8], p: usize) -> usize { + let mut q: usize = p; + if (head(buf, q) == B_MINUS) { + q = (q + 1); + } + let e: usize = z_tok_end(buf, q); + if (((e <= q) || (e > buf.len())) || ((e - p) > Z_LIT_MAX)) { + return p; + } + if is_digit(head(buf, q)) { + return e; + } + if (q != p) { + return p; + } + if (word_is(buf, q, e, b"true") || word_is(buf, q, e, b"false")) { + return e; + } + if (((head(buf, q) != B_QUOTE) || (e < (q + 2))) || (buf[((e - 1)) as usize] != B_QUOTE)) { + return p; + } + let mut k: usize = (q + 1); + while (k < (e - 1)) { + if ((buf[(k) as usize] == B_BACKSLASH) || (buf[(k) as usize] == B_NL)) { + return p; + } + k = (k + 1); + } + return e; +} + +pub fn z_import_const(src: &[u8], p: usize, ps: &mut [usize]) -> bool { + let mut q: usize = p; + if z_is(src, q, b"pub") { + q = skip_ws(src, z_tok_end(src, q)); + } + if !(z_is(src, q, b"const")) { + return false; + } + let na: usize = skip_ws(src, z_tok_end(src, q)); + let nb: usize = z_tok_end(src, na); + if !(is_alpha(head(src, na))) { + return false; + } + ps[(ZI_NA) as usize] = na; + ps[(ZI_NB) as usize] = nb; + ps[(ZI_TA) as usize] = 0; + ps[(ZI_TB) as usize] = 0; + let mut t: usize = skip_ws(src, nb); + if z_is(src, t, b":") { + let ta: usize = skip_ws(src, z_tok_end(src, t)); + let tb: usize = z_tok_end(src, ta); + if !(z_scalar(src, ta, tb)) { + return false; + } + ps[(ZI_TA) as usize] = ta; + ps[(ZI_TB) as usize] = tb; + t = skip_ws(src, tb); + } + if !(z_is(src, t, b"=")) { + return false; + } + let i: usize = skip_ws(src, z_tok_end(src, t)); + if !(z_is(src, i, b"@import")) { + return false; + } + let o: usize = skip_ws(src, z_tok_end(src, i)); + if !(z_is(src, o, b"(")) { + return false; + } + let s: usize = skip_ws(src, z_tok_end(src, o)); + let sb: usize = z_tok_end(src, s); + if (((head(src, s) != B_QUOTE) || (sb < (s + 3))) || (head(src, (sb - 1)) != B_QUOTE)) { + return false; + } + if !(z_import_path_ok(src, (s + 1), (sb - 1))) { + return false; + } + let c: usize = skip_ws(src, sb); + if !(z_is(src, c, b")")) { + return false; + } + let d: usize = skip_ws(src, z_tok_end(src, c)); + if !(z_is(src, d, b".")) { + return false; + } + let m: usize = skip_ws(src, z_tok_end(src, d)); + let mb: usize = z_tok_end(src, m); + if !(is_alpha(head(src, m))) { + return false; + } + let semi: usize = skip_ws(src, mb); + if (head(src, semi) != B_SEMI) { + return false; + } + ps[(ZI_PA) as usize] = (s + 1); + ps[(ZI_PB) as usize] = (sb - 1); + ps[(ZI_MA) as usize] = m; + ps[(ZI_MB) as usize] = mb; + ps[(ZI_SEMI) as usize] = semi; + return true; +} + +pub fn z_ext_value(ext: &[u8], nb: usize, ps: &mut [usize]) -> bool { + ps[(ZI_XA) as usize] = 0; + ps[(ZI_XB) as usize] = 0; + let mut t: usize = skip_ws(ext, nb); + if z_is(ext, t, b":") { + let ta: usize = skip_ws(ext, z_tok_end(ext, t)); + let tb: usize = z_tok_end(ext, ta); + if !(z_scalar(ext, ta, tb)) { + return false; + } + ps[(ZI_XA) as usize] = ta; + ps[(ZI_XB) as usize] = tb; + t = skip_ws(ext, tb); + } + if !(z_is(ext, t, b"=")) { + return false; + } + let la: usize = skip_ws(ext, z_tok_end(ext, t)); + let le: usize = z_lit_end(ext, la); + if ((le == la) || (head(ext, skip_ws(ext, le)) != B_SEMI)) { + return false; + } + ps[(ZI_LA) as usize] = la; + ps[(ZI_LB) as usize] = le; + return true; +} + +pub fn z_ext_const(ext: &[u8], lo: usize, hi: usize, src: &[u8], ps: &mut [usize]) -> bool { + let mut p: usize = skip_ws(ext, lo); + while (p < hi) { + let mut end: usize = z_item_end(ext, p); + if (end <= p) { + end = (p + 1); + } + if (end > hi) { + return false; + } + let mut q: usize = p; + if z_is(ext, q, b"pub") { + q = skip_ws(ext, z_tok_end(ext, q)); + if z_is(ext, q, b"const") { + let na: usize = skip_ws(ext, z_tok_end(ext, q)); + let nb: usize = z_tok_end(ext, na); + if word_is(ext, na, nb, &src[(ps[(ZI_MA) as usize]) as usize..(ps[(ZI_MB) as usize]) as usize]) { + return z_ext_value(ext, nb, ps); + } + } + } + p = skip_ws(ext, end); + } + return false; +} + +pub fn z_seg_end(ext: &[u8], from: usize) -> usize { + let mut k: usize = from; + while ((k < ext.len()) && (ext[(k) as usize] != Z_B_REC)) { + k = (k + 1); + } + return k; +} + +pub fn z_rec_text(ext: &[u8], src: &[u8], a: usize, b: usize) -> usize { + let n: usize = (b - a); + let mut k: usize = 0; + while (((k + n) + 1) < ext.len()) { + if (((ext[(k) as usize] == Z_B_REC) && (ext[(((k + 1) + n)) as usize] == B_NL)) && word_is(ext, (k + 1), ((k + 1) + n), &src[(a) as usize..(b) as usize])) { + return ((k + n) + 2); + } + k = (k + 1); + } + return ext.len(); +} + +pub fn z_listed(out: &[u8], w: usize, src: &[u8], a: usize, b: usize) -> bool { + let n: usize = (b - a); + let mut k: usize = 0; + while (((k + n) < w) && ((k + n) < out.len())) { + if ((((k == 0) || (out[((k - 1)) as usize] == B_NL)) && (out[((k + n)) as usize] == B_NL)) && word_is(out, k, (k + n), &src[(a) as usize..(b) as usize])) { + return true; + } + k = (k + 1); + } + return false; +} + +pub fn z_one_line(src: &[u8], a: usize, b: usize) -> bool { + let mut k: usize = a; + while ((k < b) && (k < src.len())) { + if ((src[(k) as usize] == B_NL) || (src[(k) as usize] == B_CR)) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn imports(src: &[u8], out: &mut [u8], st: &mut [usize]) -> usize { + if (st.len() < ZI_SLOTS) { + return 0; + } + st[(W) as usize] = 0; + let mut p: usize = skip_ws(src, 0); + while (p < src.len()) { + let mut end: usize = z_item_end(src, p); + if (end <= p) { + end = (p + 1); + } + if (z_import_const(src, p, st) && !(z_listed(out, st[(W) as usize], src, st[(ZI_PA) as usize], st[(ZI_PB) as usize]))) { + put_src(src, st, out, st[(ZI_PA) as usize], st[(ZI_PB) as usize]); + put(st, out, B_NL); + } + p = skip_ws(src, end); + } + return st[(W) as usize]; +} + +pub fn inline_imports(src: &[u8], ext: &[u8], out: &mut [u8], st: &mut [usize]) -> usize { + if (st.len() < ZI_SLOTS) { + return 0; + } + st[(W) as usize] = 0; + let mut done: usize = 0; + let mut p: usize = skip_ws(src, 0); + while (p < src.len()) { + let mut end: usize = z_item_end(src, p); + if (end <= p) { + end = (p + 1); + } + if z_import_const(src, p, st) { + let lo: usize = z_rec_text(ext, src, st[(ZI_PA) as usize], st[(ZI_PB) as usize]); + if (((lo < ext.len()) && z_ext_const(ext, lo, z_seg_end(ext, lo), src, st)) && z_one_line(src, st[(ZI_NB) as usize], st[(ZI_SEMI) as usize])) { + put_src(src, st, out, done, st[(ZI_NB) as usize]); + if (st[(ZI_TA) as usize] != 0) { + puts(st, out, b": "); + put_src(src, st, out, st[(ZI_TA) as usize], st[(ZI_TB) as usize]); + puts(st, out, b" = "); + } else { + if (st[(ZI_XA) as usize] != 0) { + puts(st, out, b": "); + put_src(ext, st, out, st[(ZI_XA) as usize], st[(ZI_XB) as usize]); + puts(st, out, b" = "); + } else { + puts(st, out, b" = "); + } + } + put_src(ext, st, out, st[(ZI_LA) as usize], st[(ZI_LB) as usize]); + done = st[(ZI_SEMI) as usize]; + } + } + p = skip_ws(src, end); + } + put_src(src, st, out, done, src.len()); + return st[(W) as usize]; +} + pub fn z_ci_at(src: &[u8], i: usize, s: &[u8]) -> bool { if ((i + s.len()) > src.len()) { return false; @@ -3484,6 +3802,27 @@ pub fn to_t27(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize]) -> usiz return z_passes(src, name, out, st, true); } +pub fn z_ext_add(ext: &mut [u8], w: usize, rel: &[u8], text: &[u8]) -> usize { + let mut n: usize = w; + ext[(n) as usize] = Z_B_REC; + n = (n + 1); + let mut k: usize = 0; + while (k < rel.len()) { + ext[(n) as usize] = rel[(k) as usize]; + n = (n + 1); + k = (k + 1); + } + ext[(n) as usize] = B_NL; + n = (n + 1); + k = 0; + while (k < text.len()) { + ext[(n) as usize] = text[(k) as usize]; + n = (n + 1); + k = (k + 1); + } + return n; +} + pub const AT: usize = 3; pub const B_AMP: u8 = 38; diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 index 6625764423..a96ee3b109 100644 --- a/specs/tools/t27o/zig_convert.t27 +++ b/specs/tools/t27o/zig_convert.t27 @@ -20,6 +20,10 @@ // `const B: f64 = std.math.pi * 2.0;`), and of an array literal (`const T = [_]u8{ 1, 2, 3 };` is // `const T : [3]u8 = [1, 2, 3];`); `test "name" { .. }`. `const std = @import("std");` and `const math = std.math;` leave // no trace: the table knows them as namespaces (std, math, mem, testing, ascii) and `math.pi`, `std.math.pi` mean the same. +// - Consts of another file (#10823): `pub const C = @import("x.zig").C;` where x.zig is a file of this one's own directory tree +// and its `pub const C` is a scalar literal is written as that const, before the file is read (`imports` names the files the +// glue reads, `inline_imports` writes the values; see the section "consts imported from another file"). Any other import +// stays external: a use of its name is refused ("uses external"). // - Containers: the fns and consts of a `struct`, `enum` or `union` are read one by one and named `Container_name` // (`Optimizer.bitLength(n)` and a bare `bitLength(n)` inside it are `Optimizer_bitLength(n)`), as the Rust front end names // a method `Type_method`. A container that holds only fns and consts is a namespace and leaves no comment; one with a @@ -64,7 +68,7 @@ // Refused, with the kind a sweep ranks them by: pointers and `.*`, optionals and `.?` / orelse, error unions / try / catch / // error values, types other than the scalars above (structs, enums, unions as values, u128, arbitrary-width ints), comptime and // anytype, payload captures, labels, defer, unreachable, anonymous literals, export / extern / inline fns, mutable -// file-level vars, a const that is another file's, names nobody declares, bare braces as a statement. Step 2 of #10597 +// file-level vars, a const that is another file's and no scalar literal of its own tree, names nobody declares, bare braces as a statement. Step 2 of #10597 // reads same-file structs / enums and `*T` self methods; step 3 error unions. // -- // Three t27c behaviours shaped this spec: `use` splices by NAME (every word outside a `//` comment, string literals @@ -2873,6 +2877,273 @@ pub fn z_item(src: []const u8, st: []usize, out: []u8) -> void { st[P] = skip_ws(src, end); } +// ---------------------------------------------------------------- consts imported from another file (#10823) +// `pub const C = @import("x.zig").C;` makes C another file's: the converter alone knows nothing of x.zig, so every use +// of C is refused ("uses external"). When x.zig is a file of this file's own directory tree and its `pub const C` is a +// scalar literal, the value is written in place of the import, before the file is read: `pub const C: u32 = 5;`. +// Two fns, both pure, and the glue between them (it alone reads files): `imports` lists the paths to read, once each; +// the glue reads them and passes the bundle on, one record per file it could read, `Z_B_REC path \n text`; +// `inline_imports` finds each record by its path. A line is never added or taken: a declaration spread over several +// lines is left as it is, and so is anything that is not a literal. What is left stays refused with its reason: +// "uses external `C`". +// Allowed: a path of the form `a.zig` or `dir/a.zig` (also `./a.zig`), no `..`, no leading `/`, no `\`, no `:`: the +// directory tree of the file that imports, never above it. Values: an integer, a float, `true`, `false`, or a string +// with no escape, of at most Z_LIT_MAX bytes, under a scalar type or none. A const of any other type, an expression, a +// const that is not `pub` (Zig itself refuses it through @import), a fn or a type is not resolved. + +/// Starts each record of the bundle `inline_imports` reads: this byte, the path, a newline, the file's text. A file that +/// holds this byte is not resolved from. +pub const Z_B_REC : u8 = 1; +/// The longest literal written in: with it, the output is never more than 4 times the source and 64 bytes. +pub const Z_LIT_MAX : usize = 64; + +/// The slots of one import declaration, in the state vector the glue passes (from 8: slot 1 is W, where the output is +/// counted): the name, the type written in the importing file (0 when none), the path without its quotes, the member, the +/// `;` that ends it; then, once the imported file is read, its type and its literal. +pub const ZI_NA : usize = 8; +pub const ZI_NB : usize = 9; +pub const ZI_TA : usize = 10; +pub const ZI_TB : usize = 11; +pub const ZI_PA : usize = 12; +pub const ZI_PB : usize = 13; +pub const ZI_MA : usize = 14; +pub const ZI_MB : usize = 15; +pub const ZI_SEMI : usize = 16; +pub const ZI_XA : usize = 17; +pub const ZI_XB : usize = 18; +pub const ZI_LA : usize = 19; +pub const ZI_LB : usize = 20; +/// The state vector `imports` and `inline_imports` need holds at least this many slots. +pub const ZI_SLOTS : usize = 21; + +/// Is src[a .. b] a path inside the directory tree of the file that imports it: ends in .zig, relative, no `..` +/// component, no backslash, no colon, no control byte? +pub fn z_import_path_ok(src: []const u8, a: usize, b: usize) -> bool { + if (b < a + 5 or b > src.len) { return false; } + if (!word_is(src, b - 4, b, ".zig")) { return false; } + if (src[a] == B_SLASH) { return false; } + var comp : usize = a; + var k : usize = a; + while (k < b) { + const c : u8 = src[k]; + if (c == B_BACKSLASH or c == B_COLON or c < B_SPACE or c == B_DEL) { return false; } + if (c == B_SLASH) { + if (k - comp == 2 and src[comp] == B_DOT and src[comp + 1] == B_DOT) { return false; } + comp = k + 1; + } + k = k + 1; + } + return true; +} + +/// The end of the scalar literal at buf[p]: a number (a minus sign before it allowed), `true`, `false`, or a string with no +/// escape and no newline, of at most Z_LIT_MAX bytes. p itself when there is none. +pub fn z_lit_end(buf: []const u8, p: usize) -> usize { + var q : usize = p; + if (head(buf, q) == B_MINUS) { q = q + 1; } + const e : usize = z_tok_end(buf, q); + if (e <= q or e > buf.len or e - p > Z_LIT_MAX) { return p; } + if (is_digit(head(buf, q))) { return e; } + if (q != p) { return p; } + if (word_is(buf, q, e, "true") or word_is(buf, q, e, "false")) { return e; } + if (head(buf, q) != B_QUOTE or e < q + 2 or buf[e - 1] != B_QUOTE) { return p; } + var k : usize = q + 1; + while (k < e - 1) { + if (buf[k] == B_BACKSLASH or buf[k] == B_NL) { return p; } + k = k + 1; + } + return e; +} + +/// Does the item starting at src[p] have the form `[pub] const NAME [: scalar] = @import("path.zig").MEMBER;` with a path +/// z_import_path_ok allows? Its slots are written in ps (ZI_NA .. ZI_SEMI). +pub fn z_import_const(src: []const u8, p: usize, ps: []usize) -> bool { + var q : usize = p; + if (z_is(src, q, "pub")) { q = skip_ws(src, z_tok_end(src, q)); } + if (!z_is(src, q, "const")) { return false; } + const na : usize = skip_ws(src, z_tok_end(src, q)); + const nb : usize = z_tok_end(src, na); + if (!is_alpha(head(src, na))) { return false; } + ps[ZI_NA] = na; + ps[ZI_NB] = nb; + ps[ZI_TA] = 0; + ps[ZI_TB] = 0; + var t : usize = skip_ws(src, nb); + if (z_is(src, t, ":")) { + const ta : usize = skip_ws(src, z_tok_end(src, t)); + const tb : usize = z_tok_end(src, ta); + if (!z_scalar(src, ta, tb)) { return false; } + ps[ZI_TA] = ta; + ps[ZI_TB] = tb; + t = skip_ws(src, tb); + } + if (!z_is(src, t, "=")) { return false; } + const i : usize = skip_ws(src, z_tok_end(src, t)); + if (!z_is(src, i, "@import")) { return false; } + const o : usize = skip_ws(src, z_tok_end(src, i)); + if (!z_is(src, o, "(")) { return false; } + const s : usize = skip_ws(src, z_tok_end(src, o)); + const sb : usize = z_tok_end(src, s); + if (head(src, s) != B_QUOTE or sb < s + 3 or head(src, sb - 1) != B_QUOTE) { return false; } + if (!z_import_path_ok(src, s + 1, sb - 1)) { return false; } + const c : usize = skip_ws(src, sb); + if (!z_is(src, c, ")")) { return false; } + const d : usize = skip_ws(src, z_tok_end(src, c)); + if (!z_is(src, d, ".")) { return false; } + const m : usize = skip_ws(src, z_tok_end(src, d)); + const mb : usize = z_tok_end(src, m); + if (!is_alpha(head(src, m))) { return false; } + const semi : usize = skip_ws(src, mb); + if (head(src, semi) != B_SEMI) { return false; } + ps[ZI_PA] = s + 1; + ps[ZI_PB] = sb - 1; + ps[ZI_MA] = m; + ps[ZI_MB] = mb; + ps[ZI_SEMI] = semi; + return true; +} + +/// The value of the const the declaration named by ps imports, read from ext[nb ..] (the text after the const's name in the +/// imported file): `[: scalar] = literal;`. Its type and its literal are written in ps (ZI_XA .. ZI_LB). +pub fn z_ext_value(ext: []const u8, nb: usize, ps: []usize) -> bool { + ps[ZI_XA] = 0; + ps[ZI_XB] = 0; + var t : usize = skip_ws(ext, nb); + if (z_is(ext, t, ":")) { + const ta : usize = skip_ws(ext, z_tok_end(ext, t)); + const tb : usize = z_tok_end(ext, ta); + if (!z_scalar(ext, ta, tb)) { return false; } + ps[ZI_XA] = ta; + ps[ZI_XB] = tb; + t = skip_ws(ext, tb); + } + if (!z_is(ext, t, "=")) { return false; } + const la : usize = skip_ws(ext, z_tok_end(ext, t)); + const le : usize = z_lit_end(ext, la); + if (le == la or head(ext, skip_ws(ext, le)) != B_SEMI) { return false; } + ps[ZI_LA] = la; + ps[ZI_LB] = le; + return true; +} + +/// Is there, in the file ext[lo .. hi), a file-level `pub const MEMBER` whose value is a scalar literal? MEMBER is +/// src[ps[ZI_MA] .. ps[ZI_MB]]. An item that runs past hi belongs to a broken file: no answer. +pub fn z_ext_const(ext: []const u8, lo: usize, hi: usize, src: []const u8, ps: []usize) -> bool { + var p : usize = skip_ws(ext, lo); + while (p < hi) { + var end : usize = z_item_end(ext, p); + if (end <= p) { end = p + 1; } + if (end > hi) { return false; } + var q : usize = p; + if (z_is(ext, q, "pub")) { + q = skip_ws(ext, z_tok_end(ext, q)); + if (z_is(ext, q, "const")) { + const na : usize = skip_ws(ext, z_tok_end(ext, q)); + const nb : usize = z_tok_end(ext, na); + if (word_is(ext, na, nb, src[ps[ZI_MA] .. ps[ZI_MB]])) { return z_ext_value(ext, nb, ps); } + } + } + p = skip_ws(ext, end); + } + return false; +} + +/// Where the text of the file that starts at ext[from] ends: at the next Z_B_REC, or at the end of the bundle. +pub fn z_seg_end(ext: []const u8, from: usize) -> usize { + var k : usize = from; + while (k < ext.len and ext[k] != Z_B_REC) { k = k + 1; } + return k; +} + +/// Where the text of the file imported as src[a .. b] starts in the bundle: after its record's header. ext.len when the +/// bundle has no record of that path. +pub fn z_rec_text(ext: []const u8, src: []const u8, a: usize, b: usize) -> usize { + const n : usize = b - a; + var k : usize = 0; + while (k + n + 1 < ext.len) { + if (ext[k] == Z_B_REC and ext[k + 1 + n] == B_NL and word_is(ext, k + 1, k + 1 + n, src[a .. b])) { return k + n + 2; } + k = k + 1; + } + return ext.len; +} + +/// Is the path src[a .. b] a line of the list out[0 .. w) already? +pub fn z_listed(out: []const u8, w: usize, src: []const u8, a: usize, b: usize) -> bool { + const n : usize = b - a; + var k : usize = 0; + while (k + n < w and k + n < out.len) { + if ((k == 0 or out[k - 1] == B_NL) and out[k + n] == B_NL and word_is(out, k, k + n, src[a .. b])) { return true; } + k = k + 1; + } + return false; +} + +/// Is there no line break in src[a .. b)? +pub fn z_one_line(src: []const u8, a: usize, b: usize) -> bool { + var k : usize = a; + while (k < b and k < src.len) { + if (src[k] == B_NL or src[k] == B_CR) { return false; } + k = k + 1; + } + return true; +} + +/// The paths `inline_imports` will want, one per line, each once, in the order they are first declared: the glue reads each +/// and passes the texts on as records (Z_B_REC, the path, a newline, the text). The result is the size of the list; it is +/// above out.len when the buffer was too small (a list is never longer than src). +pub fn imports(src: []const u8, out: []u8, st: []usize) -> usize { + if (st.len < ZI_SLOTS) { return 0; } + st[W] = 0; + var p : usize = skip_ws(src, 0); + while (p < src.len) { + var end : usize = z_item_end(src, p); + if (end <= p) { end = p + 1; } + if (z_import_const(src, p, st) and !z_listed(out, st[W], src, st[ZI_PA], st[ZI_PB])) { + put_src(src, st, out, st[ZI_PA], st[ZI_PB]); + put(st, out, B_NL); + } + p = skip_ws(src, end); + } + return st[W]; +} + +/// src with each import of a scalar literal written as the const it is: `pub const C = @import("x.zig").C;` and `pub +/// const C: u32 = 5;` in x.zig make `pub const C: u32 = 5;`; the type written in the importing file wins over the +/// imported one. ext holds the files `imports` listed, as records (see Z_B_REC). The result is the size of +/// the output, above out.len when the buffer was too small (4 times src.len and 64 always suffice, see Z_LIT_MAX). +pub fn inline_imports(src: []const u8, ext: []const u8, out: []u8, st: []usize) -> usize { + if (st.len < ZI_SLOTS) { return 0; } + st[W] = 0; + var done : usize = 0; + var p : usize = skip_ws(src, 0); + while (p < src.len) { + var end : usize = z_item_end(src, p); + if (end <= p) { end = p + 1; } + if (z_import_const(src, p, st)) { + const lo : usize = z_rec_text(ext, src, st[ZI_PA], st[ZI_PB]); + if (lo < ext.len and z_ext_const(ext, lo, z_seg_end(ext, lo), src, st) and z_one_line(src, st[ZI_NB], st[ZI_SEMI])) { + put_src(src, st, out, done, st[ZI_NB]); + if (st[ZI_TA] != 0) { + puts(st, out, ": "); + put_src(src, st, out, st[ZI_TA], st[ZI_TB]); + puts(st, out, " = "); + } else if (st[ZI_XA] != 0) { + puts(st, out, ": "); + put_src(ext, st, out, st[ZI_XA], st[ZI_XB]); + puts(st, out, " = "); + } else { + puts(st, out, " = "); + } + put_src(ext, st, out, st[ZI_LA], st[ZI_LB]); + done = st[ZI_SEMI]; + } + } + p = skip_ws(src, end); + } + put_src(src, st, out, done, src.len); + return st[W]; +} + // ---------------------------------------------------------------- the file /// Is the text s at src[i], whatever the case? @@ -3387,3 +3658,156 @@ test negative_control_the_five_counters_are_five_different_slots_and_none_is_the assert(Z_STMT != NTEST); assert(Z_GREW != NSTUB); } + +// ---------------------------------------------------------------- tests: consts imported from another file (#10823) + +/// Test helper: the record of one imported file, Z_B_REC, the file's name, a newline, the text, appended to ext at w; the new w. +pub fn z_ext_add(ext: []u8, w: usize, rel: []const u8, text: []const u8) -> usize { + var n : usize = w; + ext[n] = Z_B_REC; + n = n + 1; + var k : usize = 0; + while (k < rel.len) { + ext[n] = rel[k]; + n = n + 1; + k = k + 1; + } + ext[n] = B_NL; + n = n + 1; + k = 0; + while (k < text.len) { + ext[n] = text[k]; + n = n + 1; + k = k + 1; + } + return n; +} + +test imports_lists_each_path_once_in_the_order_it_is_first_imported_as_a_member { + var st : [64]usize = [0] ** 64; + var out : [128]u8 = undefined; + const n : usize = imports("const std = @import(\"std\");\nconst x = @import(\"x.zig\");\npub const A = @import(\"gen_common.zig\").A;\nconst B: u32 = @import(\"./d/c.zig\").B;\nconst T = @import(\"x.zig\").T;\npub const C = @import(\"gen_common.zig\").C;\nfn f() u32 {\n return 1;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "gen_common.zig\n./d/c.zig\nx.zig\n")); +} + +test negative_control_a_path_above_the_tree_or_not_a_zig_file_or_not_a_member_is_not_listed { + var st : [64]usize = [0] ** 64; + var out : [128]u8 = undefined; + const n : usize = imports("const a = @import(\"../up.zig\").A;\nconst b = @import(\"/abs/p.zig\").B;\nconst c = @import(\"d/../e.zig\").C;\nconst d = @import(\"readme.txt\").D;\nconst e = @import(\"std\").E;\nconst f = @import(\"x.zig\").F.G;\nvar g = @import(\"y.zig\").G;\nexport const h = @import(\"z.zig\").H;\nconst i = @import(\"a\\b.zig\").I;\nconst j = @import(\"c:d.zig\").J;\nconst k: Foo = @import(\"k.zig\").K;\nconst l = @import(\".zig\").L;\n", &out, &st); + assert(n == 0); +} + +test an_imported_scalar_literal_is_written_in_place_of_the_import { + var st : [64]usize = [0] ** 64; + var ext : [512]u8 = undefined; + var w : usize = z_ext_add(&ext, 0, "a.zig", "pub const SIZE: usize = 32;\npub const NEG: i32 = -1;\npub const RATIO = 1.585;\n"); + w = z_ext_add(&ext, w, "d/b.zig", "pub const ON: bool = true;\npub const NAME = \"tri\";\n"); + var out : [512]u8 = undefined; + const n : usize = inline_imports("pub const SIZE = @import(\"a.zig\").SIZE;\nconst N: i64 = @import(\"a.zig\").NEG;\npub const RATIO = @import(\"a.zig\").RATIO;\npub const ON = @import(\"d/b.zig\").ON;\nconst NAME = @import(\"d/b.zig\").NAME;\n", ext[0 .. w], &out, &st); + assert(out_is(n, &out, 0, "pub const SIZE: usize = 32;\nconst N: i64 = -1;\npub const RATIO = 1.585;\npub const ON: bool = true;\nconst NAME = \"tri\";\n")); +} + +test negative_control_what_is_not_a_scalar_literal_of_a_pub_const_is_left_as_it_was { + var st : [64]usize = [0] ** 64; + var ext : [1024]u8 = undefined; + const theirs : []const u8 = "pub const T = struct { x: u32 };\npub fn f() u32 { return 1; }\nconst PRIV: u32 = 7;\npub const EXPR = 2 * 3;\npub const TY: Trit = -1;\npub const TEXT = \"a\\nb\";\npub const LONG = 1234567890123456789012345678901234567890123456789012345678901234567890;\npub const REF = other.X;\npub const ARR = [_]u8{ 1, 2 };\npub const NEG = -x;\npub const HEXEXPR = 0xFF - 1;\n"; + var w : usize = z_ext_add(&ext, 0, "a.zig", theirs); + var out : [1024]u8 = undefined; + const src : []const u8 = "const A = @import(\"a.zig\").T;\nconst B = @import(\"a.zig\").f;\nconst C = @import(\"a.zig\").PRIV;\nconst D = @import(\"a.zig\").EXPR;\nconst E = @import(\"a.zig\").TY;\nconst F = @import(\"a.zig\").TEXT;\nconst G = @import(\"a.zig\").LONG;\nconst H = @import(\"a.zig\").REF;\nconst I = @import(\"a.zig\").ARR;\nconst J = @import(\"a.zig\").NEG;\nconst K = @import(\"a.zig\").HEXEXPR;\nconst L = @import(\"a.zig\").MISSING;\nconst M = @import(\"gone.zig\").M;\n"; + const n : usize = inline_imports(src, ext[0 .. w], &out, &st); + assert(out_is(n, &out, 0, src)); +} + +test a_declaration_over_several_lines_or_a_bundle_with_no_record_is_left_as_it_was { + var st : [64]usize = [0] ** 64; + var ext : [128]u8 = undefined; + var w : usize = z_ext_add(&ext, 0, "a.zig", "pub const SIZE: usize = 32;\n"); + var out : [256]u8 = undefined; + const spread : []const u8 = "pub const SIZE =\n @import(\"a.zig\").SIZE;\n"; + const n : usize = inline_imports(spread, ext[0 .. w], &out, &st); + assert(out_is(n, &out, 0, spread)); + const one : []const u8 = "pub const SIZE = @import(\"a.zig\").SIZE;\n"; + const m : usize = inline_imports(one, ext[0 .. 0], &out, &st); + assert(out_is(m, &out, 0, one)); + const k : usize = inline_imports(one, ext[0 .. w], &out, &st); + assert(out_is(k, &out, 0, "pub const SIZE: usize = 32;\n")); +} + +test an_imported_const_is_found_by_its_name_among_the_items_of_its_file_and_not_inside_one { + var st : [64]usize = [0] ** 64; + var ext : [512]u8 = undefined; + var w : usize = z_ext_add(&ext, 0, "a.zig", "const std = @import(\"std\");\nfn local() u32 {\n const SIZE: u32 = 1;\n return SIZE;\n}\ntest \"t\" {\n const SIZE: u32 = 2;\n _ = SIZE;\n}\npub const SIZE: u32 = 3;\n"); + var out : [128]u8 = undefined; + const n : usize = inline_imports("const SIZE = @import(\"a.zig\").SIZE;\n", ext[0 .. w], &out, &st); + assert(out_is(n, &out, 0, "const SIZE: u32 = 3;\n")); +} + +test a_missing_record_in_the_middle_does_not_shift_the_next_import_and_the_line_count_never_changes { + var st : [64]usize = [0] ** 64; + var ext : [256]u8 = undefined; + var w : usize = z_ext_add(&ext, 0, "b.zig", "pub const B: u8 = 2;\n"); + w = z_ext_add(&ext, w, "c.zig", "pub const C: u8 = 3;\n"); + var out : [256]u8 = undefined; + const n : usize = inline_imports("const A = @import(\"a.zig\").A;\nconst B = @import(\"b.zig\").B;\n\n// kept\nconst C = @import(\"c.zig\").C;\n", ext[0 .. w], &out, &st); + assert(out_is(n, &out, 0, "const A = @import(\"a.zig\").A;\nconst B: u8 = 2;\n\n// kept\nconst C: u8 = 3;\n")); +} + +test the_imported_value_makes_a_fn_that_used_it_convert_and_without_it_the_fn_stays_refused { + var ext : [256]u8 = undefined; + var w : usize = z_ext_add(&ext, 0, "gen_common.zig", "pub const SIMD_WIDTH: usize = 32;\n"); + const src : []const u8 = "pub const SIMD_WIDTH = @import(\"gen_common.zig\").SIMD_WIDTH;\npub fn lanes(n: usize) usize {\n return n / SIMD_WIDTH;\n}\n"; + var flat : [512]u8 = undefined; + var ist : [64]usize = [0] ** 64; + const m : usize = inline_imports(src, ext[0 .. w], &flat, &ist); + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text(flat[0 .. m], &out, &st); + assert(out_is(n, &out, 0, "pub const SIMD_WIDTH : usize = 32;\n\npub fn lanes(n: usize) -> usize {\n return n / SIMD_WIDTH;\n}\n\n")); + assert(st[NOK] == 1); + var out2 : [512]u8 = undefined; + var st2 : [2048]usize = [0] ** 2048; + const n2 : usize = z_body_text(src, &out2, &st2); + assert(out_is(n2, &out2, 0, "// t27o: refused fn lanes (line 2): uses external `SIMD_WIDTH` at line 3\n\n")); + assert(st2[NOK] == 0); +} + +test the_output_is_never_more_than_four_times_the_source_and_64_bytes { + var st : [64]usize = [0] ** 64; + var text : [128]u8 = undefined; + var k : usize = 0; + while (k < 64) { + text[k] = 49; + k = k + 1; + } + var theirs : [128]u8 = undefined; + var t : usize = 0; + const head_text : []const u8 = "pub const B: usize = "; + while (t < head_text.len) { + theirs[t] = head_text[t]; + t = t + 1; + } + var j : usize = 0; + while (j < 64) { + theirs[t + j] = 49; + j = j + 1; + } + theirs[t + 64] = 59; + var ext : [256]u8 = undefined; + const w : usize = z_ext_add(&ext, 0, "a.zig", theirs[0 .. t + 65]); + const src : []const u8 = "const A=@import(\"a.zig\").B;"; + var out : [512]u8 = undefined; + const n : usize = inline_imports(src, ext[0 .. w], &out, &st); + assert(n <= 4 * src.len + 64); + assert(n == 82); +} + +test negative_control_a_short_state_vector_gives_nothing_and_a_short_buffer_is_told_by_the_size { + var short : [8]usize = [0] ** 8; + var out : [64]u8 = undefined; + assert(imports("const A = @import(\"a.zig\").A;\n", &out, &short) == 0); + assert(inline_imports("const A = @import(\"a.zig\").A;\n", "", &out, &short) == 0); + var st : [64]usize = [0] ** 64; + var tiny : [4]u8 = undefined; + assert(imports("const A = @import(\"a.zig\").A;\n", &tiny, &st) == 6); + assert(inline_imports("const A = @import(\"a.zig\").A;\n", "", &tiny, &st) == 30); +} From 59eb35c18b3a3a1d600209e07a9c2500aca409e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 13:30:24 +0700 Subject: [PATCH 4/5] t27o: std.math.pow(f64, x, y) converts, bit for bit Zig's, through a new zig_std.t27 (Closes #10823) 103 refusals, the largest left by step 1, were `std.math.pow`. t27 can produce Zig's result, so it does instead of keeping the refusal. Zig 0.16's pow is not libm's: it is Go's math.Pow, a port that squares the significand of x (frexp), counts the exponent apart, takes exp(yf * log x) for the fraction and scales once with ldexp. Plain repeated multiplication is not Zig's result (it differs on 69 of the 191 integer powers among the recorded vectors; libm's pow differs too), so zig_std.t27 (new, like rust_std.t27 beside rust_convert.t27) holds that algorithm step for step in operations t27b and t27c both have: @exp and @log (compiler_rt's, which t27b runs bit for bit), @sqrt, @floor, and Zig's ldexp ported as it is. The port is Zig's code and not the IEEE rule: Zig's ldexp tests an exact tie by `@ctz(repr)`, which counts the exponent field's own zeros, so 0.5 * 2^-1074 becomes 2^-1074 where musl's scalbn (and IEEE) gives 0. The first version used musl's scalbn and the recorded vectors caught it. Evidence: 433 pow vectors, 124 ldexp and 16 frexp vectors recorded with `zig run` (Zig 0.16.0, Debug, no libc) and tested by their bits under t27b AND t27c (the whole 1325-vector set of the lane passed under t27b and t27c before it was cut to these). Mutants of the port (the 0.5 test of the fraction, the +1 of yi, the sign of ae, the 2^63 threshold, the tie rule, the denormal exponent, the 1 / sqrt(x) branch, the 2^53 odd-integer limit, the overflow bound) are each killed. zig_convert.t27 writes `std.math.pow(f64, x, y)` as std_math_pow_f64(x, y) and a `use tools::t27o::zig_std::std_math_pow_f64;` after the header when a fn or test that stayed in the output calls it. Other types and the old two-operand form are refused as their own kind, "pow of a type other than f64"; a call in a file-level const (t27b cannot evaluate it; Zig does at compile time) as "pow in a file-level const". Tests and negative controls for each, and the use line. Measured on trinity 0770abb99 src/ and trios 483dd9ee crates/: converted fns 1837 -> 2022, translated tests 1766 -> 1887; 52 specs call the port, 45 pass under t27b, 5 fail the tests that fail under `zig test` too (phi_lerp, baryon_formulas, spectrum, in both repositories), 2 are not pow's (an `@as(f64, u64)` that Zig 0.16 refuses as well). Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oZig.json | 16 +- .trinity/seals/t27o_T27oZigStd.json | 28 + gen/rust/tools/t27o/zig_convert.rs | 74 ++- specs/tools/t27o/zig_convert.t27 | 115 +++- specs/tools/t27o/zig_std.t27 | 953 ++++++++++++++++++++++++++++ 5 files changed, 1173 insertions(+), 13 deletions(-) create mode 100644 .trinity/seals/t27o_T27oZigStd.json create mode 100644 specs/tools/t27o/zig_std.t27 diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json index da7e0d6651..e0064a1912 100644 --- a/.trinity/seals/t27o_T27oZig.json +++ b/.trinity/seals/t27o_T27oZig.json @@ -7,23 +7,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:673010adda7df541762662c16140e3b23a0359623b1836f14e68744332f3f732", - "gen_hash_rust": "sha256:550c8f70e4bfc65cd0e8ca9dd4095f4abc7f11253d0c11ab31d860d73a3d8f13", - "gen_hash_verilog": "sha256:a4a33303f27f566873be7788e4f487c89ffa21e8a348be73aca214be9b3f2014", - "gen_hash_zig": "sha256:42c52299a562ffb982a30a8e7af775a086ed4477f1c9fc96f239be5388fb8e12", + "gen_hash_c": "sha256:61069fc34247483fbcd018ad03e650cc10478c01a7a3c908e9ff5a52e2e6b56a", + "gen_hash_rust": "sha256:5b79bfee2a5d9770de677f1717c9fb5fbf022ca8353f0ced3993efc58011cb60", + "gen_hash_verilog": "sha256:172face9cae7881f4f41c5fd65f10e3c57ddeee52e62861bfe810c8152590e4f", + "gen_hash_zig": "sha256:268a0db9adfb400279ecb45adf3c89a4f3a50d43da9c0540659cc70d739e16c7", "module": "T27oZig", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:55:46Z", + "sealed_at": "2026-10-19T06:16:16Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:0514a77d14353d2164d42d2eae4256293ff237e413f2379b1f636a08ff3a3d9f", + "spec_hash": "sha256:90d1290eb33386b4fb8f4e10721bdda9b5f610d54ac63d8f2c26bda0beb579dd", "spec_path": "specs/tools/t27o/zig_convert.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 56, - "total": 56, + "passed": 61, + "total": 61, "vacuous": 1 }, "toolchain": { diff --git a/.trinity/seals/t27o_T27oZigStd.json b/.trinity/seals/t27o_T27oZigStd.json new file mode 100644 index 0000000000..d277819ff3 --- /dev/null +++ b/.trinity/seals/t27o_T27oZigStd.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:75c0ec2458e26d4de273854fe7346972fcff9e85a19b52bd940c94f82cbaee1a", + "gen_hash_rust": "sha256:b0c91ce7507930661c0606e5764ca3c1c49fb9f18f1a3672835f156a52818ba7", + "gen_hash_verilog": "sha256:d49e1fdaf0787d3c2f03c44f1bcc3020d9c3c62fba11c7dcfb5bfa38ec1e40fa", + "gen_hash_zig": "sha256:05fe1623474e2296103ada5d5ade121c1271609c5581723d4f7fd4bdfb31c702", + "module": "T27oZigStd", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T06:07:00Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:282b6910324ebe55c2d5ba68c39ed2db1663c4fbf2864deba3265c908ebbef3a", + "spec_path": "specs/tools/t27o/zig_std.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 7, + "total": 7, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/gen/rust/tools/t27o/zig_convert.rs b/gen/rust/tools/t27o/zig_convert.rs index 81a764ed67..d588867841 100644 --- a/gen/rust/tools/t27o/zig_convert.rs +++ b/gen/rust/tools/t27o/zig_convert.rs @@ -1,6 +1,6 @@ // Generated from .t27 spec // DO NOT EDIT — generated by t27c -// NOT LOWERED BY THIS BACKEND: 56 test(s), 0 invariant(s), 0 function(s) with no body. +// NOT LOWERED BY THIS BACKEND: 61 test(s), 0 invariant(s), 0 function(s) with no body. // This backend emits declarations only. The spec's checks live in // the Zig and Verilog outputs; do not read this file as verified. @@ -30,6 +30,8 @@ pub const Z_RET: usize = 19; pub const Z_GREW: usize = 100; +pub const Z_USEPOS: usize = 101; + pub const Z_HIT: usize = 21; pub const Z_NARGS: usize = 22; @@ -195,6 +197,10 @@ pub const Z_R_BOPERAND: usize = 32; pub const Z_R_HOIST: usize = 33; +pub const Z_R_POW: usize = 34; + +pub const Z_R_POWCONST: usize = 35; + pub fn z_put_why(st: &mut [usize], out: &mut [u8], code: usize) -> () { if (code == Z_R_GENERIC) { puts(st, out, b"comptime or generic"); @@ -324,6 +330,14 @@ pub fn z_put_why(st: &mut [usize], out: &mut [u8], code: usize) -> () { puts(st, out, b"array literal outside a statement"); return (); } + if (code == Z_R_POW) { + puts(st, out, b"pow of a type other than f64"); + return (); + } + if (code == Z_R_POWCONST) { + puts(st, out, b"pow in a file-level const"); + return (); + } puts(st, out, b"syntax"); } @@ -1188,6 +1202,25 @@ pub fn z_std_member(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, ns: z_want(src, st, b")"); return L_ATOM; } + if word_is(src, ma, mb, b"pow") { + if (st[(Z_INCONST) as usize] == 1) { + refuse(st, Z_R_POWCONST, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, b"("); + if !(z_take(src, st, b"f64")) { + refuse(st, Z_R_POW, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, b","); + puts(st, out, b"std_math_pow_f64("); + z_args_rest(src, st, out); + if (st[(Z_NARGS) as usize] != 2) { + refuse(st, Z_R_SYNTAX, a0, a0, mb); + } + st[(Z_CLS) as usize] = ZC_F64; + return L_ATOM; + } if (word_is(src, ma, mb, b"maxInt") || word_is(src, ma, mb, b"minInt")) { z_want(src, st, b"("); let ta: usize = st[(P) as usize]; @@ -3709,6 +3742,31 @@ pub fn z_report(st: &mut [usize], out: &mut [u8]) -> () { puts(st, out, b"%\n"); } +pub fn z_out_mentions(out: &[u8], a: usize, b: usize, s: &[u8]) -> bool { + let mut j: usize = a; + while ((j + s.len()) <= b) { + if z_out_has(out, j, s) { + return true; + } + j = (j + 1); + } + return false; +} + +pub fn z_put_uses(st: &mut [usize], out: &mut [u8]) -> () { + let from: usize = st[(Z_USEPOS) as usize]; + let w0: usize = st[(W) as usize]; + if ((w0 <= out.len()) && !(z_out_mentions(out, from, w0, b"std_math_pow_f64("))) { + return (); + } + puts(st, out, b"use tools::t27o::zig_std::std_math_pow_f64;\n\n"); + if (st[(W) as usize] <= out.len()) { + reverse(out, from, w0); + reverse(out, w0, st[(W) as usize]); + reverse(out, from, st[(W) as usize]); + } +} + pub fn z_pass(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: bool) -> () { st[(W) as usize] = 0; st[(ERR) as usize] = 0; @@ -3738,11 +3796,13 @@ pub fn z_pass(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: if whole { z_header(st, out, name); } + st[(Z_USEPOS) as usize] = st[(W) as usize]; st[(P) as usize] = skip_ws(src, 0); while (st[(P) as usize] < src.len()) { z_item(src, st, out); } if whole { + z_put_uses(st, out); z_report(st, out); } } @@ -4326,6 +4386,18 @@ pub fn reserved(src: &[u8], a: usize, b: usize) -> bool { return ((((((((((((((((((((((((((word_is(src, a, b, b"and") || word_is(src, a, b, b"or")) || word_is(src, a, b, b"test")) || word_is(src, a, b, b"var")) || word_is(src, a, b, b"error")) || word_is(src, a, b, b"null")) || word_is(src, a, b, b"undefined")) || word_is(src, a, b, b"switch")) || word_is(src, a, b, b"orelse")) || word_is(src, a, b, b"try")) || word_is(src, a, b, b"defer")) || word_is(src, a, b, b"comptime")) || word_is(src, a, b, b"module")) || word_is(src, a, b, b"invariant")) || word_is(src, a, b, b"bench")) || word_is(src, a, b, b"catch")) || word_is(src, a, b, b"unreachable")) || word_is(src, a, b, b"anytype")) || word_is(src, a, b, b"opaque")) || word_is(src, a, b, b"packed")) || word_is(src, a, b, b"export")) || word_is(src, a, b, b"inline")) || word_is(src, a, b, b"align")) || word_is(src, a, b, b"noreturn")) || word_is(src, a, b, b"errdefer")) || word_is(src, a, b, b"spec")) || word_is(src, a, b, b"void")); } +pub fn reverse(out: &mut [u8], a: usize, b: usize) -> () { + let mut i: usize = a; + let mut j: usize = b; + while (j > (i + 1)) { + j = (j - 1); + let t: u8 = out[(i) as usize]; + out[(i) as usize] = out[(j) as usize]; + out[(j) as usize] = t; + i = (i + 1); + } +} + pub fn same_at(src: &[u8], q: usize, e: usize, a: usize) -> bool { let mut k: usize = 0; while ((q + k) < e) { diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 index a96ee3b109..66da783ca7 100644 --- a/specs/tools/t27o/zig_convert.t27 +++ b/specs/tools/t27o/zig_convert.t27 @@ -40,6 +40,12 @@ // `@floor` `@ceil` `@round` `@exp` `@log` `@cos` `@abs` `@min` `@max` `@divTrunc` `@rem` `@mod`, and std.math.pi / e / tau / phi // / sqrt2 / ln2 ..., maxInt / minInt (written as the literal), sqrt / floor / ceil / exp / cos (the builtin of the same // name), isNan / isPositiveInf / isNegativeInf / inf / nan, std.mem.eql(u8, a, b) and std.debug.assert(c) (an `assert(c)`). +// - std.math.pow(f64, x, y) (#10823) is `std_math_pow_f64(x, y)` of zig_std.t27, which the spec `use`s (the line follows the +// header, when a fn or test that stayed in the output calls it): Zig's own algorithm (Go's math.Pow: the significand of x +// squared, the exponent counted apart, exp(yf * log x) for the fraction, one ldexp), step for step, equal to Zig's bits on +// 433 recorded vectors under t27b and t27c. Plain repeated multiplication is not: it differs on 69 of the 191 integer powers among them. +// Any other type (`pow(f32, ..)`, the old two-operand `pow(x, y)`) is refused as "pow of a type other than f64", and a call in +// a file-level const as "pow in a file-level const" (t27b cannot evaluate a call there; Zig does it at compile time). // - Tests: `try std.testing.expectEqual(e, a)` is `assert(a == e)`, `expect(c)` is `assert(c)`, `expectApproxEqAbs(e, a, t)` // is `assert(@abs(a - e) <= t)` (Zig also passes two equal infinities; this does not), `expectApproxEqRel(e, a, t)` is // `assert(@abs(a - e) <= @max(@abs(a), @abs(e)) * t)`, `expectEqualStrings(e, a)` and `expectEqualSlices(u8, e, a)` are @@ -62,8 +68,8 @@ // literal (Zig narrows the result type), a float `@rem` / `@mod` / `@divTrunc`, a literal operand of `@exp` `@floor` // `@ceil` `@round` `@cos` (Zig types it from the result location), a builtin whose operand is not a literal inside a // file-level const, and an `@intCast` that is a shift count. `@truncate` `@bitCast` `@sin` `@log2` `@Vector` and the like have no -// t27b routine and are refused by name, as are `std.math.pow` / log / clamp / maxInt of a non-integer, `std.mem.startsWith` -// and the rest of std: "std call `std.math.pow`". +// t27b routine and are refused by name, as are `std.math.log` / clamp / maxInt of a non-integer, `std.mem.startsWith` +// and the rest of std: "std call `std.math.log`". // -- // Refused, with the kind a sweep ranks them by: pointers and `.*`, optionals and `.?` / orelse, error unions / try / catch / // error values, types other than the scalars above (structs, enums, unions as values, u128, arbitrary-width ints), comptime and @@ -109,6 +115,7 @@ use tools::t27o::rust_convert::reserved; use tools::t27o::rust_convert::is_upper; use tools::t27o::rust_convert::is_open; use tools::t27o::rust_convert::is_close; +use tools::t27o::rust_convert::reverse; // ---------------------------------------------------------------- state @@ -131,6 +138,8 @@ pub const Z_NFILE : usize = 17; pub const Z_ROUND : usize = 18; pub const Z_RET : usize = 19; pub const Z_GREW : usize = 100; +/// Where the `use` lines of the output go: just after the header. +pub const Z_USEPOS : usize = 101; pub const Z_HIT : usize = 21; pub const Z_NARGS : usize = 22; pub const Z_ARG0 : usize = 23; @@ -246,6 +255,8 @@ pub const Z_R_FULL : usize = 30; pub const Z_R_ITEM : usize = 31; pub const Z_R_BOPERAND : usize = 32; pub const Z_R_HOIST : usize = 33; +pub const Z_R_POW : usize = 34; +pub const Z_R_POWCONST : usize = 35; /// The reason of a refusal code, in words: the kind a sweep ranks refusals by (rust_convert's reasons read the same way). pub fn z_put_why(st: []usize, out: []u8, code: usize) -> void { @@ -281,6 +292,8 @@ pub fn z_put_why(st: []usize, out: []u8, code: usize) -> void { if (code == Z_R_ITEM) { puts(st, out, "declaration"); return; } if (code == Z_R_BOPERAND) { puts(st, out, "builtin operand t27b cannot run"); return; } if (code == Z_R_HOIST) { puts(st, out, "array literal outside a statement"); return; } + if (code == Z_R_POW) { puts(st, out, "pow of a type other than f64"); return; } + if (code == Z_R_POWCONST) { puts(st, out, "pow in a file-level const"); return; } puts(st, out, "syntax"); } @@ -1023,6 +1036,23 @@ pub fn z_std_member(src: []const u8, st: []usize, out: []u8, a0: usize, ns: usiz z_want(src, st, ")"); return L_ATOM; } + if (word_is(src, ma, mb, "pow")) { + if (st[Z_INCONST] == 1) { + refuse(st, Z_R_POWCONST, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, "("); + if (!z_take(src, st, "f64")) { + refuse(st, Z_R_POW, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, ","); + puts(st, out, "std_math_pow_f64("); + z_args_rest(src, st, out); + if (st[Z_NARGS] != 2) { refuse(st, Z_R_SYNTAX, a0, a0, mb); } + st[Z_CLS] = ZC_F64; + return L_ATOM; + } if (word_is(src, ma, mb, "maxInt") or word_is(src, ma, mb, "minInt")) { z_want(src, st, "("); const ta : usize = st[P]; @@ -3212,6 +3242,31 @@ pub fn z_report(st: []usize, out: []u8) -> void { puts(st, out, "%\n"); } +/// Does out[a .. b) hold the text s? +pub fn z_out_mentions(out: []const u8, a: usize, b: usize, s: []const u8) -> bool { + var j : usize = a; + while (j + s.len <= b) { + if (z_out_has(out, j, s)) { return true; } + j = j + 1; + } + return false; +} + +/// The `use` lines of the output, moved to just after the header: zig_std.t27's std_math_pow_f64 when a fn or test that +/// stayed in the output calls it (a refused fn's text was cut off, so a call there does not count). The output being too +/// small to read, the line is counted all the same, so the size asked for is never short. +pub fn z_put_uses(st: []usize, out: []u8) -> void { + const from : usize = st[Z_USEPOS]; + const w0 : usize = st[W]; + if (w0 <= out.len and !z_out_mentions(out, from, w0, "std_math_pow_f64(")) { return; } + puts(st, out, "use tools::t27o::zig_std::std_math_pow_f64;\n\n"); + if (st[W] <= out.len) { + reverse(out, from, w0); + reverse(out, w0, st[W]); + reverse(out, from, st[W]); + } +} + /// One pass over the items: the output starts over, the name table keeps its refusals. pub fn z_pass(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: bool) -> void { st[W] = 0; @@ -3240,9 +3295,13 @@ pub fn z_pass(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: } } if (whole) { z_header(st, out, name); } + st[Z_USEPOS] = st[W]; st[P] = skip_ws(src, 0); while (st[P] < src.len) { z_item(src, st, out); } - if (whole) { z_report(st, out); } + if (whole) { + z_put_uses(st, out); + z_report(st, out); + } } /// Passes until no fn or const is newly refused: the first learns the classes of the file's names, the next ones @@ -3384,7 +3443,7 @@ test std_calls_with_a_measured_t27_form_and_the_rest_refused_by_name { var out : [1280]u8 = undefined; var st : [2048]usize = [0] ** 2048; const n : usize = z_body_text("const std = @import(\"std\");\nconst math = std.math;\nconst mem = std.mem;\nfn consts() f64 {\n return std.math.pi + math.e + math.tau + math.phi;\n}\nfn limits() i64 {\n return std.math.maxInt(u32) + math.minInt(i8) + std.math.maxInt(i64);\n}\nfn same(a: []const u8, b: []const u8) bool {\n return mem.eql(u8, a, b);\n}\nfn root(x: f64) f64 {\n return math.sqrt(x) + math.floor(x) + std.math.cos(x);\n}\nfn special() bool {\n return std.math.isNan(std.math.nan(f64)) or std.math.isPositiveInf(std.math.inf(f32));\n}\nfn power(x: f64) f64 {\n return std.math.pow(f64, x, 2.0);\n}\nfn prefix(a: []const u8, b: []const u8) bool {\n return mem.startsWith(u8, a, b);\n}\nfn other(a: []const u16, b: []const u16) bool {\n return std.mem.eql(u16, a, b);\n}\nfn out() void {\n std.debug.print(\"x\", .{});\n}\nfn lit_root() f64 {\n return math.floor(2.5);\n}\n", &out, &st); - assert(out_is(n, &out, 0, "fn consts() -> f64 {\n return std.math.pi + std.math.e + 6.283185307179586 + 1.618033988749895;\n}\n\nfn limits() -> i64 {\n return 4294967295 + -128 + 9223372036854775807;\n}\n\nfn same(a: []const u8, b: []const u8) -> bool {\n return std.mem.eql(u8, a, b);\n}\n\nfn root(x: f64) -> f64 {\n return @sqrt(x) + @floor(x) + @cos(x);\n}\n\nfn special() -> bool {\n return std.math.isNan(std.math.nan(f64)) or std.math.isPositiveInf(std.math.inf(f32));\n}\n\n// t27o: refused fn power (line 19): std call `std.math.pow` at line 20\n\n// t27o: refused fn prefix (line 22): std call `mem.startsWith` at line 23\n\n// t27o: refused fn other (line 25): std call `std.mem.eql` at line 26\n\n// t27o: refused fn out (line 28): std call `std.debug.print` at line 29\n\n// t27o: refused fn lit_root (line 31): builtin operand t27b cannot run `math.floor(2.5)` at line 32\n\n")); + assert(out_is(n, &out, 0, "fn consts() -> f64 {\n return std.math.pi + std.math.e + 6.283185307179586 + 1.618033988749895;\n}\n\nfn limits() -> i64 {\n return 4294967295 + -128 + 9223372036854775807;\n}\n\nfn same(a: []const u8, b: []const u8) -> bool {\n return std.mem.eql(u8, a, b);\n}\n\nfn root(x: f64) -> f64 {\n return @sqrt(x) + @floor(x) + @cos(x);\n}\n\nfn special() -> bool {\n return std.math.isNan(std.math.nan(f64)) or std.math.isPositiveInf(std.math.inf(f32));\n}\n\nfn power(x: f64) -> f64 {\n return std_math_pow_f64(x, 2.0);\n}\n\n// t27o: refused fn prefix (line 22): std call `mem.startsWith` at line 23\n\n// t27o: refused fn other (line 25): std call `std.mem.eql` at line 26\n\n// t27o: refused fn out (line 28): std call `std.debug.print` at line 29\n\n// t27o: refused fn lit_root (line 31): builtin operand t27b cannot run `math.floor(2.5)` at line 32\n\n")); } test test_blocks_become_tests_and_the_expectations_asserts { @@ -3811,3 +3870,51 @@ test negative_control_a_short_state_vector_gives_nothing_and_a_short_buffer_is_t assert(imports("const A = @import(\"a.zig\").A;\n", &tiny, &st) == 6); assert(inline_imports("const A = @import(\"a.zig\").A;\n", "", &tiny, &st) == 30); } + +// ---------------------------------------------------------------- tests: std.math.pow (#10823) + +test std_math_pow_of_an_f64_is_a_call_of_zig_std_and_the_spec_uses_it { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = to_t27("const std = @import(\"std\");\nconst PHI = 1.618033988749895;\npub fn phi_n(n: f64) f64 {\n return std.math.pow(f64, PHI, n) * 2;\n}\npub fn gamma_8() f64 {\n return std.math.pow(f64, 0.2360679774997897, 8);\n}\ntest \"phi_n\" {\n try std.testing.expectApproxEqAbs(@as(f64, 2.0), phi_n(0.0), 0.0001);\n}\n", "demo", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; demo.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule demo;\n\nuse tools::t27o::zig_std::std_math_pow_f64;\n\nconst PHI = 1.618033988749895;\n\npub fn phi_n(n: f64) -> f64 {\n return std_math_pow_f64(PHI, n) * 2;\n}\n\npub fn gamma_8() -> f64 {\n return std_math_pow_f64(0.2360679774997897, 8);\n}\n\ntest phi_n {\n assert(@abs(phi_n(0.0) - @as(f64, 2.0)) <= 0.0001);\n}\n\n// t27o: 2 fn seen, 2 converted (1 of 1 tests), 0 refused (0 kept as stubs); coverage 100%\n")); +} + +test negative_control_a_file_that_never_calls_pow_or_only_in_a_refused_fn_has_no_use_line { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const a : usize = to_t27("fn f(x: u32) u32 {\n return x + 1;\n}\n", "demo", &out, &st); + assert(!z_out_mentions(&out, 0, a, "use ")); + var out2 : [1024]u8 = undefined; + var st2 : [2048]usize = [0] ** 2048; + const b : usize = to_t27("const std = @import(\"std\");\nfn g(x: f64, p: *u8) f64 {\n return std.math.pow(f64, x, 2.0) + p.*;\n}\n", "demo", &out2, &st2); + assert(!z_out_mentions(&out2, 0, b, "use ")); + assert(!z_out_mentions(&out2, 0, b, "std_math_pow_f64(")); + assert(z_out_mentions(&out2, 0, b, "refused fn g (line 2): pointer")); +} + +test negative_control_pow_of_another_type_or_the_old_two_operand_form_is_refused_as_its_own_kind { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn single(x: f32) f32 {\n return std.math.pow(f32, x, 2.0);\n}\nfn old(x: f64) f64 {\n return std.math.pow(x, 1.5);\n}\nfn wide(n: u32) u32 {\n return std.math.pow(u32, n, 2);\n}\nfn arity(x: f64) f64 {\n return std.math.pow(f64, x);\n}\nfn user(x: f64) f64 {\n return std.math.log(f64, 2.0, x);\n}\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn single (line 2): pow of a type other than f64 `std.math.pow` at line 3\n\n// t27o: refused fn old (line 5): pow of a type other than f64 `std.math.pow` at line 6\n\n// t27o: refused fn wide (line 8): pow of a type other than f64 `std.math.pow` at line 9\n\n// t27o: refused fn arity (line 11): syntax `std.math.pow` at line 12\n\n// t27o: refused fn user (line 14): std call `std.math.log` at line 15\n\n")); +} + +test a_pow_call_is_an_f64_so_it_meets_f64_operands_and_a_too_small_buffer_is_told_the_size_with_the_use_line { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const src : []const u8 = "const std = @import(\"std\");\npub fn f(x: f64) bool {\n return std.math.pow(f64, x, 2.0) > 1.0;\n}\n"; + const n : usize = to_t27(src, "d", &out, &st); + assert(z_out_mentions(&out, 0, n, "use tools::t27o::zig_std::std_math_pow_f64;\n\npub fn f(x: f64) -> bool {\n return std_math_pow_f64(x, 2.0) > 1.0;\n}")); + var tiny : [64]u8 = undefined; + var st2 : [2048]usize = [0] ** 2048; + const m : usize = to_t27(src, "d", &tiny, &st2); + assert(m == n); +} + +test negative_control_pow_in_a_file_level_const_is_refused_and_so_is_what_uses_it { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nconst PI = 3.14159;\npub const TAU4: f64 = 36.0 * std.math.pow(f64, PI, 4.0);\npub fn tau() f64 {\n return TAU4;\n}\npub fn local() f64 {\n const q = std.math.pow(f64, PI, 4.0);\n return q;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "const PI = 3.14159;\n\n// t27o: refused const TAU4 (line 3): pow in a file-level const `std.math.pow` at line 3\n\n// t27o: refused fn tau (line 4): uses refused `TAU4` at line 5\n\npub fn local() -> f64 {\n const q = std_math_pow_f64(PI, 4.0);\n return q;\n}\n\n")); +} diff --git a/specs/tools/t27o/zig_std.t27 b/specs/tools/t27o/zig_std.t27 new file mode 100644 index 0000000000..bdbc7e13a5 --- /dev/null +++ b/specs/tools/t27o/zig_std.t27 @@ -0,0 +1,953 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/tools/t27o/zig_std.t27 -- the t27 behind the Zig front end's std table (#10823): what a converted .zig spec `use`s +; where Zig's std has no t27 builtin that means the same. It sits beside zig_convert.t27 as rust_std.t27 sits beside +; rust_convert.t27; zig_convert.t27 writes a call of an entry here and a `use` line, and `t27b test` runs the file's own +; tests against it. +; -- +; std.math.pow(f64, x, y) (std_math_pow_f64). Zig 0.16's std/math/pow.zig is a port of Go's math.Pow, not libm's pow: the +; integer part of y is taken by squaring the significand of x (frexp) and counting the exponent apart, the fractional part +; is exp(yf * log(x)), and the result is scaled once at the end. So an integer power by plain repeated multiplication is +; NOT Zig's result (the negative control below finds vectors where it differs in the last bits). This is that algorithm, +; step for step, in operations t27b and t27c both have: +; - `@exp` and `@log` of a run-time f64 are the routines Zig's compiler_rt has (t27b runs ports of them, specs/tri/t27b/ +; libm.t27, bit for bit; t27c writes Zig's own builtin); `@sqrt` is the IEEE square root; `@floor` of a non-negative f64 +; is Zig's `@trunc` there. +; - `math.frexp` is an exact function of its operand (read off the bits here). `math.ldexp` (`scalbn`) is Zig's own +; integer code, ported as it is: its rounding of a subnormal result is NOT the IEEE tie rule in every case (see +; zs_ldexp), so an exact power-of-two product (musl's scalbn) is not Zig's result, and the recorded vectors include the +; case that shows it. +; The vectors below were recorded with Zig 0.16.0 (zig run, Debug, no libc): for each pair (x, y) the program in the +; comment above `ZS_POW_VEC` printed the bits of x, of y and of `std.math.pow(f64, x, y)`. A result the test compares is +; the bits, except a NaN, which has to be a NaN. +; ASCII only (L3) +; phi^2 + 1/phi^2 = 3 | TRINITY + +module T27oZigStd; + +// `@clz` of a u64, by a loop. +pub fn zs_clz64(v: u64) -> i32 { + var n : i32 = 0; + var b : u64 = 0x8000000000000000; + while (b != 0 and (v & b) == 0) { + n = n + 1; + b = b >> 1; + } + return n; +} + +// math.ldexp of an f64 (std/math/ldexp.zig, which math.scalbn is): x * 2^n on the bits. A subnormal result is rounded to +// nearest on the shifted-out bit, and an exact tie is cleared to even only when `@ctz(repr) == shift - 1` -- a test on +// the whole representation, exponent bits included. For a significand of 0.5 (all fraction bits zero) the exponent +// field's own trailing zeros make that false, so 0.5 * 2^-1074 = 2^-1075, a tie, becomes 2^-1074 and not 0, where musl's +// scalbn gives 0. Zig's result is the one a converted file saw, so this is Zig's code, not the IEEE rule: `@ctz(repr) == +// shift - 1` is written as `(repr & low) == half` (the low `shift` bits are 1 then zeros), as specs/tri/t27b/libm.t27 does. +pub fn zs_ldexp(x: f64, n: i32) -> f64 { + const repr : u64 = @bitCast(x); + const sign : u64 = repr & 0x8000000000000000; + if (x != x or (repr & 0x7FF0000000000000) == 0x7FF0000000000000) { + return x; + } + var exponent : i32 = @intCast((repr & 0x7FFFFFFFFFFFFFFF) >> 52); + if (exponent == 0) { + exponent = exponent + 11 - zs_clz64((repr & 0x7FFFFFFFFFFFFFFF) << 1); + } + if (n >= 0) { + if (n > 2046 - exponent) { + return @bitCast(0x7FF0000000000000 | sign); + } + if (exponent + n <= 0) { + return @bitCast(((repr & 0x7FFFFFFFFFFFFFFF) << n) | sign); + } + if (exponent <= 0) { + const ne : u64 = @intCast(n + exponent); + const frac : u64 = ((repr & 0x7FFFFFFFFFFFFFFF) << (1 - exponent)) & 0x000FFFFFFFFFFFFF; + return @bitCast((ne << 52) | frac | sign); + } + const nu : u64 = @intCast(n); + return @bitCast(repr + (nu << 52)); + } + if (n <= 0 - exponent) { + if (n < 0 - (52 + exponent)) { + return @bitCast(sign); + } + var s : i32 = 0 - n; + if (1 - (exponent + n) < s) { + s = 1 - (exponent + n); + } + const shift : u64 = @intCast(s); + const low : u64 = 0xFFFFFFFFFFFFFFFF >> (64 - shift); + const half : u64 = low ^ (low >> 1); + var result : u64 = repr & 0x000FFFFFFFFFFFFF; + if (exponent > 0) { + result = result | 0x0010000000000000; + } + result = result >> (shift - 1); + result = (result + 1) >> 1; + if ((repr & low) == half) { + result = result & 0xFFFFFFFFFFFFFFFE; + } + return @bitCast(result | sign); + } + const nn : u64 = @intCast(0 - n); + return @bitCast(repr - (nn << 52)); +} + +// math.frexp of a finite x that is not zero: x = significand * 2^exponent, |significand| in [0.5, 1). A subnormal is scaled +// by 2^54 first (exact), so its exponent field is that of a normal number. +pub fn zs_frexp_exp(x: f64) -> i32 { + const u : u64 = @bitCast(x); + const e : i32 = @intCast((u >> 52) & 0x7FF); + if (e == 0) { + const v : u64 = @bitCast(x * 18014398509481984.0); + const e2 : i32 = @intCast((v >> 52) & 0x7FF); + return e2 - 1022 - 54; + } + return e - 1022; +} + +pub fn zs_frexp_sig(x: f64) -> f64 { + var u : u64 = @bitCast(x); + if (((u >> 52) & 0x7FF) == 0) { + u = @bitCast(x * 18014398509481984.0); + } + return @bitCast((u & 0x800FFFFFFFFFFFFF) | 0x3FE0000000000000); +} + +// pow.zig `isOddInteger`: |x| >= 2^53 is even; else x has no fraction and an odd integer part. +pub fn zs_is_odd_integer(x: f64) -> bool { + const a : f64 = @abs(x); + if (a >= 9007199254740992.0) { + return false; + } + const ip : f64 = @floor(a); + const i : i64 = @intFromFloat(ip); + return a - ip == 0.0 and (i & 1) == 1; +} + +// The sign bit of x: 1 for -0.0 and the negatives, 0 for +0.0 and the positives. +pub fn zs_sign(x: f64) -> u64 { + const u : u64 = @bitCast(x); + return u >> 63; +} + +// std.math.pow(f64, x, y), Zig 0.16 std/math/pow.zig. +pub fn std_math_pow_f64(x: f64, y: f64) -> f64 { + const inf : f64 = std.math.inf(f64); + if (y == 0.0 or x == 1.0) { + return 1.0; + } + if (x != x or y != y) { + return std.math.nan(f64); + } + if (y == 1.0) { + return x; + } + if (x == 0.0) { + if (y < 0.0) { + if (zs_is_odd_integer(y)) { + if (zs_sign(x) == 1) { + return -inf; + } + return inf; + } + return inf; + } + if (zs_is_odd_integer(y)) { + return x; + } + return 0.0; + } + if (y == inf or y == -inf) { + if (x == -1.0) { + return 1.0; + } + if ((@abs(x) < 1.0) == (y == inf)) { + return 0.0; + } + return inf; + } + if (x == inf or x == -inf) { + if (x == -inf) { + return std_math_pow_f64(1.0 / x, -y); + } + if (y < 0.0) { + return 0.0; + } + if (y > 0.0) { + return inf; + } + } + if (y == 0.5) { + return @sqrt(x); + } + if (y == -0.5) { + return 1.0 / @sqrt(x); + } + const ay : f64 = @abs(y); + var yi : f64 = @floor(ay); + var yf : f64 = ay - yi; + if (yf != 0.0 and x < 0.0) { + return std.math.nan(f64); + } + if (yi >= 9223372036854775808.0) { + return @exp(y * @log(@abs(x))); + } + var a1 : f64 = 1.0; + var ae : i32 = 0; + if (yf != 0.0) { + if (yf > 0.5) { + yf = yf - 1.0; + yi = yi + 1.0; + } + a1 = @exp(yf * @log(x)); + } + var xe : i32 = zs_frexp_exp(x); + var x1 : f64 = zs_frexp_sig(x); + var i : i64 = @intFromFloat(yi); + while (i != 0) { + if (xe < -4096 or 4096 < xe) { + ae = ae + xe; + break; + } + if ((i & 1) == 1) { + a1 = a1 * x1; + ae = ae + xe; + } + x1 = x1 * x1; + xe = xe * 2; + if (x1 < 0.5) { + x1 = x1 + x1; + xe = xe - 1; + } + i = i >> 1; + } + if (y < 0.0) { + a1 = 1.0 / a1; + ae = 0 - ae; + } + return zs_ldexp(a1, ae); +} + + +// The vectors. Recorded with Zig 0.16.0 (`zig run`, Debug, no libc) by a program that read each (x, y) from a table of +// bits and printed `{x:0>16}` of the bits of x, of y and of `std.math.pow(f64, x, y)`; the same for `std.math.ldexp(x, n)` +// (n is stored as n + 4096) and `std.math.frexp(x)` (its exponent is stored as exponent + 2048): +// const r = std.math.pow(f64, @bitCast(PIN[i]), @bitCast(PIN[i + 1])); +// std.debug.print("P {x:0>16} {x:0>16} {x:0>16}\n", .{ PIN[i], PIN[i + 1], @as(u64, @bitCast(r)) }); +// What they cover: the constants the corpus raises (phi, pi, e, 3, 10, 2, 1/2, phi^-3) to integers from 0 to 100 and down +// to -40; fractional exponents (1/phi, tenths, 3.7, -2.7); the special values (+-0, +-1, +-inf, NaN, a negative base) against +// zero, odd and even integers, a half, an infinity and a NaN; exponents past 2^53 and 2^63; results that overflow, underflow +// to zero or land in the subnormals; random pairs. A literal that ends in f32 or f64 is a float literal to t27, so those +// entries are written in decimal. +const ZS_POW_VEC : [1299]u64 = [ + 0x3ff9e3779b97f4a8, 0x0000000000000000, 0x3ff0000000000000, + 0x3ff9e3779b97f4a8, 0x3ff0000000000000, 0x3ff9e3779b97f4a8, + 0x3ff9e3779b97f4a8, 0x4000000000000000, 0x4004f1bbcdcbfa54, + 0x3ff9e3779b97f4a8, 0x4008000000000000, 0x4010f1bbcdcbfa54, + 0x3ff9e3779b97f4a8, 0x4010000000000000, 0x401b6a99b4b1f77e, + 0x3ff9e3779b97f4a8, 0x4014000000000000, 0x40262e2ac13ef8e9, + 0x3ff9e3779b97f4a8, 0x4018000000000000, 0x4031f1bbcdcbfa54, + 0x3ff9e3779b97f4a8, 0x401c000000000000, 0x403d08d12e6b76c9, + 0x3ff9e3779b97f4a8, 0x4020000000000000, 0x40477d467e1bb88e, + 0x3ff9e3779b97f4a8, 0x4022000000000000, 0x405300d78aa8b9f9, + 0x3ff9e3779b97f4a8, 0x4024000000000000, 0x405ebf7ac9b69640, + 0x3ff9e3779b97f4a8, 0x4026000000000000, 0x4068e0292a2fa81d, + 0x3ff9e3779b97f4a8, 0x4028000000000000, 0x40741ff34785799e, + 0x3ff9e3779b97f4a8, 0x402a000000000000, 0x40804803ee4ea6d6, + 0x3ff9e3779b97f4a8, 0x4030000000000000, 0x40a13dffc49c5b88, + 0x3ff9e3779b97f4a8, 0x4034000000000000, 0x40cd8b7ffdd57461, + 0x3ff9e3779b97f4a8, 0x4038000000000000, 0x40f9501ffff5e2f9, + 0x3ff9e3779b97f4a8, 0x403f000000000000, 0x4146f796800002c9, + 0x3ff9e3779b97f4a8, 0x4040000000000000, 0x415294afbfffff23, + 0x3ff9e3779b97f4a8, 0x4049000000000000, 0x421a35fe5e8bffff, + 0x3ff9e3779b97f4a8, 0x404f800000000000, 0x42aaabf7c7b927ff, + 0x3ff9e3779b97f4a8, 0x4050000000000000, 0x42b593f12aea7eff, + 0x3ff9e3779b97f4a8, 0x4059000000000000, 0x444578187422adc4, + 0x3ff9e3779b97f4a8, 0xbff0000000000000, 0x3fe3c6ef372fe94f, + 0x3ff9e3779b97f4a8, 0xc000000000000000, 0x3fd8722191a02d61, + 0x3ff9e3779b97f4a8, 0xc008000000000000, 0x3fce3779b97f4a7c, + 0x3ff9e3779b97f4a8, 0xc014000000000000, 0x3fb715609f7c746c, + 0x3ff9e3779b97f4a8, 0xc020000000000000, 0x3f95cc0f223b8f49, + 0x3ff9e3779b97f4a8, 0xc02a000000000000, 0x3f5f727536b2bbff, + 0x3ff9e3779b97f4a8, 0xc034000000000000, 0x3f11545cf2bbbfa6, + 0x3ff9e3779b97f4a8, 0xc044000000000000, 0x3e32c50255dfa06e, + 0x400921fb54442d18, 0x0000000000000000, 0x3ff0000000000000, + 0x400921fb54442d18, 0x3ff0000000000000, 0x400921fb54442d18, + 0x400921fb54442d18, 0x4000000000000000, 0x4023bd3cc9be45de, + 0x400921fb54442d18, 0x4008000000000000, 0x403f019b59389d7b, + 0x400921fb54442d18, 0x4010000000000000, 0x40585a2e8c290825, + 0x400921fb54442d18, 0x4014000000000000, 0x40732050a0fe5033, + 0x400921fb54442d18, 0x4018000000000000, 0x408e0b1d11856df6, + 0x400921fb54442d18, 0x401c000000000000, 4658860110120189746, + 0x400921fb54442d18, 0x4020000000000000, 0x40c28843f855a861, + 0x400921fb54442d18, 0x4022000000000000, 0x40dd1c465b7aabb6, + 0x400921fb54442d18, 0x4024000000000000, 0x40f6dd00c27647fd, + 0x400921fb54442d18, 0x4026000000000000, 0x4111f4f01267bf60, + 0x400921fb54442d18, 0x4028000000000000, 0x412c34da5cf0a1b5, + 0x400921fb54442d18, 0x402a000000000000, 0x4146273ea2a374bf, + 0x400921fb54442d18, 0x4030000000000000, 0x41957721735f29e4, + 0x400921fb54442d18, 0x4034000000000000, 0x420055d75de0a95c, + 0x400921fb54442d18, 0x4038000000000000, 0x4268dcd56e253c66, + 0x400921fb54442d18, 0x403f000000000000, 0x43225547027f5478, + 0x400921fb54442d18, 0x4040000000000000, 0x433ccc2ed0b3e4ae, + 0x400921fb54442d18, 0x4049000000000000, 0x4517d4e683979e1c, + 0x400921fb54442d18, 0x404f800000000000, 0x46707f989906c6d3, + 0x400921fb54442d18, 0x4050000000000000, 0x4689ea68c264432b, + 0x400921fb54442d18, 0x4059000000000000, 0x4a41bf93d1f611f8, + 0x400921fb54442d18, 0xbff0000000000000, 0x3fd45f306dc9c883, + 0x400921fb54442d18, 0xc000000000000000, 0x3fb9f02f6222c720, + 0x400921fb54442d18, 0xc008000000000000, 0x3fa08345eb45d77f, + 0x400921fb54442d18, 0xc014000000000000, 0x3f6ac4ff3f4b8c2b, + 0x400921fb54442d18, 0xc020000000000000, 0x3f1ba0a12450c6b5, + 0x400921fb54442d18, 0xc02a000000000000, 0x3e971c972729a055, + 0x400921fb54442d18, 0xc034000000000000, 0x3ddf57d773267a67, + 0x400921fb54442d18, 0xc044000000000000, 0x3bceb32290474356, + 0x4008000000000000, 0x4000000000000000, 0x4022000000000000, + 0x4008000000000000, 0x4008000000000000, 0x403b000000000000, + 0x4008000000000000, 0x4018000000000000, 0x4086c80000000000, + 0x4008000000000000, 0x4020000000000000, 0x40b9a10000000000, + 0x4008000000000000, 0x402a000000000000, 0x413853d300000000, + 0x4008000000000000, 0x4035000000000000, 0x42037be295980000, + 0x4008000000000000, 0x4040000000000000, 0x431a553f8878fa04, + 0x4008000000000000, 0x4050000000000000, 0x4645ab6a57c7bc99, + 0x4008000000000000, 0xbff0000000000000, 0x3fd5555555555555, + 0x4008000000000000, 0xc010000000000000, 0x3f8948b0fcd6e9e0, + 0x4008000000000000, 0xc022000000000000, 0x3f0aa2f78f1b4cc6, + 0x4008000000000000, 0xc03e000000000000, 0x3cf5dfaa697ec6f7, + 0x4024000000000000, 0x4000000000000000, 0x4059000000000000, + 0x4024000000000000, 0x4008000000000000, 0x408f400000000000, + 0x4024000000000000, 0x4018000000000000, 0x412e848000000000, + 0x4024000000000000, 0x4020000000000000, 0x4197d78400000000, + 0x4024000000000000, 0x402a000000000000, 0x42a2309ce5400000, + 0x4024000000000000, 0x4035000000000000, 0x444b1ae4d6e2ef50, + 0x4024000000000000, 0x4040000000000000, 0x4693b8b5b5056e17, + 0x4024000000000000, 0x4050000000000000, 0x4d384f03e93ff9f6, + 0x4024000000000000, 0xbff0000000000000, 0x3fb999999999999a, + 0x4024000000000000, 0xc010000000000000, 0x3f1a36e2eb1c432d, + 0x4024000000000000, 0xc022000000000000, 0x3e112e0be826d695, + 0x4024000000000000, 0xc03e000000000000, 0x39b4484bfeebc29f, + 0x4000000000000000, 0x4000000000000000, 0x4010000000000000, + 0x4000000000000000, 0x4008000000000000, 0x4020000000000000, + 0x4000000000000000, 0x4018000000000000, 0x4050000000000000, + 0x4000000000000000, 0x4020000000000000, 0x4070000000000000, + 0x4000000000000000, 0x402a000000000000, 0x40c0000000000000, + 0x4000000000000000, 0x4035000000000000, 0x4140000000000000, + 0x4000000000000000, 0x4040000000000000, 0x41f0000000000000, + 0x4000000000000000, 0x4050000000000000, 0x43f0000000000000, + 0x4000000000000000, 0xbff0000000000000, 0x3fe0000000000000, + 0x4000000000000000, 0xc010000000000000, 0x3fb0000000000000, + 0x4000000000000000, 0xc022000000000000, 0x3f60000000000000, + 0x4000000000000000, 0xc03e000000000000, 0x3e10000000000000, + 0x3fe0000000000000, 0x4000000000000000, 0x3fd0000000000000, + 0x3fe0000000000000, 0x4008000000000000, 0x3fc0000000000000, + 0x3fe0000000000000, 0x4018000000000000, 0x3f90000000000000, + 0x3fe0000000000000, 0x4020000000000000, 0x3f70000000000000, + 0x3fe0000000000000, 0x402a000000000000, 0x3f20000000000000, + 0x3fe0000000000000, 0x4035000000000000, 0x3ea0000000000000, + 0x3fe0000000000000, 0x4040000000000000, 0x3df0000000000000, + 0x3fe0000000000000, 0x4050000000000000, 0x3bf0000000000000, + 0x3fe0000000000000, 0xbff0000000000000, 0x4000000000000000, + 0x3fe0000000000000, 0xc010000000000000, 0x4030000000000000, + 0x3fe0000000000000, 0xc022000000000000, 0x4080000000000000, + 0x3fe0000000000000, 0xc03e000000000000, 0x41d0000000000000, + 0x3fce3779b97f4a7c, 0x4000000000000000, 0x3fac8864680b583e, + 0x3fce3779b97f4a7c, 0x4008000000000000, 0x3f8af155173f23d7, + 0x3fce3779b97f4a7c, 0x4018000000000000, 0x3f26af46505704d9, + 0x3fce3779b97f4a7c, 0x4020000000000000, 0x3ee43a0d9e9f0076, + 0x3fce3779b97f4a7c, 0x402a000000000000, 0x3e3e5eab0717e6f2, + 0x3fce3779b97f4a7c, 0x4035000000000000, 0x3d333243676b99d0, + 0x3fce3779b97f4a7c, 0x4040000000000000, 0x3bc46ea80abc1362, + 0x3fce3779b97f4a7c, 0x4050000000000000, 0x379a17a1684eabf4, + 0x3fce3779b97f4a7c, 0xbff0000000000000, 0x4010f1bbcdcbfa54, + 0x3fce3779b97f4a7c, 0xc010000000000000, 0x40741ff34785799f, + 0x3fce3779b97f4a7c, 0xc022000000000000, 0x411ace90000098ce, + 0x3fce3779b97f4a7c, 0xc03e000000000000, 0x43d657e46337aa92, + 0xbfe3c6ef372fe950, 0x4000000000000000, 0x3fd8722191a02d62, + 0xbfe3c6ef372fe950, 0x4008000000000000, 0xbfce3779b97f4a7e, + 0xbfe3c6ef372fe950, 0x4018000000000000, 0x3fac8864680b5842, + 0xbfe3c6ef372fe950, 0x4020000000000000, 0x3f95cc0f223b8f4b, + 0xbfe3c6ef372fe950, 0x402a000000000000, 0xbf5f727536b2bc04, + 0xbfe3c6ef372fe950, 0x4035000000000000, 0xbf056ba5766d14d9, + 0xbfe3c6ef372fe950, 0x4040000000000000, 0x3e8b8e292f8a88c9, + 0xbfe3c6ef372fe950, 0x4050000000000000, 0x3d27ba5d0df4b66b, + 0xbfe3c6ef372fe950, 0xbff0000000000000, 0xbff9e3779b97f4a7, + 0xbfe3c6ef372fe950, 0xc010000000000000, 0x401b6a99b4b1f77c, + 0xbfe3c6ef372fe950, 0xc022000000000000, 0xc05300d78aa8b9f7, + 0xbfe3c6ef372fe950, 0xc03e000000000000, 0x413c6391fffff6f0, + 0xc000000000000000, 0x4000000000000000, 0x4010000000000000, + 0xc000000000000000, 0x4008000000000000, 0xc020000000000000, + 0xc000000000000000, 0x4018000000000000, 0x4050000000000000, + 0xc000000000000000, 0x4020000000000000, 0x4070000000000000, + 0xc000000000000000, 0x402a000000000000, 0xc0c0000000000000, + 0xc000000000000000, 0x4035000000000000, 0xc140000000000000, + 0xc000000000000000, 0x4040000000000000, 0x41f0000000000000, + 0xc000000000000000, 0x4050000000000000, 0x43f0000000000000, + 0xc000000000000000, 0xbff0000000000000, 0xbfe0000000000000, + 0xc000000000000000, 0xc010000000000000, 0x3fb0000000000000, + 0xc000000000000000, 0xc022000000000000, 0xbf60000000000000, + 0xc000000000000000, 0xc03e000000000000, 0x3e10000000000000, + 0x4005bf0a8b145769, 0x4000000000000000, 0x401d8e64b8d4ddad, + 0x4005bf0a8b145769, 0x4008000000000000, 0x403415e5bf6fb105, + 0x4005bf0a8b145769, 0x4018000000000000, 0x407936dc5690c08d, + 0x4005bf0a8b145769, 0x4020000000000000, 0x40a749ea7d470c6c, + 0x4005bf0a8b145769, 0x402a000000000000, 0x411b00b5916ac951, + 0x4005bf0a8b145769, 0x4035000000000000, 0x41d3a6e1fd9eecf8, + 0x4005bf0a8b145769, 0x4040000000000000, 0x42d1f43fcc4b6626, + 0x4005bf0a8b145769, 0x4050000000000000, 0x45b425982cf597bf, + 0x4005bf0a8b145769, 0xbff0000000000000, 0x3fd78b56362cef38, + 0x4005bf0a8b145769, 0xc010000000000000, 0x3f92c155b8213cf5, + 0x4005bf0a8b145769, 0xc022000000000000, 0x3f202cf22526545b, + 0x4005bf0a8b145769, 0xc03e000000000000, 0x3d3a56e0c2ac7f7e, + 0x3fe0000000000000, 0x3fe3c6ef372fe950, 0x3fe4d990a21abb54, + 0x3fe0000000000000, 0x3fb999999999999a, 0x3feddb680117ab12, + 0x3fe0000000000000, 0x3fd3333333333333, 0x3fe9fdf8bcce533e, + 0x3fe0000000000000, 0x3fe6666666666666, 0x3fe3b2c47bff8329, + 0x3fe0000000000000, 0x3ff8000000000000, 0x3fd6a09e667f3bcc, + 0x3fe0000000000000, 0x4004000000000000, 0x3fc6a09e667f3bcc, + 0x3fe0000000000000, 0xbff8000000000000, 0x4006a09e667f3bcd, + 0x3fe0000000000000, 0xc00599999999999a, 0x4019fdf8bcce533f, + 0x3fe0000000000000, 0x400d99999999999a, 0x3fb3b2c47bff8328, + 0x3fe0000000000000, 0x4024333333333333, 0x3f4ddb680117ab14, + 0x4000000000000000, 0x3fe3c6ef372fe950, 0x3ff88e77d62aa7b4, + 0x4000000000000000, 0x3fb999999999999a, 0x3ff125fbee250664, + 0x4000000000000000, 0x3fd3333333333333, 0x3ff3b2c47bff8329, + 0x4000000000000000, 0x3fe6666666666666, 0x3ff9fdf8bcce533d, + 0x4000000000000000, 0x3ff8000000000000, 0x4006a09e667f3bcc, + 0x4000000000000000, 0x4004000000000000, 0x4016a09e667f3bcc, + 0x4000000000000000, 0xbff8000000000000, 0x3fd6a09e667f3bcd, + 0x4000000000000000, 0xc00599999999999a, 0x3fc3b2c47bff8328, + 0x4000000000000000, 0x400d99999999999a, 0x4029fdf8bcce533e, + 0x4000000000000000, 0x4024333333333333, 0x409125fbee250663, + 0x4024000000000000, 0x3fe3c6ef372fe950, 0x4010997642ad3f6c, + 0x4024000000000000, 0x3fb999999999999a, 0x3ff4248ef8fc2604, + 0x4024000000000000, 0x3fd3333333333333, 0x3fffec982d5bb8b0, + 0x4024000000000000, 0x3fe6666666666666, 0x40140c28430012e6, + 0x4024000000000000, 0x3ff8000000000000, 0x403f9f6e4990f229, + 0x4024000000000000, 0x4004000000000000, 0x4073c3a4edfa975a, + 0x4024000000000000, 0xbff8000000000000, 0x3fa030dc4ea03a71, + 0x4024000000000000, 0xc00599999999999a, 0x3f60585e4c78b079, + 0x4024000000000000, 0x400d99999999999a, 0x40b393df516e1278, + 0x4024000000000000, 0x4024333333333333, 0x420773062f2f8886, + 0x3ff9e3779b97f4a8, 0x3fe3c6ef372fe950, 0x3ff58ab1a4aaa5ea, + 0x3ff9e3779b97f4a8, 0x3fb999999999999a, 0x3ff0c9ec7e89f1f0, + 0x3ff9e3779b97f4a8, 0x3fd3333333333333, 0x3ff27c2016d2edb0, + 0x3ff9e3779b97f4a8, 0x3fe6666666666666, 0x3ff6688e88c6d62b, + 0x3ff9e3779b97f4a8, 0x3ff8000000000000, 0x400077225f1da572, + 0x3ff9e3779b97f4a8, 0x4004000000000000, 0x400aa43b0271e79d, + 0x3ff9e3779b97f4a8, 0xbff8000000000000, 0x3fdf18773c56f721, + 0x3ff9e3779b97f4a8, 0xc00599999999999a, 0x3fd17469bd0d70d4, + 0x3ff9e3779b97f4a8, 0x400d99999999999a, 0x4017bb1c71ee371a, + 0x3ff9e3779b97f4a8, 0x4024333333333333, 0x406021c35dde5d5a, + 0x400921fb54442d18, 0x3fe3c6ef372fe950, 0x40003b235fe669ca, + 0x400921fb54442d18, 0x3fb999999999999a, 0x3ff1f0c5c3cb6784, + 0x400921fb54442d18, 0x3fd3333333333333, 0x3ff68e5fbeedc21f, + 0x400921fb54442d18, 0x3fe6666666666666, 0x4001d3e304d76ce5, + 0x400921fb54442d18, 0x3ff8000000000000, 0x401645f7c63f2c6b, + 0x400921fb54442d18, 0x4004000000000000, 0x40317e50a9dc6553, + 0x400921fb54442d18, 0xbff8000000000000, 0x3fc6fcb5f827b97f, + 0x400921fb54442d18, 0xc00599999999999a, 0x3fa7477000837831, + 0x400921fb54442d18, 0x400d99999999999a, 0x4051462917d1519d, + 0x400921fb54442d18, 0x4024333333333333, 0x40f9a2de72e41629, + 0x3fb999999999999a, 0x3fe3c6ef372fe950, 0x3fced828fd51b1fa, + 0x3fb999999999999a, 0x3fb999999999999a, 0x3fe96b230bcdc434, + 0x3fb999999999999a, 0x3fd3333333333333, 0x3fe009b9cf334253, + 0x3fb999999999999a, 0x3fe6666666666666, 0x3fc98a13577c93c0, + 0x3fb999999999999a, 0x3ff8000000000000, 0x3fa030dc4ea03a73, + 0x3fb999999999999a, 0x4004000000000000, 0x3f69e7c6e43390ba, + 0x3fb999999999999a, 0xbff8000000000000, 0x403f9f6e4990f226, + 0x3fb999999999999a, 0xc00599999999999a, 0x407f52fee8b01d8a, + 0x3fb999999999999a, 0x400d99999999999a, 0x3f2a26fd472780c5, + 0x3fb999999999999a, 0x4024333333333333, 0x3dd5d59689697962, + 0x3ff9e3779b97f4a8, 0x3fd3333333333334, 0x3ff27c2016d2edb0, + 0x3ff9e3779b97f4a8, 0x3fe6666666666667, 0x3ff6688e88c6d62c, + 0x3ff9e3779b97f4a8, 0x3ff199999999999a, 0x3ffb2a2fd20d51ab, + 0x3ff9e3779b97f4a8, 0x3ffe666666666667, 0x4003f5d488046c18, + 0x3ff9e3779b97f4a8, 0x4007333333333334, 0x401025f0812433b4, + 0x3ff9e3779b97f4a8, 0xbfd999999999999a, 0x3fea65a32f75330f, + 0x3ff9e3779b97f4a8, 0xbff3333333333334, 0x3fe1f6610e9fc1d7, + 0x3f847ae147ae147b, 0x3fe3c6ef372fe950, 0x3fadbb0104897dba, + 0x3fd0000000000000, 0x3fe3c6ef372fe950, 0x3fdb2b77fd8b55b6, + 0x3feccccccccccccd, 0x3fe3c6ef372fe950, 0x3fedfb9036416c8d, + 0x3ff8000000000000, 0x3fe3c6ef372fe950, 0x3ff48e7a7c52f918, + 0x408f400000000000, 0x3fe3c6ef372fe950, 0x4051ddd9a91c5dfb, + 0x3e112e0be826d695, 0x3fe3c6ef372fe950, 0x3ec6fb571bbd94f5, + 0x0000000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x0000000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x0000000000000000, 0x3ff0000000000000, 0x0000000000000000, + 0x0000000000000000, 0x4000000000000000, 0x0000000000000000, + 0x0000000000000000, 0xc008000000000000, 0x7ff0000000000000, + 0x0000000000000000, 0x4008000000000000, 0x0000000000000000, + 0x0000000000000000, 0x3fe0000000000000, 0x0000000000000000, + 0x0000000000000000, 0xbfe0000000000000, 0x7ff0000000000000, + 0x0000000000000000, 0x7ff0000000000000, 0x0000000000000000, + 0x0000000000000000, 0xfff0000000000000, 0x7ff0000000000000, + 0x0000000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0x0000000000000000, 0x3fd3333333333333, 0x0000000000000000, + 0x0000000000000000, 0x4340000000000001, 0x0000000000000000, + 0x0000000000000000, 0x43e0000000000000, 0x0000000000000000, + 0x8000000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x8000000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x8000000000000000, 0x3ff0000000000000, 0x8000000000000000, + 0x8000000000000000, 0x4000000000000000, 0x0000000000000000, + 0x8000000000000000, 0xc008000000000000, 0xfff0000000000000, + 0x8000000000000000, 0x4008000000000000, 0x8000000000000000, + 0x8000000000000000, 0x3fe0000000000000, 0x0000000000000000, + 0x8000000000000000, 0xbfe0000000000000, 0x7ff0000000000000, + 0x8000000000000000, 0x7ff0000000000000, 0x0000000000000000, + 0x8000000000000000, 0xfff0000000000000, 0x7ff0000000000000, + 0x8000000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0x8000000000000000, 0x3fd3333333333333, 0x0000000000000000, + 0x8000000000000000, 0x4340000000000001, 0x0000000000000000, + 0x8000000000000000, 0x43e0000000000000, 0x0000000000000000, + 0x3ff0000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x3ff0000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x4000000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0xc008000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x4008000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x3fe0000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0xbfe0000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x7ff0000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0xfff0000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x7ff8000000000000, 0x3ff0000000000000, + 0x3ff0000000000000, 0x3fd3333333333333, 0x3ff0000000000000, + 0x3ff0000000000000, 0x4340000000000001, 0x3ff0000000000000, + 0x3ff0000000000000, 0x43e0000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0x3ff0000000000000, 0xbff0000000000000, + 0xbff0000000000000, 0x4000000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0xc008000000000000, 0xbff0000000000000, + 0xbff0000000000000, 0x4008000000000000, 0xbff0000000000000, + 0xbff0000000000000, 0x3fe0000000000000, 0x7ff8000000000000, + 0xbff0000000000000, 0xbfe0000000000000, 0x7ff8000000000000, + 0xbff0000000000000, 0x7ff0000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0xfff0000000000000, 0x3ff0000000000000, + 0xbff0000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0xbff0000000000000, 0x3fd3333333333333, 0x7ff8000000000000, + 0xbff0000000000000, 0x4340000000000001, 0x3ff0000000000000, + 0xbff0000000000000, 0x43e0000000000000, 0x3ff0000000000000, + 0x7ff0000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x7ff0000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x7ff0000000000000, 0x3ff0000000000000, 0x7ff0000000000000, + 0x7ff0000000000000, 0x4000000000000000, 0x7ff0000000000000, + 0x7ff0000000000000, 0xc008000000000000, 0x0000000000000000, + 0x7ff0000000000000, 0x4008000000000000, 0x7ff0000000000000, + 0x7ff0000000000000, 0x3fe0000000000000, 0x7ff0000000000000, + 0x7ff0000000000000, 0xbfe0000000000000, 0x0000000000000000, + 0x7ff0000000000000, 0x7ff0000000000000, 0x7ff0000000000000, + 0x7ff0000000000000, 0xfff0000000000000, 0x0000000000000000, + 0x7ff0000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0x7ff0000000000000, 0x3fd3333333333333, 0x7ff0000000000000, + 0x7ff0000000000000, 0x4340000000000001, 0x7ff0000000000000, + 0x7ff0000000000000, 0x43e0000000000000, 0x7ff0000000000000, + 0xfff0000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0xfff0000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0xfff0000000000000, 0x3ff0000000000000, 0xfff0000000000000, + 0xfff0000000000000, 0x4000000000000000, 0x7ff0000000000000, + 0xfff0000000000000, 0xc008000000000000, 0x8000000000000000, + 0xfff0000000000000, 0x4008000000000000, 0xfff0000000000000, + 0xfff0000000000000, 0x3fe0000000000000, 0x7ff0000000000000, + 0xfff0000000000000, 0xbfe0000000000000, 0x0000000000000000, + 0xfff0000000000000, 0x7ff0000000000000, 0x7ff0000000000000, + 0xfff0000000000000, 0xfff0000000000000, 0x0000000000000000, + 0xfff0000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0xfff0000000000000, 0x3fd3333333333333, 0x7ff0000000000000, + 0xfff0000000000000, 0x4340000000000001, 0x7ff0000000000000, + 0xfff0000000000000, 0x43e0000000000000, 0x7ff0000000000000, + 0x7ff8000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x7ff8000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x7ff8000000000000, 0x3ff0000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x4000000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0xc008000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x4008000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x3fe0000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0xbfe0000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x7ff0000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0xfff0000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0x7ff8000000000000, 0x3fd3333333333333, 0x7ff8000000000000, + 0x7ff8000000000000, 0x4340000000000001, 0x7ff8000000000000, + 0x7ff8000000000000, 0x43e0000000000000, 0x7ff8000000000000, + 0xc000000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0xc000000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0xc000000000000000, 0x3ff0000000000000, 0xc000000000000000, + 0xc000000000000000, 0xc008000000000000, 0xbfc0000000000000, + 0xc000000000000000, 0x3fe0000000000000, 0x7ff8000000000000, + 0xc000000000000000, 0xbfe0000000000000, 0x7ff8000000000000, + 0xc000000000000000, 0x7ff0000000000000, 0x7ff0000000000000, + 0xc000000000000000, 0xfff0000000000000, 0x0000000000000000, + 0xc000000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0xc000000000000000, 0x3fd3333333333333, 0x7ff8000000000000, + 0xc000000000000000, 0x4340000000000001, 0x7ff0000000000000, + 0xc000000000000000, 0x43e0000000000000, 0x7ff0000000000000, + 0x3fe0000000000000, 0x0000000000000000, 0x3ff0000000000000, + 0x3fe0000000000000, 0x8000000000000000, 0x3ff0000000000000, + 0x3fe0000000000000, 0x3ff0000000000000, 0x3fe0000000000000, + 0x3fe0000000000000, 0xc008000000000000, 0x4020000000000000, + 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe6a09e667f3bcd, + 0x3fe0000000000000, 0xbfe0000000000000, 0x3ff6a09e667f3bcc, + 0x3fe0000000000000, 0x7ff0000000000000, 0x0000000000000000, + 0x3fe0000000000000, 0xfff0000000000000, 0x7ff0000000000000, + 0x3fe0000000000000, 0x7ff8000000000000, 0x7ff8000000000000, + 0x3fe0000000000000, 0x4340000000000001, 0x0000000000000000, + 0x3fe0000000000000, 0x43e0000000000000, 0x0000000000000000, + 0x6974e718d7d7625a, 0x4000000000000000, 0x7ff0000000000000, + 0x16687e92154ef7ac, 0x4000000000000000, 0x0000000000000000, + 0x4024000000000000, 0x4073400000000000, 0x7fe1ccf385ebc8a3, + 0x4024000000000000, 0x4073500000000000, 0x7ff0000000000000, + 0x4024000000000000, 0xc074300000000000, 0x0000000000000002, + 0x4024000000000000, 0xc074400000000000, 0x0000000000000000, + 0x4024000000000000, 0xc074500000000000, 0x0000000000000000, + 0x4000000000000000, 0x408ff80000000000, 0x7fe0000000000000, + 0x4000000000000000, 0x4090000000000000, 0x7ff0000000000000, + 0x4000000000000000, 0xc090c80000000000, 0x0000000000000001, + 0x4000000000000000, 0xc090cc0000000000, 0x0000000000000001, + 0x4000000000000000, 0xc08ff00000000000, 0x0010000000000000, + 0x4000000000000000, 0xc08ff80000000000, 0x0008000000000000, + 0x3ff000001ad7f29b, 0x41cdcd6500000000, 0x48f349445a543ab1, + 0x3fefffffca501acb, 0x41cdcd6500000000, 0x36ea8c1673a33933, + 0x3ff8000000000000, 0x40f86a0000000000, 0x7ff0000000000000, + 0x3ff8000000000000, 0xc0f86a0000000000, 0x0000000000000000, + 0x01a56e1fc2f8f359, 0x4008000000000000, 0x0000000000000000, + 0x01a56e1fc2f8f359, 0xc008000000000000, 0x7ff0000000000000, + 0x7e37e43c8800759c, 0xc000000000000000, 0x0000000000000000, + 0x7e37e43c8800759c, 0x3ff000000006df38, 0x7e37e43ca3b0be82, + 0x3ff0000000000001, 0x4340000000000000, 0x401d8e64b16f0b93, + 0x3fefffffffffffff, 0x4340000000000000, 0x3fd78b56333c7f23, + 0x4008000000000000, 0x43e0000000000000, 0x7ff0000000000000, + 0x3ff0000000000001, 0x43e0000000000000, 0x7ff0000000000000, + 0xbff0000000000001, 0x43d0000000000000, 0x7ff0000000000000, + 0xc008000000000000, 0x4044800000000000, 0xc3ffa2a1cf67b5fc, + 0xc008000000000000, 0xc044800000000000, 0xbbe02f38e097a78b, + 0xc202a05f20000000, 0x4014000000000000, 0xca511b0ec57e649a, + 0xbfb999999999999a, 0x4008000000000000, 0xbf50624dd2f1a9fd, + 0xbfb999999999999a, 0xc008000000000000, 0xc08f3ffffffffffe, + 0x3ee4f8b588e368f1, 0x405ec00000000000, 0x0000000000000000, + 0x0000000000000001, 0x3fe0000000000000, 0x1e60000000000000, + 0x0000000000000001, 0x3ff0000000000000, 0x0000000000000001, + 0x0000000000000001, 0x4000000000000000, 0x0000000000000000, + 0x0010000000000000, 0x3fe0000000000000, 0x2000000000000000, + 0x0010000000000000, 0x4000000000000000, 0x0000000000000000, + 0x7fefffffffffffff, 0x3fe0000000000000, 0x5fefffffffffffff, + 0x7fefffffffffffff, 0x3ff0000000000001, 0x7ff0000000000000, + 0x4000000000000000, 0x3fe199999999999a, 0x3ff76ce51f6a0bb8, + 0x4000000000000000, 0x3fe0000000000001, 0x3ff6a09e667f3bcd, + 0x4024000000000000, 0x4004000000000001, 0x4073c3a4edfa975e, + 0x3ff9e3779b97f4a8, 0x400c666666666667, 0x4016140d14cc09f0, + 0x400921fb54442d18, 0xc012333333333334, 0x3f766774df26d01d, + 0x3fe0000000000000, 0x401a666666666666, 0x3f851cb453b9536e, + 0x7e37e43c8800759c, 0x4020000000000000, 0x7ff0000000000000, + 0x7e37e43c8800759c, 0xc020000000000000, 0x0000000000000000, + 0x01a56e1fc2f8f359, 0x4020000000000000, 0x0000000000000000, + 0x01a56e1fc2f8f359, 0xc022000000000000, 0x7ff0000000000000, + 0x401c000000000000, 0x3fe1999999999999, 0x400754318ca907a0, + 0x3ff9e3779b97f4a8, 0x3ff9999999999999, 0x400146ee5e5ba891, + 0xc041a6a09a1bef47, 0x4028000000000000, 0x43c9fe17eee0423c, + 0x40ec58be76a9655f, 0xc032373f8cf20494, 0x2dea9706fa4addfe, + 0x3efeeeb3f5cd5820, 0x40337498188bf660, 0x2da295562f3ec8ac, + 0x3f92a2b3e3f18998, 0xc020000000000000, 0x42d2e5b83d89b2b9, + 0x409de6b13cd05067, 0xc032fffa5a242644, 0x32fd0cf24dc15c78, + 0x40d230b0ea99c2e9, 0xbfe0f45e8ae0c220, 0x3f7661f71bdfed60, + 0x3f01d9144e20091c, 0x4039000000000000, 0x28bebe40c49c3def, + 0x4017104cbbcb6fa2, 0xc03946acb26b459a, 0x3bf14f1f353a51d6, + 0x40aa69dd0888c0fa, 0x40307d28c5226182, 0x4c03bc8d923c01c8, + 0x4073ea3eaeba639a, 0x4022000000000000, 0x449cae8b94519ce7, + 0x4100ad3bc6cafeeb, 0xc03d4616f02990fd, 0x20b82feb84424bb8, + 0x40f582e4c5ae35dc, 0x402121389c2d7b38, 0x48b9e98a98e0bc66, + 0x3f65ca3965215082, 0x401c000000000000, 0x3c3160964200c50a, + 0x3f94e4c852ace73e, 0x402209a7a9697d8c, 0x3cc485861536122c, + 0x4060d192cb6b6652, 0x3ff743a9c0eaa390, 0x40937709755cee67, + 0x3f1139a2a38a8dec, 0xc018000000000000, 0x45248dca7d464a43, + 0x3f81b2d3148507fb, 0x402dede88feff85c, 0x39857d5a55beed9b, + 0x40de81eae036b773, 0x40308a08cb19484e, 0x4f5edf6a2aab8cbb, + 0x4000a0a51efe09af, 0xc034000000000000, 0x3e9da67f670a8088, + 0x40623392aa67a9f8, 0xc02c316f94e54f72, 0x399a091cc44858db, + 0x41029e867f7c9de8, 0xc033a01cdcaf1132, 0x2ad0da8b5264a4ef, + 0xc0f28a568b13d67f, 0xc03d000000000000, 0xa28c88c742ea1a8e, + 0x4089b8b228dc8f3f, 0xc0226fe64da7a872, 0x3a5a5731f8c6448c, + 0x4017ab00652b1462, 0xc0143483500cec4c, 0x3f207f4658d1a4da, + 0x3f84618d62ab78f7, 0xc010000000000000, 0x41984ee52fc88aed, + 0x409e946964342f63, 0xc021acdd54cf4950, 0x39e49af404b74f95, + 0x3fa22feff4a45b70, 0x4030e2d64e7d9330, 0x3ad9d4f4ba992810, + 0x3f3ca7e6a14c1586, 0xc022000000000000, 0x46359a6c43d45aa4, + 0x3ff7bfb9bf9906c4, 0x3ff0a8723de8cb70, 0x3ff823478418da54, + 0x3f37ca63482b9f82, 0x402fbb4cc506d200, 0x3499d21d47c814a9 +]; + +const ZS_LDEXP_VEC : [372]u64 = [ + 0x3fe0000000000000, 3022, 0x0000000000000001, + 0x3ff0000000000000, 3021, 0x0000000000000000, + 0x3ff0000000000000, 3022, 0x0000000000000001, + 0x3fe8000000000000, 3022, 0x0000000000000001, + 0x3ff8000000000000, 3021, 0x0000000000000001, + 0x3fe0000000000000, 3021, 0x0000000000000000, + 0x0000000000000003, 4095, 0x0000000000000002, + 0x0000000000000003, 4094, 0x0000000000000001, + 0x000fffffffffffff, 4095, 0x0008000000000000, + 0x0000000000000001, 5170, 0x3ff0000000000000, + 0x3ff0000000000000, 5119, 0x7fe0000000000000, + 0x3ff0000000000000, 5120, 0x7ff0000000000000, + 0x4000000000000000, 5119, 0x7ff0000000000000, + 0x0000000000000001, 4096, 0x0000000000000001, + 0x80003739a252b281, 5096, 0xbe2b9cd129594080, + 0x000fffffffffffff, 4097, 0x001ffffffffffffe, + 0x3fefffffffffffff, 4045, 0x3cbfffffffffffff, + 0xbff0000000000000, 4036, 0xbc30000000000000, + 0x0000000000000003, 6143, 0x7cd8000000000000, + 0x80003739a252b281, 5120, 0xbfab9cd129594080, + 0x80003739a252b281, 3023, 0x8000000000000000, + 0x3fe0000000000000, 4036, 0x3c20000000000000, + 0x3ff8000000000000, 4043, 0x3ca8000000000000, + 0x0010000000000000, 3016, 0x0000000000000000, + 0x3fefffffffffffff, 3021, 0x0000000000000000, + 0x40fe240c9fbe76c9, 5119, 0x7ff0000000000000, + 0x40fe240c9fbe76c9, 4095, 0x40ee240c9fbe76c9, + 0xbff0000000000000, 3022, 0x8000000000000001, + 0x3ff8000000000000, 3073, 0x000c000000000000, + 0x0000000000000001, 5119, 0x3cc0000000000000, + 0x0008000000000000, 5096, 0x3e80000000000000, + 0xbff0000000000000, 3073, 0x8008000000000000, + 0x7fefffffffffffff, 5096, 0x7ff0000000000000, + 0x000fffffffffffff, 4149, 0x035ffffffffffffe, + 0x40fe240c9fbe76c9, 4097, 0x410e240c9fbe76c9, + 0xbfe0000000000000, 3036, 0x8000000000002000, + 0x40fe240c9fbe76c9, 4043, 0x3dae240c9fbe76c9, + 0x0010000000000001, 4148, 0x0350000000000001, + 0x80003739a252b281, 4096, 0x80003739a252b281, + 0x3fe8000000000000, 3023, 0x0000000000000002, + 0x000fffffffffffff, 3075, 0x0000000000000000, + 0x80003739a252b281, 3074, 0x8000000000000000, + 0x3fe8000000000000, 4095, 0x3fd8000000000000, + 0x0010000000000001, 4149, 0x0360000000000001, + 0x7fefffffffffffff, 6143, 0x7ff0000000000000, + 0x3fe0000000000000, 4095, 0x3fd0000000000000, + 0x0010000000000001, 3074, 0x0000000000000000, + 0x000fffffffffffff, 3036, 0x0000000000000000, + 0x0000000000000001, 6143, 0x7cc0000000000000, + 0x3fefffffffffffff, 4149, 0x433fffffffffffff, + 0x3fefffffffffffff, 3022, 0x0000000000000001, + 0x3ff0000000000000, 4149, 0x4340000000000000, + 0x000fffffffffffff, 4044, 0x0000000000000001, + 0x80003739a252b281, 4044, 0x8000000000000000, + 0x3fe8000000000000, 6142, 0x7ff0000000000000, + 0x80003739a252b281, 4097, 0x80006e7344a56502, + 0xbff0000000000000, 4043, 0xbca0000000000000, + 0x000fffffffffffff, 5096, 0x3e8ffffffffffffe, + 0xbff0000000000000, 4045, 0xbcc0000000000000, + 0x0000000000000003, 4045, 0x0000000000000000, + 0x0010000000000001, 3036, 0x0000000000000000, + 0x0000000000000001, 5096, 0x3b50000000000000, + 0x0000000000000003, 6196, 0x7ff0000000000000, + 0x3fe0000000000000, 4097, 0x3ff0000000000000, + 0x000fffffffffffff, 6196, 0x7ff0000000000000, + 0x0010000000000000, 6143, 0x7ff0000000000000, + 0x3fefffffffffffff, 3016, 0x0000000000000000, + 0x3fe0000000000000, 3036, 0x0000000000002000, + 0x3ff0000000000000, 6142, 0x7ff0000000000000, + 0x0000000000000001, 4095, 0x0000000000000000, + 0xbfe0000000000000, 4148, 0xc320000000000000, + 0x3fe8000000000000, 4043, 0x3c98000000000000, + 0x80003739a252b281, 3016, 0x8000000000000000, + 0x7fefffffffffffff, 3074, 0x400fffffffffffff, + 0x3fefffffffffffff, 4096, 0x3fefffffffffffff, + 0x3ff0000000000000, 5121, 0x7ff0000000000000, + 0x0010000000000001, 6142, 0x7ff0000000000000, + 0x3fe8000000000000, 4044, 0x3ca8000000000000, + 0x3fe0000000000000, 4044, 0x3ca0000000000000, + 0x0010000000000000, 3022, 0x0000000000000000, + 0x3fe8000000000000, 4036, 0x3c28000000000000, + 0x40fe240c9fbe76c9, 3016, 0x0000000000000789, + 0x40fe240c9fbe76c9, 6143, 0x7ff0000000000000, + 0x3ff0000000000000, 4043, 0x3ca0000000000000, + 0x3ff0000000000000, 4044, 0x3cb0000000000000, + 0x3ff0000000000000, 3074, 0x0010000000000000, + 0x3fefffffffffffff, 4044, 0x3cafffffffffffff, + 0xbff0000000000000, 4097, 0xc000000000000000, + 0x40fe240c9fbe76c9, 4096, 0x40fe240c9fbe76c9, + 0x3fe0000000000000, 6196, 0x7ff0000000000000, + 0x3fefffffffffffff, 5121, 0x7ff0000000000000, + 0xbfe0000000000000, 5120, 0xffe0000000000000, + 0x0008000000000000, 3023, 0x0000000000000000, + 0x40fe240c9fbe76c9, 4044, 0x3dbe240c9fbe76c9, + 0x0010000000000001, 6196, 0x7ff0000000000000, + 0xbfe0000000000000, 4149, 0xc330000000000000, + 0x0000000000000001, 3075, 0x0000000000000000, + 0x3fe8000000000000, 6196, 0x7ff0000000000000, + 0xbfe0000000000000, 6143, 0xfff0000000000000, + 0x000fffffffffffff, 3016, 0x0000000000000000, + 0x40fe240c9fbe76c9, 3074, 0x011e240c9fbe76c9, + 0x0008000000000000, 3021, 0x0000000000000000, + 0x0010000000000001, 3020, 0x0000000000000000, + 0x3fefffffffffffff, 5119, 0x7fdfffffffffffff, + 0x000fffffffffffff, 5120, 0x400ffffffffffffe, + 0x000fffffffffffff, 3023, 0x0000000000000000, + 0x0010000000000000, 5120, 0x4010000000000000, + 0x80003739a252b281, 3073, 0x8000000000000000, + 0x40fe240c9fbe76c9, 4036, 0x3d3e240c9fbe76c9, + 0x3fe8000000000000, 3036, 0x0000000000003000, + 0x0000000000000001, 4097, 0x0000000000000002, + 0x0000000000000001, 4044, 0x0000000000000000, + 0x3ff8000000000000, 6196, 0x7ff0000000000000, + 0x7fefffffffffffff, 6196, 0x7ff0000000000000, + 0x0010000000000001, 4095, 0x0008000000000000, + 0x3ff0000000000000, 3075, 0x0020000000000000, + 0x40fe240c9fbe76c9, 3023, 0x000000000003c482, + 0x000fffffffffffff, 6143, 0x7ff0000000000000, + 0x0010000000000000, 4043, 0x0000000000000000, + 0x0010000000000001, 3021, 0x0000000000000000, + 0x3fe8000000000000, 6143, 0x7ff0000000000000, + 0x0008000000000000, 6143, 0x7ff0000000000000, + 0x3fe0000000000000, 3075, 0x0010000000000000, + 0x3fe8000000000000, 5096, 0x7e68000000000000 +]; + +const ZS_FREXP_VEC : [48]u64 = [ + 0x3ff0000000000000, 0x3fe0000000000000, 2049, + 0x3fe0000000000000, 0x3fe0000000000000, 2048, + 0x3fe8000000000000, 0x3fe8000000000000, 2048, + 0x4008000000000000, 0x3fe8000000000000, 2050, + 0xc008000000000000, 0xbfe8000000000000, 2050, + 0x7e37e43c8800759c, 0x3fe7e43c8800759c, 3045, + 0x01a56e1fc2f8f359, 0x3fe56e1fc2f8f359, 1052, + 0x0000000000000001, 0x3fe0000000000000, 975, + 0x0010000000000000, 0x3fe0000000000000, 1027, + 0x000fffffffffffff, 0x3feffffffffffffe, 1026, + 0x40c81cd6e631f8a1, 0x3fe81cd6e631f8a1, 2062, + 0x80003739a252b281, 0xbfeb9cd129594080, 1020, + 0x7fefffffffffffff, 0x3fefffffffffffff, 3072, + 0x4020000000000000, 0x3fe0000000000000, 2052, + 0x0010000000000000, 0x3fe0000000000000, 1027, + 0x0000000000000400, 0x3fe0000000000000, 985 +]; + +test std_math_pow_f64_is_zigs_on_every_recorded_vector { + var i : usize = 0; + var bad : usize = 0; + while (i < 433) { + const xb : u64 = ZS_POW_VEC[3 * i]; + const yb : u64 = ZS_POW_VEC[3 * i + 1]; + const wb : u64 = ZS_POW_VEC[3 * i + 2]; + const x : f64 = @bitCast(xb); + const y : f64 = @bitCast(yb); + const w : f64 = @bitCast(wb); + const r : f64 = std_math_pow_f64(x, y); + const rb : u64 = @bitCast(r); + var ok : bool = rb == wb; + if (w != w) { + ok = r != r; + } + if (ok == false) { + bad = bad + 1; + } + i = i + 1; + } + assert(bad == 0); +} + +test zs_ldexp_is_zigs_on_every_recorded_vector_including_the_subnormal_ties { + var i : usize = 0; + var bad : usize = 0; + while (i < 124) { + const xb : u64 = ZS_LDEXP_VEC[3 * i]; + const nb : u64 = ZS_LDEXP_VEC[3 * i + 1]; + const wb : u64 = ZS_LDEXP_VEC[3 * i + 2]; + const x : f64 = @bitCast(xb); + var n : i32 = @intCast(nb); + n = n - 4096; + const r : f64 = zs_ldexp(x, n); + const rb : u64 = @bitCast(r); + if (rb != wb) { + bad = bad + 1; + } + i = i + 1; + } + assert(bad == 0); +} + +test zs_frexp_is_zigs_on_every_recorded_vector { + var i : usize = 0; + var bad : usize = 0; + while (i < 16) { + const xb : u64 = ZS_FREXP_VEC[3 * i]; + const sb : u64 = ZS_FREXP_VEC[3 * i + 1]; + const eb : u64 = ZS_FREXP_VEC[3 * i + 2]; + const x : f64 = @bitCast(xb); + const s : f64 = zs_frexp_sig(x); + const sbits : u64 = @bitCast(s); + var e : i32 = @intCast(eb); + e = e - 2048; + if (sbits != sb or zs_frexp_exp(x) != e) { + bad = bad + 1; + } + i = i + 1; + } + assert(bad == 0); +} + +test the_special_values_follow_pow_zig { + const inf : f64 = std.math.inf(f64); + const nzb : u64 = 0x8000000000000000; + const nzero : f64 = @bitCast(nzb); + assert(std_math_pow_f64(0.0, 0.0) == 1.0); + assert(std_math_pow_f64(std.math.nan(f64), 0.0) == 1.0); + assert(std_math_pow_f64(1.0, std.math.nan(f64)) == 1.0); + assert(std.math.isNan(std_math_pow_f64(std.math.nan(f64), 1.0))); + assert(std_math_pow_f64(nzero, -3.0) == -inf); + assert(std_math_pow_f64(nzero, -2.0) == inf); + assert(std_math_pow_f64(nzero, 3.0) == 0.0); + assert(zs_sign(std_math_pow_f64(nzero, 3.0)) == 1); + assert(zs_sign(std_math_pow_f64(nzero, 2.0)) == 0); + assert(std_math_pow_f64(-1.0, inf) == 1.0); + assert(std_math_pow_f64(2.0, inf) == inf); + assert(std_math_pow_f64(2.0, -inf) == 0.0); + assert(std_math_pow_f64(0.5, inf) == 0.0); + assert(std_math_pow_f64(-inf, 3.0) == -inf); + assert(std_math_pow_f64(-inf, 2.0) == inf); + assert(std_math_pow_f64(inf, -1.0) == 0.0); + assert(std_math_pow_f64(-8.0, 3.0) == -512.0); + assert(std_math_pow_f64(9.0, 0.5) == 3.0); + assert(std_math_pow_f64(4.0, -0.5) == 0.5); +} + +test negative_control_a_negative_base_with_a_fraction_is_nan { + assert(std.math.isNan(std_math_pow_f64(-8.0, 1.5))); + assert(std.math.isNan(std_math_pow_f64(-2.0, 0.3))); + assert(std.math.isNan(std_math_pow_f64(-0.5, -2.5))); +} + +// Plain repeated multiplication, the obvious port of an integer power: NOT Zig's result. +pub fn zs_naive_powi(x: f64, n: u32) -> f64 { + var r : f64 = 1.0; + var k : u32 = 0; + while (k < n) { + r = r * x; + k = k + 1; + } + return r; +} + +test negative_control_repeated_multiplication_is_not_zigs_pow_and_the_vectors_tell_them_apart { + // phi^8: 46.9787137637478 by multiplying eight times, 46.97871376374779 by Zig's pow + const phi : f64 = 1.618033988749895; + const naive : f64 = zs_naive_powi(phi, 8); + const naive_bits : u64 = @bitCast(naive); + const zig_bits : u64 = 0x40477D467E1BB88E; + const mine : f64 = std_math_pow_f64(phi, 8.0); + const mine_bits : u64 = @bitCast(mine); + assert(naive_bits != zig_bits); + assert(mine_bits == zig_bits); +} + +test negative_control_an_exact_tie_below_the_subnormals_rounds_up_in_zig_where_ieee_rounds_to_even { + // 2^-1075 is half of the smallest subnormal. 0.5 * 2^-1074 rounds to even: 0. Zig's ldexp gives 2^-1074. + const one : u64 = 1; + const tiny : f64 = @bitCast(one); + const ieee : f64 = 0.5 * tiny; + const ieee_bits : u64 = @bitCast(ieee); + const zig : f64 = zs_ldexp(0.5, -1074); + const zig_bits : u64 = @bitCast(zig); + assert(ieee_bits == 0); + assert(zig_bits == 1); + assert(std_math_pow_f64(2.0, -1075.0) == tiny); +} From 00cf90dba3f72dceac8e978301747a00bbb04b6e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 13:34:32 +0700 Subject: [PATCH 5/5] t27o: zig_convert.t27 -- the counter-slot test runs its asserts at run time, vacuous passes 0 of 61 (Closes #10823) The test that pins the five counters to five different slots compared constants, which Zig folds at compile time: `t27c test-report` counted it as a pass with 0 runtime asserts. It now writes a different number to each slot and reads each back, so two slots that collide fail it at run time. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27o_T27oZig.json | 10 +++++----- specs/tools/t27o/zig_convert.t27 | 29 +++++++++++++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json index e0064a1912..84a5de9db6 100644 --- a/.trinity/seals/t27o_T27oZig.json +++ b/.trinity/seals/t27o_T27oZig.json @@ -7,16 +7,16 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:61069fc34247483fbcd018ad03e650cc10478c01a7a3c908e9ff5a52e2e6b56a", + "gen_hash_c": "sha256:7725f48195f1d63c346c554a9f476564993956aa96528440617597f9f7af0df2", "gen_hash_rust": "sha256:5b79bfee2a5d9770de677f1717c9fb5fbf022ca8353f0ced3993efc58011cb60", "gen_hash_verilog": "sha256:172face9cae7881f4f41c5fd65f10e3c57ddeee52e62861bfe810c8152590e4f", - "gen_hash_zig": "sha256:268a0db9adfb400279ecb45adf3c89a4f3a50d43da9c0540659cc70d739e16c7", + "gen_hash_zig": "sha256:8a89267d881f63316a6ff08b4ed3d755002640d19562da007fc2023179ed9f9f", "module": "T27oZig", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T06:16:16Z", + "sealed_at": "2026-10-19T06:34:03Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:90d1290eb33386b4fb8f4e10721bdda9b5f610d54ac63d8f2c26bda0beb579dd", + "spec_hash": "sha256:8322d4dd716d084c17c366d336e9b70f4720b392dd8614b3983579fdf2c28b38", "spec_path": "specs/tools/t27o/zig_convert.t27", "tests": { "failed": 0, @@ -24,7 +24,7 @@ "forced": false, "passed": 61, "total": 61, - "vacuous": 1 + "vacuous": 0 }, "toolchain": { "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 index 66da783ca7..2980813d9d 100644 --- a/specs/tools/t27o/zig_convert.t27 +++ b/specs/tools/t27o/zig_convert.t27 @@ -3706,16 +3706,25 @@ test the_counters_sit_where_rust_converts_report_reads_them_and_no_fn_is_a_stub } test negative_control_the_five_counters_are_five_different_slots_and_none_is_the_class_slot { - assert(NFN != NOK); - assert(NOK != NTS); - assert(NTS != NTEST); - assert(NTEST != NSTUB); - assert(Z_CLS != NFN); - assert(Z_CLS != NOK); - assert(Z_CLS != NTS); - assert(Z_CLS != NTEST); - assert(Z_STMT != NTEST); - assert(Z_GREW != NSTUB); + var st : [128]usize = [0] ** 128; + st[NFN] = 1; + st[NOK] = 2; + st[NTS] = 3; + st[NTEST] = 4; + st[NSTUB] = 5; + st[Z_CLS] = 6; + st[Z_STMT] = 7; + st[Z_GREW] = 8; + st[Z_USEPOS] = 9; + assert(st[NFN] == 1); + assert(st[NOK] == 2); + assert(st[NTS] == 3); + assert(st[NTEST] == 4); + assert(st[NSTUB] == 5); + assert(st[Z_CLS] == 6); + assert(st[Z_STMT] == 7); + assert(st[Z_GREW] == 8); + assert(st[Z_USEPOS] == 9); } // ---------------------------------------------------------------- tests: consts imported from another file (#10823)