From b1670df4be44bbafa57f7d8b2758c66c55d12526 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:41:18 +0700 Subject: [PATCH 1/6] Make the package buildable: manifest, build.zig, and four repointed imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was no build script at all, the manifest had no fingerprint and an unhashed dependency, and the four imports pointed at files that live in zig-golden-float — which imported this repository's knowledge_graph.zig right back. Dependency pinned to a commit rather than main, because a url-plus-hash against a moving branch invalidates itself on every merge. --- .github/workflows/build.yml | 32 + .gitignore | 2 + build.zig | 53 + build.zig.zon | 21 +- src/knowledge_graph.zig | 15 +- .../LICENSE | 21 + .../README.md | 260 ++ .../build.zig | 262 ++ .../build.zig.zon | 17 + .../src/c/gf16.h | 405 +++ .../src/c/gf_ladder.h | 59 + .../src/c/gft.h | 100 + .../src/c_abi.zig | 671 +++++ .../src/formats/formats_root.zig | 687 ++++++ .../src/formats/gf8.zig | 286 +++ .../src/formats/gf_binary.zig | 299 +++ .../src/formats/gft.zig | 298 +++ .../src/formats/golden_float16.zig | 463 ++++ .../src/jepa_t.zig | 70 + .../src/main.rs | 46 + .../src/math/constants.zig | 148 ++ .../src/math/gen_bench.zig | 566 +++++ .../src/math/gen_commands.zig | 470 ++++ .../src/math/gen_constants.zig | 374 +++ .../src/math/gen_eval.zig | 497 ++++ .../src/math/gen_format.zig | 394 +++ .../src/math/gen_identities.zig | 235 ++ .../src/math/gen_riemann_gamma.zig | 308 +++ .../src/math/transcendental.zig | 184 ++ .../src/phi_attention.zig | 86 + .../src/root.zig | 127 + .../src/ternary/bigint.zig | 1192 +++++++++ .../src/ternary/hybrid.zig | 732 ++++++ .../src/ternary/packed_trit.zig | 306 +++ .../src/trinity_constants.zig | 82 + .../src/trinity_init.zig | 92 + .../src/vm/jit_arm64.zig | 2175 +++++++++++++++++ .../src/vm/jit_unified.zig | 434 ++++ .../src/vm/jit_x86_64.zig | 471 ++++ .../src/vm/opcodes.zig | 161 ++ .../src/vm/vm.zig | 1250 ++++++++++ .../src/vm/vsa_jit.zig | 688 ++++++ .../src/vsa/10k_vsa.zig | 461 ++++ .../src/vsa/common.zig | 20 + .../src/vsa/concurrency.zig | 295 +++ .../src/vsa/core.zig | 816 +++++++ .../src/vsa/fpga_bind.zig | 532 ++++ .../src/vsa/gen_core.zig | 247 ++ .../src/vsa/gen_encoding.zig | 340 +++ .../src/vsa/hrr.zig | 412 ++++ .../src/vsa/packed_vsa.zig | 494 ++++ .../src/vsa_jit.zig | 688 ++++++ 52 files changed, 19333 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 build.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig create mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..57b0a58 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,32 @@ +# This repository had no build.zig and no workflow. Three source files, fifteen +# test blocks, and a manifest that declared a dependency by URL with no hash — +# so nothing could fetch it, nothing could build it, and nobody could depend on +# it. The four imports in knowledge_graph.zig pointed at sibling files that live +# in gHashTag/zig-golden-float, whose own packed_vsa.zig imported +# "knowledge_graph.zig" right back. Neither half compiled. +name: build + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v2 + with: + # The version this package targets. Local 0.16 reports failures that + # do not exist here (std.io.getStdOut, ArrayList.init, std.fs) and + # would hide at least one that does. + version: 0.15.2 + + - name: zig build + run: zig build + + - name: zig build test + run: zig build test --summary all diff --git a/.gitignore b/.gitignore index 330fecf..28c3941 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ zig-out/ .env* !.env.example +.zig-cache/ +zig-out/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..03a5483 --- /dev/null +++ b/build.zig @@ -0,0 +1,53 @@ +const std = @import("std"); + +// There was no build.zig in this repository at all. Three source files, fifteen +// test blocks, a manifest declaring a dependency without a hash — and nothing +// that could compile any of it. +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const golden = b.dependency("zig_golden_float", .{ + .target = target, + .optimize = optimize, + }).module("golden-float"); + + const kg_mod = b.addModule("zig-knowledge-graph", .{ + .root_source_file = b.path("src/knowledge_graph.zig"), + .target = target, + .optimize = optimize, + }); + kg_mod.addImport("zig_golden_float", golden); + + // kg_cli and kg_server both have a main(); they were never buildable either. + inline for (.{ + .{ "kg-cli", "src/kg_cli.zig" }, + .{ "kg-server", "src/kg_server.zig" }, + }) |exe_spec| { + const mod = b.createModule(.{ + .root_source_file = b.path(exe_spec[1]), + .target = target, + .optimize = optimize, + }); + mod.addImport("zig_golden_float", golden); + b.installArtifact(b.addExecutable(.{ .name = exe_spec[0], .root_module = mod })); + } + + // Each root gets its own test target. A single root would reach only what it + // references, and under Zig's lazy analysis an unreferenced import is not a + // weakly-checked file — it is an absent one, along with its test blocks. + const test_step = b.step("test", "Run tests"); + inline for (.{ + .{ "knowledge_graph", "src/knowledge_graph.zig" }, + .{ "kg_server", "src/kg_server.zig" }, + .{ "kg_cli", "src/kg_cli.zig" }, + }) |t| { + const tm = b.createModule(.{ + .root_source_file = b.path(t[1]), + .target = target, + .optimize = optimize, + }); + tm.addImport("zig_golden_float", golden); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .name = t[0], .root_module = tm })).step); + } +} diff --git a/build.zig.zon b/build.zig.zon index 9e14e19..b9a5dba 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,9 +1,16 @@ -{ - .name = zig_knowledge_graph, - .version = "0.1.0", - .dependencies = .{ - .zig_golden_float = .{ - .url = "https://github.com/gHashTag/zig-golden-float/archive/main.tar.gz", +.{ + // Was: a dependency declared by URL with no .hash, and no .fingerprint at + // all — so nothing could fetch it and nothing could depend on this package. + // There was also no build.zig, so there was nothing to run either. + .name = .zig_knowledge_graph, + .version = "0.1.0", + .fingerprint = 0x2a92e04f54905954, + .minimum_zig_version = "0.15.0", + .dependencies = .{ + .zig_golden_float = .{ + .url = "https://github.com/gHashTag/zig-golden-float/archive/e7ce32885de2a8c50b7b6a3030d0592202145dd1.tar.gz", + .hash = "golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF", + }, }, - }, + .paths = .{ "build.zig", "build.zig.zon", "src", "README.md", "LICENSE" }, } diff --git a/src/knowledge_graph.zig b/src/knowledge_graph.zig index 9984c9c..006db93 100644 --- a/src/knowledge_graph.zig +++ b/src/knowledge_graph.zig @@ -11,10 +11,17 @@ // φ² + 1/φ² = 3 const std = @import("std"); -const vsa = @import("vsa.zig"); -const hybrid = @import("hybrid.zig"); -const packed_vsa = @import("packed_vsa.zig"); -const packed_trit = @import("packed_trit.zig"); +// These four were flat relative imports of files that are not in this +// repository. They are in gHashTag/zig-golden-float, whose own +// src/vsa/packed_vsa.zig imported "knowledge_graph.zig" — a file that is not +// in THAT repository, but is right here. One directory was split into two and +// every relative import was left pointing at the sibling that stayed behind, +// so neither half compiled. Pointed at the dependency instead. +const golden = @import("zig_golden_float"); +const vsa = golden.vsa; +const hybrid = golden.bigint; +const packed_vsa = golden.packed_vsa; +const packed_trit = golden.packed_trit; const HybridBigInt = hybrid.HybridBigInt; const PackedBigInt = packed_trit.PackedBigInt; diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE new file mode 100644 index 0000000..b2a4770 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 gHashTag + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md new file mode 100644 index 0000000..8ff4402 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md @@ -0,0 +1,260 @@ +# GoldenFloat + +[![Zig](https://img.shields.io/badge/Zig-0.15+-F7A41D?logo=zig&logoColor=white)](https://ziglang.org/) +[![CI](https://github.com/gHashTag/zig-golden-float/actions/workflows/test-bindings.yml/badge.svg)](https://github.com/gHashTag/zig-golden-float/actions/workflows/test-bindings.yml) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![Release](https://img.shields.io/github/v/release/gHashTag/zig-golden-float?label=release)](https://github.com/gHashTag/zig-golden-float/releases/latest) +[![Golden Ratio](https://img.shields.io/badge/%CF%86-1.618033988-gold)](https://en.wikipedia.org/wiki/Golden_ratio) + +> 16-bit floating point in base-φ with multi-format support, φ-optimized FMA, ternary arithmetic, VSA hypervectors, and unified JIT — the numerical core of the [Trinity](https://github.com/gHashTag/trinity) ecosystem. + +--- + +## Formats + +| Format | Layout | Bias | Range | Notes | +|--------|--------|------|-------|-------| +| **GF16** | `[s:1][e:6][m:9]` | 31 | ~±65504 | Golden ratio base, no subnormals | +| **fp16** | IEEE 754 binary16 | 15 | ±65504 | Full subnormal support | +| **bf16** | IEEE 754 brain16 | 127 | ~±3.4e38 | Canonical `(bits +\| 0x7FFF) >> 16` encoder | +| **GF8** | `[s:1][e:3][m:4]` | 7 | ~±4.24 | 3-bit φ-exponent, 4-bit mantissa; saturates outside φ³ | +| **GFTernary** | `{-1, 0, +1}` | — | ±1 | ±0.5 threshold, 100% sparse | + +All formats use **round-to-nearest-even** via `quantizeValue()` dispatch. + +## The GoldenFloat Ladder (GF + GF-T) + +Two ladders share one idea — a φ-structured fixed-field float with **no regime +decode** (unlike posit/tekum) — differing only in how the exponent is stored. + +### GF — binary-exponent rung ladder + +One normative rule sizes every binary rung (FORMAT-SPEC-001 v1.2): +`e = round((N−1)/φ²)`, `m = N−1−e`, `bias = 2^(e−1)−1`, `exp_max = 2^e−1`. + +| Format | Bits | Layout `[s:e:m]` | Bias | Status | +|--------|------|------------------|------|--------| +| GF4 | 4 | `[1:1:2]` | 0 | Verified | +| **GF8** | 8 | `[1:3:4]` | 3 † | Verified — edge / sensors | +| GF12 | 12 | `[1:4:7]` | 7 | Verified — mid-range / audio | +| **GF16** | 16 | `[1:6:9]` | 31 | **Primary** — FPGA 35/35 @ 323 MHz Artix-7 | +| GF20 | 20 | `[1:7:12]` | 63 | Experimental | +| GF24 | 24 | `[1:9:14]` | 255 | Experimental | +| GF32 | 32 | `[1:12:19]` | 2047 | Spec | + +The ladder continues to GF1024 (17 binary rungs total); GF16 is the sole primary +production rung. The whole rule-derived ladder is implemented in +[`src/formats/gf_binary.zig`](src/formats/gf_binary.zig) as a comptime factory — +`gf_binary.GF4/GF8/GF12/GF16/GF20/GF24/GF32`, or `gf_binary.GF(bits)` for any width: + +```zig +const golden = @import("golden-float"); +const x = golden.gf_binary.GF12.fromF32(3.14159); // [1:4:7], bias 7 +std.debug.print("{d}\n", .{x.toF32()}); +const Custom = golden.gf_binary.GF(48); // rule-sized on demand +``` + +(GF8/GF16 additionally have dedicated φ-FMA implementations in `formats`.) † The +normative bias for GF8 is `2^(e−1)−1 = 3` and `gf_binary.GF8` uses it; the older +standalone `gf8.zig` codec encodes bias 7 — a known code/spec discrepancy tracked +for reconciliation. + +### GF-T — balanced-ternary-exponent ladder + +The exponent is a **balanced-ternary** number (digits −1/0/+1, stored as codes +0/1/2) added natively in ternary — no binary exponent, no regime decode — while the +mantissa keeps GF's uniform binary precision. Value = `(−1)^sign · (1 + M/2^m) · 2^e` +with `e = offset − EXP_OFFSET`; the top offset row `3^E − 1` is reserved (Inf/NaN). + +| Format | Layout `[s : E trits : M bits]` | EXP_OFFSET | Special row `3^E−1` | Exponent range | Dynamic range | +|--------|----------------------------------|-----------|---------------------|----------------|---------------| +| GF-T4 | `[1 : 2t : 1]` | 4 | 8 | ±4 | ~2.4 decades | +| GF-T8 | `[1 : 3t : 4]` | 13 | 26 | ±13 | ~8 decades | +| GF-T16 | `[1 : 4t : 9]` | 40 | 80 | ±40 | ~24 decades | +| GF-T32 | `[1 : 6t : 25]` | 364 | 728 | ±364 | ~219 decades | + +GF-T16 keeps GF16's φ-optimal 9-bit mantissa across its whole range, where +tekum16 tapers to ~4 bits at the extremes. The authoritative parameters live in +[`specs/gft.tri`](specs/gft.tri); the codec is [`src/formats/gft.zig`](src/formats/gft.zig). + +### Using GF-T in code + +```zig +const std = @import("std"); +const golden = @import("golden-float"); + +pub fn main() void { + // Pick a rung by name: GFT4 / GFT8 / GFT16 / GFT32. + const a = golden.GFT16.fromF32(3.14159); + const b = golden.GFT16.fromF32(2.71828); + + const prod = a.mul(b); // add / sub / mul / div + std.debug.print("{d}\n", .{prod.toF32()}); // ~8.539 + + // Inspect / round-trip the raw storage bits (FFI, serialization). + const raw = a.bits(); // unsigned integer (GFT16.Repr) + const a2 = golden.GFT16.fromBits(raw); + std.debug.assert(a2.bits() == raw); + + // Specials behave like a float: Inf saturates, NaN is contagious. + std.debug.assert(!golden.GFT16.fromF32(1e30).isFinite()); // overflow -> Inf + std.debug.assert(golden.GFT16.fromF32(1e-30).toF32() == 0); // underflow -> 0 + + // GF-T32 reaches ~219 decades (1e30, 6.022e23, ...) at 25-bit precision. + const avo = golden.GFT32.fromF32(6.022e23); + std.debug.print("{d}\n", .{avo.toF32()}); +} +``` + +Every rung is one instance of a comptime factory, so you can mint a custom rung +too: `const MyRung = golden.gft.GFT(5, 12); // 5 exp-trits, 12 mantissa bits`. +Each type exposes `fromF32` / `toF32` / `add` / `sub` / `mul` / `div` / `neg` / +`abs` / `bits` / `fromBits` / `isFinite` plus the constants `EXP_TRITS`, +`MANT_BITS`, `EXP_OFFSET`, `OFFSET_MAX`, `BITS`, `Repr`. A runnable copy lives in +[`examples/gft_usage.zig`](examples/gft_usage.zig). + +## Quick Start + +```bash +zig fetch --save https://github.com/gHashTag/zig-golden-float/archive/refs/tags/v2.1.0.tar.gz +``` + +```zig +const gf = @import("golden_float"); + +const x = gf.GF16.fromF32(3.14); +const y = gf.GF16.fromF32(2.71); +const z = x.add(y); +std.debug.print("{d}\n", .{z.toF32()}); // 5.85... +``` + +## Architecture + +``` +src/ +├── formats/ GF16/GF8 (golden_float16), gf_binary.zig (GF ladder GF4..GF32), +│ gft.zig (GF-T4/8/16/32), fp16, bf16, GFTernary codecs +├── math/ constants, transcendental (sin, cos, exp, log) +├── ternary/ HybridBigInt, packed trit storage +├── vsa/ core, HRR, 10K-dim hypervectors, FPGA bind +├── vm/ stack interpreter, ARM64 & x86_64 JIT +├── c_abi.zig FFI layer → libgoldenfloat.{so,dylib,dll} +└── root.zig public API +``` + +## Language Bindings + +| Language | Path | Status | +|----------|------|--------| +| **Zig** | `src/` | Native | +| **C/C++** | `src/c/{gf16,gf_ladder,gft}.h` + `cpp/` | C-ABI + header-only wrappers | +| **Rust** | `rust/goldenfloat-sys/` | FFI crate | +| **Python** | `python/goldenfloat/` | ctypes bridge | +| **Go** | `go/goldenfloat/` | cgo wrapper | + +### Format coverage across bindings + +Every rung below is a thin FFI wrapper over the **same** `libgoldenfloat` shared +library, so all languages execute the identical Zig codec — the wrappers differ only +in surface syntax. + +| Format family | Zig | C-ABI | C++ | Rust | Python | Go | +|---------------|:---:|:-----:|:---:|:----:|:------:|:--:| +| **GF16** (rich: arith, cmp, min/max, fma, φ-quant, predicates) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **Binary GF ladder** GF8 / GF12 / GF20 / GF24 / GF32 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **GF-T16** (arith, neg/abs, is_finite) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **GF-T8 / GF-T32** (arith, neg/abs, is_finite) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **GF-T4** (minimal E2M1 — from/to/mul/is_finite) | ✓ | ✓ | — | — | — | — | +| **GF4** (`[1:1:2]`, degenerate — no normal values) | factory | — | — | — | — | — | + +Wrapper names follow the rung: C++ `goldenfloat::Gf12` / `Gft8`, Rust `gf12_t` / +`gft8_t`, Python `goldenfloat.Gf12` / `Gft8`, Go `goldenfloat.Gf12` / `Gft8`. The +binary ladder covers `from/to_f32`, `add/sub/mul/div`, unary `neg`, `abs`, and +`is_finite`; GF16 additionally carries the rich comparison / FMA / φ-quantization API. +GF4 is intentionally unwrapped — a 1-bit exponent leaves only zero / Inf / NaN. + +### Building & Testing + +```bash +# Build shared library (required for bindings) +zig build shared + +# Run Zig tests +zig build test + +# Test all bindings +./scripts/test_bindings.sh + +# Individual bindings +cd rust/goldenfloat-sys && cargo test +cd python && python -m goldenfloat.tests.test_gf16 +cd cpp && cmake -S . -B build && cmake --build build && ./build/test_gf16 +cd go/goldenfloat && go test -v ./... +``` + +## φ-Optimized FMA + +```c +// Standard +gf16_fma(a, b, c); // a×b + c +gf16_fms(a, b, c); // a×b - c +gf16_fnma(a, b, c); // -(a×b) + c + +// φ-weighted +gf16_phi_fma(a, b, c); // (a×b)×φ + c×φ⁻¹ +gf16_phi_dot(n, a, b); // φ-weighted dot product +``` + +## IGLA-GF16 Architecture + +Neural network architecture built on φ-math: + +| Module | Description | +|--------|-------------| +| Trinity Constants | φ, α_φ, Fibonacci dimensions | +| φ-Sparse Attention | Fibonacci distance mask `{1,2,3,5,8,13,21,34,55,89,144}` — 2.15% sparsity | +| Trinity Weight Init | 4 physics sectors: gauge / higgs / lepton / cosmology | +| φ-LR Schedule | Warmup Fib(7)=21 steps, φ-decay | +| JEPA-T Predictor | Encoder 6 + Predictor 3 layers, φ-split | + +## Benchmarks + +| Metric | Result | +|--------|--------| +| GF16 accuracy vs fp32 (σ=1.0) | > 99.99% | +| GF16 vs bf16 MSE ratio (uniform ±100) | 16.2× better | +| GF16 sparsity at [-10,10] | 0% (no saturation) | +| GFTernary sparsity (He init σ=0.05) | 100% | +| Pearson r(φ-distance, MSE) | −0.34 | + +Full results in `.trinity/results/` and benches under `benches/`. + +## C-ABI + +```c +#include "gf16.h" + +gf16_t a = gf16_from_f32(3.14f); +gf16_t b = gf16_from_f32(2.71f); +gf16_t c = gf16_add(a, b); +printf("%.6f\n", gf16_to_f32(c)); + +double phi = goldenfloat_phi(); // 1.6180339887... +double trinity = goldenfloat_trinity(); // φ² + φ⁻² = 3 +``` + +## Ecosystem + +- [zig-sacred-geometry](https://github.com/gHashTag/zig-sacred-geometry) +- [zig-physics](https://github.com/gHashTag/zig-physics) +- [zig-hdc](https://github.com/gHashTag/zig-hdc) +- [trinity-training](https://github.com/gHashTag/trinity-training) +- [trinity](https://github.com/gHashTag/trinity) + +## Version + +**2.1.0** — see [CHANGELOG.md](CHANGELOG.md) for release history. + +## License + +[MIT](LICENSE) © gHashTag diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig new file mode 100644 index 0000000..a746228 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig @@ -0,0 +1,262 @@ +//! GoldenFloat — φ-Optimized Zig Kernel Build System +//! Zig 0.15 package system — module-only library +//! +//! **Build Targets:** +//! - `zig build` — Build module only +//! - `zig build test` — Run all tests +//! - `zig build shared` — Build libgoldenfloat.{so,dylib,dll} +//! - `zig build c-abi-test` — Test C-ABI layer + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // ───────────────────────────────────────────────────────────────── + // Library module (what users import via @import("golden-float")) + // ───────────────────────────────────────────────────────────────── + _ = b.addModule("golden-float", .{ + .root_source_file = b.path("src/root.zig"), + }); + + // ───────────────────────────────────────────────────────────────── + // tri_gen executable — code generator from .tri specs + // ───────────────────────────────────────────────────────────────── + const tri_gen_module = b.createModule(.{ + .root_source_file = b.path("tools/gen/tri_gen.zig"), + .target = target, + .optimize = optimize, + }); + + const tri_gen = b.addExecutable(.{ + .name = "tri_gen", + .root_module = tri_gen_module, + }); + + // NOT installed by default, and not because it is broken. + // + // tri_gen and tri_reader are written against Zig 0.16 on purpose -- their own + // comments say so, and they use std.Io, std.Io.Dir and std.process.Init, + // none of which exist in 0.15. This package declares + // minimum_zig_version 0.15.0 and both of its consumers build with 0.15.2, so + // installing a 0.16-only tool by default made the LIBRARY unbuildable for + // everybody in order to keep a tool nobody can run at that version. + // + // The library itself compiles on 0.15 -- the only failure was here. So the + // tool moves behind an explicit step and the version claim becomes true + // rather than aspirational. `zig build gen` still builds and runs it, on a + // toolchain that has the API it was written for. + // + // This is not making a build green by deleting what failed: what failed is + // still built, by a step that names the toolchain it needs. + const tools_step = b.step("tools", "Build the .tri code generator (requires Zig 0.16)"); + tools_step.dependOn(&b.addInstallArtifact(tri_gen, .{}).step); + + const run_tri_gen = b.addRunArtifact(tri_gen); + const gen_step = b.step("gen", "Generate code from .tri specs"); + gen_step.dependOn(&run_tri_gen.step); + + // ───────────────────────────────────────────────────────────────── + // C-ABI Shared Library — libgoldenfloat.{so,dylib,dll} + // ───────────────────────────────────────────────────────────────── + const c_abi_module = b.createModule(.{ + .root_source_file = b.path("src/c_abi.zig"), + .target = target, + .optimize = optimize, + }); + + const c_abi_lib = b.addLibrary(.{ + .name = "goldenfloat", + .root_module = c_abi_module, + .linkage = .dynamic, + .version = .{ .major = 2, .minor = 1, .patch = 0 }, + }); + + b.installArtifact(c_abi_lib); + + // Install C header alongside library + const header_install = b.addInstallHeaderFile(b.path("src/c/gf16.h"), "gf16.h"); + + const shared_step = b.step("shared", "Build C-ABI shared library (libgoldenfloat)"); + shared_step.dependOn(&b.addInstallArtifact(c_abi_lib, .{}).step); + shared_step.dependOn(&header_install.step); + + // ───────────────────────────────────────────────────────────────── + // C-ABI Tests + // ───────────────────────────────────────────────────────────────── + const c_abi_test_module = b.createModule(.{ + .root_source_file = b.path("src/c_abi.zig"), + .target = target, + .optimize = optimize, + }); + + // The module root, analysed in full. Nothing rooted src/root.zig before, so + // the surface consumers actually import was the one part never compiled. + const root_tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }), + }); + + const c_abi_tests = b.addTest(.{ + .name = "c-abi-tests", + .root_module = c_abi_test_module, + }); + + const run_c_abi_tests = b.addRunArtifact(c_abi_tests); + const c_abi_test_step = b.step("c-abi-test", "Run C-ABI tests"); + c_abi_test_step.dependOn(&run_c_abi_tests.step); + + // ───────────────────────────────────────────────────────────────── + // Tests — formats (GF16/TF3) + // ───────────────────────────────────────────────────────────────── + const formats_tests_root = b.createModule(.{ + .root_source_file = b.path("src/formats/golden_float16.zig"), + .target = target, + .optimize = optimize, + }); + const formats_tests = b.addTest(.{ + .name = "formats-tests", + .root_module = formats_tests_root, + }); + + // ───────────────────────────────────────────────────────────────── + // Tests — GF-T ternary-exponent ladder (GF-T4/8/16/32) + // ───────────────────────────────────────────────────────────────── + const gft_tests_root = b.createModule(.{ + .root_source_file = b.path("src/formats/gft.zig"), + .target = target, + .optimize = optimize, + }); + const gft_tests = b.addTest(.{ + .name = "gft-tests", + .root_module = gft_tests_root, + }); + const run_gft_tests = b.addRunArtifact(gft_tests); + + // ───────────────────────────────────────────────────────────────── + // Tests — GF binary-exponent ladder factory (GF4/8/12/16/20/24/32) + // ───────────────────────────────────────────────────────────────── + const gf_binary_tests_root = b.createModule(.{ + .root_source_file = b.path("src/formats/gf_binary.zig"), + .target = target, + .optimize = optimize, + }); + const gf_binary_tests = b.addTest(.{ + .name = "gf-binary-tests", + .root_module = gf_binary_tests_root, + }); + const run_gf_binary_tests = b.addRunArtifact(gf_binary_tests); + + // ───────────────────────────────────────────────────────────────── + // Tests — transcendental functions (Wave 4B) + // ───────────────────────────────────────────────────────────────── + const transcendent_tests_root = b.createModule(.{ + .root_source_file = b.path("src/math/transcendental.zig"), + .target = target, + .optimize = optimize, + }); + const transcendent_tests = b.addTest(.{ + .name = "transcendent-tests", + .root_module = transcendent_tests_root, + }); + + // ───────────────────────────────────────────────────────────────── + // Tests — .tri spec parser (tri_reader) + // Spec files live in specs/, outside tools/gen/, so they cannot be + // @embedFile'd directly (module-path restriction). Supply them as named + // anonymous imports the test embeds via @embedFile("spec_gf8"/"spec_gf16"). + // ───────────────────────────────────────────────────────────────── + const tri_reader_tests_root = b.createModule(.{ + .root_source_file = b.path("tools/gen/tri_reader.zig"), + .target = target, + .optimize = optimize, + }); + tri_reader_tests_root.addAnonymousImport("spec_gf8", .{ + .root_source_file = b.path("specs/gf8.tri"), + }); + tri_reader_tests_root.addAnonymousImport("spec_gf16", .{ + .root_source_file = b.path("specs/gf16.tri"), + }); + const tri_reader_tests = b.addTest(.{ + .name = "tri-reader-tests", + .root_module = tri_reader_tests_root, + }); + const run_tri_reader_tests = b.addRunArtifact(tri_reader_tests); + + const run_tests = b.addRunArtifact(formats_tests); + const run_transcendent_tests = b.addRunArtifact(transcendent_tests); + + const trinity_tests_root = b.createModule(.{ + .root_source_file = b.path("src/trinity_constants.zig"), + .target = target, + .optimize = optimize, + }); + const trinity_tests = b.addTest(.{ + .name = "trinity-constants-tests", + .root_module = trinity_tests_root, + }); + const run_trinity_tests = b.addRunArtifact(trinity_tests); + + const phi_attention_tests_root = b.createModule(.{ + .root_source_file = b.path("src/phi_attention.zig"), + .target = target, + .optimize = optimize, + }); + const phi_attention_tests = b.addTest(.{ + .name = "phi-attention-tests", + .root_module = phi_attention_tests_root, + }); + const run_phi_attention_tests = b.addRunArtifact(phi_attention_tests); + + const trinity_init_tests_root = b.createModule(.{ + .root_source_file = b.path("src/trinity_init.zig"), + .target = target, + .optimize = optimize, + }); + const trinity_init_tests = b.addTest(.{ + .name = "trinity-init-tests", + .root_module = trinity_init_tests_root, + }); + const run_trinity_init_tests = b.addRunArtifact(trinity_init_tests); + + const jepa_t_tests_root = b.createModule(.{ + .root_source_file = b.path("src/jepa_t.zig"), + .target = target, + .optimize = optimize, + }); + const jepa_t_tests = b.addTest(.{ + .name = "jepa-t-tests", + .root_module = jepa_t_tests_root, + }); + const run_jepa_t_tests = b.addRunArtifact(jepa_t_tests); + + const test_step = b.step("test", "Run all tests"); + test_step.dependOn(&run_tests.step); + test_step.dependOn(&run_gft_tests.step); + test_step.dependOn(&run_gf_binary_tests.step); + test_step.dependOn(&run_transcendent_tests.step); + test_step.dependOn(&run_c_abi_tests.step); + test_step.dependOn(&run_trinity_tests.step); + test_step.dependOn(&run_phi_attention_tests.step); + test_step.dependOn(&run_trinity_init_tests.step); + test_step.dependOn(&run_jepa_t_tests.step); + test_step.dependOn(&run_tri_reader_tests.step); + + const igla_bench_module = b.createModule(.{ + .root_source_file = b.path("benches/igla_gf16_bench.zig"), + .target = target, + .optimize = optimize, + }); + const igla_bench = b.addExecutable(.{ + .name = "igla_gf16_bench", + .root_module = igla_bench_module, + }); + const run_igla_bench = b.addRunArtifact(igla_bench); + const igla_bench_step = b.step("bench-igla", "Run IGLA-GF16 architecture verification (Module 7)"); + igla_bench_step.dependOn(&run_igla_bench.step); + test_step.dependOn(&b.addRunArtifact(root_tests).step); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon new file mode 100644 index 0000000..2a06457 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon @@ -0,0 +1,17 @@ +.{ + .name = .golden_float, + .version = "2.1.0", + .minimum_zig_version = "0.15.0", + + .paths = .{ + "src", + "build.zig", + "build.zig.zon", + "README.md", + "LICENSE", + }, + + .dependencies = .{}, + + .fingerprint = 0x9fba9f8d85cab287, +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h new file mode 100644 index 0000000..1052ab9 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h @@ -0,0 +1,405 @@ +/** + * GoldenFloat v2.0.0 — C-ABI Header + * + * Minimal C99 header for GF16 (Golden Float16) format. + * This header is the SPECIFICATION for libgoldenfloat.{so,dylib,dll} + * + * **Format Layout:** [sign:1][exp:6][mant:9] (16 bits total) + * **Exponent Bias:** 31 + * **Special Values:** exp=0x3F (63) = infinity/NaN + * + * **Usage:** + * ```c + * // Include header + * #include + * + * // Convert values + * gf16_t a = gf16_from_f32(3.14f); + * gf16_t b = gf16_from_f32(2.71f); + * gf16_t sum = gf16_add(a, b); + * float result = gf16_to_f32(sum); + * ``` + * + * MIT License — Copyright (c) 2026 Trinity Project + * Repository: https://github.com/gHashTag/zig-golden-float + */ + +#ifndef GOLDENFLOAT_GF16_H +#define GOLDENFLOAT_GF16_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*====================================================================== + * Type Definition + *======================================================================*/ + +/** + * GF16 value stored as raw 16-bit unsigned integer + * + * **Bit Layout:** + * [15] Sign (0 = positive, 1 = negative) + * [14:9] Exponent (bias = 31, range = -31..+32) + * [8:0] Mantissa (9 bits, fractional part) + * + * **Value Formula:** + * value = (-1)^sign × (1 + mant/512) × 2^(exp - 31) + * + * **Special Values:** + * - exp=0, mant=0: Zero (signed by sign bit) + * - exp=0x3F, mant=0: Infinity (signed by sign bit) + * - exp=0x3F, mant≠0: NaN (quiet) + */ +typedef uint16_t gf16_t; + +/*====================================================================== + * Constants + *======================================================================*/ + +/** Zero constant (positive zero) */ +#define GF16_ZERO ((gf16_t)0x0000) + +/** One constant (1.0 in GF16) */ +#define GF16_ONE ((gf16_t)0x3C00) + +/** Positive infinity */ +#define GF16_PINF ((gf16_t)0x7E00) + +/** Negative infinity */ +#define GF16_NINF ((gf16_t)0xFE00) + +/** Quiet NaN */ +#define GF16_NAN ((gf16_t)0x7E01) + +/** Negative zero */ +#define GF16_NZERO ((gf16_t)0x8000) + +/*====================================================================== + * Bit Extraction Macros + *======================================================================*/ + +/** Extract sign bit (0 or 1) */ +#define GF16_SIGN(g) (((g) >> 15) & 0x1) + +/** Extract exponent field (0..63) */ +#define GF16_EXP(g) (((g) >> 9) & 0x3F) + +/** Extract mantissa field (0..511) */ +#define GF16_MANT(g) ((g) & 0x1FF) + +/** Construct GF16 from components */ +#define GF16_MAKE(s, e, m) (((gf16_t)((s) & 1) << 15) | \ + ((gf16_t)((e) & 0x3F) << 9) | \ + ((gf16_t)(m) & 0x1FF)) + +/*====================================================================== + * Conversion Functions + *======================================================================*/ + +/** + * Convert f32 to GF16 + * + * @param x Input float value + * @return GF16 representation + * + * **Rounding:** Round-to-nearest, ties-to-even + * **Special Values:** Preserved (inf, NaN, signed zeros) + */ +gf16_t gf16_from_f32(float x); + +/** + * Convert GF16 to f32 + * + * @param g GF16 value + * @return Float representation + * + * **Precision:** Exact for all GF16 values + */ +float gf16_to_f32(gf16_t g); + +/*====================================================================== + * Arithmetic Functions + *======================================================================*/ + +/** + * Add two GF16 values + * + * @param a First operand + * @param b Second operand + * @return a + b in GF16 + * + * **Computation:** Performed in f32, rounded to GF16 + */ +gf16_t gf16_add(gf16_t a, gf16_t b); + +/** + * Subtract two GF16 values + * + * @param a First operand + * @param b Second operand + * @return a - b in GF16 + */ +gf16_t gf16_sub(gf16_t a, gf16_t b); + +/** + * Multiply two GF16 values + * + * @param a First operand + * @param b Second operand + * @return a × b in GF16 + */ +gf16_t gf16_mul(gf16_t a, gf16_t b); + +/** + * Divide two GF16 values + * + * @param a Numerator + * @param b Denominator + * @return a / b in GF16 + * + * **Note:** Division by zero returns infinity (signed) + */ +gf16_t gf16_div(gf16_t a, gf16_t b); + +/*====================================================================== + * Unary Functions + *======================================================================*/ + +/** + * Negate GF16 value + * + * @param g Input value + * @return -g + */ +gf16_t gf16_neg(gf16_t g); + +/** + * Absolute value of GF16 + * + * @param g Input value + * @return |g| + */ +gf16_t gf16_abs(gf16_t g); + +/*====================================================================== + * Comparison Functions + *======================================================================*/ + +/** + * Equality test + * + * @param a First operand + * @param b Second operand + * @return true if equal, false otherwise + * + * **Note:** NaN != NaN (IEEE 754 semantics) + */ +bool gf16_eq(gf16_t a, gf16_t b); + +/** + * Less-than test + * + * @param a First operand + * @param b Second operand + * @return true if a < b, false otherwise + * + * **Note:** NaN comparisons return false + */ +bool gf16_lt(gf16_t a, gf16_t b); + +/** + * Less-than-or-equal test + * + * @param a First operand + * @param b Second operand + * @return true if a <= b, false otherwise + */ +bool gf16_le(gf16_t a, gf16_t b); + +/** + * Three-way comparison + * + * @param a First operand + * @param b Second operand + * @return -1 if a < b, 0 if a == b, 1 if a > b + */ +int gf16_cmp(gf16_t a, gf16_t b); + +/*====================================================================== + * Predicate Functions + *======================================================================*/ + +/** + * Check if value is NaN + * + * @param g GF16 value + * @return true if NaN, false otherwise + */ +bool gf16_is_nan(gf16_t g); + +/** + * Check if value is infinity (positive or negative) + * + * @param g GF16 value + * @return true if infinity, false otherwise + */ +bool gf16_is_inf(gf16_t g); + +/** + * Check if value is zero (positive or negative) + * + * @param g GF16 value + * @return true if zero, false otherwise + */ +bool gf16_is_zero(gf16_t g); + +/** + * Check if value is subnormal + * + * @param g GF16 value + * @return true if subnormal, false otherwise + * + * **Note:** GF16 has no true subnormals (exp=0 is zero) + */ +bool gf16_is_subnormal(gf16_t g); + +/** + * Check if value is negative + * + * @param g GF16 value + * @return true if negative, false otherwise + */ +bool gf16_is_negative(gf16_t g); + +/*====================================================================== + * φ-Math Functions (Golden Ratio Optimization) + *======================================================================*/ + +/** + * φ-optimized quantization + * + * Quantizes f32 to GF16 using φ-weighted bins. + * Better distribution for ML weights. + * + * @param x Input float value + * @return φ-quantized GF16 value + * + * **Formula:** x × (1/φ²) then quantize + */ +gf16_t gf16_phi_quantize(float x); + +/** + * φ-optimized dequantization + * + * Dequantizes GF16 to f32 using φ-weighted bins. + * + * @param g GF16 value + * @return φ-dequantized float value + * + * **Formula:** to_f32(g) × φ² + */ +float gf16_phi_dequantize(gf16_t g); + +/*====================================================================== + * Utility Functions + *======================================================================*/ + +/** + * Copy sign from source to target + * + * @param target Value whose magnitude is used + * @param source Value whose sign is used + * @return target with source's sign + */ +gf16_t gf16_copysign(gf16_t target, gf16_t source); + +/** + * Minimum of two values + * + * @param a First operand + * @param b Second operand + * @return min(a, b) + */ +gf16_t gf16_min(gf16_t a, gf16_t b); + +/** + * Maximum of two values + * + * @param a First operand + * @param b Second operand + * @return max(a, b) + */ +gf16_t gf16_max(gf16_t a, gf16_t b); + +/** + * Fused multiply-add: a × b + c + * + * @param a First operand + * @param b Second operand + * @param c Third operand + * @return a × b + c in GF16 + * + * **Note:** Computed in f32, rounded to GF16 + */ +gf16_t gf16_fma(gf16_t a, gf16_t b, gf16_t c); + +/** + * φ-optimized fused multiply-add + * + * Dequantizes inputs from φ-space, computes a × b + c in f32, + * then φ-quantizes the result back. + * + * @param a First operand (φ-quantized) + * @param b Second operand (φ-quantized) + * @param c Third operand (φ-quantized) + * @return φ-quantized result of a × b + c + */ +gf16_t gf16_phi_fma(gf16_t a, gf16_t b, gf16_t c); + +/** + * φ-optimized fused multiply-subtract + * + * Dequantizes inputs from φ-space, computes a × b - c in f32, + * then φ-quantizes the result back. + * + * @param a First operand (φ-quantized) + * @param b Second operand (φ-quantized) + * @param c Third operand (φ-quantized) + * @return φ-quantized result of a × b - c + */ +gf16_t gf16_phi_fms(gf16_t a, gf16_t b, gf16_t c); + +/*====================================================================== + * Constants + *======================================================================*/ + +/** Golden ratio φ = (1 + √5) / 2 ≈ 1.6180339887498948 */ +#define GF16_PHI 1.6180339887498948482f + +/** φ² = φ × φ ≈ 2.6180339887498948 */ +#define GF16_PHI_SQ 2.6180339887498948482f + +/** 1/φ² ≈ 0.3819660112501051 */ +#define GF16_PHI_INV_SQ 0.38196601125010515f + +/** Trinity Identity: φ² + 1/φ² = 3 */ +#define GF16_TRINITY 3.0f + +/** Exponent bias for GF16 */ +#define GF16_EXP_BIAS 31 + +/** Maximum exponent value (before special values) */ +#define GF16_EXP_MAX 62 + +/** Number of mantissa bits */ +#define GF16_MANT_BITS 9 + +#ifdef __cplusplus +} +#endif + +#endif /* GOLDENFLOAT_GF16_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h new file mode 100644 index 0000000..41af52a --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h @@ -0,0 +1,59 @@ +/** + * GoldenFloat — Binary GF ladder C-ABI Header + * + * The φ²-sized binary rungs from the gf_binary.zig factory, exported from + * libgoldenfloat.{so,dylib,dll}. Each rung sizes its exponent by the rule + * e = round((N-1) / φ²), m = N-1-e, bias = 2^(e-1)-1, exp_max = 2^e-1 + * so the exp:mantissa split tracks 1/φ at every width. + * + * Rung Layout bias ~normal range + * GF8 [1:3:4] 3 ~[0.25, 15.5] + * GF12 [1:4:7] 7 ~[0.016, 256] + * GF20 [1:7:12] 63 ~[2^-62, 2^63] + * GF24 [1:9:14] 255 ~[2^-254, 2^255] + * GF32 [1:12:19] 2047 ~[2^-2046, 2^2047] + * + * GF16 [1:6:9] b31 is the rich API in gf16.h (identical layout). GF4 [1:1:2] is + * omitted: a 1-bit exponent leaves no normal values (only zero / Inf / NaN). + * + * The packed N-bit value rides in the low bits of the next byte-sized carrier. + * Semantics: round-to-nearest, saturate to Inf, flush subnormals to zero. + * + * phi^2 + 1/phi^2 = 3 | TRINITY + * MIT License — Copyright (c) 2026 Trinity Project + */ + +#ifndef GOLDENFLOAT_GF_LADDER_H +#define GOLDENFLOAT_GF_LADDER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Each macro declares the 9-function API for one rung over carrier type T. */ +#define GOLDENFLOAT_GF_RUNG(PFX, T) \ + T PFX##_from_f32(float x); \ + float PFX##_to_f32(T g); \ + T PFX##_add(T a, T b); \ + T PFX##_sub(T a, T b); \ + T PFX##_mul(T a, T b); \ + T PFX##_div(T a, T b); \ + T PFX##_neg(T g); \ + T PFX##_abs(T g); \ + uint8_t PFX##_is_finite(T g); + +GOLDENFLOAT_GF_RUNG(gf8, uint8_t) /* [1:3:4] b3 */ +GOLDENFLOAT_GF_RUNG(gf12, uint16_t) /* [1:4:7] b7 */ +GOLDENFLOAT_GF_RUNG(gf20, uint32_t) /* [1:7:12] b63 */ +GOLDENFLOAT_GF_RUNG(gf24, uint32_t) /* [1:9:14] b255 */ +GOLDENFLOAT_GF_RUNG(gf32, uint32_t) /* [1:12:19] b2047 */ + +#undef GOLDENFLOAT_GF_RUNG + +#ifdef __cplusplus +} +#endif + +#endif /* GOLDENFLOAT_GF_LADDER_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h new file mode 100644 index 0000000..595d068 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h @@ -0,0 +1,100 @@ +/** + * GoldenFloat — GF-T16 C-ABI Header + * + * Minimal C99 header for GF-T16 (ternary-exponent Golden Float). + * SPECIFICATION for the gft16_* symbols in libgoldenfloat.{so,dylib,dll} + * (implemented in src/c_abi.zig over src/formats/gft.zig). + * + * **Format:** [sign:1][exp:4 balanced-ternary trits][mant:9] — the exponent is a + * balanced-ternary number stored as an unsigned OFFSET in [0,80]; the balanced + * exponent is e = offset - 40; the top offset row (80) is reserved (Inf/NaN). + * value = (-1)^sign * (1 + M/2^9) * 2^e, e in [-40,+39] (~24 decades). + * + * The 17-bit packed value is carried in the low bits of a uint32_t (gft16_t). + * + * **Usage:** + * ```c + * #include + * gft16_t a = gft16_from_f32(3.14159f); + * gft16_t b = gft16_from_f32(2.71828f); + * float p = gft16_to_f32(gft16_mul(a, b)); // ~8.539 + * ``` + * + * phi^2 + 1/phi^2 = 3 | TRINITY + */ + +#ifndef GOLDENFLOAT_GFT_H +#define GOLDENFLOAT_GFT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Raw 17-bit GF-T16 pattern carried in the low bits of a uint32_t. */ +typedef uint32_t gft16_t; + +/** Encode an IEEE float32 into GF-T16 (round-to-nearest, saturate to Inf). */ +gft16_t gft16_from_f32(float x); +/** Decode a GF-T16 value back to float32. */ +float gft16_to_f32(gft16_t g); + +gft16_t gft16_add(gft16_t a, gft16_t b); +gft16_t gft16_sub(gft16_t a, gft16_t b); +gft16_t gft16_mul(gft16_t a, gft16_t b); +gft16_t gft16_div(gft16_t a, gft16_t b); + +gft16_t gft16_neg(gft16_t g); +gft16_t gft16_abs(gft16_t g); + +/** 1 if g is finite (not the reserved Inf/NaN row), else 0. */ +uint8_t gft16_is_finite(gft16_t g); + +/** Balanced zero point: offset that encodes exponent 0 (value in [1,2)). */ +#define GFT16_EXP_OFFSET 40 +/** Reserved special row (Inf/NaN): offset 3^4 - 1. */ +#define GFT16_OFFSET_MAX 80 +/** Number of exponent trits. */ +#define GFT16_EXP_TRITS 4 +/** Number of mantissa bits. */ +#define GFT16_MANT_BITS 9 + +/* ---- The other GF-T rungs (packed value in the low bits of the carrier) ---- */ +/** GF-T4 : E=2 trits, M=1 bit (6-bit value in a uint8). EXP_OFFSET=4, max=8. */ +typedef uint8_t gft4_t; +/** GF-T8 : E=3 trits, M=4 bits (10-bit value in a uint16). EXP_OFFSET=13, max=26. */ +typedef uint16_t gft8_t; +/** GF-T32 : E=6 trits, M=25 bits (36-bit value in a uint64). EXP_OFFSET=364, max=728, ~219 decades. */ +typedef uint64_t gft32_t; + +gft4_t gft4_from_f32(float x); +float gft4_to_f32(gft4_t g); +gft4_t gft4_mul(gft4_t a, gft4_t b); +uint8_t gft4_is_finite(gft4_t g); + +gft8_t gft8_from_f32(float x); +float gft8_to_f32(gft8_t g); +gft8_t gft8_add(gft8_t a, gft8_t b); +gft8_t gft8_sub(gft8_t a, gft8_t b); +gft8_t gft8_mul(gft8_t a, gft8_t b); +gft8_t gft8_div(gft8_t a, gft8_t b); +gft8_t gft8_neg(gft8_t g); +gft8_t gft8_abs(gft8_t g); +uint8_t gft8_is_finite(gft8_t g); + +gft32_t gft32_from_f32(float x); +float gft32_to_f32(gft32_t g); +gft32_t gft32_add(gft32_t a, gft32_t b); +gft32_t gft32_sub(gft32_t a, gft32_t b); +gft32_t gft32_mul(gft32_t a, gft32_t b); +gft32_t gft32_div(gft32_t a, gft32_t b); +gft32_t gft32_neg(gft32_t g); +gft32_t gft32_abs(gft32_t g); +uint8_t gft32_is_finite(gft32_t g); + +#ifdef __cplusplus +} +#endif + +#endif /* GOLDENFLOAT_GFT_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig new file mode 100644 index 0000000..92a6768 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig @@ -0,0 +1,671 @@ +//! GoldenFloat C-ABI v1.1.0 — Zig Implementation +//! +//! This file provides extern "C" functions that implement the GF16 API +//! defined in src/c/gf16.h. The shared library (libgoldenfloat) is +//! compiled from this Zig source. +//! +//! **Architecture:** +//! - Header (src/c/gf16.h) = specification +//! - This file (src/c_abi.zig) = Zig implementation +//! - build.zig = compiles to libgoldenfloat.{so,dylib,dll} +//! +//! **Usage from other languages:** +//! ```rust +//! // Rust +//! extern "C" { +//! fn gf16_from_f32(x: f32) -> u16; +//! fn gf16_to_f32(g: u16) -> f32; +//! } +//! ``` +//! +//! ```python +//! # Python +//! import ctypes +//! lib = ctypes.CDLL("libgoldenfloat.so") +//! lib.gf16_from_f32.restype = ctypes.c_uint16 +//! lib.gf16_from_f32.argtypes = [ctypes.c_float] +//! ``` + +const std = @import("std"); +const golden = @import("formats/golden_float16.zig"); + +// ═══════════════════════════════════════════════════════════════════ +// Type Aliases +// ═════════════════════════════════════════════════════════════════ + +/// gf16_t is a raw u16 bit pattern +const gf16_t = u16; + +/// Convert GF16 struct to raw u16 +inline fn gf16ToRaw(gf: golden.GF16) gf16_t { + return @as(u16, @bitCast(gf)); +} + +/// Convert raw u16 to GF16 struct +inline fn rawToGf16(raw: gf16_t) golden.GF16 { + return @as(golden.GF16, @bitCast(raw)); +} + +// ═════════════════════════════════════════════════════════════════════ +// Conversion Functions +// ═════════════════════════════════════════════════════════════════ + +export fn gf16_from_f32(x: f32) callconv(.c) gf16_t { + return gf16ToRaw(golden.GF16.fromF32(x)); +} + +export fn gf16_to_f32(g: gf16_t) callconv(.c) f32 { + return rawToGf16(g).toF32(); +} + +// ═══════════════════════════════════════════════════════════════════ +// Arithmetic Functions +// ═════════════════════════════════════════════════════════════════════ + +export fn gf16_add(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + return gf16ToRaw(golden.GF16.add(gf_a, gf_b)); +} + +export fn gf16_sub(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + return gf16ToRaw(golden.GF16.sub(gf_a, gf_b)); +} + +export fn gf16_mul(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + return gf16ToRaw(golden.GF16.mul(gf_a, gf_b)); +} + +export fn gf16_div(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + return gf16ToRaw(golden.GF16.div(gf_a, gf_b)); +} + +// ═════════════════════════════════════════════════════════════════════ +// Unary Functions +// ═════════════════════════════════════════════════════════════════ + +export fn gf16_neg(g: gf16_t) callconv(.c) gf16_t { + return gf16ToRaw(rawToGf16(g).neg()); +} + +export fn gf16_abs(g: gf16_t) callconv(.c) gf16_t { + return gf16ToRaw(rawToGf16(g).abs()); +} + +// ═════════════════════════════════════════════════════════════════════ +// Comparison Functions +// ═════════════════════════════════════════════════════════════════════ + +export fn gf16_eq(a: gf16_t, b: gf16_t) callconv(.c) bool { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + const fa = gf_a.toF32(); + const fb = gf_b.toF32(); + // Handle NaN: NaN != NaN (IEEE 754 semantics) + if (std.math.isNan(fa) or std.math.isNan(fb)) return false; + return fa == fb; +} + +export fn gf16_lt(a: gf16_t, b: gf16_t) callconv(.c) bool { + const gf_a = rawToGf16(a); + const gf_b = rawToGf16(b); + const fa = gf_a.toF32(); + const fb = gf_b.toF32(); + // Handle NaN: comparisons with NaN are false + if (std.math.isNan(fa) or std.math.isNan(fb)) return false; + return fa < fb; +} + +export fn gf16_le(a: gf16_t, b: gf16_t) callconv(.c) bool { + return gf16_lt(a, b) or gf16_eq(a, b); +} + +export fn gf16_cmp(a: gf16_t, b: gf16_t) callconv(.c) c_int { + if (gf16_lt(a, b)) return -1; + if (gf16_eq(a, b)) return 0; + return 1; +} + +// ═══════════════════════════════════════════════════════════════════════ +// Predicate Functions +// ═══════════════════════════════════════════════════════════════════ + +export fn gf16_is_nan(g: gf16_t) callconv(.c) bool { + const gf = rawToGf16(g); + // NaN: exp = 0x3F and mant != 0 + return gf.exp == 0x3F and gf.mant != 0; +} + +export fn gf16_is_inf(g: gf16_t) callconv(.c) bool { + const gf = rawToGf16(g); + // Infinity: exp = 0x3F and mant = 0 + return gf.exp == 0x3F and gf.mant == 0; +} + +export fn gf16_is_zero(g: gf16_t) callconv(.c) bool { + const gf = rawToGf16(g); + // Zero: exp = 0 and mant = 0 + return gf.exp == 0 and gf.mant == 0; +} + +export fn gf16_is_subnormal(g: gf16_t) callconv(.c) bool { + const gf = rawToGf16(g); + // GF16 has no true subnormals (exp = 0 means zero) + return gf.exp == 0 and gf.mant != 0; +} + +export fn gf16_is_negative(g: gf16_t) callconv(.c) bool { + const gf = rawToGf16(g); + return gf.sign == 1; +} + +// ═════════════════════════════════════════════════════════════════════ +// φ-Math Functions +// ═══════════════════════════════════════════════════════════════════════ + +export fn gf16_phi_quantize(x: f32) callconv(.c) gf16_t { + return gf16ToRaw(golden.GF16.phiQuantize(x)); +} + +export fn gf16_phi_dequantize(g: gf16_t) callconv(.c) f32 { + const gf = rawToGf16(g); + return golden.GF16.phiDequantize(gf); +} + +// ═══════════════════════════════════════════════════════════════════════ +// Utility Functions +// ═════════════════════════════════════════════════════════════════════════════ + +export fn gf16_copysign(target: gf16_t, source: gf16_t) callconv(.c) gf16_t { + const gf_target = rawToGf16(target); + const gf_source = rawToGf16(source); + return gf16ToRaw(.{ + .mant = gf_target.mant, + .exp = gf_target.exp, + .sign = gf_source.sign, + }); +} + +export fn gf16_min(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + return if (gf16_lt(a, b)) a else b; +} + +export fn gf16_max(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { + return if (gf16_lt(a, b)) b else a; +} + +export fn gf16_fma(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { + const fa = rawToGf16(a).toF32(); + const fb = rawToGf16(b).toF32(); + const fc = rawToGf16(c).toF32(); + return gf16ToRaw(golden.GF16.fromF32(fa * fb + fc)); +} + +export fn gf16_phi_fma(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { + return gf16ToRaw(golden.GF16.phiFma(rawToGf16(a), rawToGf16(b), rawToGf16(c))); +} + +export fn gf16_phi_fms(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { + return gf16ToRaw(golden.GF16.phiFms(rawToGf16(a), rawToGf16(b), rawToGf16(c))); +} + +// ═══════════════════════════════════════════════════════════════════ +// Library Info +// ═════════════════════════════════════════════════════════════════════ + +export fn goldenfloat_version() callconv(.c) [*:0]const u8 { + return "1.1.0"; +} + +export fn goldenfloat_phi() callconv(.c) f64 { + return golden.PHI; +} + +export fn goldenfloat_trinity() callconv(.c) f64 { + return golden.TRINITY; +} + +// ═════════════════════════════════════════════════════════════════════ +// Compile-Time Guards +// ═══════════════════════════════════════════════════════════════════════════════ + +comptime { + std.debug.assert(@sizeOf(gf16_t) == 2); + std.debug.assert(@sizeOf(golden.GF16) == 2); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// GF-T16 — balanced-ternary-exponent GoldenFloat (thin FFI over formats/gft.zig). +// The raw value is the 17-bit packed encoding carried in a u32 (declared in +// src/c/gft.h). This is FFI glue only; all arithmetic lives in the gft.zig codec. +// ═══════════════════════════════════════════════════════════════════════════════ + +const gft = @import("formats/gft.zig"); + +/// gft16_t is the raw 17-bit GF-T16 pattern in the low bits of a u32. +const gft16_t = u32; + +inline fn gft16ToRaw(g: gft.GFT16) gft16_t { + return @as(gft16_t, g.bits()); +} +inline fn rawToGft16(raw: gft16_t) gft.GFT16 { + return gft.GFT16.fromBits(@truncate(raw)); +} + +export fn gft16_from_f32(x: f32) callconv(.c) gft16_t { + return gft16ToRaw(gft.GFT16.fromF32(x)); +} +export fn gft16_to_f32(g: gft16_t) callconv(.c) f32 { + return rawToGft16(g).toF32(); +} +export fn gft16_add(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(gft.GFT16.add(rawToGft16(a), rawToGft16(b))); +} +export fn gft16_sub(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(gft.GFT16.sub(rawToGft16(a), rawToGft16(b))); +} +export fn gft16_mul(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(gft.GFT16.mul(rawToGft16(a), rawToGft16(b))); +} +export fn gft16_div(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(gft.GFT16.div(rawToGft16(a), rawToGft16(b))); +} +export fn gft16_neg(g: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(rawToGft16(g).neg()); +} +export fn gft16_abs(g: gft16_t) callconv(.c) gft16_t { + return gft16ToRaw(rawToGft16(g).abs()); +} +export fn gft16_is_finite(g: gft16_t) callconv(.c) u8 { + return @intFromBool(rawToGft16(g).isFinite()); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// The other GF-T rungs — GF-T4 (u8), GF-T8 (u16), GF-T32 (u64). Same thin glue +// pattern as gft16; the packed value rides in the low bits of the C carrier type. +// ═══════════════════════════════════════════════════════════════════════════════ + +const gft4_t = u8; +const gft8_t = u16; +const gft32_t = u64; + +export fn gft4_from_f32(x: f32) callconv(.c) gft4_t { + return @as(gft4_t, gft.GFT4.fromF32(x).bits()); +} +export fn gft4_to_f32(g: gft4_t) callconv(.c) f32 { + return gft.GFT4.fromBits(@truncate(g)).toF32(); +} +export fn gft4_mul(a: gft4_t, b: gft4_t) callconv(.c) gft4_t { + return @as(gft4_t, gft.GFT4.mul(gft.GFT4.fromBits(@truncate(a)), gft.GFT4.fromBits(@truncate(b))).bits()); +} +export fn gft4_is_finite(g: gft4_t) callconv(.c) u8 { + return @intFromBool(gft.GFT4.fromBits(@truncate(g)).isFinite()); +} + +export fn gft8_from_f32(x: f32) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.fromF32(x).bits()); +} +export fn gft8_to_f32(g: gft8_t) callconv(.c) f32 { + return gft.GFT8.fromBits(@truncate(g)).toF32(); +} +export fn gft8_add(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.add(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); +} +export fn gft8_mul(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.mul(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); +} +export fn gft8_sub(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.sub(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); +} +export fn gft8_div(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.div(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); +} +export fn gft8_neg(g: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.neg(gft.GFT8.fromBits(@truncate(g))).bits()); +} +export fn gft8_abs(g: gft8_t) callconv(.c) gft8_t { + return @as(gft8_t, gft.GFT8.abs(gft.GFT8.fromBits(@truncate(g))).bits()); +} +export fn gft8_is_finite(g: gft8_t) callconv(.c) u8 { + return @intFromBool(gft.GFT8.fromBits(@truncate(g)).isFinite()); +} + +export fn gft32_from_f32(x: f32) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.fromF32(x).bits()); +} +export fn gft32_to_f32(g: gft32_t) callconv(.c) f32 { + return gft.GFT32.fromBits(@truncate(g)).toF32(); +} +export fn gft32_add(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.add(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); +} +export fn gft32_mul(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.mul(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); +} +export fn gft32_sub(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.sub(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); +} +export fn gft32_div(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.div(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); +} +export fn gft32_neg(g: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.neg(gft.GFT32.fromBits(@truncate(g))).bits()); +} +export fn gft32_abs(g: gft32_t) callconv(.c) gft32_t { + return @as(gft32_t, gft.GFT32.abs(gft.GFT32.fromBits(@truncate(g))).bits()); +} +export fn gft32_is_finite(g: gft32_t) callconv(.c) u8 { + return @intFromBool(gft.GFT32.fromBits(@truncate(g)).isFinite()); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// Binary GF ladder — the φ²-sized rungs from the gf_binary.zig factory. +// GF16 is already covered by the rich gf16_* API above (identical [1:6:9] b31), so +// this exposes GF8/GF12/GF20/GF24/GF32. GF4 is intentionally omitted: [1:1:2] gives a +// 1-bit exponent (exp 0 = zero, exp 1 = reserved Inf/NaN) with NO normal values. +// The packed N-bit value rides in the low bits of the next byte-sized carrier. +// ═══════════════════════════════════════════════════════════════════════════════ + +const gfl = @import("formats/gf_binary.zig"); + +// ---- GF8 (8-bit value in u8) ---- +export fn gf8_from_f32(x: f32) callconv(.c) u8 { + return @as(u8, gfl.GF8.fromF32(x).bits_()); +} +export fn gf8_to_f32(g: u8) callconv(.c) f32 { + return gfl.GF8.fromBits(@truncate(g)).toF32(); +} +export fn gf8_add(a: u8, b: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.add(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); +} +export fn gf8_sub(a: u8, b: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.sub(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); +} +export fn gf8_mul(a: u8, b: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.mul(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); +} +export fn gf8_div(a: u8, b: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.div(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); +} +export fn gf8_neg(g: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.neg(gfl.GF8.fromBits(@truncate(g))).bits_()); +} +export fn gf8_abs(g: u8) callconv(.c) u8 { + return @as(u8, gfl.GF8.abs(gfl.GF8.fromBits(@truncate(g))).bits_()); +} +export fn gf8_is_finite(g: u8) callconv(.c) u8 { + return @intFromBool(gfl.GF8.fromBits(@truncate(g)).isFinite()); +} + +// ---- GF12 (12-bit value in u16) ---- +export fn gf12_from_f32(x: f32) callconv(.c) u16 { + return @as(u16, gfl.GF12.fromF32(x).bits_()); +} +export fn gf12_to_f32(g: u16) callconv(.c) f32 { + return gfl.GF12.fromBits(@truncate(g)).toF32(); +} +export fn gf12_add(a: u16, b: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.add(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); +} +export fn gf12_sub(a: u16, b: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.sub(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); +} +export fn gf12_mul(a: u16, b: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.mul(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); +} +export fn gf12_div(a: u16, b: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.div(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); +} +export fn gf12_neg(g: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.neg(gfl.GF12.fromBits(@truncate(g))).bits_()); +} +export fn gf12_abs(g: u16) callconv(.c) u16 { + return @as(u16, gfl.GF12.abs(gfl.GF12.fromBits(@truncate(g))).bits_()); +} +export fn gf12_is_finite(g: u16) callconv(.c) u8 { + return @intFromBool(gfl.GF12.fromBits(@truncate(g)).isFinite()); +} + +// ---- GF20 (20-bit value in u32) ---- +export fn gf20_from_f32(x: f32) callconv(.c) u32 { + return @as(u32, gfl.GF20.fromF32(x).bits_()); +} +export fn gf20_to_f32(g: u32) callconv(.c) f32 { + return gfl.GF20.fromBits(@truncate(g)).toF32(); +} +export fn gf20_add(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.add(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); +} +export fn gf20_sub(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.sub(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); +} +export fn gf20_mul(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.mul(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); +} +export fn gf20_div(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.div(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); +} +export fn gf20_neg(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.neg(gfl.GF20.fromBits(@truncate(g))).bits_()); +} +export fn gf20_abs(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF20.abs(gfl.GF20.fromBits(@truncate(g))).bits_()); +} +export fn gf20_is_finite(g: u32) callconv(.c) u8 { + return @intFromBool(gfl.GF20.fromBits(@truncate(g)).isFinite()); +} + +// ---- GF24 (24-bit value in u32) ---- +export fn gf24_from_f32(x: f32) callconv(.c) u32 { + return @as(u32, gfl.GF24.fromF32(x).bits_()); +} +export fn gf24_to_f32(g: u32) callconv(.c) f32 { + return gfl.GF24.fromBits(@truncate(g)).toF32(); +} +export fn gf24_add(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.add(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); +} +export fn gf24_sub(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.sub(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); +} +export fn gf24_mul(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.mul(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); +} +export fn gf24_div(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.div(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); +} +export fn gf24_neg(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.neg(gfl.GF24.fromBits(@truncate(g))).bits_()); +} +export fn gf24_abs(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF24.abs(gfl.GF24.fromBits(@truncate(g))).bits_()); +} +export fn gf24_is_finite(g: u32) callconv(.c) u8 { + return @intFromBool(gfl.GF24.fromBits(@truncate(g)).isFinite()); +} + +// ---- GF32 (32-bit value in u32) ---- +export fn gf32_from_f32(x: f32) callconv(.c) u32 { + return @as(u32, gfl.GF32.fromF32(x).bits_()); +} +export fn gf32_to_f32(g: u32) callconv(.c) f32 { + return gfl.GF32.fromBits(@truncate(g)).toF32(); +} +export fn gf32_add(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.add(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); +} +export fn gf32_sub(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.sub(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); +} +export fn gf32_mul(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.mul(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); +} +export fn gf32_div(a: u32, b: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.div(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); +} +export fn gf32_neg(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.neg(gfl.GF32.fromBits(@truncate(g))).bits_()); +} +export fn gf32_abs(g: u32) callconv(.c) u32 { + return @as(u32, gfl.GF32.abs(gfl.GF32.fromBits(@truncate(g))).bits_()); +} +export fn gf32_is_finite(g: u32) callconv(.c) u8 { + return @intFromBool(gfl.GF32.fromBits(@truncate(g)).isFinite()); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// Tests +// ═════════════════════════════════════════════════════════════════════════════ + +test "C-ABI: gf16_from_f32 and gf16_to_f32" { + const val: f32 = 3.14; + const gf = gf16_from_f32(val); + const back = gf16_to_f32(gf); + const err = @abs(val - back) / (@abs(val) + 0.001); + try std.testing.expect(err < 0.05); +} + +test "C-ABI: gf16_add" { + const a = gf16_from_f32(1.5); + const b = gf16_from_f32(2.5); + const sum = gf16_add(a, b); + const result = gf16_to_f32(sum); + try std.testing.expectApproxEqAbs(@as(f32, 4.0), result, 0.05); +} + +test "C-ABI: gf16_mul" { + const a = gf16_from_f32(2.0); + const b = gf16_from_f32(3.0); + const prod = gf16_mul(a, b); + const result = gf16_to_f32(prod); + try std.testing.expectApproxEqAbs(@as(f32, 6.0), result, 0.05); +} + +test "C-ABI: gf16_neg and gf16_abs" { + const val = gf16_from_f32(-3.14); + const neg = gf16_neg(val); + const abs = gf16_abs(val); + try std.testing.expect(gf16_to_f32(neg) > 0); + try std.testing.expect(gf16_to_f32(abs) > 0); +} + +test "C-ABI: gf16_eq and gf16_lt" { + const a = gf16_from_f32(1.0); + const b = gf16_from_f32(1.0); + const c = gf16_from_f32(2.0); + try std.testing.expect(gf16_eq(a, b)); + try std.testing.expect(gf16_lt(a, c)); + try std.testing.expect(!gf16_lt(c, a)); +} + +test "C-ABI: gf16_is_nan and gf16_is_inf" { + const inf_val = gf16_from_f32(std.math.inf(f32)); + try std.testing.expect(gf16_is_inf(inf_val)); + try std.testing.expect(!gf16_is_nan(inf_val)); + + const zero = gf16_from_f32(0.0); + try std.testing.expect(gf16_is_zero(zero)); + + const nan_val = gf16_from_f32(std.math.nan(f32)); + try std.testing.expect(gf16_is_nan(nan_val)); + try std.testing.expect(!gf16_is_inf(nan_val)); +} + +test "C-ABI: gf16_phi_quantize" { + const original = 2.71828; + const quantized = gf16_phi_quantize(original); + const dequantized = gf16_phi_dequantize(quantized); + + const error_pct = @abs((dequantized - original) / original) * 100.0; + try std.testing.expect(error_pct < 10.0); +} + +test "C-ABI: gf16_fma" { + const a = gf16_from_f32(2.0); + const b = gf16_from_f32(3.0); + const c = gf16_from_f32(4.0); + const result = gf16_fma(a, b, c); + const val = gf16_to_f32(result); + try std.testing.expectApproxEqAbs(@as(f32, 10.0), val, 0.05); +} + +test "C-ABI: gf16_phi_fma" { + const a = gf16_phi_quantize(2.0); + const b = gf16_phi_quantize(3.0); + const c = gf16_phi_quantize(4.0); + const result = gf16_phi_fma(a, b, c); + const deq = gf16_phi_dequantize(result); + try std.testing.expectApproxEqAbs(@as(f32, 10.0), deq, 1.5); +} + +test "C-ABI: gf16_phi_fms" { + const a = gf16_phi_quantize(5.0); + const b = gf16_phi_quantize(3.0); + const c = gf16_phi_quantize(4.0); + const result = gf16_phi_fms(a, b, c); + const deq = gf16_phi_dequantize(result); + try std.testing.expectApproxEqAbs(@as(f32, 11.0), deq, 2.0); +} + +test "C-ABI: library version" { + const version = std.mem.span(goldenfloat_version()); + try std.testing.expectEqualStrings("1.1.0", version); +} + +test "C-ABI: goldenfloat_trinity returns 3.0" { + const trinity = goldenfloat_trinity(); + try std.testing.expectApproxEqAbs(@as(f64, 3.0), trinity, 1e-10); +} + +test "C-ABI: gft16_from_f32 and gft16_to_f32" { + const val: f32 = 3.14159; + const g = gft16_from_f32(val); + const back = gft16_to_f32(g); + try std.testing.expect(@abs(val - back) / (@abs(val) + 1e-9) < 0.005); + // raw is a 17-bit value carried in u32 + try std.testing.expect(g <= 0x1FFFF); +} + +test "C-ABI: gft16 arithmetic matches the codec" { + const a = gft16_from_f32(1.5); + const b = gft16_from_f32(2.5); + try std.testing.expectApproxEqAbs(@as(f32, 4.0), gft16_to_f32(gft16_add(a, b)), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 1.0), gft16_to_f32(gft16_sub(b, a)), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 3.75), gft16_to_f32(gft16_mul(a, b)), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 0.6), gft16_to_f32(gft16_div(a, b)), 0.02); +} + +test "C-ABI: gft16_neg / gft16_abs / gft16_is_finite" { + const x = gft16_from_f32(3.5); + try std.testing.expectApproxEqAbs(@as(f32, -3.5), gft16_to_f32(gft16_neg(x)), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 3.5), gft16_to_f32(gft16_abs(gft16_neg(x))), 0.02); + try std.testing.expectEqual(@as(u8, 1), gft16_is_finite(gft16_from_f32(1.0))); + try std.testing.expectEqual(@as(u8, 0), gft16_is_finite(gft16_from_f32(1e30))); // overflow -> Inf +} + +test "C-ABI: gft16 round-trips through the raw u32 (FFI stability)" { + const g = gft16_from_f32(-6.28); + try std.testing.expectEqual(gft16_to_f32(g), gft16_to_f32(gft16_from_f32(gft16_to_f32(g)))); +} + +test "C-ABI: gft4 / gft8 / gft32 from/to + carrier widths" { + // GF-T4 (u8, 1-bit mantissa -> coarse) + try std.testing.expect(gft4_from_f32(2.0) <= 0x3F); // 6-bit value + try std.testing.expectApproxEqAbs(@as(f32, 2.0), gft4_to_f32(gft4_from_f32(2.0)), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 4.0), gft4_to_f32(gft4_mul(gft4_from_f32(2.0), gft4_from_f32(2.0))), 0.5); + // GF-T8 (u16, 4-bit mantissa) + try std.testing.expect(gft8_from_f32(3.0) <= 0x3FF); // 10-bit value + try std.testing.expectApproxEqAbs(@as(f32, 3.0), gft8_to_f32(gft8_from_f32(3.0)), 0.1); + try std.testing.expectApproxEqAbs(@as(f32, 5.0), gft8_to_f32(gft8_add(gft8_from_f32(2.0), gft8_from_f32(3.0))), 0.2); + // GF-T32 (u64, 25-bit mantissa, huge range) + try std.testing.expect(gft32_from_f32(1.0) <= 0xFFFFFFFFF); // 36-bit value + try std.testing.expectApproxEqAbs(@as(f32, 3.14159), gft32_to_f32(gft32_from_f32(3.14159)), 1e-4); + try std.testing.expect(gft32_is_finite(gft32_from_f32(6.022e23)) == 1); // GF-T32 holds it + try std.testing.expect(gft32_to_f32(gft32_mul(gft32_from_f32(1e10), gft32_from_f32(1e10))) > 5e19); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig new file mode 100644 index 0000000..d52a013 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig @@ -0,0 +1,687 @@ +//! Format Conversion Utilities for Trinity Benchmarks +//! +//! GF16 bit layout (as specified in whitepaper, identical to DLFloat 6:9): +//! [S(1) E(6) M(9)] = [15:15][14:9][8:0] +//! +//! - Sign: bit 15 (0x8000) +//! - Exponent: bits 14-9 (0x7E00), bias = 31 +//! - Mantissa: bits 8-0 (0x01FF) +//! +//! Range: 2^-31 to 2^32 + +const std = @import("std"); + +// ═══════════════════════════════════════════════════════════════════ +// GF16 Constants +// ═══════════════════════════════════════════════════════════════════ + +pub const SignMask: u16 = 0b1_000000_000000000; // 0x8000 +pub const ExpMask: u16 = 0b0_111111_000000000; // 0x7E00 +pub const MantMask: u16 = 0b0_000000_111111111; // 0x01FF + +pub const ExpShift: u5 = 9; +pub const SignShift: u4 = 15; +pub const Bias: i32 = 31; + +pub const ExpMax: u16 = 0b111111; // 63 +pub const ExpMin: u16 = 0; + +// ═══════════════════════════════════════════════════════════════════ +// GF16 → f32 (decode) +// ═══════════════════════════════════════════════════════════════════ + +pub fn gf16ToF32(x: u16) f32 { + const s = @as(i32, (x >> SignShift) & 1); + const e = @as(i32, (x & ExpMask) >> ExpShift); + const m = @as(i32, x & MantMask); + + if (e == 0 and m == 0) { + // Signed zero + return if (s == 0) 0.0 else -0.0; + } else if (e == 0) { + // Denormals: treat as subnormal + const exp = 1 - Bias; + const frac = @as(f32, @floatFromInt(m)) / 512.0; // 2^9 + const val = std.math.exp2(@as(f32, @floatFromInt(exp))) * frac; + return if (s == 0) val else -val; + } else if (e == ExpMax) { + // Special values (Inf/NaN) + if (m == 0) { + return if (s == 0) std.math.inf(f32) else -std.math.inf(f32); + } else { + return std.math.nan(f32); + } + } else { + // Normal: value = (-1)^s * (1 + m/2^9) * 2^(e - Bias) + const exp = e - Bias; + const frac = 1.0 + @as(f32, @floatFromInt(m)) / 512.0; + const val = frac * std.math.exp2(@as(f32, @floatFromInt(exp))); + return if (s == 0) val else -val; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// f32 → GF16 (encode, round-to-nearest) +// ═══════════════════════════════════════════════════════════════════ + +pub fn f32ToGf16(a: f32) u16 { + // Handle signed zero explicitly + if (a == 0.0) { + return if (@as(u32, @bitCast(a)) & 0x80000000 != 0) 0x8000 else 0; + } + + const sign_bit: u16 = if (a < 0) 1 << SignShift else 0; + const abs = if (a < 0) -a else a; + + // Handle special cases + if (std.math.isPositiveInf(abs)) return sign_bit | ExpMask; + if (std.math.isNan(abs)) return sign_bit | ExpMask | 1; + + // Get exponent and mantissa via frexp: abs = m * 2^e, m in [0.5, 1) + // Zig 0.15: frexp returns struct { fract: f32, exp: i32 } + const frexp_result = std.math.frexp(abs); + var m = frexp_result.significand; + var exp_i = frexp_result.exponent; + + // Normalize: want 1.x * 2^(E - Bias), frexp gives m in [0.5, 1) + m *= 2.0; + exp_i -= 1; + + var e = exp_i + Bias; + if (e <= 0) { + // Underflow → zero + return sign_bit; + } else if (e >= ExpMax) { + // Overflow → INF + return sign_bit | ExpMask; + } + + // Mantissa: (m - 1.0) * 2^9, round to nearest + const mant_f = (m - 1.0) * 512.0; + var mant_i = @as(i32, @intFromFloat(std.math.round(mant_f))); + + // Handle mantissa overflow + if (mant_i == 512) { // 2^9 + mant_i = 0; + e += 1; + if (e >= ExpMax) { + return sign_bit | ExpMask; + } + } + + const e_bits: u16 = @as(u16, @intCast(e)) << ExpShift; + const m_bits: u16 = @as(u16, @intCast(mant_i)) & MantMask; + + return sign_bit | e_bits | m_bits; +} + +// ═══════════════════════════════════════════════════════════════════ +// Software fp16 encode/decode (IEEE 754 binary16) +fn f32ToFp16(a: f32) u16 { + if (std.math.isNan(a)) return 0x7E00; + const bits: u32 = @bitCast(a); + const sign: u16 = @intCast((bits >> 16) & 0x8000); + const abs_bits = bits & 0x7FFFFFFF; + + if (abs_bits == 0) return sign; + if (std.math.isInf(a)) return sign | 0x7C00; + + const f32_exp = @as(i32, @intCast((abs_bits >> 23) & 0xFF)) - 127; + const f32_mant = abs_bits & 0x7FFFFF; + + if (f32_exp > 15) return sign | 0x7C00; + + if (f32_exp >= -14) { + const fp16_mant = @as(u16, @intCast(f32_mant >> 13)); + const fp16_exp = @as(u16, @intCast(f32_exp + 15)) << 10; + return sign | fp16_exp | fp16_mant; + } + + const shift = @as(u5, @intCast(@as(i32, 13) - f32_exp - 14 + 1)); + if (shift >= 32) return sign; + const fp16_mant = @as(u16, @intCast(f32_mant >> shift)); + if (fp16_mant == 0) return sign; + return sign | fp16_mant; +} + +fn fp16ToF32(x: u16) f32 { + const sign: u32 = @as(u32, x & 0x8000) << 16; + const e = (x >> 10) & 0x1F; + const m = x & 0x03FF; + + if (e == 0) { + if (m == 0) return @bitCast(sign); + var mant = @as(u32, m) << 13; + var shifts: u32 = 0; + while ((mant & 0x00800000) == 0) : (shifts += 1) { + mant <<= 1; + } + const biased_exp: u32 = 113 - shifts; + const f32_bits = sign | (biased_exp << 23) | (mant & 0x7FFFFF); + return @bitCast(f32_bits); + } + if (e == 0x1F) { + if (m == 0) return @bitCast(sign | 0x7F800000); + return @bitCast(sign | 0x7FC00000); + } + + const f32_bits = sign | ((@as(u32, e) + 112) << 23) | (@as(u32, m) << 13); + return @bitCast(f32_bits); +} + +// Software bf16 encode/decode (Brain Float 16) — IEEE 754 canonical +fn f32ToBf16(a: f32) u16 { + if (std.math.isNan(a)) return 0x7FC0; + const bits: u32 = @bitCast(a); + const rounding: u32 = ((bits >> 16) & 1) + 0x7FFF; + return @intCast((bits +| rounding) >> 16); +} + +fn bf16ToF32(x: u16) f32 { + return @bitCast(@as(u32, x) << 16); +} + +// ═══════════════════════════════════════════════════════════════════ +// Ternary Format: {-1, 0, +1} Symmetric +// ═══════════════════════════════════════════════════════════════════ + +/// Symmetric quantization: w -> {-1, 0, +1} +/// Threshold: |w| > 0.5 -> +/-1, else -> 0 +pub fn f32ToTernary(x: f32) i8 { + if (x > 0.5) return 1; + if (x < -0.5) return -1; + return 0; +} + +pub fn ternaryToF32(t: i8) f32 { + return @as(f32, @floatFromInt(t)); +} + +// ═══════════════════════════════════════════════════════════════════ +// Format Enum and Conversion Interface +// ═══════════════════════════════════════════════════════════════════ + +pub const Format = enum { + fp32, + fp16, + bf16, + gf16, + ternary, +}; + +pub fn formatBytes(fmt: Format) usize { + return switch (fmt) { + .fp32 => 4, + .fp16 => 2, + .bf16 => 2, + .gf16 => 2, + .ternary => 1, + }; +} + +/// Quantize single f32 value to target format (returns f32 for convenience) +pub fn quantizeValue(x: f32, fmt: Format) f32 { + return switch (fmt) { + .fp32 => x, + .fp16 => fp16ToF32(f32ToFp16(x)), + .bf16 => bf16ToF32(f32ToBf16(x)), + .gf16 => gf16ToF32(f32ToGf16(x)), + .ternary => ternaryToF32(f32ToTernary(x)), + }; +} + +// ═════════════════════════════════════════════════════════════════════ +// CNN Operations (2D Convolution + Max Pooling) +// ═════════════════════════════════════════════════════════════════════════════ + +/// 2D convolution: output[y,x,c] = sum over kernel weights +/// +/// Parameters: +/// - input: flattened input [H_in * W_in * C_in] (channel-major layout) +/// - weights: filter weights [C_out * C_in * K_h * K_w] +/// - bias: per-channel bias [C_out] +/// - output: pre-allocated output buffer [H_out * W_out * C_out] +/// - config: layer dimensions and kernel parameters +/// +/// Supports valid padding (padding = kernel_size / 2) +pub fn conv2d( + input: []const f32, + weights: []const f32, + bias: []const f32, + output: []f32, + config: struct { + in_channels: u32, + out_channels: u32, + in_height: u32, + in_width: u32, + kernel_size: u32, + stride: u32, + padding: u32, + }, +) void { + const k = config.kernel_size; + const p = config.padding; + const s = config.stride; + + // Output dimensions with valid padding + const out_h = (config.in_height + 2 * p - k) / s + 1; + const out_w = (config.in_width + 2 * p - k) / s + 1; + + const in_area = config.in_height * config.in_width; + + // For each output channel + for (0..config.out_channels) |oc| { + const bias_val = bias[oc]; + const out_offset = oc * out_h * out_w; + + // For each output position + for (0..out_h) |oy| { + for (0..out_w) |ox| { + var sum: f32 = bias_val; + + // For each input channel + for (0..config.in_channels) |ic| { + // For each kernel position + for (0..k) |ky| { + for (0..k) |kx| { + // Input position + const in_y = oy * s + ky - p; + const in_x = ox * s + kx - p; + + if (in_y >= 0 and in_y < config.in_height and + in_x >= 0 and in_x < config.in_width) + { + const in_idx = ic * in_area + in_y * config.in_width + in_x; + sum += input[in_idx] * weights[oc * config.in_channels * k * k + ic * k * k + ky * k + kx]; + } + } + } + } + + output[out_offset + oy * out_w + ox] = sum; + } + } + } +} + +/// 2D max pooling: output[y,x,c] = max over kernel window +/// +/// Parameters: +/// - input: [H_in * W_in * C_in] +/// - output: pre-allocated output buffer [H_out * W_out * C_in] +/// - config: input dimensions and pooling parameters +pub fn maxPool2d( + input: []const f32, + output: []f32, + config: struct { + height: u32, + width: u32, + channels: u32, + kernel_size: u32, + stride: u32, + }, +) void { + const k = config.kernel_size; + const s = config.stride; + + const out_h = config.height / s; + const out_w = config.width / s; + const in_area = config.height * config.width; + + // For each channel + for (0..config.channels) |c| { + const out_offset = c * out_h * out_w; + + // For each output position + for (0..out_h) |oy| { + for (0..out_w) |ox| { + // Find max in kernel window + var max_val: f32 = -std.math.inf(f32); + for (0..k) |ky| { + const in_y = oy * s + ky; + if (in_y < config.height) { + for (0..k) |kx| { + const in_x = ox * s + kx; + if (in_x < config.width) { + const in_idx = c * in_area + in_y * config.width + in_x; + max_val = @max(max_val, input[in_idx]); + } + } + } + } + + output[out_offset + oy * out_w + ox] = max_val; + } + } + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Trained MLP Weights Loader +// ═══════════════════════════════════════════════════════════════════ + +/// Trained MLP weights loaded from binary file +pub const MlpWeights = struct { + input_dim: u32, + hidden_dim: u32, + output_dim: u32, + + W1: []f32, // hidden_dim * input_dim, row-major + b1: []f32, // hidden_dim + W2: []f32, // output_dim * hidden_dim, row-major + b2: []f32, // output_dim + + allocator: std.mem.Allocator, + + /// Free all allocated arrays + pub fn deinit(self: *const MlpWeights) void { + self.allocator.free(self.W1); + self.allocator.free(self.b1); + self.allocator.free(self.W2); + self.allocator.free(self.b2); + } +}; + +/// Error set for weight loading +pub const LoadWeightsError = error{ + BadMagic, + UnsupportedVersion, + DimensionMismatch, + InvalidFileSize, +}; + +/// Load trained MLP weights from binary file +/// +/// File format (little-endian): +/// - Header (20 bytes): +/// - u32 magic = 0x4D4E4953 ("MNIS") +/// - u32 version = 1 +/// - u32 input_dim +/// - u32 hidden_dim +/// - u32 output_dim +/// - Data (all f32, little-endian): +/// - W1: hidden_dim * input_dim values (row-major) +/// - b1: hidden_dim values +/// - W2: output_dim * hidden_dim values (row-major) +/// - b2: output_dim values +pub fn loadMlpWeights( + allocator: std.mem.Allocator, + path: []const u8, +) !MlpWeights { + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + const file_size = try file.getEndPos(); + if (file_size < 20) return error.InvalidFileSize; + + // Read header (20 bytes) + var header: [20]u8 = undefined; + _ = try file.readAll(&header); + const magic = std.mem.readInt(u32, header[0..4], .little); + if (magic != 0x4D4E4953) return LoadWeightsError.BadMagic; + + const version = std.mem.readInt(u32, header[4..8], .little); + if (version != 1) return LoadWeightsError.UnsupportedVersion; + + const input_dim = std.mem.readInt(u32, header[8..12], .little); + const hidden_dim = std.mem.readInt(u32, header[12..16], .little); + const output_dim = std.mem.readInt(u32, header[16..20], .little); + + // Calculate sizes + const w1_len = @as(usize, hidden_dim) * @as(usize, input_dim); + const b1_len = @as(usize, hidden_dim); + const w2_len = @as(usize, output_dim) * @as(usize, hidden_dim); + const b2_len = @as(usize, output_dim); + + // Verify file size matches expected + const expected_size = 20 + (w1_len + b1_len + w2_len + b2_len) * 4; + if (file_size != expected_size) return error.InvalidFileSize; + + // Allocate arrays + const W1 = try allocator.alloc(f32, w1_len); + errdefer allocator.free(W1); + const b1 = try allocator.alloc(f32, b1_len); + errdefer allocator.free(b1); + const W2 = try allocator.alloc(f32, w2_len); + errdefer allocator.free(W2); + const b2 = try allocator.alloc(f32, b2_len); + errdefer allocator.free(b2); + + // Read tensor data directly into arrays + var data_offset: usize = 20; + { + const w1_bytes = std.mem.sliceAsBytes(W1); + const n = try file.read(w1_bytes); + if (n != w1_len * 4) return error.InvalidFileSize; + data_offset += n; + } + { + const b1_bytes = std.mem.sliceAsBytes(b1); + const n = try file.read(b1_bytes); + if (n != b1_len * 4) return error.InvalidFileSize; + data_offset += n; + } + { + const w2_bytes = std.mem.sliceAsBytes(W2); + const n = try file.read(w2_bytes); + if (n != w2_len * 4) return error.InvalidFileSize; + data_offset += n; + } + { + const b2_bytes = std.mem.sliceAsBytes(b2); + _ = try file.readAll(b2_bytes); + } + + return MlpWeights{ + .input_dim = input_dim, + .hidden_dim = hidden_dim, + .output_dim = output_dim, + .W1 = W1, + .b1 = b1, + .W2 = W2, + .b2 = b2, + .allocator = allocator, + }; +} + +// ═══════════════════════════════════════════════════════════════════ +// Tests +// ═══════════════════════════════════════════════════════════════════ + +test "GF16: zero" { + try std.testing.expectEqual(@as(u16, 0), f32ToGf16(0.0)); + try std.testing.expectEqual(@as(u16, 0x8000), f32ToGf16(-0.0)); +} + +test "GF16: roundtrip zero" { + try std.testing.expectEqual(@as(f32, 0.0), gf16ToF32(f32ToGf16(0.0))); +} + +test "GF16: infinity" { + try std.testing.expectEqual(@as(u16, 0x7E00), f32ToGf16(std.math.inf(f32))); + try std.testing.expectEqual(@as(u16, 0xFE00), f32ToGf16(-std.math.inf(f32))); +} + +test "GF16: roundtrip small values" { + const values = [_]f32{ 1.0, -1.0, 0.5, -0.5, 2.0, -2.0, 0.1, -0.1, 1.5, -1.5 }; + for (values) |v| { + const gf16 = f32ToGf16(v); + const recovered = gf16ToF32(gf16); + // Allow some error due to quantization + const err = @abs(recovered - v); + try std.testing.expect(err < 0.01); + } +} + +test "GF16: bit masks correct" { + try std.testing.expectEqual(@as(u16, 0x8000), SignMask); + try std.testing.expectEqual(@as(u16, 0x7E00), ExpMask); + try std.testing.expectEqual(@as(u16, 0x01FF), MantMask); +} + +test "GF16: encode preserves sign" { + try std.testing.expect(f32ToGf16(1.0) & 0x8000 == 0); + try std.testing.expect(f32ToGf16(-1.0) & 0x8000 != 0); +} + +test "Ternary: quantization" { + try std.testing.expectEqual(@as(i8, 1), f32ToTernary(1.0)); + try std.testing.expectEqual(@as(i8, -1), f32ToTernary(-1.0)); + try std.testing.expectEqual(@as(i8, 0), f32ToTernary(0.3)); + try std.testing.expectEqual(@as(i8, 0), f32ToTernary(-0.3)); + try std.testing.expectEqual(@as(i8, 1), f32ToTernary(0.6)); +} + +test "formatBytes" { + try std.testing.expectEqual(@as(usize, 4), formatBytes(.fp32)); + try std.testing.expectEqual(@as(usize, 2), formatBytes(.gf16)); + try std.testing.expectEqual(@as(usize, 1), formatBytes(.ternary)); +} + +test "BF16: roundtrip 1.0" { + const bf16 = f32ToBf16(1.0); + try std.testing.expectEqual(@as(u16, 0x3F80), bf16); + const back = bf16ToF32(bf16); + try std.testing.expectEqual(@as(f32, 1.0), back); +} + +test "BF16: roundtrip 100.0" { + const bf16 = f32ToBf16(100.0); + const back = bf16ToF32(bf16); + const err = @abs(back - 100.0); + try std.testing.expect(err < 1.0); +} + +test "BF16: roundtrip 1e10" { + const bf16 = f32ToBf16(1e10); + const back = bf16ToF32(bf16); + const err = @abs(back - 1e10) / 1e10; + try std.testing.expect(err < 0.01); +} + +test "BF16: roundtrip small values" { + const values = [_]f32{ 0.5, -0.5, 2.0, -2.0, 3.14, -3.14, 1e-10, -1e-10 }; + for (values) |v| { + const bf16 = f32ToBf16(v); + const back = bf16ToF32(bf16); + const err = if (@abs(v) > 0.001) @abs(back - v) / @abs(v) else @abs(back - v); + try std.testing.expect(err < 0.01); + } +} + +test "FP16: roundtrip basic values" { + const values = [_]f32{ 1.0, -1.0, 0.5, -0.5, 2.0, -2.0, 0.1, 0.25, 1.5 }; + for (values) |v| { + const fp16 = f32ToFp16(v); + const recovered = fp16ToF32(fp16); + const err = @abs(recovered - v) / @max(@abs(v), 1e-30); + try std.testing.expect(err < 0.005); + } +} + +test "FP16: special values" { + try std.testing.expectEqual(@as(u16, 0x0000), f32ToFp16(0.0)); + try std.testing.expectEqual(@as(u16, 0x8000), f32ToFp16(-0.0)); + try std.testing.expectEqual(@as(u16, 0x7C00), f32ToFp16(std.math.inf(f32))); + try std.testing.expectEqual(@as(u16, 0xFC00), f32ToFp16(-std.math.inf(f32))); + const nan_enc = f32ToFp16(std.math.nan(f32)); + try std.testing.expect(std.math.isNan(fp16ToF32(nan_enc))); +} + +test "FP16: large values (full IEEE exponent)" { + const fp16 = f32ToFp16(100.0); + const back = fp16ToF32(fp16); + try std.testing.expect(@abs(back - 100.0) < 1.0); + + const fp16_big = f32ToFp16(65000.0); + const back_big = fp16ToF32(fp16_big); + try std.testing.expect(back_big > 60000.0); + try std.testing.expect(back_big < 65536.0); +} + +test "FP16: overflow to infinity" { + const fp16 = f32ToFp16(1e10); + try std.testing.expectEqual(@as(u16, 0x7C00), fp16); + try std.testing.expect(std.math.isInf(fp16ToF32(fp16))); +} + +test "FP16: roundtrip 1.0 exact" { + const fp16 = f32ToFp16(1.0); + try std.testing.expectEqual(@as(u16, 0x3C00), fp16); + try std.testing.expectEqual(@as(f32, 1.0), fp16ToF32(fp16)); +} + +test "FP16: denormal roundtrip" { + const small = fp16ToF32(@as(u16, 0x0001)); + try std.testing.expect(small > 0.0); + try std.testing.expect(small < 0.001); +} + +test "BF16: special values" { + try std.testing.expectEqual(@as(u16, 0x3F80), f32ToBf16(1.0)); + try std.testing.expect(bf16ToF32(f32ToBf16(std.math.inf(f32))) > 1e30); + try std.testing.expect(std.math.isNan(bf16ToF32(f32ToBf16(std.math.nan(f32))))); + try std.testing.expectEqual(@as(u16, 0), f32ToBf16(0.0)); + try std.testing.expectEqual(@as(u16, 0x8000), f32ToBf16(-0.0)); + try std.testing.expectEqual(@as(u16, 0x7F80), f32ToBf16(std.math.inf(f32))); + try std.testing.expectEqual(@as(u16, 0xFF80), f32ToBf16(-std.math.inf(f32))); +} + +test "BF16: large values do not flush" { + const bf16_1e10 = f32ToBf16(1e10); + const back_1e10 = bf16ToF32(bf16_1e10); + try std.testing.expect(back_1e10 > 5e9); + try std.testing.expect(back_1e10 < 2e10); + + const bf16_1e_10 = f32ToBf16(1e-10); + const back_1e_10 = bf16ToF32(bf16_1e_10); + try std.testing.expect(back_1e_10 > 5e-11); + try std.testing.expect(back_1e_10 < 2e-9); +} + +test "BF16: quantizeValue roundtrip all formats" { + const test_val: f32 = 42.0; + const gf16_round = quantizeValue(test_val, .gf16); + const bf16_round = quantizeValue(test_val, .bf16); + const fp16_round = quantizeValue(test_val, .fp16); + try std.testing.expect(@abs(gf16_round - test_val) / test_val < 0.05); + try std.testing.expect(@abs(bf16_round - test_val) / test_val < 0.05); + try std.testing.expect(@abs(fp16_round - test_val) / test_val < 0.05); +} + +test "FP16: subnormal decode mantissa=1" { + const bits: u16 = 0x0001; + const val = fp16ToF32(bits); + const expected: f32 = 5.960464e-8; + try std.testing.expectApproxEqAbs(expected, val, 1e-14); +} + +test "FP16: subnormal decode mantissa=2" { + const bits: u16 = 0x0002; + const val = fp16ToF32(bits); + const expected: f32 = 1.192093e-7; + try std.testing.expectApproxEqAbs(expected, val, 1e-14); +} + +test "FP16: subnormal decode mantissa=1023 (max)" { + const bits: u16 = 0x03FF; + const val = fp16ToF32(bits); + try std.testing.expect(val > 0.0); + try std.testing.expect(val < 6.1e-5); +} + +test "FP16: quantizeValue small values preserve sign" { + const pos = quantizeValue(0.003, .fp16); + const neg = quantizeValue(-0.003, .fp16); + try std.testing.expect(pos > 0.0); + try std.testing.expect(neg < 0.0); +} + +test "FP16: subnormal roundtrip accuracy" { + const vals = [_]f32{ 1e-5, 5e-5, 1e-4, 5e-4 }; + for (vals) |v| { + const q = quantizeValue(v, .fp16); + const rel_err = @abs(q - v) / v; + try std.testing.expect(rel_err < 0.1); + } +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig new file mode 100644 index 0000000..0f12990 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig @@ -0,0 +1,286 @@ +//! GoldenFloat8 — φ-Optimized 8-bit Floating-Point Format +//! +//! Bit Layout: [sign:1][exp:3][mant:4] = 8 bits +//! Exponent bias: 7 +//! φ-optimal distribution: exp/mant ≈ 0.5714 (distance: 0.047) +//! +//! 8-bit = 2^3, so 3-bit exponent (values 0-7) is correct! + +const std = @import("std"); + +// ══════════════════════════════════════════════════════════════════════════════════════════ +// GF8 CONSTANTS +// ════════════════════════════════════════════════════════════════════════════════════════════ + +pub const SignMask: u8 = 0x80; +pub const ExpMask: u8 = 0x70; +pub const MantMask: u8 = 0x0F; + +pub const ExpShift: u3 = 4; +pub const SignShift: u3 = 7; +pub const Bias: i8 = 7; +pub const ExpBits: u8 = 3; +pub const MantBits: u8 = 4; + +// ════════════════════════════════════════════════════════════════════════════════════════════════════════ +// GF8 TYPE DEFINITION +// ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +pub const GF8 = packed struct(u8) { + /// Mantissa (4 bits) + mant: u4, + + /// Exponent (3 bits, bias 7) - values 0-7 (stored in 3 bits) + exp: u3, + + /// Sign bit + sign: u1, +}; + +// ════════════════════════════════════════════════════════════════════════════════════════════════════════ +// GF8 ZERO CONSTANT +// ═══════════════════════════════════════════════════════════════════════════════════════════════════════ + +pub const GF8_ZERO: GF8 = .{ + .mant = 0, + .exp = 0, + .sign = 0, +}; + +pub const GF8_NEG_ZERO: GF8 = .{ + .mant = 0, + .exp = 0, + .sign = 1, +}; + +// ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ +// GF8 CONSTRUCTION +// ═══════════════════════════════════════════════════════════════════════════════════════════════════════ + +pub inline fn fromF32(x: f32) GF8 { + if (x == 0.0) return GF8_ZERO; + if (x < 0.0) return encodeNegative(x); + return encodePositive(x); +} + +pub inline fn toF32(g: GF8) f32 { + if (g == GF8_ZERO or g == GF8_NEG_ZERO) { + return 0.0; + } + + // Exponent bias: 7, range [0, 7] (unbiased: [-7, 0]) + const exp_biased: i32 = @as(i32, g.exp); + + // Denormals: exp = 0 (biased), mant != 0 + if (exp_biased == 0 and g.mant != 0) { + // value = mant/16 * 2^(1 - bias) = mant/16 * 2^(-6) + const denorm = @as(f32, @floatFromInt(g.mant)) / 16.0; + const value = denorm * @as(f32, std.math.pow(f32, 2.0, 1 - Bias)); + return if (g.sign == 0) value else -value; + } + + // Normal: value = (1 + mant/16) * 2^(exp_biased - bias) + const exp_unbiased = exp_biased - Bias; + const mant_scaled: f32 = 1.0 + @as(f32, @floatFromInt(g.mant)) / 16.0; + const value = mant_scaled * std.math.pow(f32, 2.0, @floatFromInt(exp_unbiased)); + return if (g.sign == 0) value else -value; +} + +pub inline fn add(a: GF8, b: GF8) GF8 { + return fromF32(toF32(a) + toF32(b)); +} + +pub inline fn sub(a: GF8, b: GF8) GF8 { + return fromF32(toF32(a) - toF32(b)); +} + +pub inline fn mul(a: GF8, b: GF8) GF8 { + return fromF32(toF32(a) * toF32(b)); +} + +pub inline fn div(a: GF8, b: GF8) GF8 { + return fromF32(toF32(a) / toF32(b)); +} + +pub inline fn fma(a: GF8, b: GF8, c: GF8) GF8 { + // FMA with f32 intermediate, then quantize back to GF8 + const ab = toF32(a) * toF32(b); + const result = ab + toF32(c); + return fromF32(result); +} + +pub inline fn sqrt(a: GF8) GF8 { + if (a.sign == 1) { + return GF8{ .mant = 0, .exp = 7, .sign = 1 }; + } + const abs_v = toF32(a); + if (abs_v <= 0.0) { + return GF8{ .mant = 0, .exp = 0, .sign = 0 }; + } + return fromF32(std.math.sqrt(abs_v)); +} + +pub inline fn abs(a: GF8) GF8 { + return .{ + .mant = a.mant, + .exp = a.exp, + .sign = 0, + }; +} + +pub inline fn neg(a: GF8) GF8 { + return .{ + .mant = a.mant, + .exp = a.exp, + .sign = 1 - a.sign, + }; +} + +pub inline fn eq(a: GF8, b: GF8) bool { + return a.mant == b.mant and a.exp == b.exp and a.sign == b.sign; +} + +pub inline fn ne(a: GF8, b: GF8) bool { + return !eq(a, b); +} + +pub inline fn lt(a: GF8, b: GF8) bool { + if (a.sign != b.sign) return (a.sign < b.sign); + if (a.exp != b.exp) return (a.exp < b.exp); + return a.mant < b.mant; +} + +pub inline fn le(a: GF8, b: GF8) bool { + if (a.sign != b.sign) return (a.sign < b.sign); + if (a.exp != b.exp) return (a.exp < b.exp); + return a.mant <= b.mant; +} + +pub inline fn gt(a: GF8, b: GF8) bool { + return !le(a, b); +} + +pub inline fn ge(a: GF8, b: GF8) bool { + return !lt(a, b); +} + +// ════════════════════════════════════════════════════════════════════════════════════════════════════════════ +// HELPER FUNCTIONS (Internal) +// ═══════════════════════════════════════════════════════════════════════════════════════════════════ + +fn encodePositive(x: f32) GF8 { + if (x == 0.0) return GF8_ZERO; + if (!std.math.isFinite(x)) { + return GF8{ .mant = 0, .exp = 7, .sign = 0 }; + } + + // GF8 max value: (1 + 15/16) * 2^0 = 1.9375 + // Clamp input to valid range + var x_clamped = x; + if (x > 1.9375) x_clamped = 1.9375; + + const frexp = std.math.frexp(x_clamped); + const m = frexp.significand * 2.0; + var e = frexp.exponent - 1; + + // Clamp exponent to valid range for GF8 + // exp_biased = e + Bias, and exp_biased must be in [1, 7] for normals + // So e must be in [-6, 0] + if (e < -6) { + e = -6; // Subnormal range + } else if (e > 0) { + e = 0; // Max normal (exp_biased = 7) + } + + // Round mantissa to 4 bits + const mant_f = (m - 1.0) * 16.0; + var mant_i: u4 = @intFromFloat(std.math.round(mant_f)); + if (mant_i == 16) { + mant_i = 15; // Clamp mantissa to max + } + + // Clamp final exp_biased to [0, 7] + var exp_biased = e + Bias; + if (exp_biased > 7) exp_biased = 7; + if (exp_biased < 0) exp_biased = 0; + + return GF8{ + .mant = mant_i, + .exp = @intCast(exp_biased), + .sign = 0, + }; +} + +fn encodeNegative(x: f32) GF8 { + const abs_x = -x; + const gf8_abs = encodePositive(abs_x); + return GF8{ + .mant = gf8_abs.mant, + .exp = gf8_abs.exp, + .sign = 1, + }; +} + +test "GF8: zero" { + try std.testing.expectEqual(@as(u8, @bitCast(GF8_ZERO)), 0); + try std.testing.expectEqual(toF32(GF8_ZERO), 0.0); +} + +test "GF8: one" { + const one = fromF32(1.0); + // 1.0: sign=0, exp=7 (biased), mant=0 → 0 111 0000 = 0x70 + try std.testing.expectEqual(@as(u8, @bitCast(one)), 0x70); + try std.testing.expectApproxEqRel(toF32(one), 1.0, 0.05); +} + +test "GF8: roundtrip positive" { + // Test values within GF8 representable range: [~0.0078, 1.9375] + const values = [_]f32{ 0.0, 0.01, 0.1, 0.5, 0.75, 1.0, 1.5, 1.9375 }; + for (values) |v| { + const gf8 = fromF32(v); + const back = toF32(gf8); + const err = @abs(back - v) / @max(@abs(v), 1.0); + try std.testing.expect(err < 0.1); // 10% error tolerance for values in range + } +} + +test "GF8: roundtrip negative" { + // Test values within GF8 representable range + const values = [_]f32{ -0.01, -0.1, -0.5, -0.75, -1.0, -1.5, -1.9375 }; + for (values) |v| { + const gf8 = fromF32(v); + const back = toF32(gf8); + const err = @abs(back - v) / @max(@abs(v), 1.0); + try std.testing.expect(err < 0.1); // 10% error tolerance for values in range + } +} + +test "GF8: clamping out of range" { + // Test that values > 1.9375 are clamped + const big = fromF32(10.0); + const back = toF32(big); + // Should be clamped to max value ~1.9375 + try std.testing.expect(back <= 2.0); +} + +test "GF8: sign bit" { + const pos = fromF32(1.0); + const neg_val = fromF32(-1.0); + try std.testing.expect(pos.sign == 0); + try std.testing.expect(neg_val.sign == 1); +} + +test "GF8: mantissa precision" { + // Test that 4-bit mantissa gives ~6% precision + const gf8 = fromF32(1.0 + 1.0/16.0); // 1.0625 + const back = toF32(gf8); + const relative_err = @abs(back - 1.0625) / 1.0625; + try std.testing.expect(relative_err < 0.2); // Allow ~20% error for 4-bit mantissa +} + +test "GF8: exponent range" { + const min_val = toF32(GF8{ .mant = 1, .exp = 0, .sign = 0 }); + const max_val = toF32(GF8{ .mant = MantMask, .exp = 7, .sign = 0 }); + try std.testing.expect(min_val > 0.0); // Smallest normal > 0 + try std.testing.expect(max_val < 15.0); // Max normal with max mantissa +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig new file mode 100644 index 0000000..4a80b4b --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig @@ -0,0 +1,299 @@ +//! GF binary-exponent ladder — the whole φ-sized rung family from one rule. +//! +//! Every binary GoldenFloat rung is sized by ONE normative rule (FORMAT-SPEC-001 v1.2): +//! +//! e = round((N - 1) / φ²), m = N - 1 - e, bias = 2^(e-1) - 1, exp_max = 2^e - 1 +//! +//! so the exp:mantissa split tracks 1/φ ≈ 0.618 at every width. This factory derives +//! the rungs the README documents: GF4 / GF8 / GF12 / GF16 / GF20 / GF24 / GF32. +//! (GF8 and GF16 also have dedicated, φ-FMA-rich implementations in +//! golden_float16.zig / formats_root.zig; those stay the production entry points — +//! this module completes the *ladder* in code and is the reference for the other rungs.) +//! +//! Value (normal): (-1)^sign * (1 + M / 2^m) * 2^(e - bias) +//! Specials: e = 0 -> zero (mantissa 0) / flush subnormals to 0 +//! e = exp_max -> Inf (mantissa 0) / NaN (mantissa != 0) +//! +//! **Usage:** +//! ```zig +//! const gfb = @import("gf_binary.zig"); +//! const x = gfb.GF12.fromF32(3.14159); +//! std.debug.print("{d}\n", .{x.toF32()}); +//! const Custom = gfb.GF(48); // any width the rule accepts +//! ``` +//! +//! phi^2 + 1/phi^2 = 3 | TRINITY + +const std = @import("std"); + +const PHI: f64 = 1.6180339887498948482; +const PHI_SQ: f64 = PHI * PHI; // ≈ 2.618033988749895 + +/// Build a binary GF rung of `bits` total width using the φ² sizing rule. +pub fn GF(comptime bits: comptime_int) type { + comptime { + if (bits < 4) @compileError("GF requires at least 4 bits (1 sign + >=1 exp + >=1 mantissa)"); + } + const e_bits: comptime_int = @intFromFloat(@round(@as(f64, bits - 1) / PHI_SQ)); + const m_bits: comptime_int = bits - 1 - e_bits; + comptime { + if (e_bits < 1 or m_bits < 1) @compileError("degenerate rung: exp or mantissa < 1 bit"); + } + const bias_c: comptime_int = (1 << (e_bits - 1)) - 1; + const exp_max_c: comptime_int = (1 << e_bits) - 1; + + const Mant = std.meta.Int(.unsigned, m_bits); + const Exp = std.meta.Int(.unsigned, e_bits); + const ReprInt = std.meta.Int(.unsigned, bits); + + return packed struct(ReprInt) { + mant: Mant, + exp: Exp, + sign: u1, + + const Self = @This(); + + pub const BITS: u32 = bits; + pub const EXP_BITS: u32 = e_bits; + pub const MANT_BITS: u32 = m_bits; + pub const BIAS: u32 = bias_c; + pub const EXP_MAX: u32 = exp_max_c; // reserved Inf/NaN exponent + pub const Repr = ReprInt; + + const MANT_SCALE: f32 = @floatFromInt(@as(u64, 1) << m_bits); + const MANT_LIMIT: i64 = @as(i64, 1) << m_bits; + // largest finite unbiased exponent is (exp_max-1) - bias. + const MAX_E: i32 = @as(i32, exp_max_c - 1) - @as(i32, bias_c); + // smallest normal unbiased exponent is 1 - bias (exp field 1). + const MIN_E: i32 = 1 - @as(i32, bias_c); + + /// Encode f32 (round-to-nearest, saturate to Inf, flush subnormals to zero). + pub fn fromF32(v: f32) Self { + if (v == 0.0) return .{ .mant = 0, .exp = 0, .sign = @intFromBool(std.math.signbit(v)) }; + if (std.math.isNan(v)) return .{ .mant = 1, .exp = @intCast(exp_max_c), .sign = 0 }; + if (std.math.isInf(v)) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = @intFromBool(v < 0) }; + + const sign: u1 = @intFromBool(v < 0); + var f: f32 = @abs(v); + var e: i32 = 0; + while (f >= 2.0) : (e += 1) f /= 2.0; + while (f < 1.0) : (e -= 1) f *= 2.0; + + if (e > MAX_E) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = sign }; // -> Inf + if (e < MIN_E) return .{ .mant = 0, .exp = 0, .sign = sign }; // flush to zero (no subnormals) + + var mant: i64 = @intFromFloat(std.math.round((f - 1.0) * MANT_SCALE)); + var exp_field: i32 = e + @as(i32, bias_c); + if (mant >= MANT_LIMIT) { // significand rounded to 2.0 -> carry + mant = 0; + exp_field += 1; + if (exp_field >= @as(i32, exp_max_c)) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = sign }; + } + return .{ .mant = @intCast(mant), .exp = @intCast(exp_field), .sign = sign }; + } + + /// Decode back to f32. + pub fn toF32(self: Self) f32 { + if (self.exp == exp_max_c) { + if (self.mant == 0) return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); + return std.math.nan(f32); + } + if (self.exp == 0) return if (self.sign == 1) -0.0 else 0.0; // zero (subnormals flushed) + const e: i32 = @as(i32, self.exp) - @as(i32, bias_c); + const f: f32 = 1.0 + @as(f32, @floatFromInt(self.mant)) / MANT_SCALE; + const val = f * std.math.exp2(@as(f32, @floatFromInt(e))); + return if (self.sign == 1) -val else val; + } + + pub fn isFinite(self: Self) bool { + return self.exp != exp_max_c; + } + pub fn bits_(self: Self) ReprInt { + return @bitCast(self); + } + pub fn fromBits(b: ReprInt) Self { + return @bitCast(b); + } + pub fn zero() Self { + return .{ .mant = 0, .exp = 0, .sign = 0 }; + } + pub fn one() Self { + return fromF32(1.0); + } + pub fn neg(self: Self) Self { + return .{ .mant = self.mant, .exp = self.exp, .sign = self.sign ^ 1 }; + } + pub fn abs(self: Self) Self { + return .{ .mant = self.mant, .exp = self.exp, .sign = 0 }; + } + pub fn add(a: Self, b: Self) Self { + return fromF32(a.toF32() + b.toF32()); + } + pub fn sub(a: Self, b: Self) Self { + return fromF32(a.toF32() - b.toF32()); + } + pub fn mul(a: Self, b: Self) Self { + return fromF32(a.toF32() * b.toF32()); + } + pub fn div(a: Self, b: Self) Self { + return fromF32(a.toF32() / b.toF32()); + } + }; +} + +/// The φ-sized binary rungs the README documents. +pub const GF4 = GF(4); +pub const GF8 = GF(8); +pub const GF12 = GF(12); +pub const GF16 = GF(16); +pub const GF20 = GF(20); +pub const GF24 = GF(24); +pub const GF32 = GF(32); + +// ═══════════════════════════════════════════════════════════════════ +// Tests +// ═══════════════════════════════════════════════════════════════════ + +test "GF ladder rule matches the SSOT catalog (e / m / bias)" { + try std.testing.expectEqual(@as(u32, 1), GF4.EXP_BITS); + try std.testing.expectEqual(@as(u32, 2), GF4.MANT_BITS); + try std.testing.expectEqual(@as(u32, 0), GF4.BIAS); + try std.testing.expectEqual(@as(u32, 3), GF8.EXP_BITS); + try std.testing.expectEqual(@as(u32, 4), GF8.MANT_BITS); + try std.testing.expectEqual(@as(u32, 3), GF8.BIAS); + try std.testing.expectEqual(@as(u32, 4), GF12.EXP_BITS); + try std.testing.expectEqual(@as(u32, 7), GF12.MANT_BITS); + try std.testing.expectEqual(@as(u32, 7), GF12.BIAS); + try std.testing.expectEqual(@as(u32, 6), GF16.EXP_BITS); + try std.testing.expectEqual(@as(u32, 9), GF16.MANT_BITS); + try std.testing.expectEqual(@as(u32, 31), GF16.BIAS); + try std.testing.expectEqual(@as(u32, 7), GF20.EXP_BITS); + try std.testing.expectEqual(@as(u32, 12), GF20.MANT_BITS); + try std.testing.expectEqual(@as(u32, 63), GF20.BIAS); + try std.testing.expectEqual(@as(u32, 9), GF24.EXP_BITS); + try std.testing.expectEqual(@as(u32, 14), GF24.MANT_BITS); + try std.testing.expectEqual(@as(u32, 255), GF24.BIAS); + try std.testing.expectEqual(@as(u32, 12), GF32.EXP_BITS); + try std.testing.expectEqual(@as(u32, 19), GF32.MANT_BITS); + try std.testing.expectEqual(@as(u32, 2047), GF32.BIAS); +} + +test "GF ladder packed struct widths" { + inline for (.{ GF4, GF8, GF12, GF16, GF20, GF24, GF32 }, .{ 4, 8, 12, 16, 20, 24, 32 }) |T, n| { + try std.testing.expectEqual(@as(u32, n), T.BITS); + } +} + +fn checkRoundtrip(comptime T: type, values: []const f32, tol: f32) !void { + for (values) |v| { + const q = T.fromF32(v).toF32(); + const err = @abs(q - v) / (@abs(v) + 1e-9); + try std.testing.expect(err <= tol); + } +} + +test "GF16 roundtrip (9-bit mantissa)" { + const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.14159, -3.14159, 100.0, 0.001, 12345.0 }; + try checkRoundtrip(GF16, &vals, 0.005); +} + +test "GF32 roundtrip (19-bit mantissa, wide range)" { + const vals = [_]f32{ 1.0, 3.14159, 1e30, -1e30, 1e-30, 6.022e23, 1e-9 }; + try checkRoundtrip(GF32, &vals, 1e-4); +} + +test "GF8 roundtrip (small, 4-bit mantissa)" { + const vals = [_]f32{ 1.0, -1.0, 1.5, 2.0, 0.5, 0.25 }; + try checkRoundtrip(GF8, &vals, 0.05); +} + +test "GF specials: Inf / NaN / zero across the ladder" { + inline for (.{ GF4, GF8, GF12, GF16, GF20, GF24, GF32 }) |T| { + try std.testing.expect(std.math.isInf(T.fromF32(std.math.inf(f32)).toF32())); + try std.testing.expect(T.fromF32(-std.math.inf(f32)).toF32() < 0); + try std.testing.expect(std.math.isNan(T.fromF32(std.math.nan(f32)).toF32())); + try std.testing.expectEqual(@as(f32, 0.0), T.zero().toF32()); + try std.testing.expect(!T.fromF32(std.math.inf(f32)).isFinite()); + } +} + +test "GF wider rungs strictly extend range (GF16 overflows where GF32 holds)" { + try std.testing.expect(std.math.isInf(GF16.fromF32(1e30).toF32())); // GF16 max ~2^31 + try std.testing.expect(GF32.fromF32(1e30).isFinite()); // GF32 max ~2^2047 +} + +test "GF arithmetic (GF16)" { + const a = GF16.fromF32(1.5); + const b = GF16.fromF32(2.5); + try std.testing.expectApproxEqAbs(@as(f32, 4.0), a.add(b).toF32(), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 3.75), a.mul(b).toF32(), 0.02); +} + +test "GF bits roundtrip" { + const x = GF20.fromF32(-6.28); + try std.testing.expectEqual(x.bits_(), GF20.fromBits(x.bits_()).bits_()); +} + +// Exact-bit golden vectors — pin the encoding, not just an approximate round-trip. +// Tolerance tests are blind to a systematic layout shift (e.g. a wrong exponent bias +// still round-trips symmetrically); these catch it. Values are exact in a 4-bit +// mantissa and inside GF8's tight range. Hand-check: GF8(-2.5) = sign 1, |2.5| = +// 1.25·2^1 -> exp field bias+1 = 4 = 0b100, mant 0.25·16 = 4 = 0b0100 -> 0b1_100_0100 +// = 0xC4. A deliberate codec change must update these on purpose. +test "GF ladder: exact-bit golden vectors (encoding regression guard)" { + const E = std.testing.expectEqual; + // gf8 [1:3:4] b3 + try E(@as(GF8.Repr, 0x30), GF8.fromF32(1.0).bits_()); + try E(@as(GF8.Repr, 0x38), GF8.fromF32(1.5).bits_()); + try E(@as(GF8.Repr, 0x40), GF8.fromF32(2.0).bits_()); + try E(@as(GF8.Repr, 0xC4), GF8.fromF32(-2.5).bits_()); + // gf12 [1:4:7] b7 + try E(@as(GF12.Repr, 0x380), GF12.fromF32(1.0).bits_()); + try E(@as(GF12.Repr, 0x3C0), GF12.fromF32(1.5).bits_()); + try E(@as(GF12.Repr, 0x400), GF12.fromF32(2.0).bits_()); + try E(@as(GF12.Repr, 0xC20), GF12.fromF32(-2.5).bits_()); + // gf16 [1:6:9] b31 — the primary production rung (shared with golden_float16.GF16) + try E(@as(GF16.Repr, 0x3E00), GF16.fromF32(1.0).bits_()); + try E(@as(GF16.Repr, 0x3F00), GF16.fromF32(1.5).bits_()); + try E(@as(GF16.Repr, 0x4000), GF16.fromF32(2.0).bits_()); + try E(@as(GF16.Repr, 0xC080), GF16.fromF32(-2.5).bits_()); + // gf20 [1:7:12] b63 + try E(@as(GF20.Repr, 0x3F000), GF20.fromF32(1.0).bits_()); + try E(@as(GF20.Repr, 0x3F800), GF20.fromF32(1.5).bits_()); + try E(@as(GF20.Repr, 0x40000), GF20.fromF32(2.0).bits_()); + try E(@as(GF20.Repr, 0xC0400), GF20.fromF32(-2.5).bits_()); + // gf24 [1:9:14] b255 + try E(@as(GF24.Repr, 0x3FC000), GF24.fromF32(1.0).bits_()); + try E(@as(GF24.Repr, 0x3FE000), GF24.fromF32(1.5).bits_()); + try E(@as(GF24.Repr, 0x400000), GF24.fromF32(2.0).bits_()); + try E(@as(GF24.Repr, 0xC01000), GF24.fromF32(-2.5).bits_()); + // gf32 [1:12:19] b2047 + try E(@as(GF32.Repr, 0x3FF80000), GF32.fromF32(1.0).bits_()); + try E(@as(GF32.Repr, 0x3FFC0000), GF32.fromF32(1.5).bits_()); + try E(@as(GF32.Repr, 0x40000000), GF32.fromF32(2.0).bits_()); + try E(@as(GF32.Repr, 0xC0020000), GF32.fromF32(-2.5).bits_()); +} + +// Normative-rule conformance — machine-check that every rung's factory constants satisfy +// the ONE sizing rule the spec encodes (FORMAT-SPEC-001): +// e = round((N-1)/φ²), m = N-1-e, bias = 2^(e-1)-1, exp_max = 2^e-1 +// Re-derived here independently of the factory, so a future edit to GF() that drifts from +// the rule fails loudly. This is the class of guard that was missing when GF8 carried a +// wrong bias (spec bias=7 vs canonical=3, #84). (A full spec<->.tri parse-time check is a +// separate effort — tri_reader lives outside this module's import path.) +test "GF ladder: factory constants obey the normative φ² rule" { + const rungs = [_]u32{ 8, 12, 16, 20, 24, 32 }; + inline for (rungs) |N| { + const T = GF(N); + const e: u32 = @intFromFloat(@round(@as(f64, N - 1) / PHI_SQ)); + const m: u32 = N - 1 - e; + const bias: u32 = (@as(u32, 1) << @intCast(e - 1)) - 1; + const exp_max: u32 = (@as(u32, 1) << @intCast(e)) - 1; + try std.testing.expectEqual(e, T.EXP_BITS); + try std.testing.expectEqual(m, T.MANT_BITS); + try std.testing.expectEqual(bias, T.BIAS); + try std.testing.expectEqual(exp_max, T.EXP_MAX); + try std.testing.expectEqual(@as(u32, N), T.BITS); + try std.testing.expectEqual(N, 1 + T.EXP_BITS + T.MANT_BITS); // fields tile the width + } +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig new file mode 100644 index 0000000..8921acb --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig @@ -0,0 +1,298 @@ +//! GF-T — balanced-ternary-EXPONENT GoldenFloat ladder (GF-T4 / GF-T8 / GF-T16 / GF-T32). +//! +//! GF-T is the ternary-exponent sibling of the binary GF ladder. The exponent is a +//! balanced-ternary number (digits -1/0/+1) stored as an unsigned OFFSET in +//! `[0, 3^E - 1]`; the balanced exponent is `e = offset - EXP_OFFSET`. There is no +//! regime decode (unlike posit/tekum) and the mantissa keeps GF's uniform binary +//! precision. +//! +//! value = (-1)^sign * (1 + M / 2^m) * 2^e, e = offset - EXP_OFFSET +//! EXP_OFFSET = (3^E - 1) / 2 (balanced zero point; offset EXP_OFFSET => e = 0) +//! OFFSET_MAX = 3^E - 1 (reserved top row: Inf / NaN) +//! finite iff offset < OFFSET_MAX +//! +//! Rungs (authoritative — t27/specs/numeric/gft{4,8,16,32}.t27, see specs/gft.tri): +//! GF-T4 : E=2 trits, M=1 bit , EXP_OFFSET=4 , e in [-4 ,+3 ], ~2.4 decades +//! GF-T8 : E=3 trits, M=4 bits, EXP_OFFSET=13 , e in [-13 ,+12 ], ~8 decades +//! GF-T16 : E=4 trits, M=9 bits, EXP_OFFSET=40 , e in [-40 ,+39 ], ~24 decades +//! GF-T32 : E=6 trits, M=25 bits, EXP_OFFSET=364, e in [-364,+363], ~219 decades +//! +//! **Usage:** +//! ```zig +//! const gft = @import("gft.zig"); +//! const x = gft.GFT16.fromF32(3.14159); +//! const y = gft.GFT16.fromF32(2.71828); +//! const z = x.mul(y); +//! std.debug.print("{d}\n", .{z.toF32()}); // ~8.539 +//! ``` +//! +//! phi^2 + 1/phi^2 = 3 | TRINITY + +const std = @import("std"); + +/// Build a GF-T rung from its exponent-trit count and mantissa-bit count. +/// `exp_trits` and `mant_bits` fully determine the format; everything else +/// (offset range, bias, storage width) is derived at comptime. +pub fn GFT(comptime exp_trits: comptime_int, comptime mant_bits: comptime_int) type { + // 3^exp_trits + const pow3: comptime_int = blk: { + var p: comptime_int = 1; + var i: comptime_int = 0; + while (i < exp_trits) : (i += 1) p *= 3; + break :blk p; + }; + const offset_max: comptime_int = pow3 - 1; // reserved special row + const exp_offset: comptime_int = (pow3 - 1) / 2; // balanced zero point + + // Bits needed to hold an offset in [0, offset_max]: smallest b with 2^b >= pow3. + const exp_field_bits: comptime_int = blk: { + var b: comptime_int = 0; + while ((1 << b) < pow3) : (b += 1) {} + break :blk b; + }; + const total_bits: comptime_int = 1 + exp_field_bits + mant_bits; + + const Mant = std.meta.Int(.unsigned, mant_bits); + const Off = std.meta.Int(.unsigned, exp_field_bits); + const ReprInt = std.meta.Int(.unsigned, total_bits); + + return packed struct(ReprInt) { + // Field order is low-to-high: mantissa in the low bits, sign in the top bit + // (mirrors GF16's `[mant][exp][sign]` packed layout). + mant: Mant, + offset: Off, + sign: u1, + + const Self = @This(); + + pub const EXP_TRITS: u32 = exp_trits; + pub const MANT_BITS: u32 = mant_bits; + pub const EXP_OFFSET: u32 = exp_offset; + pub const OFFSET_MAX: u32 = offset_max; // reserved Inf/NaN row + pub const BITS: u32 = total_bits; + /// Underlying unsigned storage integer (use with `bits()` / `fromBits()`). + pub const Repr = ReprInt; + + const MAX_E: i32 = @as(i32, exp_offset) - 1; // max finite exponent (offset_max-1 - exp_offset) + const MIN_E: i32 = -@as(i32, exp_offset); // min finite exponent (offset 0) + const MANT_SCALE: f32 = @floatFromInt(@as(u64, 1) << mant_bits); + const MANT_LIMIT: i64 = @as(i64, 1) << mant_bits; + + /// Encode an f32 into this GF-T rung (round-to-nearest, saturate to Inf, + /// flush-to-zero on underflow). + pub fn fromF32(v: f32) Self { + if (v == 0.0) return .{ .mant = 0, .offset = 0, .sign = @intFromBool(std.math.signbit(v)) }; + if (std.math.isNan(v)) return .{ .mant = 1, .offset = @intCast(offset_max), .sign = 0 }; + if (std.math.isInf(v)) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = @intFromBool(v < 0) }; + + const sign: u1 = @intFromBool(v < 0); + var f: f32 = @abs(v); + var e: i32 = 0; + // Normalize the significand into [1, 2). + while (f >= 2.0) : (e += 1) f /= 2.0; + while (f < 1.0) : (e -= 1) f *= 2.0; + + if (e > MAX_E) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = sign }; // -> Inf + if (e < MIN_E) return .{ .mant = 0, .offset = 0, .sign = sign }; // underflow -> 0 + + var mant: i64 = @intFromFloat(std.math.round((f - 1.0) * MANT_SCALE)); + var off: i32 = e + @as(i32, exp_offset); + if (mant >= MANT_LIMIT) { // significand rounded up to 2.0 -> carry into exponent + mant = 0; + off += 1; + if (off >= @as(i32, offset_max)) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = sign }; + } + return .{ .mant = @intCast(mant), .offset = @intCast(off), .sign = sign }; + } + + /// Decode this GF-T rung back to f32 (exact for the represented value). + pub fn toF32(self: Self) f32 { + if (self.offset == offset_max) { + if (self.mant == 0) return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); + return std.math.nan(f32); + } + if (self.offset == 0 and self.mant == 0) return if (self.sign == 1) -0.0 else 0.0; + const e: i32 = @as(i32, self.offset) - @as(i32, exp_offset); + const f: f32 = 1.0 + @as(f32, @floatFromInt(self.mant)) / MANT_SCALE; + const val = f * std.math.exp2(@as(f32, @floatFromInt(e))); + return if (self.sign == 1) -val else val; + } + + /// True unless this is the reserved Inf/NaN row. + pub fn isFinite(self: Self) bool { + return self.offset != offset_max; + } + + /// Raw storage bits (for serialization / FFI). + pub fn bits(self: Self) ReprInt { + return @bitCast(self); + } + /// Rebuild from raw storage bits. + pub fn fromBits(b: ReprInt) Self { + return @bitCast(b); + } + + pub fn zero() Self { + return .{ .mant = 0, .offset = 0, .sign = 0 }; + } + pub fn one() Self { + return fromF32(1.0); + } + pub fn neg(self: Self) Self { + return .{ .mant = self.mant, .offset = self.offset, .sign = self.sign ^ 1 }; + } + pub fn abs(self: Self) Self { + return .{ .mant = self.mant, .offset = self.offset, .sign = 0 }; + } + + // Arithmetic via f32 (exact-enough; the format is the storage, f32 is the ALU). + pub fn add(a: Self, b: Self) Self { + return fromF32(a.toF32() + b.toF32()); + } + pub fn sub(a: Self, b: Self) Self { + return fromF32(a.toF32() - b.toF32()); + } + pub fn mul(a: Self, b: Self) Self { + return fromF32(a.toF32() * b.toF32()); + } + pub fn div(a: Self, b: Self) Self { + return fromF32(a.toF32() / b.toF32()); + } + }; +} + +/// The four practical rungs of the GF-T ladder. +pub const GFT4 = GFT(2, 1); +pub const GFT8 = GFT(3, 4); +pub const GFT16 = GFT(4, 9); +pub const GFT32 = GFT(6, 25); + +// ═══════════════════════════════════════════════════════════════════ +// Tests +// ═══════════════════════════════════════════════════════════════════ + +test "GF-T constants match the authoritative ladder" { + try std.testing.expectEqual(@as(u32, 4), GFT4.EXP_OFFSET); + try std.testing.expectEqual(@as(u32, 8), GFT4.OFFSET_MAX); + try std.testing.expectEqual(@as(u32, 13), GFT8.EXP_OFFSET); + try std.testing.expectEqual(@as(u32, 26), GFT8.OFFSET_MAX); + try std.testing.expectEqual(@as(u32, 40), GFT16.EXP_OFFSET); + try std.testing.expectEqual(@as(u32, 80), GFT16.OFFSET_MAX); + try std.testing.expectEqual(@as(u32, 364), GFT32.EXP_OFFSET); + try std.testing.expectEqual(@as(u32, 728), GFT32.OFFSET_MAX); +} + +test "GF-T unity encodes to the balanced zero offset" { + const one16 = GFT16.fromF32(1.0); + try std.testing.expectEqual(@as(u32, 40), @as(u32, one16.offset)); // e = 0 + try std.testing.expectEqual(@as(u9, 0), one16.mant); + try std.testing.expectApproxEqAbs(@as(f32, 1.0), one16.toF32(), 1e-6); +} + +test "GF-T zero and negative zero" { + try std.testing.expectEqual(@as(f32, 0.0), GFT16.zero().toF32()); + try std.testing.expectEqual(@as(f32, 0.0), GFT16.fromF32(0.0).toF32()); + try std.testing.expect(std.math.signbit(GFT16.fromF32(-0.0).toF32())); +} + +fn checkRoundtrip(comptime T: type, values: []const f32, tol: f32) !void { + for (values) |v| { + const q = T.fromF32(v).toF32(); + const err = @abs(q - v) / (@abs(v) + 1e-9); + try std.testing.expect(err <= tol); + } +} + +test "GF-T16 roundtrip (9-bit mantissa, tight)" { + const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.14159, -3.14159, 100.0, 0.001, -0.001, 12345.0, 1e-9 }; + try checkRoundtrip(GFT16, &vals, 0.005); // < 0.5% for a 9-bit mantissa +} + +test "GF-T8 roundtrip (4-bit mantissa, looser)" { + const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.0, -3.0, 50.0, 0.01 }; + try checkRoundtrip(GFT8, &vals, 0.05); // < 5% for a 4-bit mantissa +} + +test "GF-T4 roundtrip (1-bit mantissa, coarse)" { + const vals = [_]f32{ 1.0, -1.0, 1.5, 2.0, 4.0, 0.5, 0.25 }; + try checkRoundtrip(GFT4, &vals, 0.30); // 1-bit mantissa -> ~25% steps +} + +test "GF-T32 huge dynamic range" { + const vals = [_]f32{ 1e30, -1e30, 1e-30, 1e18, 1e-18, 6.022e23 }; + try checkRoundtrip(GFT32, &vals, 0.001); // 25-bit mantissa is very precise +} + +test "GF-T Inf and NaN roundtrip" { + inline for (.{ GFT4, GFT8, GFT16, GFT32 }) |T| { + try std.testing.expect(std.math.isInf(T.fromF32(std.math.inf(f32)).toF32())); + try std.testing.expect(T.fromF32(-std.math.inf(f32)).toF32() < 0); + try std.testing.expect(std.math.isNan(T.fromF32(std.math.nan(f32)).toF32())); + try std.testing.expect(!T.fromF32(std.math.inf(f32)).isFinite()); + try std.testing.expect(T.fromF32(1.0).isFinite()); + } +} + +test "GF-T overflow saturates to Inf, underflow flushes to zero" { + // GF-T16 max finite exponent is +39 (~5.5e11); 1e30 overflows. + try std.testing.expect(std.math.isInf(GFT16.fromF32(1e30).toF32())); + // ...and 1e-30 underflows below 2^-40. + try std.testing.expectEqual(@as(f32, 0.0), GFT16.fromF32(1e-30).toF32()); + // GF-T32 covers both. + try std.testing.expect(GFT32.fromF32(1e30).isFinite()); +} + +test "GF-T neg / abs" { + const x = GFT16.fromF32(3.5); + try std.testing.expectApproxEqAbs(@as(f32, -3.5), x.neg().toF32(), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 3.5), x.neg().abs().toF32(), 0.02); +} + +test "GF-T arithmetic (GF-T16)" { + const a = GFT16.fromF32(1.5); + const b = GFT16.fromF32(2.5); + try std.testing.expectApproxEqAbs(@as(f32, 4.0), a.add(b).toF32(), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 1.0), b.sub(a).toF32(), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 3.75), a.mul(b).toF32(), 0.02); + try std.testing.expectApproxEqAbs(@as(f32, 0.6), a.div(b).toF32(), 0.02); +} + +test "GF-T bits roundtrip" { + const x = GFT16.fromF32(-6.28); + const y = GFT16.fromBits(x.bits()); + try std.testing.expectEqual(x.bits(), y.bits()); + try std.testing.expectApproxEqAbs(x.toF32(), y.toF32(), 1e-9); +} + +test "GF-T storage widths (nominal name vs real bits)" { + // The nominal name (4/8/16/32) tags the GF lineage; ternary exponent + full + // mantissa need a wider container than the binary rung. + try std.testing.expectEqual(@as(u32, 6), GFT4.BITS); // 1 + 4 + 1 + try std.testing.expectEqual(@as(u32, 10), GFT8.BITS); // 1 + 5 + 4 + try std.testing.expectEqual(@as(u32, 17), GFT16.BITS); // 1 + 7 + 9 + try std.testing.expectEqual(@as(u32, 36), GFT32.BITS); // 1 + 10 + 25 +} + +// Exact-bit golden vectors — pin the ternary-exponent encoding. A tolerance-based +// round-trip is blind to an offset/bias shift; these pin it. Hand-check: +// GFT16(-2.5) = sign 1, |2.5| = 1.25·2^1 -> offset EXP_OFFSET+1 = 41, mant 0.25·512 = +// 128 = 0x80 -> (41<<9) | 0x80 | (1<<16) = 0x15280. Update deliberately if the codec +// layout changes. +test "GF-T: exact-bit golden vectors (encoding regression guard)" { + const E = std.testing.expectEqual; + // gft8 (E3 M4, offset 13) + try E(@as(GFT8.Repr, 0x0D0), GFT8.fromF32(1.0).bits()); + try E(@as(GFT8.Repr, 0x0E0), GFT8.fromF32(2.0).bits()); + try E(@as(GFT8.Repr, 0x2D0), GFT8.fromF32(-1.0).bits()); + try E(@as(GFT8.Repr, 0x2E4), GFT8.fromF32(-2.5).bits()); + // gft16 (E4 M9, offset 40) + try E(@as(GFT16.Repr, 0x05000), GFT16.fromF32(1.0).bits()); + try E(@as(GFT16.Repr, 0x05200), GFT16.fromF32(2.0).bits()); + try E(@as(GFT16.Repr, 0x15000), GFT16.fromF32(-1.0).bits()); + try E(@as(GFT16.Repr, 0x15280), GFT16.fromF32(-2.5).bits()); + // gft32 (E6 M25, offset 364) + try E(@as(GFT32.Repr, 0x2D8000000), GFT32.fromF32(1.0).bits()); + try E(@as(GFT32.Repr, 0x2DA000000), GFT32.fromF32(2.0).bits()); + try E(@as(GFT32.Repr, 0xAD8000000), GFT32.fromF32(-1.0).bits()); + try E(@as(GFT32.Repr, 0xADA800000), GFT32.fromF32(-2.5).bits()); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig new file mode 100644 index 0000000..e793775 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig @@ -0,0 +1,463 @@ +//! Trinity ML Formats — GF16 and TF3-9 (Consolidated) +//! +//! This module provides φ-optimized number formats for Trinity's HSLM (Hybrid Symbolic Language Model). +//! +//! **Formats:** +//! - GF16: Golden Float16 — φ-optimized 16-bit format [sign:1][exp:6][mant:9] +//! - TF3: Ternary Float3 — packed ternary [sign:1][exp:6][mant:11] (18 bits) +//! +//! **Mathematical Foundation:** +//! φ² + 1/φ² = 3 | TRINITY +//! where φ = (1 + √5) / 2 ≈ 1.6180339887498949 +//! +//! **Reference:** +//! - IBM DLFloat: https://research.ibm.com/publications/dlfloat-a-16-floating-point-format-designed-for-deep-learning-training-and-inference +//! +//! **Usage:** +//! ```zig +//! const std = @import("std"); +//! const golden = @import("golden_float16.zig"); +//! +//! const gf = golden.GF16.fromF32(3.14159); +//! const tf3 = golden.TF3.fromF32(2.71828); +//! ``` +//! + +const std = @import("std"); +const gf_binary = @import("gf_binary.zig"); + +// ═════════════════════════════════════════════════════════════════════════ +// TRINITY CONSTANTS +// ═════════════════════════════════════════════════════════════════════ + +/// Golden ratio φ = (1 + √5) / 2 +pub const PHI = 1.6180339887498948482; + +/// φ² = φ × φ +pub const PHI_SQ = PHI * PHI; + +/// 1/φ² +pub const PHI_INV_SQ = 1.0 / PHI_SQ; + +/// Trinity Identity: φ² + 1/φ² = 3 +pub const TRINITY = PHI_SQ + PHI_INV_SQ; + +// ═════════════════════════════════════════════════════════════════════ +// GF16: GOLDEN FLOAT16 +// ═════════════════════════════════════════════════════════════════════════ + +/// GF16: Golden Float16 — φ-optimized packed format +/// +/// **Bit Layout:** +/// ``` +/// ┌──────┬─────────┬─────────┐ +/// │ sign │ exp │ mant │ +/// │ 1bit │ 6bit │ 9bit │ +/// └──────┴─────────┴─────────┘ +/// ``` +/// +/// **Phi-optimal distribution** — Unlike IEEE 754 f16 [sign:1][exp:5][mant:10], +/// GF16 has phi-optimal bit distribution: [sign:1][exp:6][mant:9]. +/// +/// **Parameters:** +/// - Exponent bias: 31 (0x1F) +/// - Min positive: 2^(-31) ≈ 4.66e-10 +/// - Max value: ~2^31 × 1.999 ≈ 4.29e9 +/// - phi-distance: |exp/mant - 1/φ| ≈ 0.049 (close to φ-optimal) +/// +/// **Example:** +/// ```zig +/// const gf = GF16.fromF32(3.14159); +/// try std.testing.expectApproxEqAbs(3.14, gf.toF32(), 0.01); +/// ``` +pub const GF16 = packed struct(u16) { + /// Mantissa (9 bits) — φ-optimized precision + mant: u9, + + /// Exponent (6 bits, bias 31) + exp: u6, + + /// Sign bit (1 = negative) + sign: u1, + + /// phi-distance: measures how close bit distribution is to φ-optimal + /// Lower is better — GF16 achieves 0.049 (vs 0.082 for IEEE f16) + /// comptime calculation (0.049 for GF16) + // pub const phi_distance: comptime_float = @import("std").math.fabs(6.0 / 9.0 - 1.0 / PHI); + + /// Create GF16 from f32. + /// + /// Delegates to the single normative codec `gf_binary.GF16` (the φ²-sized + /// binary rung factory) so GF16 has exactly ONE encoding across the repo: + /// the standard `(1 + M/512)·2^(E−31)` significand with the full 9-bit + /// mantissa (FORMAT-SPEC-001, specs/gf16.tri). e.g. 1.0 → 0x3E00. + pub fn fromF32(v: f32) GF16 { + return @bitCast(gf_binary.GF16.fromF32(v).bits_()); + } + + /// Convert GF16 to f32 (via the same normative `gf_binary.GF16` codec). + pub fn toF32(self: GF16) f32 { + return gf_binary.GF16.fromBits(@bitCast(self)).toF32(); + } + + /// GF16 addition (via f32 for precision) + pub fn add(a: GF16, b: GF16) GF16 { + return fromF32(a.toF32() + b.toF32()); + } + + /// GF16 subtraction + pub fn sub(a: GF16, b: GF16) GF16 { + return fromF32(a.toF32() - b.toF32()); + } + + /// GF16 multiplication + pub fn mul(a: GF16, b: GF16) GF16 { + return fromF32(a.toF32() * b.toF32()); + } + + /// GF16 division + pub fn div(a: GF16, b: GF16) GF16 { + return fromF32(a.toF32() / b.toF32()); + } + + /// Zero GF16 + pub inline fn zero() GF16 { + return .{ .mant = 0, .exp = 0, .sign = 0 }; + } + + /// One GF16 + pub inline fn one() GF16 { + return fromF32(1.0); + } + + /// Negate GF16 + pub inline fn neg(self: GF16) GF16 { + return .{ + .mant = self.mant, + .exp = self.exp, + .sign = if (self.sign == 1) 0 else 1, + }; + } + + /// Absolute value + pub inline fn abs(self: GF16) GF16 { + return .{ + .mant = self.mant, + .exp = self.exp, + .sign = 0, + }; + } + + /// φ-weighted quantization for better distribution + pub fn phiQuantize(v: f32) GF16 { + return fromF32(v * PHI_INV_SQ); + } + + /// φ-weighted dequantization + pub fn phiDequantize(gf: GF16) f32 { + return gf.toF32() * PHI_SQ; + } + + /// φ-optimized fused multiply-add: dequantize(a)*dequantize(b) + dequantize(c), then φ-quantize + pub fn phiFma(a: GF16, b: GF16, c: GF16) GF16 { + const fa = phiDequantize(a); + const fb = phiDequantize(b); + const fc = phiDequantize(c); + return phiQuantize(fa * fb + fc); + } + + /// φ-optimized fused multiply-subtract: dequantize(a)*dequantize(b) - dequantize(c), then φ-quantize + pub fn phiFms(a: GF16, b: GF16, c: GF16) GF16 { + const fa = phiDequantize(a); + const fb = phiDequantize(b); + const fc = phiDequantize(c); + return phiQuantize(fa * fb - fc); + } + + /// Standard fused multiply-add (no φ scaling): a*b + c in f32, rounded to GF16 + pub fn fma(a: GF16, b: GF16, c: GF16) GF16 { + return fromF32(a.toF32() * b.toF32() + c.toF32()); + } +}; + +// ═════════════════════════════════════════════════════════════════════════════ +// TF3: TERNARY FLOAT3 +// ═══════════════════════════════════════════════════════════════════════ + +/// TF3: Ternary Float3 — packed ternary format +/// +/// **Bit Layout:** +/// ``` +/// ┌──────┬─────────┬────────────┐ +/// │ sign │ exp │ mant │ +/// │ 1bit │ 6bit │ 11 bit │ +/// └──────┴─────────┴────────────┘ +/// ``` +/// (18 bits total) +/// +/// **Structure:** +/// - sign: 1 sign bit +/// - exp: 6 exponent bits (values -31..+32, base 3) +/// - mant: 11 mantissa bits (ternary digits: {-1, 0, +1}) +/// +/// **Encoding:** +/// ``` +/// trit value | TF3 encoding +/// ----------|------------- +/// -1 | NEG = 2 (binary: 10) +/// 0 | ZERO = 0 +/// +1 | POS = 1 +/// ``` +/// +/// **Example:** +/// ```zig +/// const tf3 = TF3.fromF32(2.71828); +/// try std.testing.expect(tf3.toF32() > 2.5 and tf3.toF32() < 3.0); +/// ``` +pub const TF3 = packed struct(u18) { + /// Mantissa (11 bits) — ternary digits packed as unsigned + mant: u11, + + /// Exponent (6 bits, bias 31 for ternary base 3) + exp: u6, + + /// Sign bit (1 = negative) + sign: u1, + + /// Exponent bias for TF3 (ternary base 3) + const EXP_BIAS: u6 = 31; + + /// Ternary value encodings for packing + const NEG: u2 = 2; + const ZERO: u2 = 0; + const POS: u2 = 1; + + /// phi-distance for ternary format + /// comptime calculation (0.194 for TF3) + // pub const phi_distance: comptime_float = @import("std").math.fabs(3.0 / 11.0 - 1.0 / PHI); + + /// Create TF3 from f32 (ternary base 3) + pub fn fromF32(v: f32) TF3 { + if (v == 0.0) return .{ .mant = 0, .exp = 0, .sign = 0 }; + + if (!std.math.isFinite(v)) { + return .{ .mant = 0, .exp = 0x3F, .sign = @intFromBool(v < 0) }; + } + + const sign_bit: u1 = @intFromBool(v < 0); + const abs_v = @abs(v); + + // Find exponent (ternary base 3) + // Use i16 to avoid overflow during calculations + var exp: i16 = 0; + var mant_f = abs_v; + + // Normalize: mant_f in [1/3, 1] + const MAX_EXP: i16 = 31; + const MIN_EXP: i16 = -31; + + while (mant_f >= 1.0 and exp < MAX_EXP) : (exp += 1) mant_f /= 3.0; + while (mant_f < 1.0 / 3.0 and exp > MIN_EXP) : (exp -= 1) mant_f *= 3.0; + + // Clamp and convert to u6 (biased exponent) + const exp_biased = @min(@max(exp + 31, 0), 63); + const exp_u6: u6 = @intCast(exp_biased); + const mant_u11: u11 = @intFromFloat(@min(mant_f * 2047.0, 2047.0)); + + return .{ + .mant = mant_u11, + .exp = exp_u6, + .sign = sign_bit, + }; + } + + /// Convert TF3 to f32 + pub fn toF32(self: TF3) f32 { + if (self.exp == 0 and self.mant == 0) { + return if (self.sign == 1) -0.0 else 0.0; + } + if (self.exp == 0x3F) { + return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); + } + + const exp_unbiased = @as(i16, self.exp) - 31; + const mant_f = @as(f32, @floatFromInt(self.mant)) / 2047.0; + const value = mant_f * std.math.pow(f32, 3.0, @floatFromInt(exp_unbiased)); + return if (self.sign == 1) -value else value; + } + + /// Get ternary sign {-1, 0, +1} + pub inline fn getSign(self: TF3) i8 { + return if (self.sign == 1) -1 else if (self.mant == 0) 0 else 1; + } + + /// Zero TF3 + pub inline fn zero() TF3 { + return .{ .mant = 0, .exp = 0, .sign = 0 }; + } + + /// One TF3 + pub inline fn one() TF3 { + return fromF32(1.0); + } +}; + +// ═════════════════════════════════════════════════════════════════════════════ +// COMPILE-TIME GUARDS +// ═════════════════════════════════════════════════════════════════════════════ + +comptime { + // Check packed struct sizes + std.debug.assert(@sizeOf(GF16) == 2); + std.debug.assert(@sizeOf(TF3) == @sizeOf(u18)); +} + +// ═════════════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═════════════════════════════════════════════════════════════════════════════════ + +test "GF16 zero and one" { + const zero = GF16.zero(); + try std.testing.expectEqual(@as(f32, 0), zero.toF32()); + + const one = GF16.one(); + try std.testing.expectApproxEqAbs(@as(f32, 1.0), one.toF32(), 0.01); +} + +test "GF16 roundtrip positive" { + const values = [_]f32{ 0.0, 0.5, 1.0, 2.0, 3.14, 100.0, 1000.0 }; + for (values) |v| { + const gf = GF16.fromF32(v); + const result = gf.toF32(); + const err = @abs(v - result) / (@abs(v) + 0.001); + try std.testing.expect(err < 0.05); // 5% error tolerance + } +} + +test "GF16 roundtrip negative" { + const values = [_]f32{ -0.5, -1.0, -2.0, -3.14, -100.0, -1000.0 }; + for (values) |v| { + const gf = GF16.fromF32(v); + const result = gf.toF32(); + const err = @abs(v - result) / (@abs(v) + 0.001); + try std.testing.expect(err < 0.05); + } +} + +test "GF16 exact-bit encoding is the standard (1 + M/512) form" { + // Pin the wire format: 1.0 -> 0x3E00 (E=31, mantissa 0), NOT the old + // waste-a-bit 0x4000. Full 9-bit mantissa is now reachable. See specs/gf16.tri + // and testdata/gf_conformance.csv (gf16 rows). + const E = std.testing.expectEqual; + try E(@as(u16, 0x3E00), @as(u16, @bitCast(GF16.fromF32(1.0)))); + try E(@as(u16, 0x3F00), @as(u16, @bitCast(GF16.fromF32(1.5)))); + try E(@as(u16, 0x4000), @as(u16, @bitCast(GF16.fromF32(2.0)))); + try E(@as(u16, 0x4100), @as(u16, @bitCast(GF16.fromF32(3.0)))); + try E(@as(u16, 0x3C00), @as(u16, @bitCast(GF16.fromF32(0.5)))); + try E(@as(u16, 0xBE00), @as(u16, @bitCast(GF16.fromF32(-1.0)))); + try E(@as(u16, 0xC080), @as(u16, @bitCast(GF16.fromF32(-2.5)))); +} + +test "GF16 is bit-identical to the normative gf_binary.GF16 codec" { + // One implementation: golden_float16.GF16 delegates to gf_binary.GF16, so + // every value must encode to the exact same raw u16. Guards against re-drift. + const vals = [_]f32{ 0.0, 1.0, -1.0, 0.5, 1.5, 2.0, 3.0, -2.5, 3.14159, 100.0, 0.001, 12345.0, 1e30, -1e30 }; + for (vals) |v| { + const a: u16 = @bitCast(GF16.fromF32(v)); + const b: u16 = gf_binary.GF16.fromF32(v).bits_(); + try std.testing.expectEqual(b, a); + } + // NaN encodes consistently too (exp all-ones, mantissa != 0). + const na: u16 = @bitCast(GF16.fromF32(std.math.nan(f32))); + const nb: u16 = gf_binary.GF16.fromF32(std.math.nan(f32)).bits_(); + try std.testing.expectEqual(nb, na); +} + +test "GF16 arithmetic" { + const a = GF16.fromF32(1.5); + const b = GF16.fromF32(2.5); + const sum = GF16.add(a, b); + const diff = GF16.sub(b, a); + const prod = GF16.mul(a, b); + const quot = GF16.div(a, b); + + try std.testing.expectApproxEqAbs(@as(f32, 4.0), sum.toF32(), 0.05); + try std.testing.expectApproxEqAbs(@as(f32, 1.0), diff.toF32(), 0.05); + try std.testing.expectApproxEqAbs(@as(f32, 3.75), prod.toF32(), 0.05); + try std.testing.expectApproxEqAbs(@as(f32, 0.6), quot.toF32(), 0.05); +} + +test "GF16 phi quantization roundtrip" { + const original = 2.71828; + const quantized = GF16.phiQuantize(original); + const dequantized = GF16.phiDequantize(quantized); + + const error_pct = @abs((dequantized - original) / original) * 100.0; + try std.testing.expect(error_pct < 10.0); +} + +test "TF3 zero and one" { + const zero = TF3.zero(); + try std.testing.expectEqual(@as(i8, 0), zero.getSign()); + try std.testing.expectEqual(@as(f32, 0), zero.toF32()); + + const one = TF3.one(); + try std.testing.expectEqual(@as(i8, 1), one.getSign()); + try std.testing.expect(one.toF32() > 0.5 and one.toF32() < 1.5); +} + +test "TF3 roundtrip" { + const values = [_]f32{ 0.0, 0.1, 0.5, 1.0, -0.5, -1.0 }; + for (values) |v| { + const tf3 = TF3.fromF32(v); + const result = tf3.toF32(); + const err = @abs(v - result) / (@abs(v) + 0.001); + try std.testing.expect(err < 0.5); // Ternary format less precise + } +} + +// TODO: Implement pack8/unpack8 with proper type handling +test "TF3 pack unpack 8 (pending)" { + try std.testing.expect(true); +} + +test "TRINITY constant" { + try std.testing.expectApproxEqAbs(@as(f32, 3.0), TRINITY, 1e-10); +} + +test "PHI constant" { + try std.testing.expectApproxEqAbs(@as(f32, 1.6180339887498948482), PHI, 1e-15); +} + +test "PHI_SQ + 1/PHI_SQ equals 3" { + const computed = PHI_SQ + 1.0 / PHI_SQ; + try std.testing.expectApproxEqAbs(@as(f32, 3.0), computed, 1e-10); +} + +test "GF16 phi-fused multiply-add" { + const a = GF16.phiQuantize(2.0); + const b = GF16.phiQuantize(3.0); + const c = GF16.phiQuantize(4.0); + const result = GF16.phiFma(a, b, c); + const deq = GF16.phiDequantize(result); + try std.testing.expectApproxEqAbs(@as(f32, 10.0), deq, 1.5); +} + +test "GF16 phi-fused multiply-subtract" { + const a = GF16.phiQuantize(5.0); + const b = GF16.phiQuantize(3.0); + const c = GF16.phiQuantize(4.0); + const result = GF16.phiFms(a, b, c); + const deq = GF16.phiDequantize(result); + try std.testing.expectApproxEqAbs(@as(f32, 11.0), deq, 2.0); +} + +test "GF16 standard fused multiply-add" { + const a = GF16.fromF32(2.0); + const b = GF16.fromF32(3.0); + const c = GF16.fromF32(4.0); + const result = GF16.fma(a, b, c); + try std.testing.expectApproxEqAbs(@as(f32, 10.0), result.toF32(), 0.5); +} + +// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig new file mode 100644 index 0000000..545bfe6 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig @@ -0,0 +1,70 @@ +const std = @import("std"); +const tc = @import("trinity_constants.zig"); + +pub const EncoderLayers: u32 = 6; +pub const PredictorLayers: u32 = 3; +pub const PhiSplit: f64 = @as(f64, @floatFromInt(EncoderLayers)) / @as(f64, @floatFromInt(EncoderLayers + PredictorLayers)); + +pub fn encoderParams() u64 { + const embed_params = @as(u64, tc.VOCAB) * tc.D_MODEL; + const per_layer = 4 * @as(u64, tc.D_MODEL) * tc.D_MODEL + 2 * @as(u64, tc.D_MODEL) * tc.D_FFN + 4 * tc.D_MODEL; + return embed_params + EncoderLayers * per_layer; +} + +pub fn predictorParams() u64 { + const per_layer = 4 * @as(u64, tc.D_MODEL) * tc.D_MODEL + 2 * @as(u64, tc.D_MODEL) * tc.D_FFN + 4 * tc.D_MODEL; + return PredictorLayers * per_layer; +} + +pub fn totalParams() u64 { + return encoderParams() + predictorParams(); +} + +pub fn totalBytesGF16() u64 { + return totalParams() * 2; +} + +pub fn totalMB() f64 { + return @as(f64, @floatFromInt(totalBytesGF16())) / (1024.0 * 1024.0); +} + +pub fn jepaLoss( + pred: []const f64, + target: []const f64, +) f64 { + std.debug.assert(pred.len == target.len); + var sum: f64 = 0; + for (pred, target) |p, t| { + const d = p - t; + sum += d * d; + } + return sum / @as(f64, @floatFromInt(pred.len)); +} + +test "JEPA-T: phi split ratio" { + try std.testing.expectApproxEqAbs(@as(f64, 0.667), PhiSplit, 0.01); +} + +test "JEPA-T: total params fit in 17MB GF16" { + const mb = totalMB(); + try std.testing.expect(mb <= 17.0); + try std.testing.expect(mb > 10.0); +} + +test "JEPA-T: jepaLoss correct" { + const pred = [_]f64{ 1.0, 2.0, 3.0 }; + const tgt = [_]f64{ 1.0, 2.0, 3.0 }; + const loss = jepaLoss(&pred, &tgt); + try std.testing.expectApproxEqAbs(@as(f64, 0.0), loss, 1e-10); +} + +test "JEPA-T: jepaLoss nonzero for mismatch" { + const pred = [_]f64{ 1.0, 0.0 }; + const tgt = [_]f64{ 0.0, 1.0 }; + const loss = jepaLoss(&pred, &tgt); + try std.testing.expect(loss > 0); +} + +test "JEPA-T: encoder > predictor" { + try std.testing.expect(encoderParams() > predictorParams()); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs new file mode 100644 index 0000000..3853db0 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs @@ -0,0 +1,46 @@ +// GoldenFloat Rust Wrapper +// +// Provides FFI bindings to Zig-compiled golden-float binary +// Downloads the appropriate binary from GitHub releases + +pub const VERSION: &str = "1.0.0"; +pub const GITHUB_RELEASES: &str = "https://github.com/gHashTag/zig-golden-float/releases/download"; + +#[cfg(target_os = "windows")] +use std::os::windows::process::Command; + +/// Get binary path for current platform +pub fn get_binary_path() -> std::path.PathBuf { + let bin_name = "golden-float"; + let mut path = std::env::var("HOME").unwrap(); + path.push(".golden-float"); + path.push(bin_name); + + #[cfg(windows)] + { + path.set_extension("exe"); + } + + path +} + +/// Launch golden-float binary +pub fn run_golden_float(args: &[&str]) -> std::process::Child { + let binary = get_binary_path(); + + let cmd = Command::new(&binary); + cmd.args(args); + + cmd.spawn().expect("Failed to spawn golden-float binary") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_binary_path() { + let path = get_binary_path(); + assert!(path.to_str().unwrap().contains("golden-float")); + } +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig new file mode 100644 index 0000000..e4183c9 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig @@ -0,0 +1,148 @@ +// @origin(spec:constants.tri) @regen(manual-impl) +//! Mathematical Constants v8.21 +//! +//! Foundation of AGENT MU intelligence calculations +//! Features: +//! - Golden Ratio φ (Phi) from canonical source +//! - Trinity Identity: φ² + 1/φ² = 3 +//! - MU = 1/φ²/10 = 0.0382 (intelligence gain per fix) +//! - Lucas numbers and Berry phase +// @origin(manual) @regen(pending) + +const std = @import("std"); + +// Import from canonical source (ANTI-PATTERN: no inline constants!) +// sacred/constants.zig does not exist in this repository and never has, so +// this file could not compile and neither could anything importing it -- +// including src/root.zig, which is the module root every consumer gets. +// The two values it supplied are PHI and PHI squared, and this repository +// already carries them at 1.6180339887498948482 in trinity_constants.zig, +// gf_binary.zig and golden_float16.zig, all three identical. Pointing at +// the repository's own constants keeps the value rather than inventing one. +const sacred_constants = @import("../trinity_constants.zig"); + +/// Golden Ratio φ = (1 + √5) / 2 ≈ 1.618033988749895 +pub const PHI = sacred_constants.PHI; + +/// φ² = φ + 1 ≈ 2.618033988749895 +pub const PHI_SQUARED = sacred_constants.PHI_SQ; + +/// 1/φ² ≈ 0.381966011250105 +pub const INVERSE_PHI_SQUARED: f64 = 1.0 / PHI_SQUARED; + +/// Trinity Identity: φ² + 1/φ² = 3 (exactly) +pub const TRINITY_SUM: f64 = PHI_SQUARED + INVERSE_PHI_SQUARED; + +/// MU = 1/φ²/10 = 0.0382 (intelligence gain per successful fix) +pub const MU: f64 = INVERSE_PHI_SQUARED / 10.0; + +/// Lucas number L(10) = 123 (used in checksum validation) +pub const LAMBDA_10: f64 = 123.0; + +/// Lambda scaling factor for predictive intelligence +pub const LAMBDA_SCALE: f64 = 1.105572809; + +/// Berry phase for quantum-inspired computation +pub const BERRY_PHASE: f64 = std.math.pi * (1.0 - 1.0 / PHI); + +/// SU3 energy harvesting constant +pub const SU3_CONSTANT: f64 = 3.0 / (2.0 * PHI); + +// Verify Trinity identity at compile time +comptime { + if (!(TRINITY_SUM >= 2.999 and TRINITY_SUM <= 3.001)) { + @compileError("Trinity identity violation: φ² + 1/φ² must equal 3"); + } +} + +/// Sacred math utilities +pub const SacredMath = struct { + /// Calculate intelligence multiplier after n successful fixes + /// Formula: I(t) = I₀ × e^(μ×fixes) + pub fn intelligenceMultiplier(fixes: usize) f64 { + return @exp(MU * @as(f64, @floatFromInt(fixes))); + } + + /// Calculate φ-weighted consensus score + pub fn phiWeightedConsensus(scores: []const f64) f64 { + var weighted_sum: f64 = 0; + var total_weight: f64 = 0; + + for (scores, 0..) |score, i| { + // Use powers of φ as weights + const weight = std.math.pow(f64, PHI, @as(f64, @floatFromInt(i))); + weighted_sum += score * weight; + total_weight += weight; + } + + return if (total_weight > 0) weighted_sum / total_weight else 0; + } + + /// Calculate Berry phase rotation + pub fn berryPhaseRotation(angle: f64) f64 { + return angle + BERRY_PHASE; + } + + /// Generate sacred checksum for validation + pub fn sacredChecksum(data: []const u8) u64 { + // The multiplier is 2^64/φ, which is what φ IS in integer hashing -- + // Knuth's multiplicative constant. The previous line multiplied a u64 + // by the f64 PHI and then called @intFromFloat on the u64 result, so + // this function has never compiled and no stored checksum can depend + // on it. Taking the standard constant keeps the golden ratio rather + // than inventing a number. + const PHI_U64: u64 = 0x9E3779B97F4A7C15; + var hash: u64 = @intFromFloat(LAMBDA_10); + for (data) |byte| { + hash = hash *% PHI_U64 +% byte; + } + return hash; + } + + /// Verify Trinity alignment + pub fn isTrinityAligned(value: f64) bool { + return value >= (3.0 - 0.01) and value <= (3.0 + 0.01); + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// Tests +// ═══════════════════════════════════════════════════════════════════════════════ + +test "Sacred Constants: Trinity Identity" { + try std.testing.expectApproxEqAbs(3.0, TRINITY_SUM, 0.001); +} + +test "Sacred Constants: MU calculation" { + try std.testing.expectApproxEqAbs(0.0382, MU, 0.0001); +} + +test "Sacred Constants: PHI squared" { + try std.testing.expectApproxEqAbs(2.6180, PHI_SQUARED, 0.001); +} + +test "Sacred Math: Intelligence multiplier" { + const mult_0 = SacredMath.intelligenceMultiplier(0); + try std.testing.expectApproxEqAbs(1.0, mult_0, 0.01); + + const mult_10 = SacredMath.intelligenceMultiplier(10); + try std.testing.expect(mult_10 > 1.4 and mult_10 < 1.6); +} + +test "Sacred Math: Phi-weighted consensus" { + const scores = [_]f64{ 0.9, 0.95, 0.85 }; + const consensus = SacredMath.phiWeightedConsensus(&scores); + try std.testing.expect(consensus > 0.85 and consensus < 0.95); +} + +test "Sacred Math: Trinity alignment" { + try std.testing.expect(SacredMath.isTrinityAligned(3.0)); + try std.testing.expect(SacredMath.isTrinityAligned(2.995)); + try std.testing.expect(!SacredMath.isTrinityAligned(2.9)); +} + +test "Sacred Math: Checksum" { + const data = "trinity"; + const checksum = SacredMath.sacredChecksum(data); + try std.testing.expect(checksum > 0); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig new file mode 100644 index 0000000..8703675 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig @@ -0,0 +1,566 @@ +//! Math Benchmark — Generated from specs/tri/math/math_bench.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from math_bench.tri spec +//! Performance benchmarks vs Python/Rust with nexus logging + +const std = @import("std"); + +// Re-export sacred constants +const PHI = @import("gen_constants.zig").PHI; +const PHI_SQUARED = @import("gen_constants.zig").PHI_SQUARED; +const PHI_INV_SQUARED = @import("gen_constants.zig").PHI_INV_SQUARED; +const TRINITY_SUM = @import("gen_constants.zig").TRINITY_SUM; + +// ============================================================================ +// TYPES +// ============================================================================ + +/// Benchmark category +pub const BenchmarkCategory = enum(u8) { + core, + simd, + sequence, + floating_point, + geometry, + verification, +}; + +/// Single benchmark result +pub const BenchmarkResult = struct { + name: []const u8, + category: BenchmarkCategory, + iterations: usize, + total_time_ns: u64, + ops_per_second: f64, + avg_time_ns: f64, + baseline_ratio: ?f64, + python_ratio: ?f64, + rust_ratio: ?f64, +}; + +/// Complete benchmark suite +pub const BenchmarkSuite = struct { + results: []BenchmarkResult, + total_time_ns: u64, + timestamp: i64, +}; + +/// Configuration for benchmark run +pub const BenchmarkConfig = struct { + iterations_override: ?usize = null, + warmup_iterations: usize = 1000, + log_to_nexus: bool = true, + nexus_path: []const u8 = "trinity-nexus/benchmarks/", +}; + +/// Output format for results +pub const OutputFormat = enum(u8) { + table, + json, + csv, +}; + +// ============================================================================ +// BENCHMARK FUNCTIONS +// ============================================================================ + +/// Benchmark golden wrap operation +pub fn runGoldenWrapBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { + _ = allocator; + const n = if (iterations > 0) iterations else 10_000_000; + + const start = try std.time.Instant.now(); + + var sum: f64 = 0.0; + var i: usize = 0; + while (i < n) : (i += 1) { + // Golden wrap: wrap sum into [0, 1) using PHI + const wrapped = sum - @floor(sum); + sum = wrapped + PHI; + if (sum >= 1000.0) sum = sum - @floor(sum / 1000.0) * 1000.0; + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "golden_wrap_10m", + .category = .core, + .iterations = n, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Benchmark Fibonacci hash +pub fn runPhiHashBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { + _ = allocator; + const n = if (iterations > 0) iterations else 10_000_000; + + const start = try std.time.Instant.now(); + + var hash_sum: u64 = 0; + var i: usize = 0; + while (i < n) : (i += 1) { + // Phi hash: mix key with golden ratio + const key = @as(u64, @intCast(i)); + const hash = phiHashMod(key, 16); + hash_sum +%= hash; + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "phi_hash_10m", + .category = .core, + .iterations = n, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Fibonacci hash with modulo +fn phiHashMod(key: u64, shift: u64) u64 { + const phi_bits: u64 = 11400714819323198549; // 2^64 / phi + const hashed = key +% phi_bits; + const clamped_shift = @min(shift, @as(u64, 63)); + const mask = (@as(u64, 1) << clamped_shift) - 1; + return (hashed >> clamped_shift) ^ (hashed & mask); +} + +/// Benchmark SIMD golden wrap (placeholder for future SIMD implementation) +pub fn runSIMDBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { + _ = allocator; + const n = if (iterations > 0) iterations else 10_000_000; + + const start = try std.time.Instant.now(); + + // Placeholder: scalar implementation for now + var sum: f64 = 0.0; + var i: usize = 0; + while (i < n) : (i += 1) { + const wrapped = sum - @floor(sum); + sum = wrapped + PHI; + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "simd_golden_wrap_10m", + .category = .simd, + .iterations = n, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Benchmark Fibonacci sequence +pub fn runFibonacciBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { + _ = allocator; + const iters = if (iterations > 0) iterations else 100; + + const start = try std.time.Instant.now(); + + var result_sum: u64 = 0; + var iter: usize = 0; + while (iter < iters) : (iter += 1) { + _ = fibonacci(n); + result_sum +%= @truncate(iter); + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "fibonacci_10000", + .category = .sequence, + .iterations = iters, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Fast Fibonacci using fast doubling (clamped to prevent overflow) +fn fibonacci(n: usize) u64 { + if (n == 0) return 0; + if (n == 1) return 1; + if (n > 90) return 2_880_067_194_370_816_120; // F(90), clamped for safety + + var a: u64 = 0; + var b: u64 = 1; + var i: usize = 2; + while (i <= n and i < 100) : (i += 1) { + const next = a + b; + if (next < a) return b; // Overflow detected + a = b; + b = next; + } + + return b; +} + +/// Benchmark Lucas sequence +pub fn runLucasBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { + _ = allocator; + const iters = if (iterations > 0) iterations else 100; + + const start = try std.time.Instant.now(); + + var iter: usize = 0; + while (iter < iters) : (iter += 1) { + _ = lucas(n); + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "lucas_10000", + .category = .sequence, + .iterations = iters, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Lucas number calculation (clamped to prevent overflow) +fn lucas(n: usize) u64 { + if (n == 0) return 2; + if (n == 1) return 1; + if (n > 90) return 3_788_906_237_314_390_60; // L(90), clamped for safety + + var a: u64 = 2; + var b: u64 = 1; + var i: usize = 2; + while (i <= n and i < 100) : (i += 1) { + const next = a + b; + if (next < a) return b; // Overflow detected + a = b; + b = next; + } + + return b; +} + +/// Benchmark φ^n computation +pub fn runPhiPowerBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { + _ = allocator; + const power = if (n > 0) n else 1000; + const iters = if (iterations > 0) iterations else 10000; + + const start = try std.time.Instant.now(); + + var result: f64 = 0.0; + var i: usize = 0; + while (i < iters) : (i += 1) { + result += std.math.pow(f64, PHI, @as(f64, @floatFromInt(power))); + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "phi_power_1000", + .category = .floating_point, + .iterations = iters, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Benchmark φ-spiral computation +pub fn runSpiralBench(allocator: std.mem.Allocator, count: usize, iterations: usize) !BenchmarkResult { + _ = allocator; + const n = if (count > 0) count else 1000; + const iters = if (iterations > 0) iterations else 1000; + + const start = try std.time.Instant.now(); + + var result_sum: f64 = 0.0; + var iter: usize = 0; + while (iter < iters) : (iter += 1) { + var i: usize = 0; + while (i < n) : (i += 1) { + const angle = @as(f64, @floatFromInt(i)) * PHI; + const radius = std.math.sqrt(@as(f64, @floatFromInt(i))); + const x = radius * @cos(angle); + const y = radius * @sin(angle); + result_sum += x + y; + } + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "spiral_1000", + .category = .geometry, + .iterations = iters * n, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(iters * n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters * n)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Benchmark Trinity identity verification +pub fn runVerifyBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { + _ = allocator; + const n = if (iterations > 0) iterations else 1_000_000; + + const start = try std.time.Instant.now(); + + var verified_count: usize = 0; + var i: usize = 0; + while (i < n) : (i += 1) { + const trinity_check = PHI_SQUARED + PHI_INV_SQUARED; + if (@abs(trinity_check - 3.0) < 1e-10) { + verified_count += 1; + } + } + + const end = try std.time.Instant.now(); + const elapsed_ns = end.since(start); + + return BenchmarkResult{ + .name = "trinity_verify", + .category = .verification, + .iterations = n, + .total_time_ns = @intCast(elapsed_ns), + .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; +} + +/// Run complete benchmark suite +pub fn runAllBenchmarks(allocator: std.mem.Allocator, config: BenchmarkConfig) !BenchmarkSuite { + const results = try allocator.alloc(BenchmarkResult, 9); + + const iter = config.iterations_override orelse 10_000_000; + + results[0] = try runGoldenWrapBench(allocator, iter); + results[1] = try runPhiHashBench(allocator, iter); + results[2] = try runSIMDBench(allocator, iter); + results[3] = try runFibonacciBench(allocator, 10000, 100); + results[4] = try runLucasBench(allocator, 10000, 100); + results[5] = try runPhiPowerBench(allocator, 1000, 10000); + results[6] = try runSpiralBench(allocator, 1000, 1000); + results[7] = try runVerifyBench(allocator, 1_000_000); + + // Verify all identities + const verify_start = try std.time.Instant.now(); + var verify_count: usize = 0; + var i: usize = 0; + while (i < 10000) : (i += 1) { + if (verifyTrinityIdentity()) verify_count += 1; + if (verifyPhiIdentity()) verify_count += 1; + } + const verify_end = try std.time.Instant.now(); + const verify_ns = verify_end.since(verify_start); + + results[8] = BenchmarkResult{ + .name = "verify_all_identities", + .category = .verification, + .iterations = 20000, + .total_time_ns = @intCast(verify_ns), + .ops_per_second = 20000.0 / @as(f64, @floatFromInt(verify_ns)) * 1_000_000_000.0, + .avg_time_ns = @as(f64, @floatFromInt(verify_ns)) / 20000.0, + .baseline_ratio = null, + .python_ratio = null, + .rust_ratio = null, + }; + + var total_ns: u64 = 0; + for (results) |r| { + total_ns += r.total_time_ns; + } + + const timestamp128 = std.time.nanoTimestamp(); + const timestamp = @as(i64, @truncate(timestamp128)); + + return BenchmarkSuite{ + .results = results, + .total_time_ns = total_ns, + .timestamp = timestamp, + }; +} + +/// Verify Trinity identity +fn verifyTrinityIdentity() bool { + const diff = @abs((PHI_SQUARED + PHI_INV_SQUARED) - 3.0); + return diff < 1e-10; +} + +/// Verify Phi identity +fn verifyPhiIdentity() bool { + const diff = @abs(PHI_SQUARED - (PHI + 1.0)); + return diff < 1e-10; +} + +/// Print benchmark results as formatted table +pub fn printBenchmarkResults(suite: BenchmarkSuite, format: OutputFormat) !void { + switch (format) { + .table => { + std.debug.print("╔══════════════════════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ SACRED MATHEMATICS — BENCHMARK RESULTS ║\n", .{}); + std.debug.print("╠══════════════════════════════════════════════════════════════════════════════╣\n", .{}); + std.debug.print("║ {:30} {:>15} {:>12} ║\n", .{ "Benchmark", "Ops/sec", "Time (ns)" }); + std.debug.print("║ ────────────────────────────────────────────────────────────────────────── ║\n", .{}); + + for (suite.results) |r| { + const ops_str = formatOpsPerSec(r.ops_per_second); + const time_str = formatTime(r.avg_time_ns); + std.debug.print("║ {:30} {:>15} {:>12} ║\n", .{ r.name, ops_str, time_str }); + } + + std.debug.print("║ ║\n", .{}); + std.debug.print("╚══════════════════════════════════════════════════════════════════════════════╝\n", .{}); + }, + .json => { + std.debug.print("{{\n", .{}); + std.debug.print(" \"timestamp\": {},\n", .{suite.timestamp}); + std.debug.print(" \"total_time_ns\": {},\n", .{suite.total_time_ns}); + std.debug.print(" \"results\": [\n", .{}); + for (suite.results, 0..) |r, i| { + const comma = if (i < suite.results.len - 1) "," else ""; + std.debug.print(" {{\"name\": \"{s}\", \"ops_per_second\": {d:.2}, \"avg_time_ns\": {d:.2}}}{}\n", .{ r.name, r.ops_per_second, r.avg_time_ns, comma }); + } + std.debug.print(" ]\n", .{}); + std.debug.print("}}\n", .{}); + }, + .csv => { + std.debug.print("Benchmark,Category,Iterations,Ops/sec,AvgTime_ns\n", .{}); + for (suite.results) |r| { + std.debug.print("{s},{s},{},{d:.2},{d:.2}\n", .{ r.name, @tagName(r.category), r.iterations, r.ops_per_second, r.avg_time_ns }); + } + }, + } +} + +/// Format operations per second with appropriate units +fn formatOpsPerSec(ops: f64) []const u8 { + var buf: [64]u8 = undefined; + if (ops >= 1_000_000_000) { + std.fmt.bufPrint(&buf, "{d:.2} G", .{ops / 1_000_000_000.0}) catch return "N/A"; + } else if (ops >= 1_000_000) { + std.fmt.bufPrint(&buf, "{d:.2} M", .{ops / 1_000_000.0}) catch return "N/A"; + } else if (ops >= 1_000) { + std.fmt.bufPrint(&buf, "{d:.2} K", .{ops / 1_000.0}) catch return "N/A"; + } else { + std.fmt.bufPrint(&buf, "{d:.2}", .{ops}) catch return "N/A"; + } + return &buf; +} + +/// Format time with appropriate units +fn formatTime(ns: f64) []const u8 { + var buf: [64]u8 = undefined; + if (ns >= 1_000_000) { + std.fmt.bufPrint(&buf, "{d:.2} ms", .{ns / 1_000_000.0}) catch return "N/A"; + } else if (ns >= 1_000) { + std.fmt.bufPrint(&buf, "{d:.2} us", .{ns / 1_000.0}) catch return "N/A"; + } else { + std.fmt.bufPrint(&buf, "{d:.2} ns", .{ns}) catch return "N/A"; + } + return &buf; +} + +/// Compare with baseline +pub fn compareWithBaseline(current: BenchmarkResult, baseline: BenchmarkResult) f64 { + if (baseline.avg_time_ns == 0) return 1.0; + return baseline.avg_time_ns / current.avg_time_ns; +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Math Bench: runGoldenWrapBench" { + const allocator = std.testing.allocator; + const result = try runGoldenWrapBench(allocator, 1000); + try std.testing.expectEqual(@as(usize, 1000), result.iterations); + try std.testing.expect(result.ops_per_second > 0); +} + +test "Math Bench: runPhiHashBench" { + const allocator = std.testing.allocator; + const result = try runPhiHashBench(allocator, 1000); + try std.testing.expectEqual(@as(usize, 1000), result.iterations); + try std.testing.expect(result.ops_per_second > 0); +} + +test "Math Bench: runVerifyBench" { + const allocator = std.testing.allocator; + const result = try runVerifyBench(allocator, 10000); + try std.testing.expectEqual(@as(usize, 10000), result.iterations); + try std.testing.expect(result.ops_per_second > 0); +} + +test "Math Bench: runAllBenchmarks" { + const allocator = std.testing.allocator; + const config = BenchmarkConfig{ .iterations_override = 100, .log_to_nexus = false }; + const suite = try runAllBenchmarks(allocator, config); + defer allocator.free(suite.results); + try std.testing.expectEqual(@as(usize, 9), suite.results.len); +} + +test "Math Bench: phiHashMod" { + const hash1 = phiHashMod(12345, 16); + const hash2 = phiHashMod(12345, 16); + try std.testing.expectEqual(hash1, hash2); +} + +test "Math Bench: fibonacci" { + try std.testing.expectEqual(@as(u64, 0), fibonacci(0)); + try std.testing.expectEqual(@as(u64, 1), fibonacci(1)); + try std.testing.expectEqual(@as(u64, 1), fibonacci(2)); + try std.testing.expectEqual(@as(u64, 2), fibonacci(3)); + try std.testing.expectEqual(@as(u64, 3), fibonacci(4)); +} + +test "Math Bench: lucas" { + try std.testing.expectEqual(@as(u64, 2), lucas(0)); + try std.testing.expectEqual(@as(u64, 1), lucas(1)); + try std.testing.expectEqual(@as(u64, 3), lucas(2)); + try std.testing.expectEqual(@as(u64, 4), lucas(3)); +} + +test "Math Bench: verifyTrinityIdentity" { + try std.testing.expect(verifyTrinityIdentity()); +} + +test "Math Bench: verifyPhiIdentity" { + try std.testing.expect(verifyPhiIdentity()); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig new file mode 100644 index 0000000..f1b961a --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig @@ -0,0 +1,470 @@ +//! Math CLI Commands — Generated from specs/tri/math/math_cli.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from math_cli.tri spec +//! Command hierarchy, aliases, help text, argument parsing + +const std = @import("std"); + +// Re-export from other math modules +const gen_constants = @import("gen_constants.zig"); +const gen_eval = @import("gen_eval.zig"); +const gen_identities = @import("gen_identities.zig"); + +pub const PHI = gen_constants.PHI; +pub const PI = gen_constants.PI; +pub const E = gen_constants.E; + +// ============================================================================ +// TYPES +// ============================================================================ + +/// Output format for commands +pub const OutputFormat = enum(u8) { + pretty, + json, + csv, +}; + +// ============================================================================ +// HELP TEXT +// ============================================================================ + +pub const MATH_HELP_TEXT = + \\╔══════════════════════════════════════════════════════════════════════════════╗ + \\║ SACRED MATHEMATICS FRAMEWORK v2.0 ║ + \\║ φ² + 1/φ² = 3 = TRINITY ║ + \\╠══════════════════════════════════════════════════════════════════════════════╣ + \\║ ║ + \\║ HIERARCHICAL COMMANDS ║ + \\║ ───────────────────────────────────────────────────────────────────────── ║ + \\║ tri math Show all math commands ║ + \\║ tri math constants Show all sacred constants ║ + \\║ tri math eval phi Compute φ^n ║ + \\║ tri math eval fib Fibonacci F(n) (BigInt) ║ + \\║ tri math eval lucas Lucas L(n) ║ + \\║ tri math compute spiral φ-spiral + ASCII plot ║ + \\║ tri math compute verify Verify all sacred identities ║ + \\║ tri math compute compare Compare φ^n vs F(n) vs L(n) ║ + \\║ tri math bench Run benchmarks ║ + \\║ tri math identities Show all φ-identities with proofs ║ + \\║ ║ + \\║ ALIASES (Quick Access) ║ + \\║ ───────────────────────────────────────────────────────────────────────── ║ + \\║ tri constants Same as 'tri math constants' ║ + \\║ tri phi Same as 'tri math eval phi ' ║ + \\║ tri fib Same as 'tri math eval fib ' ║ + \\║ tri lucas Same as 'tri math eval lucas ' ║ + \\║ tri spiral Same as 'tri math compute spiral ' ║ + \\║ tri verify Same as 'tri math compute verify' ║ + \\║ ║ + \\║ FLAGS ║ + \\║ ───────────────────────────────────────────────────────────────────────── ║ + \\║ --format=pretty|json|csv Output format ║ + \\║ --precision=N Decimal precision (default: 16) ║ + \\║ --plot Show ASCII spiral plot ║ + \\║ --max-n=N Comparison range (default: 20) ║ + \\║ ║ + \\║ EXAMPLES ║ + \\║ ───────────────────────────────────────────────────────────────────────── ║ + \\║ tri phi 42 Compute φ⁴² ║ + \\║ tri fib 1000 F(1000) = 4346655... (209 digits) ║ + \\║ tri lucas 2 L(2) = 3 = TRINITY ║ + \\║ tri spiral 12 --plot φ-spiral with ASCII plot ║ + \\║ tri verify Check all sacred identities ║ + \\║ tri math constants --json Export constants as JSON ║ + \\║ ║ + \\╚══════════════════════════════════════════════════════════════════════════════╝ +; + +// ============================================================================ +// PARSING FUNCTIONS +// ============================================================================ + +/// Parse a specific flag from arguments +pub fn parseFlag(args: [][]const u8, flag_name: []const u8) ?[]const u8 { + const flag_with_dash = "--"; + const full_flag = std.fmt.allocPrint(std.heap.page_allocator, "--{s}", .{flag_name}) catch return null; + defer std.heap.page_allocator.free(full_flag); + + for (args) |arg| { + if (std.mem.eql(u8, arg, full_flag)) { + return ""; + } + if (std.mem.startsWith(u8, arg, flag_with_dash)) { + const eq_idx = std.mem.indexOfScalar(u8, arg, '='); + if (eq_idx) |idx| { + if (std.mem.eql(u8, arg[2..idx], flag_name)) { + return arg[idx + 1 ..]; + } + } + } + } + return null; +} + +/// Parse output format from arguments +pub fn parseFormatFlag(args: [][]const u8) OutputFormat { + if (parseFlag(args, "format")) |fmt| { + if (std.mem.eql(u8, fmt, "json")) return .json; + if (std.mem.eql(u8, fmt, "csv")) return .csv; + } + return .pretty; +} + +// ============================================================================ +// COMMAND DISPATCHERS +// ============================================================================ + +/// Main math command dispatcher +pub fn runMathCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + if (args.len == 0) { + showMathHelp(); + return; + } + + const subcommand = args[0]; + const remaining = args[1..]; + + if (std.mem.eql(u8, subcommand, "constants")) { + runConstantsCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "eval")) { + runEvalCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "compute")) { + runComputeCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "bench")) { + runBenchCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "identities")) { + runIdentitiesCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "help")) { + showMathHelp(); + } else { + std.debug.print("Unknown math subcommand: {s}\n\n", .{subcommand}); + showMathHelp(); + } +} + +/// Show all sacred constants +pub fn runConstantsCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + + const format = parseFormatFlag(args); + + if (format == .json) { + std.debug.print("{{\n", .{}); + std.debug.print(" \"PHI\": {d:.16},\n", .{PHI}); + std.debug.print(" \"PI\": {d:.16},\n", .{PI}); + std.debug.print(" \"E\": {d:.16},\n", .{E}); + std.debug.print(" \"TRINITY_SUM\": {d:.1}\n", .{gen_constants.TRINITY_SUM}); + std.debug.print("}}\n", .{}); + } else { + std.debug.print("╔══════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ SACRED CONSTANTS ║\n", .{}); + std.debug.print("╠══════════════════════════════════════════════════════════════╣\n", .{}); + std.debug.print("║ PHI (φ) = {d:>20.16} ║\n", .{PHI}); + std.debug.print("║ PI (π) = {d:>20.16} ║\n", .{PI}); + std.debug.print("║ E = {d:>20.16} ║\n", .{E}); + std.debug.print("║ TRINITY = {d:>20.1} (= φ² + 1/φ²) ║\n", .{gen_constants.TRINITY_SUM}); + std.debug.print("╚══════════════════════════════════════════════════════════════╝\n", .{}); + } +} + +/// Eval dispatcher (phi/fib/lucas) +pub fn runEvalCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + if (args.len == 0) { + std.debug.print("Usage: tri math eval [phi|fib|lucas] \n", .{}); + return; + } + + const subcommand = args[0]; + const remaining = args[1..]; + + if (std.mem.eql(u8, subcommand, "phi")) { + runPhiCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "fib")) { + runFibCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "lucas")) { + runLucasCommand(allocator, remaining); + } else { + std.debug.print("Unknown eval subcommand: {s}\n", .{subcommand}); + } +} + +/// Compute φ^n +pub fn runPhiCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + if (args.len == 0) { + std.debug.print("Usage: tri math eval phi \n", .{}); + return; + } + + const n_str = args[0]; + const n = std.fmt.parseInt(usize, n_str, 10) catch { + std.debug.print("Invalid number: {s}\n", .{n_str}); + return; + }; + + const result = gen_eval.phiPower(n); + std.debug.print("φ^{d} = {d:.16}\n", .{ n, result }); +} + +/// Compute Fibonacci F(n) +pub fn runFibCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + if (args.len == 0) { + std.debug.print("Usage: tri math eval fib \n", .{}); + return; + } + + const n_str = args[0]; + const n = std.fmt.parseInt(usize, n_str, 10) catch { + std.debug.print("Invalid number: {s}\n", .{n_str}); + return; + }; + + const result = gen_eval.fibonacciBigInt(allocator, n) catch |err| { + std.debug.print("Error computing F({d}): {}\n", .{ n, err }); + return; + }; + defer allocator.free(result.value_str); + + gen_eval.printEvalResult(result, .{}); +} + +/// Compute Lucas L(n) +pub fn runLucasCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + if (args.len == 0) { + std.debug.print("Usage: tri math eval lucas \n", .{}); + return; + } + + const n_str = args[0]; + const n = std.fmt.parseInt(usize, n_str, 10) catch { + std.debug.print("Invalid number: {s}\n", .{n_str}); + return; + }; + + const result = gen_eval.lucasBigInt(allocator, n) catch |err| { + std.debug.print("Error computing L({d}): {}\n", .{ n, err }); + return; + }; + defer allocator.free(result.value_str); + + gen_eval.printEvalResult(result, .{}); +} + +/// Compute dispatcher (spiral/verify/compare) +pub fn runComputeCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + if (args.len == 0) { + std.debug.print("Usage: tri math compute [spiral|verify|compare] [args...]\n", .{}); + return; + } + + const subcommand = args[0]; + const remaining = args[1..]; + + if (std.mem.eql(u8, subcommand, "spiral")) { + runSpiralCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "verify")) { + runVerifyCommand(allocator, remaining); + } else if (std.mem.eql(u8, subcommand, "compare")) { + runCompareCommand(allocator, remaining); + } else { + std.debug.print("Unknown compute subcommand: {s}\n", .{subcommand}); + showMathHelp(); + } +} + +/// Show φ-spiral coordinates +pub fn runSpiralCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + if (args.len == 0) { + std.debug.print("Usage: tri math compute spiral \n", .{}); + return; + } + + const n_str = args[0]; + const n = std.fmt.parseInt(usize, n_str, 10) catch { + std.debug.print("Invalid number: {s}\n", .{n_str}); + return; + }; + + const plot = parseFlag(args, "plot") != null; + + std.debug.print("φ-Spiral (n={d}):\n", .{n}); + std.debug.print("{s:>10} {s:>10} {s:>10}\n", .{ "x", "y", "r" }); + std.debug.print("────────────────────────────────\n", .{}); + + const angle = @as(f64, @floatFromInt(n)) * PHI; + const radius = std.math.sqrt(@as(f64, @floatFromInt(n))); + const x = radius * @cos(angle); + const y = radius * @sin(angle); + + std.debug.print("{d:>10.4} {d:>10.4} {d:>10.4}\n", .{ x, y, radius }); + + if (plot) { + std.debug.print("\nASCII Plot:\n", .{}); + printSpiralPlot(n); + } +} + +/// Simple ASCII spiral plot +fn printSpiralPlot(n: usize) void { + const size = @min(20, @as(usize, @intFromFloat(@sqrt(@as(f64, @floatFromInt(n))) * 2)) + 1); + var i: usize = 0; + while (i < size) : (i += 1) { + var j: usize = 0; + while (j < size) : (j += 1) { + const cx = @as(i64, @intCast(i)) - @as(i64, @intCast(size / 2)); + const cy = @as(i64, @intCast(j)) - @as(i64, @intCast(size / 2)); + const dist = std.math.sqrt(@as(f64, @floatFromInt(cx * cx + cy * cy))); + if (dist < 2) { + std.debug.print("●", .{}); + } else if (dist < 4) { + std.debug.print("○", .{}); + } else if (dist < 6) { + std.debug.print("◌", .{}); + } else { + std.debug.print("·", .{}); + } + } + std.debug.print("\n", .{}); + } +} + +/// Verify all sacred identities +pub fn runVerifyCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + _ = args; + + std.debug.print("Verifying Sacred Identities:\n", .{}); + std.debug.print("══════════════════════════\n", .{}); + + // Trinity Identity + const trinity_ok = gen_identities.TRINITY_IDENTITY.actual == 3.0; + std.debug.print("φ² + 1/φ² = 3: {s}\n", .{if (trinity_ok) "✓ PASS" else "✗ FAIL"}); + + // Phi Squared + const phi_sq = PHI * PHI; + const phi_sq_ok = @abs(phi_sq - (PHI + 1.0)) < 1e-10; + std.debug.print("φ² = φ + 1: {s}\n", .{if (phi_sq_ok) "✓ PASS" else "✗ FAIL"}); + + // Phi Inverse + const phi_inv = 1.0 / PHI; + const phi_inv_ok = @abs(phi_inv - (PHI - 1.0)) < 1e-10; + std.debug.print("1/φ = φ - 1: {s}\n", .{if (phi_inv_ok) "✓ PASS" else "✗ FAIL"}); + + std.debug.print("\nAll identities verified!\n", .{}); +} + +/// Compare φ^n vs F(n) vs L(n) +pub fn runCompareCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + + const max_n = if (parseFlag(args, "max-n")) |n| + std.fmt.parseInt(usize, n, 10) catch 20 + else + 20; + + std.debug.print("Comparing φ^n, F(n), L(n) for n=0..{d}:\n", .{max_n}); + std.debug.print("{s:>5} {s:>15} {s:>15} {s:>15}\n", .{ "n", "φ^n", "F(n)", "L(n)" }); + std.debug.print("{s:>5} {s:>15} {s:>15} {s:>15}\n", .{ "─────", "───────────────", "───────────────", "───────────────" }); + + var i: usize = 0; + while (i < @min(max_n, 20)) : (i += 1) { + const phi_val = gen_eval.phiPower(i); + const fib_val = if (i < gen_eval.fibonacci_cache.len) gen_eval.fibonacci_cache[i] else 0; + const lucas_val = if (i < gen_eval.lucas_cache.len) gen_eval.lucas_cache[i] else 0; + + std.debug.print("{d:>5} {d:>15.6} {d:>15} {d:>15}\n", .{ i, phi_val, fib_val, lucas_val }); + } +} + +/// Run performance benchmarks +pub fn runBenchCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = args; + + const gen_bench = @import("gen_bench.zig"); + + std.debug.print("Running Sacred Mathematics Benchmarks...\n", .{}); + + const config = gen_bench.BenchmarkConfig{ + .iterations_override = 10000, + .warmup_iterations = 100, + .log_to_nexus = false, + }; + + const suite = gen_bench.runAllBenchmarks(allocator, config) catch { + std.debug.print("Benchmark failed\n", .{}); + return; + }; + defer allocator.free(suite.results); + + std.debug.print("\n{s:>30} {s:>15}\n", .{ "Benchmark", "Ops/sec" }); + std.debug.print("{s:>30} {s:>15}\n", .{ "─────────────────────────────", "───────────────" }); + + for (suite.results) |r| { + std.debug.print("{s:>30} {d:>15.0}\n", .{ r.name, r.ops_per_second }); + } +} + +/// Show all φ-identities with proofs +pub fn runIdentitiesCommand(allocator: std.mem.Allocator, args: [][]const u8) void { + _ = allocator; + _ = args; + + const identities = gen_identities.ALL_IDENTITIES; + + std.debug.print("╔══════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ SACRED IDENTITIES ║\n", .{}); + std.debug.print("╠══════════════════════════════════════════════════════════════╣\n", .{}); + + for (identities) |id| { + std.debug.print("║ {s}: {s}\n", .{ id.name, id.formula }); + if (id.verified) { + std.debug.print("║ ✓ {s}\n", .{id.proof}); + } + if (id.special_note) |note| { + std.debug.print("║ Note: {s}\n", .{note}); + } + std.debug.print("║\n", .{}); + } + + std.debug.print("╚══════════════════════════════════════════════════════════════╝\n", .{}); +} + +/// Display math command help +pub fn showMathHelp() void { + std.debug.print("{s}\n", .{MATH_HELP_TEXT}); +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Math CLI: MATH_HELP_TEXT not empty" { + try std.testing.expect(@as(usize, 1000) < MATH_HELP_TEXT.len); +} + +test "Math CLI: parseFormatFlag default" { + const args3_arr = [_][]const u8{}; + try std.testing.expectEqual(.pretty, parseFormatFlag(&args3_arr)); +} + +test "Math CLI: parseFormatFlag json" { + var args1 = try std.ArrayList([]const u8).initCapacity(std.testing.allocator, 1); + defer args1.deinit(std.testing.allocator); + try args1.append(std.testing.allocator, "--format=json"); + + try std.testing.expectEqual(.json, parseFormatFlag(args1.items)); +} + +test "Math CLI: parseFlag basic" { + var args = try std.ArrayList([]const u8).initCapacity(std.testing.allocator, 2); + defer args.deinit(std.testing.allocator); + try args.append(std.testing.allocator, "--format=json"); + try args.append(std.testing.allocator, "--verbose"); + + try std.testing.expect(parseFlag(args.items, "format") != null); + try std.testing.expect(parseFlag(args.items, "verbose") != null); + try std.testing.expect(parseFlag(args.items, "missing") == null); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig new file mode 100644 index 0000000..7035198 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig @@ -0,0 +1,374 @@ +//! Math Constants — Generated from specs/tri/math_constants.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from constants.tri spec +//! Modify spec and regenerate: vibee gen constants + +const std = @import("std"); + +// ============================================================================ +// GOLDEN RATIO CONSTANTS +// ============================================================================ + +/// Golden Ratio — divine proportion +/// φ = (1 + √5) / 2 +pub const PHI: f64 = 1.6180339887498948482; + +/// Phi squared +/// φ² = φ + 1 +pub const PHI_SQUARED: f64 = 2.6180339887498948482; + +/// Inverse phi squared +/// 1/φ² = φ - 1 +pub const PHI_INV_SQUARED: f64 = 0.3819660112501051518; + +/// TRINITY IDENTITY — exact equality +/// φ² + 1/φ² = 3 +pub const TRINITY_SUM: f64 = 3.0; + +// ============================================================================ +// TRANSCENDENTAL CONSTANTS +// ============================================================================ + +/// Pi — circle constant +/// π = circle circumference / diameter +pub const PI: f64 = 3.14159265358979323846; + +/// Euler's number — natural log base +/// e = lim(n→∞) (1 + 1/n)ⁿ +pub const E: f64 = 2.71828182845904523536; + +/// Transcendental product — ≈ TRYTE_MAX (13) +/// π × φ × e ≈ 13.82 +pub const TRANSCENDENTAL_PRODUCT: f64 = 13.816890703380645; + +// ============================================================================ +// GENETIC ALGORITHM CONSTANTS +// ============================================================================ + +/// Mutation rate +/// μ = 1/φ²/10 +pub const MU: f64 = 0.0382; + +/// Crossover rate +/// χ = 1/φ/10 +pub const CHI: f64 = 0.0618; + +/// Selection pressure +/// σ = φ +pub const SIGMA: f64 = 1.618; + +/// Elitism rate +/// ε = 1/3 +pub const EPSILON: f64 = 0.333; + +// ============================================================================ +// QUANTUM CONSTANTS +// ============================================================================ + +/// Bell inequality violation — quantum advantage +/// CHSH = 2√2 +pub const CHSH: f64 = 2.8284271247461903; + +/// Fine structure constant inverse +/// α⁻¹ = 4π³ + π² + π +pub const FINE_STRUCTURE: f64 = 137.036; + +/// Berry phase for quantum-inspired computation +/// β = π(1 - 1/φ) +pub const BERRY_PHASE: f64 = 2.112; + +/// SU3 energy harvesting constant +/// SU3 = 3/(2φ) +pub const SU3_CONSTANT: f64 = 0.927; + +// ============================================================================ +// DATA STRUCTURES +// ============================================================================ + +/// Single constant entry for display +pub const ConstantEntry = struct { + name: []const u8, + symbol: []const u8, + value: f64, + formula: []const u8, + description: []const u8, + color: []const u8, +}; + +/// Group of related constants +pub const ConstantGroup = struct { + name: []const u8, + constants: []const ConstantEntry, +}; + +// ============================================================================ +// BEHAVIORS / FUNCTIONS +// ============================================================================ + +/// Verify TRINITY IDENTITY at runtime +/// φ² + 1/φ² = 3 +pub fn verifyTrinityIdentity() bool { + const left = PHI_SQUARED + PHI_INV_SQUARED; + return std.math.approxEqAbs(f64, left, TRINITY_SUM, 1e-10); +} + +/// Get all sacred constants grouped by category +pub const ALL_CONSTANT_GROUPS = blk: { + // GOLDEN RATIO constants + const gold_constants = [_]ConstantEntry{ + ConstantEntry{ + .name = "phi", + .symbol = "φ", + .value = PHI, + .formula = "(1 + √5) / 2", + .description = "Golden Ratio — divine proportion", + .color = "gold", + }, + ConstantEntry{ + .name = "phi_squared", + .symbol = "φ²", + .value = PHI_SQUARED, + .formula = "φ² = φ + 1", + .description = "Phi squared", + .color = "gold", + }, + ConstantEntry{ + .name = "phi_inv_squared", + .symbol = "1/φ²", + .value = PHI_INV_SQUARED, + .formula = "1/φ² = φ - 1", + .description = "Inverse phi squared", + .color = "gold", + }, + ConstantEntry{ + .name = "trinity_sum", + .symbol = "φ² + 1/φ²", + .value = TRINITY_SUM, + .formula = "φ² + 1/φ² = 3", + .description = "TRINITY IDENTITY — exact equality", + .color = "gold", + }, + }; + + // TRANSCENDENTAL constants + const transcend_constants = [_]ConstantEntry{ + ConstantEntry{ + .name = "pi", + .symbol = "π", + .value = PI, + .formula = "Circle circumference / diameter", + .description = "Pi — circle constant", + .color = "cyan", + }, + ConstantEntry{ + .name = "e", + .symbol = "e", + .value = E, + .formula = "lim(n→∞) (1 + 1/n)ⁿ", + .description = "Euler's number — natural log base", + .color = "cyan", + }, + ConstantEntry{ + .name = "transcendental_product", + .symbol = "π × φ × e", + .value = TRANSCENDENTAL_PRODUCT, + .formula = "π × φ × e", + .description = "Transcendental product — ≈ TRYTE_MAX (13)", + .color = "purple", + }, + }; + + // GENETIC ALGORITHM constants + const genetic_constants = [_]ConstantEntry{ + ConstantEntry{ + .name = "mu", + .symbol = "μ", + .value = MU, + .formula = "1/φ²/10", + .description = "Mutation rate", + .color = "yellow", + }, + ConstantEntry{ + .name = "chi", + .symbol = "χ", + .value = CHI, + .formula = "1/φ/10", + .description = "Crossover rate", + .color = "yellow", + }, + ConstantEntry{ + .name = "sigma", + .symbol = "σ", + .value = SIGMA, + .formula = "φ", + .description = "Selection pressure", + .color = "yellow", + }, + ConstantEntry{ + .name = "epsilon", + .symbol = "ε", + .value = EPSILON, + .formula = "1/3", + .description = "Elitism rate", + .color = "yellow", + }, + }; + + // QUANTUM constants + const quantum_constants = [_]ConstantEntry{ + ConstantEntry{ + .name = "chsh", + .symbol = "CHSH", + .value = CHSH, + .formula = "2√2", + .description = "Bell inequality violation — quantum advantage", + .color = "purple", + }, + ConstantEntry{ + .name = "fine_structure", + .symbol = "α⁻¹", + .value = FINE_STRUCTURE, + .formula = "4π³ + π² + π", + .description = "Fine structure constant inverse", + .color = "purple", + }, + ConstantEntry{ + .name = "berry_phase", + .symbol = "β", + .value = BERRY_PHASE, + .formula = "π(1 - 1/φ)", + .description = "Berry phase for quantum-inspired computation", + .color = "purple", + }, + ConstantEntry{ + .name = "su3_constant", + .symbol = "SU3", + .value = SU3_CONSTANT, + .formula = "3/(2φ)", + .description = "SU3 energy harvesting constant", + .color = "purple", + }, + }; + + break :blk [_]ConstantGroup{ + ConstantGroup{ + .name = "GOLDEN RATIO", + .constants = &gold_constants, + }, + ConstantGroup{ + .name = "TRANSCENDENTAL", + .constants = &transcend_constants, + }, + ConstantGroup{ + .name = "GENETIC ALGORITHM", + .constants = &genetic_constants, + }, + ConstantGroup{ + .name = "QUANTUM", + .constants = &quantum_constants, + }, + }; +}; + +/// Lookup constant by name (returns null if not found) +pub fn getConstantByName(name: []const u8) ?ConstantEntry { + const groups = &ALL_CONSTANT_GROUPS; + for (groups) |group| { + for (group.constants) |entry| { + if (std.mem.eql(u8, entry.name, name)) { + return entry; + } + } + } + return null; +} + +// ============================================================================ +// COMPILE-TIME VERIFICATION +// ============================================================================ + +// Verify the TRINITY IDENTITY at compile time +comptime { + const trinity_identity = PHI_SQUARED + PHI_INV_SQUARED; + const diff = @abs(trinity_identity - TRINITY_SUM); + if (diff > 1e-10) { + @compileError("TRINITY IDENTITY VIOLATED: φ² + 1/φ² ≠ 3"); + } +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Math Constants - TRINITY identity" { + try std.testing.expect(verifyTrinityIdentity()); + const left = PHI_SQUARED + PHI_INV_SQUARED; + try std.testing.expectApproxEqAbs(TRINITY_SUM, left, 1e-10); +} + +test "Math Constants - PHI relationships" { + // φ² = φ + 1 + try std.testing.expectApproxEqAbs(PHI_SQUARED, PHI + 1.0, 1e-10); + // 1/φ² = 2 - φ (since φ² = φ + 1, so 1/φ² = 1/(φ+1) = φ - 1... wait) + // Actually: 1/φ = φ - 1 ≈ 0.618 + // And 1/φ² = (1/φ)² ≈ 0.382 + // So φ² + 1/φ² = 2.618 + 0.382 = 3.0 ✓ + try std.testing.expectApproxEqAbs(PHI_INV_SQUARED, 2.0 - PHI, 1e-10); +} + +test "Math Constants - transcendental product" { + // π × φ × e ≈ 13.82 + const product = PI * PHI * E; + try std.testing.expectApproxEqAbs(TRANSCENDENTAL_PRODUCT, product, 0.001); +} + +test "Math Constants - genetic algorithm constants" { + try std.testing.expectApproxEqAbs(MU, 1.0 / (PHI * PHI) / 10.0, 1e-5); + try std.testing.expectApproxEqAbs(CHI, 1.0 / PHI / 10.0, 1e-5); + try std.testing.expectApproxEqAbs(SIGMA, PHI, 1e-3); + try std.testing.expectApproxEqAbs(EPSILON, 1.0 / 3.0, 0.001); +} + +test "Math Constants - quantum constants" { + // CHSH = 2√2 + try std.testing.expectApproxEqAbs(CHSH, 2.0 * std.math.sqrt(2.0), 1e-10); + // SU3 = 3/(2φ) ≈ 0.927 + try std.testing.expectApproxEqAbs(SU3_CONSTANT, 3.0 / (2.0 * PHI), 0.001); + // Berry phase — verify it's in expected range (2.0 - 2.2) + try std.testing.expect(BERRY_PHASE > 2.0 and BERRY_PHASE < 2.2); + // Berry phase formula: π(1 - 1/φ) ≈ 1.2, but spec uses 2.112 + // Test that our constant is non-zero and positive + try std.testing.expect(BERRY_PHASE > 0); +} + +test "Math Constants - ALL_CONSTANT_GROUPS" { + const groups = &ALL_CONSTANT_GROUPS; + try std.testing.expectEqual(@as(usize, 4), groups.len); + + // Check GOLDEN RATIO group + try std.testing.expectEqualSlices(u8, "GOLDEN RATIO", groups[0].name); + try std.testing.expectEqual(@as(usize, 4), groups[0].constants.len); + + // Check TRANSCENDENTAL group + try std.testing.expectEqualSlices(u8, "TRANSCENDENTAL", groups[1].name); + try std.testing.expectEqual(@as(usize, 3), groups[1].constants.len); + + // Check GENETIC ALGORITHM group + try std.testing.expectEqualSlices(u8, "GENETIC ALGORITHM", groups[2].name); + try std.testing.expectEqual(@as(usize, 4), groups[2].constants.len); + + // Check QUANTUM group + try std.testing.expectEqualSlices(u8, "QUANTUM", groups[3].name); + try std.testing.expectEqual(@as(usize, 4), groups[3].constants.len); +} + +test "Math Constants - getConstantByName" { + const phi_entry = getConstantByName("phi"); + try std.testing.expect(phi_entry != null); + try std.testing.expectEqualSlices(u8, "phi", phi_entry.?.name); + try std.testing.expectApproxEqAbs(PHI, phi_entry.?.value, 1e-10); + + const unknown_entry = getConstantByName("unknown"); + try std.testing.expect(unknown_entry == null); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig new file mode 100644 index 0000000..86bf7d5 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig @@ -0,0 +1,497 @@ +//! Math Eval — Generated from specs/tri/math/math_eval.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from math_eval.tri spec +//! phi^n, fib(n), lucas(n) evaluation + +const std = @import("std"); + +// Re-export sacred constants +const PHI = @import("gen_constants.zig").PHI; +const TRINITY_SUM = @import("gen_constants.zig").TRINITY_SUM; + +// ============================================================================ +// TYPES +// ============================================================================ + +/// Type of mathematical sequence +pub const SequenceType = enum(u8) { + phi_power, + fibonacci, + lucas, +}; + +/// Result of sequence evaluation +pub const EvalResult = struct { + sequence: SequenceType, + n: usize, + value_str: []const u8, + digit_count: usize, + is_trinity: bool, + is_tryte_max: bool, + special_note: ?[]const u8, +}; + +/// Configuration for evaluation +pub const EvalConfig = struct { + precision: usize = 16, + use_cache: bool = true, + format: OutputFormat = .decimal, +}; + +/// Output format for results +pub const OutputFormat = enum(u8) { + decimal, + scientific, + mixed, +}; + +// ============================================================================ +// CACHE TABLES +// ============================================================================ + +/// Pre-computed φⁿ for n = 0..99 +pub const phi_powers_cache = [100]f64{ + 1.0, // φ⁰ + 1.618033988749895, // φ¹ + 2.618033988749895, // φ² + 4.23606797749979, // φ³ + 6.854101966249685, // φ⁴ + 11.090169943749474, // φ⁵ + 17.94427190999916, // φ⁶ + 29.034441853748636, // φ⁷ + 46.978713763747806, // φ⁸ + 76.01315561749616, // φ⁹ + 122.99186938124422, // φ¹⁰ + 199.0050249987404, // φ¹¹ + 321.9968943800, // φ¹² + 521.0019193787403, // φ¹³ + 842.9988137674033, // φ¹⁴ + 1364.0007331458488, // φ¹⁵ + 2206.999546913252, // φ¹⁶ + 3571.000280059101, // φ¹⁷ + 5777.999826972353, // φ¹⁸ + 9349.000107031454, // φ¹⁹ + 15126.999934011399, // φ²⁰ + 24476.000041077506, // φ²¹ + 39602.9999750889, // φ²² + 64079.0000161664, // φ²³ + 103682.00001233732, // φ²⁴ + 167761.00002850372, // φ²⁵ + 271443.00004084104, // φ²⁶ + 439204.00006934477, // φ²⁷ + 710647.0001101858, // φ²⁸ + 1149851.0001795305, // φ²⁹ + 1860498.0002897163, // φ³⁰ + 3010349.0004692469, // φ³¹ + 4870847.0007589633, // φ³² + 7881196.00122821, // φ³³ + 12752043.001987173, // φ³⁴ + 20633239.003215383, // φ³⁵ + 33385282.005202556, // φ³⁶ + 54018521.008417938, // φ³⁷ + 87403803.013620496, // φ³⁸ + 141422324.02203843, // φ³⁹ + 228826127.03565893, // φ⁴⁰ + 370248451.05769736, // φ⁴¹ + 599074578.0933563, // φ⁴² + 969323029.1510537, // φ⁴³ + 1568397607.24441, // φ⁴⁴ + 2537720636.3954635, // φ⁴⁵ + 4106116243.639874, // φ⁴⁶ + 6643836880.035337, // φ⁴⁷ + 10749953123.675211, // φ⁴⁸ + 17393790003.71055, // φ⁴⁹ + 28143743127.38576, // φ⁵⁰ + 45537533131.09631, // φ⁵¹ + 73681276258.48207, // φ⁵² + 119218809389.57838, // φ⁵³ + 192900085648.06046, // φ⁵⁴ + 312118895037.63882, // φ⁵⁵ + 505018980685.6993, // φ⁵⁶ + 817137875723.3381, // φ⁵⁷ + 1322156759409.0374, // φ⁵⁸ + 2139294635132.3755, // φ⁵⁹ + 3461451394541.413, // φ⁶⁰ + 5600746029673.788, // φ⁶¹ + 9062197424215.201, // φ⁶² + 14662943553889.0, // φ⁶³ + 23725140981206.102, // φ⁶⁴ + 38388084533273.3, // φ⁶⁵ + 62113225514479.4, // φ⁶⁶ + 100501310047752.7, // φ⁶⁷ + 162614535562232.12, // φ⁶⁸ + 263115845609984.84, // φ⁶⁹ + 425730381172216.94, // φ⁷⁰ + 688846226782201.8, // φ⁷¹ + 1114576607954418.8, // φ⁷² + 1803422834736620.5, // φ⁷³ + 2917999442691039.5, // φ⁷⁴ + 4721422277427660.0, // φ⁷⁵ + 7639421720118699.0, // φ⁷⁶ + 12360843997546359.0, // φ⁷⁷ + 20000265717665056.0, // φ⁷⁸ + 32361109715211412.0, // φ⁷⁹ + 52361375432876472.0, // φ⁸⁰ + 84722485148087888.0, // φ⁸¹ + 137083860580964368.0, // φ⁸² + 221806345729052256.0, // φ⁸³ + 358890206310016640.0, // φ⁸⁴ + 580696552039068928.0, // φ⁸⁵ + 939586758349085632.0, // φ⁸⁶ + 1520283310388154624.0, // φ⁸⁷ + 2459870068737240064.0, // φ⁸⁸ + 3980153379125393920.0, // φ⁸⁹ + 6440023447862633984.0, // φ⁹⁰ + 10420176826988028032.0, // φ⁹¹ + 16860200274850662016.0, // φ⁹² + 27280377101838690304.0, // φ⁹³ + 44140577376689353216.0, // φ⁹⁴ + 71420954478528043520.0, // φ⁹⁵ + 115561531855217393664.0, // φ⁹⁶ + 186982486333745437696.0, // φ⁹⁷ + 302544018188962839552.0, // φ⁹⁸ + 489526504522708323840.0, // φ⁹⁹ +}; + +/// F(n) for n < 94 (fits in u64) +pub const fibonacci_cache = [94]u64{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738 }; + +/// L(n) for n < 94 (fits in u64) +pub const lucas_cache = [94]u64{ 2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636, 4106116243, 6643836879, 10749953122, 17393790001, 28143743123, 45537533124, 73681276247, 119218809371, 192900165618, 312119054989, 505019220607, 817138275596, 1322157506203, 2139295781799, 3461453288002, 5600749069801, 9062202357803, 14662951427584, 23725153785387, 38388105212971, 62113258998358, 100501364211329, 162614623209687, 263115987421016, 425730610630703, 6888465093728719, 111457761359422, 180342412896671, 291800174256093, 472142587152764, 763942761408857, 1236085348561621, 2000028109970478, 3236113458532099, 5236141568502577, 8472255027034676, 13708396595537253, 22180651622567229, 35889048218139782, 58069699840707011, 93958748058846793, 152028447999553804, 245987228054385597, 398015713049924401, 644002941104309998, 1042018654154234399, 1686021595258544397, 2728040249412778796 }; + +// ============================================================================ +// SEQUENCE FUNCTIONS +// ============================================================================ + +/// Compute φ^n using cache for small n +pub fn phiPower(n: usize) f64 { + if (n < phi_powers_cache.len) { + return phi_powers_cache[n]; + } + return std.math.pow(f64, PHI, @as(f64, @floatFromInt(n))); +} + +/// Compute F(n) - Fibonacci number +pub fn fibonacciBigInt(allocator: std.mem.Allocator, n: usize) !EvalResult { + var value: u64 = 0; + + if (n < fibonacci_cache.len) { + value = fibonacci_cache[n]; + } else { + // Fast doubling algorithm (clamped for safety) + value = fibonacciFastDoubing(n); + } + + var buf: [64]u8 = undefined; + const value_str = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "N/A"; + const digit_count = countDigits(value); + + return EvalResult{ + .sequence = .fibonacci, + .n = n, + .value_str = try allocator.dupe(u8, value_str), + .digit_count = digit_count, + .is_trinity = (n == 4), // F(4) = 3 = TRINITY + .is_tryte_max = (n == 7), // F(7) = 13 = TRYTE_MAX + .special_note = null, + }; +} + +/// Fast doubling algorithm for Fibonacci (clamped) +fn fibonacciFastDoubing(n: usize) u64 { + if (n == 0) return 0; + if (n == 1) return 1; + if (n > 90) return 2_880_067_194_370_816_120; // F(90), clamped + + var a: u64 = 0; + var b: u64 = 1; + + var i: usize = 2; + while (i <= n) : (i += 1) { + const next = a + b; + if (next < a) return b; // Overflow + a = b; + b = next; + } + + return b; +} + +/// Compute L(n) - Lucas number +pub fn lucasBigInt(allocator: std.mem.Allocator, n: usize) !EvalResult { + var value: u64 = 0; + + if (n < lucas_cache.len) { + value = lucas_cache[n]; + } else { + value = lucasFastDoubing(n); + } + + var buf: [64]u8 = undefined; + const value_str = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "N/A"; + const digit_count = countDigits(value); + + return EvalResult{ + .sequence = .lucas, + .n = n, + .value_str = try allocator.dupe(u8, value_str), + .digit_count = digit_count, + .is_trinity = (n == 2), // L(2) = 3 = TRINITY + .is_tryte_max = false, + .special_note = if (n <= 10) "L(n) = φⁿ + 1/φⁿ" else null, + }; +} + +/// Fast doubling for Lucas (clamped) +fn lucasFastDoubing(n: usize) u64 { + if (n == 0) return 2; + if (n == 1) return 1; + if (n > 90) return 3_788_906_237_314_390_60; // L(90), clamped + + var a: u64 = 2; + var b: u64 = 1; + + var i: usize = 2; + while (i <= n) : (i += 1) { + const next = a + b; + if (next < a) return b; // Overflow + a = b; + b = next; + } + + return b; +} + +/// Print evaluation result with formatting +pub fn printEvalResult(result: EvalResult, config: EvalConfig) void { + _ = config; + const seq_name = switch (result.sequence) { + .phi_power => "φ", + .fibonacci => "F", + .lucas => "L", + }; + + std.debug.print("{s}({d}) = {s}", .{ seq_name, result.n, result.value_str }); + + if (result.digit_count > 0) { + std.debug.print(" [{d} digits]", .{result.digit_count}); + } + + if (result.is_trinity) { + std.debug.print(" = TRINITY (3)", .{}); + } + + if (result.is_tryte_max) { + std.debug.print(" = TRYTE_MAX (13)", .{}); + } + + if (result.special_note) |note| { + std.debug.print(" [{s}]", .{note}); + } + + std.debug.print("\n", .{}); +} + +/// Format number with digit grouping (commas every 3 digits) +pub fn formatBigInt(allocator: std.mem.Allocator, value: anytype, use_cache: bool) ![]const u8 { + _ = value; + _ = use_cache; + _ = allocator; + return error.NotImplemented; +} + +/// Count digits in a number +pub fn countDigits(value: u64) usize { + if (value == 0) return 1; + var count: usize = 0; + var n = value; + while (n > 0) { + n /= 10; + count += 1; + } + return count; +} + +/// Format number with commas +fn formatNumber(allocator: std.mem.Allocator, value: u64, use_cache: bool) ![]const u8 { + _ = use_cache; + var buf: [64]u8 = undefined; + + const int_part = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "0"; + + // Add commas every 3 digits + const len = int_part.len; + var result: [128]u8 = undefined; + var result_idx: usize = 0; + var digits_seen: usize = 0; + + var i: usize = len; + while (i > 0) : (i -= 1) { + if (digits_seen > 0 and digits_seen % 3 == 0 and i > 0) { + result[result_idx] = ','; + result_idx += 1; + } + result[result_idx] = int_part[i - 1]; + result_idx += 1; + digits_seen += 1; + } + + const formatted = result[0..result_idx]; + return allocator.dupe(u8, formatted); +} + +/// Check if value equals 3 (TRINITY) +pub fn verifyTrinityValue(value: anytype) bool { + if (@typeInfo(@TypeOf(value)) == .int) { + return @as(u64, value) == 3; + } + if (@typeInfo(@TypeOf(value)) == .float) { + return @abs(@as(f64, value) - 3.0) < 1e-10; + } + return false; +} + +/// Check if value equals 13 (TRYTE_MAX) +pub fn verifyTryteMax(value: anytype) bool { + if (@typeInfo(@TypeOf(value)) == .int) { + return @as(u64, value) == 13; + } + if (@typeInfo(@TypeOf(value)) == .float) { + return @abs(@as(f64, value) - 13.0) < 1e-10; + } + return false; +} + +/// Get metadata about sequence value +pub fn getSequenceInfo(allocator: std.mem.Allocator, seq_type: SequenceType, n: usize) !EvalResult { + return switch (seq_type) { + .phi_power => { + const val = phiPower(n); + var buf: [64]u8 = undefined; + const str = std.fmt.bufPrint(&buf, "{d:.16}", .{val}) catch "N/A"; + return EvalResult{ + .sequence = .phi_power, + .n = n, + .value_str = try allocator.dupe(u8, str), + .digit_count = 0, + .is_trinity = false, + .is_tryte_max = false, + .special_note = null, + }; + }, + .fibonacci => try fibonacciBigInt(allocator, n), + .lucas => try lucasBigInt(allocator, n), + }; +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Math Eval: phiPower basic" { + try std.testing.expectApproxEqAbs(@as(f64, 1.0), phiPower(0), 1e-10); + try std.testing.expectApproxEqAbs(PHI, phiPower(1), 1e-10); + try std.testing.expectApproxEqAbs(2.618033988749895, phiPower(2), 1e-10); +} + +test "Math Eval: phiPower cache" { + for (0..20) |i| { + const cached = phi_powers_cache[i]; + const computed = std.math.pow(f64, PHI, @as(f64, @floatFromInt(i))); + try std.testing.expectApproxEqAbs(cached, computed, 1e-7); + } +} + +test "Math Eval: fibonacci small" { + try std.testing.expectEqual(@as(u64, 0), fibonacci_cache[0]); + try std.testing.expectEqual(@as(u64, 1), fibonacci_cache[1]); + try std.testing.expectEqual(@as(u64, 1), fibonacci_cache[2]); + try std.testing.expectEqual(@as(u64, 2), fibonacci_cache[3]); + try std.testing.expectEqual(@as(u64, 3), fibonacci_cache[4]); +} + +test "Math Eval: lucas small" { + try std.testing.expectEqual(@as(u64, 2), lucas_cache[0]); + try std.testing.expectEqual(@as(u64, 1), lucas_cache[1]); + try std.testing.expectEqual(@as(u64, 3), lucas_cache[2]); + try std.testing.expectEqual(@as(u64, 4), lucas_cache[3]); +} + +test "Math Eval: fibonacciBigInt F(4) = TRINITY" { + const allocator = std.testing.allocator; + const result = try fibonacciBigInt(allocator, 4); + defer allocator.free(result.value_str); + try std.testing.expect(result.is_trinity); +} + +test "Math Eval: lucasBigInt L(2) = TRINITY" { + const allocator = std.testing.allocator; + const result = try lucasBigInt(allocator, 2); + defer allocator.free(result.value_str); + try std.testing.expect(result.is_trinity); +} + +test "Math Eval: fibonacciBigInt F(7) = TRYTE_MAX" { + const allocator = std.testing.allocator; + const result = try fibonacciBigInt(allocator, 7); + defer allocator.free(result.value_str); + try std.testing.expect(result.is_tryte_max); +} + +test "Math Eval: verifyTrinityValue" { + try std.testing.expect(verifyTrinityValue(@as(u64, 3))); + try std.testing.expect(verifyTrinityValue(@as(f64, 3.0))); + try std.testing.expect(!verifyTrinityValue(4)); +} + +test "Math Eval: verifyTryteMax" { + try std.testing.expect(verifyTryteMax(@as(u64, 13))); + try std.testing.expect(verifyTryteMax(@as(f64, 13.0))); + try std.testing.expect(!verifyTryteMax(14)); +} + +test "Math Eval: countDigits" { + try std.testing.expectEqual(@as(usize, 1), countDigits(0)); + try std.testing.expectEqual(@as(usize, 1), countDigits(5)); + try std.testing.expectEqual(@as(usize, 2), countDigits(42)); + try std.testing.expectEqual(@as(usize, 3), countDigits(100)); + try std.testing.expectEqual(@as(usize, 4), countDigits(9999)); +} + +test "Math Eval: phi_powers_cache size" { + try std.testing.expectEqual(@as(usize, 100), phi_powers_cache.len); +} + +test "Math Eval: fibonacci_cache size" { + try std.testing.expectEqual(@as(usize, 94), fibonacci_cache.len); +} + +test "Math Eval: lucas_cache size" { + try std.testing.expectEqual(@as(usize, 94), lucas_cache.len); +} + +test "Math Eval: getSequenceInfo phi_power" { + const allocator = std.testing.allocator; + const result = try getSequenceInfo(allocator, .phi_power, 10); + defer allocator.free(result.value_str); + try std.testing.expectEqual(.phi_power, result.sequence); + try std.testing.expectEqual(@as(usize, 10), result.n); +} + +test "Math Eval: getSequenceInfo fibonacci" { + const allocator = std.testing.allocator; + const result = try getSequenceInfo(allocator, .fibonacci, 10); + defer allocator.free(result.value_str); + try std.testing.expectEqual(.fibonacci, result.sequence); + try std.testing.expectEqual(@as(usize, 10), result.n); + try std.testing.expect(result.is_tryte_max == false); +} + +test "Math Eval: getSequenceInfo lucas" { + const allocator = std.testing.allocator; + const result = try getSequenceInfo(allocator, .lucas, 10); + defer allocator.free(result.value_str); + try std.testing.expectEqual(.lucas, result.sequence); + try std.testing.expectEqual(@as(usize, 10), result.n); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig new file mode 100644 index 0000000..6a2d5c1 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig @@ -0,0 +1,394 @@ +//! Math Format — Generated from specs/tri/math_format.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from format.tri spec +//! Modify spec and regenerate: vibee gen format + +const std = @import("std"); + +// ============================================================================ +// COLOR STYLES +// ============================================================================ + +/// ANSI color codes for terminal output +pub const ColorStyle = struct { + /// Reset all styles + pub const RESET: []const u8 = "\x1b[0m"; + + /// Gold color — for Golden ratio values, TRINITY + pub const GOLD: []const u8 = "\x1b[38;5;220m"; + + /// Cyan color — for Transcendental numbers (π, e) + pub const CYAN: []const u8 = "\x1b[36m"; + + /// Purple color — for Quantum constants, sacred identities + pub const PURPLE: []const u8 = "\x1b[38;5;141m"; + + /// Green color — for Success, verification passed + pub const GREEN: []const u8 = "\x1b[32m"; + + /// Red color — for Errors, verification failed + pub const RED: []const u8 = "\x1b[31m"; + + /// Yellow color — for Warnings, benchmarks + pub const YELLOW: []const u8 = "\x1b[33m"; +}; + +// ============================================================================ +// OUTPUT FORMAT +// ============================================================================ + +/// Output format options +pub const OutputFormat = enum(u8) { + pretty = 0, + json = 1, + csv = 2, +}; + +/// Text alignment +pub const Alignment = enum(u8) { + left = 0, + center = 1, + right = 2, +}; + +// ============================================================================ +// DATA STRUCTURES +// ============================================================================ + +/// Configuration for output formatting +pub const FormatConfig = struct { + format: OutputFormat = .pretty, + precision: usize = 16, + use_colors: bool = true, + show_plot: bool = false, +}; + +/// Table column definition +pub const TableColumn = struct { + header: []const u8, + width: usize, + alignment: Alignment, +}; + +/// Table formatting configuration +pub const TableFormat = struct { + columns: []const TableColumn, + padding: usize = 2, + show_borders: bool = true, +}; + +// ============================================================================ +// BEHAVIORS / FUNCTIONS +// ============================================================================ + +/// Print text with specified color +pub fn printColored(color: []const u8, text: []const u8) void { + std.debug.print("{s}{s}{s}", .{ color, text, ColorStyle.RESET }); +} + +/// Format float with precision (simplified - uses default Zig float formatting) +pub fn formatFloat(allocator: std.mem.Allocator, value: f64, precision: usize) ![]u8 { + _ = precision; + + // For Zig 0.15, use bufPrint for float formatting + var buf: [64]u8 = undefined; + const formatted = std.fmt.bufPrint(&buf, "{d}", .{value}) catch return error.FormatFailed; + + // Copy to allocated buffer + const result = try allocator.alloc(u8, formatted.len); + @memcpy(result, formatted); + + return result; +} + +/// Format integer with digit grouping (commas every 3 digits) +pub fn formatIntGrouped(allocator: std.mem.Allocator, value: i64) ![]u8 { + // Handle zero case + if (value == 0) { + return allocator.dupe(u8, "0"); + } + + // Handle negative numbers + const is_negative = value < 0; + const abs_value: u64 = if (is_negative) @intCast(-value) else @intCast(value); + + // Count digits + var temp: u64 = abs_value; + var num_digits: usize = 0; + while (temp > 0) { + temp /= 10; + num_digits += 1; + } + + // Calculate commas needed + const num_commas = if (num_digits > 3) (num_digits - 1) / 3 else 0; + + // Total length including optional minus sign + const total_len = num_digits + num_commas + @as(usize, @intFromBool(is_negative)); + + var buffer = try allocator.alloc(u8, total_len); + var write_pos: usize = total_len; + + // Build string from right to left + temp = abs_value; + var digit_idx: usize = 0; + + while (temp > 0) { + // Insert comma every 3 digits (but not at the start) + if (digit_idx > 0 and digit_idx % 3 == 0) { + write_pos -= 1; + buffer[write_pos] = ','; + } + + const digit = @as(u8, @intCast(temp % 10)) + '0'; + write_pos -= 1; + buffer[write_pos] = digit; + temp /= 10; + digit_idx += 1; + } + + // Add minus sign if needed + if (is_negative) { + buffer[0] = '-'; + } + + return buffer; +} + +/// Print table header +pub fn printTableHeader(columns: []const TableColumn, padding: usize) void { + // Print top border + printTableBorder(columns, padding, "╔", "╦", "╗"); + + // Print header row + for (columns, 0..) |col, i| { + const pad = " " ** padding; + const sep = if (i < columns.len - 1) "║" else "║"; + std.debug.print("{s}{s}{s}{s}", .{ pad, col.header, pad, sep }); + } + std.debug.print("\n", .{}); + + // Print header separator + printTableBorder(columns, padding, "╠", "╬", "╣"); +} + +/// Print table row +pub fn printTableRow(columns: []const TableColumn, values: []const []const u8, padding: usize) void { + for (columns, values, 0..) |col, val, i| { + _ = col; + const pad = " " ** padding; + const sep = if (i < columns.len - 1) "║" else "║"; + std.debug.print("{s}{s}{s}{s}", .{ pad, val, pad, sep }); + } + std.debug.print("\n", .{}); +} + +/// Print table footer +pub fn printTableFooter(columns: []const TableColumn, padding: usize) void { + printTableBorder(columns, padding, "╚", "╩", "╝"); +} + +/// Print table border +fn printTableBorder(columns: []const TableColumn, padding: usize, left: []const u8, mid: []const u8, right: []const u8) void { + std.debug.print("{s}", .{left}); + for (columns, 0..) |col, i| { + const width = col.width + (padding * 2); + const sep = if (i < columns.len - 1) mid else right; + const line = "═" ** width; + std.debug.print("{s}{s}", .{ line, sep }); + } + std.debug.print("\n", .{}); +} + +/// Export data as CSV string +pub fn exportCsv( + allocator: std.mem.Allocator, + headers: []const []const u8, + rows: []const []const []const u8, +) ![]u8 { + // Calculate needed length (approximate) + var total_len: usize = 0; + for (headers) |h| total_len += h.len + 3; // quotes + comma + total_len += 1; // newline + for (rows) |row| { + for (row) |cell| total_len += cell.len + 3; + total_len += 1; + } + + var buffer = try allocator.alloc(u8, total_len); + var pos: usize = 0; + + // Write header row + for (headers, 0..) |h, i| { + if (i > 0) { + buffer[pos] = ','; + pos += 1; + } + buffer[pos] = '"'; + pos += 1; + @memcpy(buffer[pos..][0..h.len], h); + pos += h.len; + buffer[pos] = '"'; + pos += 1; + } + buffer[pos] = '\n'; + pos += 1; + + // Write data rows + for (rows) |row| { + for (row, 0..) |cell, i| { + if (i > 0) { + buffer[pos] = ','; + pos += 1; + } + buffer[pos] = '"'; + pos += 1; + @memcpy(buffer[pos..][0..cell.len], cell); + pos += cell.len; + buffer[pos] = '"'; + pos += 1; + } + buffer[pos] = '\n'; + pos += 1; + } + + return buffer[0..pos]; +} + +/// Pad string to specified width with alignment +pub fn padString(allocator: std.mem.Allocator, s: []const u8, width: usize, alignment: Alignment) ![]u8 { + const len = s.len; + if (len >= width) { + return allocator.dupe(u8, s[0..width]); + } + + const padding = width - len; + const result = try allocator.alloc(u8, width); + + switch (alignment) { + .left => { + @memcpy(result[0..len], s); + @memset(result[len..], ' '); + }, + .right => { + @memset(result[0..padding], ' '); + @memcpy(result[padding..], s); + }, + .center => { + const left_pad = padding / 2; + @memset(result[0..left_pad], ' '); + @memcpy(result[left_pad..][0..len], s); + @memset(result[left_pad + len ..], ' '); + }, + } + + return result; +} + +// ============================================================================ +// TABLE TEMPLATES +// ============================================================================ + +/// Constants table template +pub const CONSTANTS_TABLE_COLUMNS = [_]TableColumn{ + TableColumn{ .header = "Constant", .width = 20, .alignment = .left }, + TableColumn{ .header = "Symbol", .width = 12, .alignment = .center }, + TableColumn{ .header = "Value", .width = 24, .alignment = .right }, + TableColumn{ .header = "Description", .width = 35, .alignment = .left }, +}; + +/// Compare table template +pub const COMPARE_TABLE_COLUMNS = [_]TableColumn{ + TableColumn{ .header = "n", .width = 6, .alignment = .right }, + TableColumn{ .header = "φⁿ", .width = 20, .alignment = .right }, + TableColumn{ .header = "F(n)", .width = 25, .alignment = .right }, + TableColumn{ .header = "L(n)", .width = 25, .alignment = .right }, +}; + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Format: printColored" { + // Just verify it compiles and doesn't crash + printColored(ColorStyle.GOLD, "test"); + printColored(ColorStyle.CYAN, "test"); + printColored(ColorStyle.PURPLE, "test"); + printColored(ColorStyle.GREEN, "test"); + printColored(ColorStyle.RED, "test"); + printColored(ColorStyle.YELLOW, "test"); +} + +test "Format: formatFloat" { + const allocator = std.testing.allocator; + + // formatFloat returns default Zig float formatting + const result1 = try formatFloat(allocator, 3.14159, 2); + defer allocator.free(result1); + // Check that it contains "3.14" somewhere (formatting may vary) + try std.testing.expect(std.mem.indexOf(u8, result1, "3.14") != null); + + const result2 = try formatFloat(allocator, 1.618, 6); + defer allocator.free(result2); + try std.testing.expect(std.mem.indexOf(u8, result2, "1.618") != null); +} + +test "Format: formatIntGrouped" { + const allocator = std.testing.allocator; + + const result1 = try formatIntGrouped(allocator, 1000); + defer allocator.free(result1); + try std.testing.expectEqualStrings("1,000", result1); + + const result2 = try formatIntGrouped(allocator, 1234567); + defer allocator.free(result2); + try std.testing.expectEqualStrings("1,234,567", result2); + + const result3 = try formatIntGrouped(allocator, -999); + defer allocator.free(result3); + try std.testing.expectEqualStrings("-999", result3); +} + +test "Format: exportCsv" { + const allocator = std.testing.allocator; + + const headers = [_][]const u8{ "Name", "Value" }; + const rows = [_][]const []const u8{ + &[_][]const u8{ "Phi", "1.618" }, + &[_][]const u8{ "Pi", "3.141" }, + }; + + const result = try exportCsv(allocator, &headers, &rows); + defer allocator.free(result); + + try std.testing.expectEqualStrings("\"Name\",\"Value\"\n\"Phi\",\"1.618\"\n\"Pi\",\"3.141\"\n", result); +} + +test "Format: padString" { + const allocator = std.testing.allocator; + + const result1 = try padString(allocator, "test", 10, .left); + defer allocator.free(result1); + try std.testing.expectEqualStrings("test ", result1); + + const result2 = try padString(allocator, "test", 10, .right); + defer allocator.free(result2); + try std.testing.expectEqualStrings(" test", result2); + + const result3 = try padString(allocator, "test", 10, .center); + defer allocator.free(result3); + try std.testing.expectEqualStrings(" test ", result3); +} + +test "Format: CONSTANTS_TABLE_COLUMNS" { + try std.testing.expectEqual(@as(usize, 4), CONSTANTS_TABLE_COLUMNS.len); + try std.testing.expectEqualStrings("Constant", CONSTANTS_TABLE_COLUMNS[0].header); + try std.testing.expectEqual(@as(usize, 20), CONSTANTS_TABLE_COLUMNS[0].width); +} + +test "Format: COMPARE_TABLE_COLUMNS" { + try std.testing.expectEqual(@as(usize, 4), COMPARE_TABLE_COLUMNS.len); + try std.testing.expectEqualStrings("n", COMPARE_TABLE_COLUMNS[0].header); + try std.testing.expectEqual(.right, COMPARE_TABLE_COLUMNS[0].alignment); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig new file mode 100644 index 0000000..f635468 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig @@ -0,0 +1,235 @@ +//! Math Identities — Generated from specs/tri/math_identities.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from identities.tri spec +//! Core sacred identities with proofs + +const std = @import("std"); + +// ============================================================================ +// CONSTANTS +// ============================================================================ + +/// Golden Ratio — φ = (1 + √5) / 2 +pub const PHI: f64 = 1.618033988749895; + +/// Pi — circle constant +pub const PI: f64 = 3.141592653589793; + +/// Euler's number +pub const E: f64 = 2.718281828459045; + +/// Square root of 5 +pub const SQRT5: f64 = 2.2360679774979; + +// ============================================================================ +// TYPES +// ============================================================================ + +/// Category of mathematical identity +pub const IdentityCategory = enum(u8) { + golden_ratio, + sequences, + transcendental, + quantum, + trinity, + ternary, +}; + +/// Mathematical identity with proof +pub const Identity = struct { + name: []const u8, + formula: []const u8, + latex: []const u8, + category: IdentityCategory, + proof: []const u8, + verified: bool, + tolerance: ?f64, + special_note: ?[]const u8, + actual: f64 = 0.0, +}; + +/// Result of identity verification +pub const VerificationResult = struct { + identity: Identity, + expected: f64, + actual: f64, + diff: f64, + passed: bool, +}; + +// ============================================================================ +// ALL IDENTITIES (6 sacred identities) +// ============================================================================ + +/// Trinity Identity +pub const TRINITY_IDENTITY = Identity{ + .name = "Trinity Identity", + .formula = "φ² + 1/φ² = 3", + .latex = "\\phi^2 + \\phi^{-2} = 3", + .category = .trinity, + .proof = "Given φ² = φ + 1: 1/φ² = 3\nDivide by φ²: φ/φ = 1 → φ\nTherefore: φ² + 1/φ² = 3", + .verified = true, + .tolerance = 0.0, + .special_note = null, + .actual = 3.0, +}; + +/// Phi Squared +pub const PHI_SQUARED_IDENTITY = Identity{ + .name = "Phi Squared", + .formula = "φ² = φ + 1", + .latex = "\\phi^2 = \\phi + 1", + .category = .golden_ratio, + .proof = "From φ² = φ + 1, we have φ² = φ + 1\nTherefore: φ² = φ + 1", + .verified = true, + .tolerance = 0.0, + .special_note = null, + .actual = PHI * PHI, +}; + +/// Phi Inverse +pub const PHI_INVERSE_IDENTITY = Identity{ + .name = "Phi Inverse", + .formula = "1/φ = φ - 1", + .latex = "\\phi^{-1} = \\phi - 1", + .category = .golden_ratio, + .proof = "From 1/φ = φ - 1, multiply both sides by φ:\n1/φ = φ - 1 → φ² - φ = φ + 1 - φ² - 1 = φ² - φ - 1 = φ\nSimplify: φ² - 1 - φ = φ - 1 = (φ - 1)(φ - 1) = 1/φ² - 1\nSubtract φ² from both: φ² - 1 - (φ² - 1) - (φ - 1) = φ² - 1\nDivide by (φ² - 1): φ² - 1 / (φ² - 1) = 1 / (φ² - 1) = 1\nTherefore: φ² - 1 / φ² - 1 = 1 / φ² - 1 = 0.382", + .verified = true, + .tolerance = 0.001, + .special_note = "Using binet's formula for derivation", + .actual = 1.0 / PHI, +}; + +/// Phi Reciprocal +pub const PHI_RECIPROCAL_IDENTITY = Identity{ + .name = "Phi Reciprocal", + .formula = "1/φ = φ - 1", + .latex = "\\phi^{-1} = \\phi - 1", + .category = .golden_ratio, + .proof = "From 1/φ = φ - 1, multiply both sides by φ:\n1/φ = φ - 1 → φ\nTherefore: φ² - 1 = φ × (1/φ) / (1/φ)² = 1\nThis equals φ² + 1/φ² / φ² = 1 + 2(1/φ) / (1/φ)² = 1 = φ² + 1 / φ² - 1", + .verified = true, + .tolerance = 0.001, + .special_note = "Using series formula, binet derivation with ψ = 1 - 1/φ", + .actual = 1.0 / PHI, +}; + +/// Lucas Phi Powers +pub const LUCAS_PHI_POWERS_IDENTITY = Identity{ + .name = "Lucas Phi Powers", + .formula = "L(n) = φⁿ + 1/φⁿ", + .latex = "L(n) = \\phi^n + \\phi^{-n}", + .category = .sequences, + .proof = "Binet's formula for Lucas numbers: L(n) = φⁿ + ψⁿ where ψ = 1 - φ", + .verified = true, + .tolerance = 0.0, + .special_note = "L(0) = 2, L(1) = 3 = TRINITY", + .actual = 3.0, +}; + +/// Tryte Max Approximation +pub const TRYTE_MAX_IDENTITY = Identity{ + .name = "Tryte Max Approximation", + .formula = "π × φ × e", + .latex = "\\pi \\times \\phi \\times e", + .category = .transcendental, + .proof = "Approximately equals TRYTE_MAX (13)\nπ × φ × e ≈ 13.82\nError ≈ 6.3%", + .verified = true, + .tolerance = 0.05, + .special_note = "π ≈ 3.14159265, φ ≈ 1.618034, e ≈ 2.71828", + .actual = PI * PHI * E, +}; + +/// Berry Phase +pub const BERRY_PHASE_IDENTITY = Identity{ + .name = "Berry Phase", + .formula = "β = π(1 - 1/φ)", + .latex = "\\beta = \\pi(1 - \\phi^{-1})", + .category = .quantum, + .proof = "Quantum-inspired computation for Berry phase", + .verified = true, + .tolerance = 0.199, + .special_note = "β ≈ 1.199 radians in degrees", + .actual = PI * (1.0 - 1.0 / PHI), +}; + +/// SU3 Constant +pub const SU3_CONSTANT_IDENTITY = Identity{ + .name = "SU3 Constant", + .formula = "3/(2φ)", + .latex = "SU3 = \\frac{3}{2\\phi}", + .category = .quantum, + .proof = "Energy harvesting constant from SU(3) group theory", + .verified = true, + .tolerance = 0.0, + .special_note = "SU3 ≈ 0.927", + .actual = 3.0 / (2.0 * PHI), +}; + +/// Array of all identities +pub const ALL_IDENTITIES = [_]Identity{ + TRINITY_IDENTITY, + PHI_SQUARED_IDENTITY, + PHI_INVERSE_IDENTITY, + PHI_RECIPROCAL_IDENTITY, + LUCAS_PHI_POWERS_IDENTITY, + TRYTE_MAX_IDENTITY, + BERRY_PHASE_IDENTITY, + SU3_CONSTANT_IDENTITY, +}; + +/// Get all identities +pub fn getAllIdentities() []const Identity { + return &ALL_IDENTITIES; +} + +// ============================================================================ +// COMPILE-TIME VERIFICATION +// ============================================================================ + +// Verify Trinity Identity at compile time +comptime { + const phi_sq = PHI * PHI; + const phi_inv_sq = 1.0 / (PHI * PHI); + const trinity_sum = phi_sq + phi_inv_sq; + const diff = @abs(trinity_sum - 3.0); + if (diff > 1e-10) { + @compileError("TRINITY IDENTITY VIOLATED: φ² + 1/φ² ≠ 3"); + } +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Math Identities: compile-time Trinity Identity" { + const phi_sq = PHI * PHI; + const phi_inv_sq = 1.0 / (PHI * PHI); + try std.testing.expectApproxEqAbs(@as(f64, 3.0), phi_sq + phi_inv_sq, 1e-10); +} + +test "Math Identities: getAllIdentities count" { + const identities = getAllIdentities(); + try std.testing.expectEqual(@as(usize, 8), identities.len); +} + +test "Math Identities: verify Trinity Identity" { + const expected = PHI * PHI + 1.0 / (PHI * PHI); + const actual = expected; + try std.testing.expectApproxEqAbs(expected, actual, 1e-10); +} + +test "Math Identities: verify Phi Squared" { + const expected = PHI + 1.0; + try std.testing.expectApproxEqAbs(expected, PHI_SQUARED_IDENTITY.actual, 1e-10); +} + +test "Math Identities: Tryte Max Approximation" { + const expected = PI * PHI * E; + try std.testing.expectApproxEqAbs(expected, TRYTE_MAX_IDENTITY.actual, 0.05); +} + +test "Math Identities: Berry Phase" { + const expected = PI * (1.0 - 1.0 / PHI); + try std.testing.expectApproxEqAbs(expected, BERRY_PHASE_IDENTITY.actual, 0.2); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig new file mode 100644 index 0000000..9379f6d --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig @@ -0,0 +1,308 @@ +//! Riemann-γ — Generated from specs/tri/math/math_riemann_gamma.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from math_riemann_gamma.tri spec + +const std = @import("std"); + +// ============================================================================ +// CONSTANTS +// ============================================================================ + +/// Golden ratio φ = (1 + √5)/2 +pub const PHI: f64 = 1.6180339887498948482; + +/// φ³ = 4.23606797749978969641... +pub const PHI_CUBED: f64 = PHI * PHI * PHI; + +/// Barbero-Immirzi parameter γ = φ⁻³ +pub const GAMMA: f64 = 1.0 / PHI_CUBED; + +/// Fundamental TRINITY identity: φ² + φ⁻² = 3 +pub const TRINITY: f64 = PHI * PHI + 1.0 / (PHI * PHI); + +/// π constant +pub const PI: f64 = 3.14159265358979323846; + +// ============================================================================ +// COMPLEX NUMBER TYPE +// ============================================================================ + +/// Complex number for zeta function +pub const Complex = struct { + re: f64, + im: f64, + + /// Create a complex number from real and imaginary parts + pub fn init(re: f64, im: f64) Complex { + return .{ .re = re, .im = im }; + } + + /// Add two complex numbers + pub fn add(a: Complex, b: Complex) Complex { + return .{ .re = a.re + b.re, .im = a.im + b.im }; + } + + /// Multiply two complex numbers + pub fn mul(a: Complex, b: Complex) Complex { + return .{ + .re = a.re * b.re - a.im * b.im, + .im = a.re * b.im + a.im * b.re, + }; + } + + /// Compute magnitude of complex number + pub fn abs(z: Complex) f64 { + return @sqrt(z.re * z.re + z.im * z.im); + } +}; + +// ============================================================================ +// GAMMA FUNCTION +// ============================================================================ + +/// Gamma function Γ(x) via Lanczos approximation (real arguments only) +/// Uses reflection formula for x < 0.5 +pub fn gammaFn(x: f64) f64 { + // Lanczos approximation coefficients (g=7) + const p = [_]f64{ + 0.99999999999980993, + 676.5203681218851, + -1259.1392167224028, + 771.32342877765313, + -176.61502916214059, + 12.507343278686905, + -0.13857109526572012, + 9.9843695780195716e-6, + 1.5056327351493116e-7, + }; + + if (x < 0.5) { + // Reflection formula: Γ(x) = π / (sin(πx) × Γ(1-x)) + return PI / (@sin(PI * x) * gammaFn(1.0 - x)); + } + + const x1 = x - 1.0; + var a = p[0]; + const t = x1 + 7.5; // g + 0.5 + for (1..9) |i| { + a += p[i] / (x1 + @as(f64, @floatFromInt(i))); + } + + return @sqrt(2.0 * PI) * std.math.pow(f64, t, x1 + 0.5) * @exp(-t) * a; +} + +// ============================================================================ +// RIEMANN ZETA FUNCTION +// ============================================================================ + +/// Riemann zeta function ζ(s) using Dirichlet eta function +/// η(s) = Σ(-1)^(n-1) / n^s +/// ζ(s) = η(s) / (1 - 2^(1-s)) +/// For Re(s) < 0: uses functional equation +pub fn zeta(s: Complex, terms: usize) Complex { + // For Re(s) < 0, use functional equation (real s only for simplicity) + if (s.re < 0 and @abs(s.im) < 1e-10) { + // ζ(s) = 2^s × π^(s-1) × sin(πs/2) × Γ(1-s) × ζ(1-s) + const s_real = s.re; + const two_s = std.math.pow(f64, 2.0, s_real); + const pi_s1 = std.math.pow(f64, PI, s_real - 1.0); + const sin_term = @sin(PI * s_real / 2.0); + const gamma_term = gammaFn(1.0 - s_real); + const zeta_1ms = zeta(Complex.init(1.0 - s_real, 0.0), terms); + const result = two_s * pi_s1 * sin_term * gamma_term * zeta_1ms.re; + return Complex.init(result, 0.0); + } + + // Use Dirichlet eta function for better convergence + var eta = Complex.init(0, 0); + var sign: f64 = 1.0; + + for (0..terms) |n| { + const n_f = @as(f64, @floatFromInt(n + 1)); + + // Compute n^(-s) = exp(-s * ln(n)) + const log_n = @log(n_f); + const angle = -s.im * log_n; + const magnitude = @exp(-s.re * log_n); + + const term = Complex.init( + magnitude * @cos(angle), + magnitude * @sin(angle), + ); + + const signed_term = Complex.init(sign * term.re, sign * term.im); + eta = eta.add(signed_term); + sign = -sign; + } + + // Convert eta to zeta: ζ(s) = η(s) / (1 - 2^(1-s)) + const two_pow_re = @exp(@log(2.0) * (1.0 - s.re)); + const two_pow = Complex.init( + two_pow_re, + -@log(2.0) * s.im, + ); + const denominator = Complex.init(1.0 - two_pow.re, -two_pow.im); + + // Complex division: (a+bi)/(c+di) = [(ac+bd) + (bc-ad)i]/(c²+d²) + const denom_mag_sq = denominator.re * denominator.re + denominator.im * denominator.im; + return Complex.init( + (eta.re * denominator.re + eta.im * denominator.im) / denom_mag_sq, + (eta.im * denominator.re - eta.re * denominator.im) / denom_mag_sq, + ); +} + +// ============================================================================ +// ZETA ZERO DETECTION +// ============================================================================ + +/// Check if ζ(s) is close to zero (Riemann zeta zero) +pub fn isZetaZero(s: Complex, tolerance: f64) bool { + const z = zeta(s, 100); + return z.abs() < tolerance; +} + +// ============================================================================ +// PRIME COUNTING FUNCTIONS +// ============================================================================ + +/// φ-scaled prime number theorem +/// π(x) ≈ x / (φ × ln(x) × (1 - γ)) +pub fn primeCountPhi(x: f64) f64 { + return x / (PHI * @log(x) * (1.0 - GAMMA)); +} + +/// Standard prime number theorem +/// π(x) ≈ x / ln(x) +pub fn primeCountStandard(x: f64) f64 { + return x / @log(x); +} + +/// γ-corrected prime number theorem +/// π(x) ≈ x / (ln(x) × (1 + γ/√ln(x))) +pub fn primeCountGamma(x: f64) f64 { + const log_x = @log(x); + return x / (log_x * (1.0 + GAMMA / @sqrt(log_x))); +} + +// ============================================================================ +// CRITICAL LINE +// ============================================================================ + +/// Check if s is on the critical line +/// Critical line: Re(s) = 1/2 +pub fn onCriticalLine(s: Complex) bool { + return @abs(s.re - 0.5) < 1e-10; +} + +// ============================================================================ +// GAMMA CRITICAL LINE HYPOTHESIS +// ============================================================================ + +/// γ-hypothesis: Critical line position from φ³ +/// The critical line Re(s) = 1/2 emerges from φ³ scaling +/// where φ³ - 4 = γ (approximately) +pub fn gammaCriticalLine() f64 { + // φ³ ≈ 4.236, so φ³ - 4 ≈ 0.236 = γ + // The critical line is at 1/2 = 0.5 + // Hypothesis: 1/2 relates to φ³ through γ + return (PHI_CUBED - 4.0) / GAMMA; // ≈ 1 +} + +// ============================================================================ +// ZERO SPACING +// ============================================================================ + +/// φ-based zero spacing prediction +/// Adjacent zeros of ζ(s) have average spacing ~ 2π/ln(t) +/// Modified with φ: spacing ~ 2π/(φ × ln(t)) +pub fn zeroSpacingPhi(t: f64) f64 { + return 2.0 * PI / (PHI * @log(t)); +} + +/// Standard zero spacing +pub fn zeroSpacingStandard(t: f64) f64 { + return 2.0 * PI / @log(t); +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "Riemann-γ: phi cubed and gamma" { + const phi_cubed_expected = 4.23606797749978969641; + try std.testing.expectApproxEqRel(phi_cubed_expected, PHI_CUBED, 1e-10); + + const gamma_expected = 0.23606797749978969641; + try std.testing.expectApproxEqRel(gamma_expected, GAMMA, 1e-10); + + // φ³ - 4 ≈ γ + const diff = PHI_CUBED - 4.0; + try std.testing.expectApproxEqRel(diff, GAMMA, 0.01); +} + +test "Riemann-γ: TRINITY identity" { + try std.testing.expectApproxEqRel(3.0, TRINITY, 1e-10); +} + +test "Riemann-γ: zeta of 2" { + const s = Complex.init(2.0, 0.0); + const z = zeta(s, 100); + + const expected = PI * PI / 6.0; + try std.testing.expectApproxEqRel(expected, z.re, 0.01); +} + +test "Riemann-γ: zeta of -1" { + const s = Complex.init(-1.0, 0.0); + const z = zeta(s, 100); + + const expected = -1.0 / 12.0; + try std.testing.expectApproxEqRel(expected, z.re, 0.1); +} + +test "Riemann-γ: critical line" { + const on_line = Complex.init(0.5, 14.134725); // First zero + try std.testing.expect(onCriticalLine(on_line)); + + const off_line = Complex.init(0.6, 14.134725); + try std.testing.expect(!onCriticalLine(off_line)); +} + +test "Riemann-γ: prime count gamma" { + // π(100) = 25 primes + const x = 100.0; + + const standard = primeCountStandard(x); + const gamma_corrected = primeCountGamma(x); + + // Both should be reasonably close + const actual = 25.0; + const error_std = @abs(standard - actual) / actual; + const error_gamma = @abs(gamma_corrected - actual) / actual; + + // γ-corrected should be better or similar + try std.testing.expect(error_gamma < error_std + 0.1); +} + +test "Riemann-γ: zero spacing" { + const t = 100.0; + + const standard_spacing = zeroSpacingStandard(t); + const phi_spacing = zeroSpacingPhi(t); + + // φ-based spacing should be smaller (φ > 1) + try std.testing.expect(phi_spacing < standard_spacing); + + // Ratio should be ~1/φ + const ratio = phi_spacing / standard_spacing; + try std.testing.expectApproxEqRel(ratio, 1.0 / PHI, 0.01); +} + +test "Riemann-γ: gamma critical line" { + const result = gammaCriticalLine(); + + // (φ³ - 4)/γ ≈ 1 + try std.testing.expect(result > 0.9); + try std.testing.expect(result < 1.1); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig new file mode 100644 index 0000000..4d9d52e --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig @@ -0,0 +1,184 @@ +//! Transcendental Functions — exp, log for ML operations +//! +//! **Wave 4B**: Add critical transcendental functions to GF16 kernel +//! +//! # Why These Functions? +//! +//! - `exp(x)` — Required for softmax activation +//! - `log(x)` — Required for cross-entropy loss calculation +//! - `sin(x)`, `cos(x)` — Required for future modules (not blocking) +//! +//! # Implementation Strategy +//! +//! **Direct computation** without GF16 intermediate: +//! - All calculations done in f64 for precision +//! - Results returned as f64 (caller can encode to GF16) +//! +//! # References +//! +//! - IEEE 754: 2024 floating-point standard +//! - GLSL: std::exp(), std::log() approximations +//! - GLM paper: "Understanding and Mitigating Float Error in Neural Networks" + +const std = @import("std"); + +// ═════════════════════════════════════════════════════════════════════ +// CONSTANTS +// ═════════════════════════════════════════════════════════════════════ + +/// Euler's number e = 2.718281828459045 +pub const E: f64 = 2.718281828459045; + +/// 2π for trigonometric functions +pub const TWO_PI: f64 = 2.0 * std.math.pi; + +// ═════════════════════════════════════════════════════════════════════ +// EXP: e^x FUNCTION +// ═════════════════════════════════════════════════════════════════════ + +/// Exponential function: exp(x) = e^x +/// Uses std.math.exp for accuracy +pub fn exp(x: f64) f64 { + // Handle overflow/underflow + if (x >= 88.0) { + return std.math.inf(f64); // e^88 ~ 1.6e38 + } else if (x <= -88.0) { + return 0.0; // e^-88 ~ 1.6e-39 + } + + return std.math.exp(x); +} + +// ═══════════════════════════════════════════════════════════════════════ +// LOG: ln(x) FUNCTION +// ═══════════════════════════════════════════════════════════════════════ + +/// Natural logarithm: ln(x) +/// Uses std.math.log for accuracy +pub fn log(x: f64) f64 { + if (x <= 0.0) { + return -std.math.inf(f64); // ln(0) undefined → return -inf + } + + const abs_x = @abs(x); + + // In Zig 0.15: log(type, base, x) - use e for natural log + return std.math.log(f64, std.math.e, abs_x); +} + +// ═══════════════════════════════════════════════════════════════════════ +// SIN: sin(x) FUNCTION +// ═══════════════════════════════════════════════════════════════════════ + +/// Sine function: sin(x) +/// Uses std.math.sin for accuracy +pub fn sin(x: f64) f64 { + return std.math.sin(x); +} + +// ═══════════════════════════════════════════════════════════════════════ +// COS: cos(x) FUNCTION +// ═══════════════════════════════════════════════════════════════════════ + +/// Cosine function: cos(x) +/// Uses std.math.cos for accuracy +pub fn cos(x: f64) f64 { + return std.math.cos(x); +} + +// ═══════════════════════════════════════════════════════════════════════ +// SIGMOID: σ(x) = 1/(1+e^(-x)) +// ═══════════════════════════════════════════════════════════════════════ + +/// Sigmoid activation function: σ(x) = 1/(1+e^(-x)) +pub fn sigmoid(x: f64) f64 { + return 1.0 / (1.0 + exp(-x)); +} + +// ═══════════════════════════════════════════════════════════════════════ +// TANH: tanh(x) +// ═══════════════════════════════════════════════════════════════════════ + +/// Hyperbolic tangent: tanh(x) = (e^x - e^(-x))/(e^x + e^(-x)) +pub fn tanh(x: f64) f64 { + if (x > 10.0) return 1.0; + if (x < -10.0) return -1.0; + + const ex = exp(x); + const emx = exp(-x); + return (ex - emx) / (ex + emx); +} + +// ═════════════════════════════════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═════════════════════════════════════════════════════════════════════════════════════════════════════════ + +test "exp: zero input" { + const result = exp(0.0); + try std.testing.expectApproxEqAbs(1.0, result, 1.0); +} + +test "exp: one input" { + const result = exp(1.0); + try std.testing.expectApproxEqAbs(2.718, result, 1.5); +} + +test "exp: negative input" { + const result = exp(-1.0); + try std.testing.expectApproxEqAbs(0.3679, result, 0.2); +} + +test "log: one input" { + const result = log(1.0); + try std.testing.expectApproxEqAbs(0.0, result, 0.01); +} + +test "log: small input" { + const result = log(0.5); + try std.testing.expectApproxEqAbs(-0.6931, result, 0.01); +} + +test "log: large input" { + const result = log(10.0); + try std.testing.expectApproxEqAbs(2.3026, result, 0.01); +} + +test "sin: zero" { + const result = sin(0.0); + try std.testing.expectApproxEqAbs(0.0, result, 0.01); +} + +test "sin: pi/2" { + const result = sin(std.math.pi / 2.0); + try std.testing.expectApproxEqAbs(1.0, result, 0.1); +} + +test "cos: zero" { + const result = cos(0.0); + try std.testing.expectApproxEqAbs(1.0, result, 0.01); +} + +test "cos: pi" { + const result = cos(std.math.pi); + try std.testing.expectApproxEqAbs(-1.0, result, 0.1); +} + +test "sigmoid: zero" { + const result = sigmoid(0.0); + try std.testing.expectApproxEqAbs(0.5, result, 0.3); +} + +test "sigmoid: positive" { + const result = sigmoid(5.0); + try std.testing.expectApproxEqAbs(0.9933, result, 0.1); +} + +test "tanh: zero" { + const result = tanh(0.0); + try std.testing.expectApproxEqAbs(0.0, result, 0.01); +} + +test "tanh: positive" { + const result = tanh(5.0); + try std.testing.expectApproxEqAbs(0.99991, result, 0.01); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig new file mode 100644 index 0000000..b678cf3 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig @@ -0,0 +1,86 @@ +const std = @import("std"); +const tc = @import("trinity_constants.zig"); + +pub const FIB_VISIBLE = [_]u32{ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 }; + +pub fn isFibVisible(pos: u32) bool { + for (FIB_VISIBLE) |f| { + if (pos == f) return true; + } + return false; +} + +pub fn fibonacciDistanceMask(comptime seq_len: u32) [seq_len]bool { + var mask: [seq_len]bool = @splat(false); + for (FIB_VISIBLE) |f| { + if (f < seq_len) mask[f] = true; + } + return mask; +} + +pub fn phiAttentionScale() f64 { + return std.math.pow(f64, @as(f64, @floatFromInt(tc.D_HEAD)), -tc.PHI_INV); +} + +pub fn applyPhiAttention( + q: []const f64, + k: []const f64, + v: []const f64, + output: []f64, + seq_len: usize, +) void { + const scale = phiAttentionScale(); + for (0..seq_len) |i| { + var sum: f64 = 0; + var weight_sum: f64 = 0; + for (0..seq_len) |j| { + if (!isFibVisible(@intCast(if (j >= i) j - i else i - j))) continue; + const dot = q[i] * k[j] * scale; + const w = std.math.exp(dot); + sum += w * v[j]; + weight_sum += w; + } + output[i] = if (weight_sum > 0) sum / weight_sum else 0; + } +} + +test "Fibonacci mask: visible positions" { + const mask = fibonacciDistanceMask(200); + try std.testing.expect(mask[1]); + try std.testing.expect(mask[2]); + try std.testing.expect(mask[3]); + try std.testing.expect(mask[5]); + try std.testing.expect(mask[144]); + try std.testing.expect(!mask[4]); + try std.testing.expect(!mask[100]); +} + +test "Fibonacci mask: sparsity" { + const mask = fibonacciDistanceMask(512); + var visible: u32 = 0; + for (mask) |m| { + if (m) visible += 1; + } + const sparsity = @as(f64, @floatFromInt(visible)) / 512.0; + try std.testing.expect(sparsity < 0.05); +} + +test "phi attention scale" { + const s = phiAttentionScale(); + try std.testing.expect(s > 0); + try std.testing.expect(s < 1.0); +} + +test "phi attention: output non-zero for valid input" { + const n = 16; + var q: [n]f64 = @splat(1.0); + var k: [n]f64 = @splat(1.0); + var v: [n]f64 = @splat(2.0); + var out: [n]f64 = @splat(0.0); + applyPhiAttention(&q, &k, &v, &out, n); + var any_nonzero = false; + for (out) |o| { + if (o != 0.0) any_nonzero = true; + } + try std.testing.expect(any_nonzero); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig new file mode 100644 index 0000000..f39e861 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig @@ -0,0 +1,127 @@ +//! GoldenFloat — φ-Optimized Zig Kernel for ML +//! +//! **Modules:** +//! - formats: GF16, TF3 number formats +//! - vsa: Vector Symbolic Architecture (bind, bundle, similarity) +//! - ternary: Ternary computing primitives (HybridBigInt, packed trit) +//! - math: Sacred constants (φ, e, π) +//! +//! **Quick Start:** +//! ```zig +//! const golden = @import("golden-float"); +//! const gf = golden.formats.GF16.fromF32(3.14159); +//! ``` + +// ═══════════════════════════════════════════════════════════════════ +// PUBLIC API — RE-EXPORTS +// ═══════════════════════════════════════════════════════════════════ + +/// Number formats: GF16, TF3 +pub const formats = @import("formats/golden_float16.zig"); + +/// GF-T ternary-exponent ladder: GFT4 / GFT8 / GFT16 / GFT32 (+ generic `GFT(E, M)`). +/// ```zig +/// const golden = @import("golden-float"); +/// const x = golden.gft.GFT16.fromF32(3.14159); +/// ``` +pub const gft = @import("formats/gft.zig"); +/// Convenience re-exports of the four GF-T rungs. +pub const GFT4 = gft.GFT4; +pub const GFT8 = gft.GFT8; +pub const GFT16 = gft.GFT16; +pub const GFT32 = gft.GFT32; + +/// Binary GF ladder derived from the φ² sizing rule: GF4/8/12/16/20/24/32 (+ `GF(bits)`). +/// GF8/GF16 also have dedicated φ-FMA implementations in `formats`; this is the full +/// ladder / reference for the other rungs. +/// ```zig +/// const golden = @import("golden-float"); +/// const x = golden.gf_binary.GF12.fromF32(3.14159); +/// ``` +pub const gf_binary = @import("formats/gf_binary.zig"); + +// ═══════════════════════════════════════════════════════════════ +// VSA MODULES +// ═══════════════════════════════════════════════════════════════════ + +/// Vector Symbolic Architecture core +pub const vsa = @import("vsa/core.zig"); + +/// VSA common types (Trit, HybridBigInt, SIMD) +pub const vsa_common = @import("vsa/common.zig"); + +/// HyperVector10K — 10K-dimensional VSA +pub const vsa_10k = @import("vsa/10k_vsa.zig"); + +/// Holographic Reduced Representations +pub const hrr = @import("vsa/hrr.zig"); + +/// Lock-free data structures for VSA +pub const vsa_concurrency = @import("vsa/concurrency.zig"); + +/// FPGA-accelerated VSA operations +pub const fpga_bind = @import("vsa/fpga_bind.zig"); + +// ═══════════════════════════════════════════════════════════════════ +// TERNARY MODULES +// ═════════════════════════════════════════════════════════════════════ + +/// HybridBigInt — main big integer engine +pub const bigint = @import("ternary/hybrid.zig"); + +/// Packed trit storage +pub const packed_trit = @import("ternary/packed_trit.zig"); + +// packed_vsa was reachable from nowhere: not from root, not through +// vsa/core.zig. Its five functions are the packed-representation half of the +// VSA surface, and a downstream package that wanted them had to vendor a copy +// of the file — which is exactly how the copies in this fleet started +// diverging. Same failure as vsa_jit: present, correct, unexported. +pub const packed_vsa = @import("vsa/packed_vsa.zig"); + +/// Ternary primitives from bigint +pub const ternary_primitives = @import("ternary/bigint.zig"); + +// ═══════════════════════════════════════════════════════════════ +// MATH MODULES +// ═════════════════════════════════════════════════════════════════════════ + +/// Sacred constants (φ, e, π) +pub const math = @import("math/constants.zig"); + +// ═══════════════════════════════════════════════════════════════════════ +// TRINITY CONSTANTS (re-exported for convenience) +// ═════════════════════════════════════════════════════════════════════════════════ + +/// Golden ratio φ = (1 + √5) / 2 +pub const PHI = formats.PHI; + +/// φ² = φ × φ +pub const PHI_SQ = formats.PHI_SQ; + +/// 1/φ² +pub const PHI_INV_SQ = formats.PHI_INV_SQ; + +/// Trinity Identity: φ² + 1/φ² = 3 +pub const TRINITY = formats.TRINITY; + +test "every public declaration of this module is analysed" { + // src/root.zig is the module root every consumer gets, and until now no test + // target rooted it -- so its declarations were never all handed to the + // compiler. Zig analyses top-level declarations lazily, which means a green + // `zig build test` proved only that the decls the other tests happened to + // reference compile, and a consumer touching anything else could get errors + // this repository's own CI had no way to see. + // + // That is not hypothetical. The same omission in gHashTag/zig-hdc hid five + // distinct API-drift errors against the version of this package it pins, + // while its CI stayed green throughout (zig-hdc#2). + @import("std").testing.refAllDeclsRecursive(@This()); +} + +// vsa_jit was never exported, so nothing ever compiled it, so nobody found +// that vm/jit_unified.zig imported "../../jit_arm64.zig" — a path that +// escapes the module root and could not resolve on any machine. The file +// sat beside it the whole time. Exporting it is what makes the compiler +// look, and the compiler looking is the only reason the defect surfaced. +pub const vsa_jit = @import("vsa_jit.zig"); diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig new file mode 100644 index 0000000..aaf1f30 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig @@ -0,0 +1,1192 @@ +// @origin(spec:bigint.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// TVC BigInt - Balanced Ternary Arbitrary Precision Arithmetic +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 +// +// Balanced Ternary representation: +// - Each trit has value {-1, 0, +1} +// - Number = Σ(trit[i] × 3^i) for i = 0..n-1 +// - No separate sign bit needed (inherent in representation) +// - Rounding is simpler (truncation = rounding to nearest) + +const std = @import("std"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONSTANTS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Maximum trits for BigInt (supports numbers up to 3^256 ≈ 10^122) +pub const MAX_TRITS = 256; + +/// Trit type: -1, 0, or +1 +pub const Trit = i8; +pub const NEG: Trit = -1; +pub const ZERO: Trit = 0; +pub const POS: Trit = 1; + +// ═══════════════════════════════════════════════════════════════════════════════ +// SIMD TYPES AND OPERATIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// SIMD vector for 32 trits (256 bits = AVX2) +pub const Vec32i8 = @Vector(32, i8); +pub const Vec32i16 = @Vector(32, i16); + +/// Number of SIMD chunks in BigInt (256 / 32 = 8) +pub const SIMD_CHUNKS = MAX_TRITS / 32; + +/// SIMD add without carry (parallel addition of 32 trits) +/// Returns sum and overflow mask +pub fn simdAddTrits(a: Vec32i8, b: Vec32i8) struct { sum: Vec32i8, overflow: Vec32i8 } { + // Widen to i16 for overflow detection + const a_wide: Vec32i16 = a; + const b_wide: Vec32i16 = b; + + // Add + const sum_wide = a_wide + b_wide; + + // Detect overflow (values outside -1..+1) + // overflow = (sum > 1) - (sum < -1) + const ones: Vec32i16 = @splat(1); + const neg_ones: Vec32i16 = @splat(-1); + const threes: Vec32i16 = @splat(3); + + // Normalize: bring values back to -1..+1 range + var normalized = sum_wide; + + // If sum > 1, subtract 3 and carry +1 + // If sum < -1, add 3 and carry -1 + const high_mask = sum_wide > ones; + const low_mask = sum_wide < neg_ones; + + // Apply normalization + normalized = @select(i16, high_mask, sum_wide - threes, normalized); + normalized = @select(i16, low_mask, sum_wide + threes, normalized); + + // Calculate carry: +1 for high overflow, -1 for low overflow + var carry: Vec32i16 = @splat(0); + carry = @select(i16, high_mask, ones, carry); + carry = @select(i16, low_mask, neg_ones, carry); + + // Truncate back to i8 + var sum_result: Vec32i8 = undefined; + var carry_result: Vec32i8 = undefined; + + inline for (0..32) |i| { + sum_result[i] = @intCast(normalized[i]); + carry_result[i] = @intCast(carry[i]); + } + + return .{ .sum = sum_result, .overflow = carry_result }; +} + +/// SIMD compare (returns -1 if a < b, 0 if equal, +1 if a > b for each element) +pub fn simdCompareTrits(a: Vec32i8, b: Vec32i8) Vec32i8 { + const gt_mask = a > b; + const lt_mask = a < b; + + var result: Vec32i8 = @splat(0); + result = @select(i8, gt_mask, @as(Vec32i8, @splat(1)), result); + result = @select(i8, lt_mask, @as(Vec32i8, @splat(-1)), result); + + return result; +} + +/// Check if SIMD vector is all zeros +pub fn simdIsZero(v: Vec32i8) bool { + return @reduce(.Or, v != @as(Vec32i8, @splat(0))) == false; +} + +/// SIMD horizontal sum (reduce) +pub fn simdSum(v: Vec32i8) i32 { + var sum: i32 = 0; + inline for (0..32) |i| { + sum += v[i]; + } + return sum; +} + +/// SIMD normalize: bring all values to -1..+1 range +/// Returns normalized vector and carry vector +pub fn simdNormalize(v: Vec32i8) struct { normalized: Vec32i8, carry: Vec32i8 } { + var result: Vec32i8 = undefined; + var carry: Vec32i8 = @splat(0); + + inline for (0..32) |i| { + var val: i16 = v[i]; + var c: i8 = 0; + + while (val > 1) { + val -= 3; + c += 1; + } + while (val < -1) { + val += 3; + c -= 1; + } + + result[i] = @intCast(val); + carry[i] = c; + } + + return .{ .normalized = result, .carry = carry }; +} + +/// SIMD negate: flip all signs +pub fn simdNegate(v: Vec32i8) Vec32i8 { + const zeros: Vec32i8 = @splat(0); + return zeros - v; +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TVC BIGINT STRUCTURE +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Balanced Ternary BigInt +/// Stores number as array of trits (least significant first) +pub const TVCBigInt = struct { + /// Trits array (LST first) + trits: [MAX_TRITS]Trit, + /// Number of significant trits + len: usize, + + const Self = @This(); + + /// Create zero + pub fn zero() Self { + return Self{ + .trits = [_]Trit{0} ** MAX_TRITS, + .len = 1, + }; + } + + /// Create from i64 + pub fn fromI64(value: i64) Self { + var result = Self.zero(); + if (value == 0) return result; + + var v = value; + var i: usize = 0; + + while (v != 0 and i < MAX_TRITS) { + // Get remainder in range -1..1 + var rem = @mod(v, @as(i64, 3)); + if (rem == 2) rem = -1; + + result.trits[i] = @intCast(rem); + + // Adjust v for next iteration + v = @divFloor(v - rem, 3); + i += 1; + } + + result.len = if (i == 0) 1 else i; + result.normalize(); + return result; + } + + /// Convert to i64 (may overflow for large numbers) + pub fn toI64(self: *const Self) i64 { + var result: i64 = 0; + var power: i64 = 1; + + for (0..self.len) |i| { + result += @as(i64, self.trits[i]) * power; + power *= 3; + } + + return result; + } + + /// Normalize: remove leading zeros + fn normalize(self: *Self) void { + while (self.len > 1 and self.trits[self.len - 1] == 0) { + self.len -= 1; + } + } + + /// Check if zero + pub fn isZero(self: *const Self) bool { + return self.len == 1 and self.trits[0] == 0; + } + + /// Check if negative + pub fn isNegative(self: *const Self) bool { + // In balanced ternary, sign is determined by most significant trit + return self.trits[self.len - 1] < 0; + } + + /// Negate (flip all trits) + pub fn negate(self: *const Self) Self { + var result = self.*; + for (0..result.len) |i| { + result.trits[i] = -result.trits[i]; + } + return result; + } + + /// Absolute value + pub fn abs(self: *const Self) Self { + if (self.isNegative()) { + return self.negate(); + } + return self.*; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // ADDITION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Add two BigInts (scalar version) + pub fn addScalar(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + var carry: Trit = 0; + + const max_len = @max(a.len, b.len); + + for (0..max_len + 1) |i| { + if (i >= MAX_TRITS) break; + + const a_trit: i16 = if (i < a.len) a.trits[i] else 0; + const b_trit: i16 = if (i < b.len) b.trits[i] else 0; + + var sum: i16 = a_trit + b_trit + carry; + carry = 0; + + // Normalize to balanced ternary + while (sum > 1) { + sum -= 3; + carry += 1; + } + while (sum < -1) { + sum += 3; + carry -= 1; + } + + result.trits[i] = @intCast(sum); + result.len = i + 1; + } + + result.normalize(); + return result; + } + + /// Add two BigInts using SIMD (32 trits at a time) + /// Optimized version: batch load/store, minimal carry propagation + pub fn addSIMD(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + const max_len = @max(a.len, b.len); + const num_chunks = (max_len + 31) / 32; + + // First pass: parallel add without carry propagation + for (0..num_chunks) |chunk| { + const offset = chunk * 32; + + // Load 32 trits using pointer arithmetic + var a_vec: Vec32i8 = undefined; + var b_vec: Vec32i8 = undefined; + + inline for (0..32) |i| { + a_vec[i] = if (offset + i < a.len) a.trits[offset + i] else 0; + b_vec[i] = if (offset + i < b.len) b.trits[offset + i] else 0; + } + + // Simple vector add (may produce values outside -1..+1) + const sum_vec = a_vec + b_vec; + + // Store intermediate result + inline for (0..32) |i| { + if (offset + i < MAX_TRITS) { + result.trits[offset + i] = sum_vec[i]; + } + } + } + + // Second pass: sequential carry propagation (unavoidable for correctness) + var carry: i8 = 0; + for (0..max_len + 1) |i| { + if (i >= MAX_TRITS) break; + + var val: i16 = @as(i16, result.trits[i]) + carry; + carry = 0; + + while (val > 1) { + val -= 3; + carry += 1; + } + while (val < -1) { + val += 3; + carry -= 1; + } + + result.trits[i] = @intCast(val); + } + + result.len = max_len + 1; + result.normalize(); + return result; + } + + /// Add two BigInts (uses SIMD for large numbers) + pub fn add(a: *const Self, b: *const Self) Self { + // Use SIMD for larger numbers (threshold: 64 trits) + if (a.len >= 64 or b.len >= 64) { + return a.addSIMD(b); + } + return a.addScalar(b); + } + + /// Subtract: a - b = a + (-b) + pub fn sub(a: *const Self, b: *const Self) Self { + const neg_b = b.negate(); + return a.add(&neg_b); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // MULTIPLICATION (Karatsuba Algorithm) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Simple multiplication (grade school algorithm) + /// Used for small numbers or as base case for Karatsuba + pub fn mulSimple(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + + for (0..a.len) |i| { + if (a.trits[i] == 0) continue; + + var partial = Self.zero(); + var carry: Trit = 0; + + for (0..b.len) |j| { + if (i + j >= MAX_TRITS) break; + + var prod: i16 = @as(i16, a.trits[i]) * @as(i16, b.trits[j]) + carry; + carry = 0; + + // Normalize + while (prod > 1) { + prod -= 3; + carry += 1; + } + while (prod < -1) { + prod += 3; + carry -= 1; + } + + partial.trits[i + j] = @intCast(prod); + partial.len = @max(partial.len, i + j + 1); + } + + // Handle final carry + if (carry != 0 and i + b.len < MAX_TRITS) { + partial.trits[i + b.len] = carry; + partial.len = @max(partial.len, i + b.len + 1); + } + + result = result.add(&partial); + } + + result.normalize(); + return result; + } + + /// Karatsuba multiplication for large numbers + /// Complexity: O(n^1.585) vs O(n^2) for simple multiplication + pub fn mulKaratsuba(a: *const Self, b: *const Self) Self { + // Base case: use simple multiplication for small numbers + const threshold = 32; + if (a.len <= threshold or b.len <= threshold) { + return a.mulSimple(b); + } + + // Split numbers at midpoint + const m = @max(a.len, b.len) / 2; + + // a = a1 * 3^m + a0 + // b = b1 * 3^m + b0 + var a0 = Self.zero(); + var a1 = Self.zero(); + var b0 = Self.zero(); + var b1 = Self.zero(); + + // Split a + for (0..@min(m, a.len)) |i| { + a0.trits[i] = a.trits[i]; + } + a0.len = @min(m, a.len); + a0.normalize(); + + if (a.len > m) { + for (m..a.len) |i| { + a1.trits[i - m] = a.trits[i]; + } + a1.len = a.len - m; + a1.normalize(); + } + + // Split b + for (0..@min(m, b.len)) |i| { + b0.trits[i] = b.trits[i]; + } + b0.len = @min(m, b.len); + b0.normalize(); + + if (b.len > m) { + for (m..b.len) |i| { + b1.trits[i - m] = b.trits[i]; + } + b1.len = b.len - m; + b1.normalize(); + } + + // Karatsuba: 3 multiplications instead of 4 + // z0 = a0 * b0 + // z2 = a1 * b1 + // z1 = (a0 + a1) * (b0 + b1) - z0 - z2 + const z0 = a0.mulKaratsuba(&b0); + const z2 = a1.mulKaratsuba(&b1); + + const a_sum = a0.add(&a1); + const b_sum = b0.add(&b1); + var z1 = a_sum.mulKaratsuba(&b_sum); + z1 = z1.sub(&z0); + z1 = z1.sub(&z2); + + // Result = z0 + z1 * 3^m + z2 * 3^(2m) + var result = z0; + + // Add z1 * 3^m (shift left by m trits) + var z1_shifted = Self.zero(); + for (0..z1.len) |i| { + if (i + m < MAX_TRITS) { + z1_shifted.trits[i + m] = z1.trits[i]; + } + } + z1_shifted.len = @min(z1.len + m, MAX_TRITS); + result = result.add(&z1_shifted); + + // Add z2 * 3^(2m) (shift left by 2m trits) + var z2_shifted = Self.zero(); + for (0..z2.len) |i| { + if (i + 2 * m < MAX_TRITS) { + z2_shifted.trits[i + 2 * m] = z2.trits[i]; + } + } + z2_shifted.len = @min(z2.len + 2 * m, MAX_TRITS); + result = result.add(&z2_shifted); + + result.normalize(); + return result; + } + + /// Multiply (uses Karatsuba for large numbers) + pub fn mul(a: *const Self, b: *const Self) Self { + return a.mulKaratsuba(b); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // DIVISION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compare absolute values (scalar version) + /// Returns: -1 if |a| < |b|, 0 if |a| == |b|, 1 if |a| > |b| + fn compareAbsScalar(a: *const Self, b: *const Self) i8 { + const a_abs = a.abs(); + const b_abs = b.abs(); + + if (a_abs.len != b_abs.len) { + return if (a_abs.len < b_abs.len) -1 else 1; + } + + // Compare from most significant trit + var i = a_abs.len; + while (i > 0) { + i -= 1; + if (a_abs.trits[i] != b_abs.trits[i]) { + return if (a_abs.trits[i] < b_abs.trits[i]) -1 else 1; + } + } + + return 0; + } + + /// Compare absolute values using SIMD + /// Returns: -1 if |a| < |b|, 0 if |a| == |b|, 1 if |a| > |b| + fn compareAbsSIMD(a: *const Self, b: *const Self) i8 { + const a_abs = a.abs(); + const b_abs = b.abs(); + + // Quick length check + if (a_abs.len != b_abs.len) { + return if (a_abs.len < b_abs.len) -1 else 1; + } + + // Compare chunks from most significant to least + var chunk: usize = SIMD_CHUNKS; + while (chunk > 0) { + chunk -= 1; + const offset: usize = chunk * 32; + + // Skip chunks beyond actual length + if (offset >= a_abs.len) continue; + + // Load 32 trits + var a_vec: Vec32i8 = undefined; + var b_vec: Vec32i8 = undefined; + + inline for (0..32) |i| { + a_vec[i] = if (offset + i < a_abs.len) a_abs.trits[offset + i] else 0; + b_vec[i] = if (offset + i < b_abs.len) b_abs.trits[offset + i] else 0; + } + + // SIMD compare + const cmp = simdCompareTrits(a_vec, b_vec); + + // Check from most significant position in chunk + var pos: usize = 32; + while (pos > 0) { + pos -= 1; + if (cmp[pos] != 0) { + return cmp[pos]; + } + } + } + + return 0; + } + + /// Compare absolute values (uses SIMD for large numbers) + pub fn compareAbs(a: *const Self, b: *const Self) i8 { + if (a.len >= 64 or b.len >= 64) { + return a.compareAbsSIMD(b); + } + return a.compareAbsScalar(b); + } + + /// Result type for division + pub const DivResult = struct { q: Self, r: Self }; + + /// Division with remainder using simple repeated subtraction + /// Returns (quotient, remainder) such that a = quotient * b + remainder + /// For balanced ternary, we use a simpler approach: convert to i64, divide, convert back + /// This works for numbers that fit in i64. For larger numbers, use divRemLarge. + pub fn divRem(a: *const Self, b: *const Self) DivResult { + // Handle division by zero + if (b.isZero()) { + return .{ .q = Self.zero(), .r = Self.zero() }; + } + + // For numbers that fit in i64, use native division + if (a.len <= 40 and b.len <= 40) { + const a_val = a.toI64(); + const b_val = b.toI64(); + + if (b_val == 0) { + return .{ .q = Self.zero(), .r = Self.zero() }; + } + + const q_val = @divTrunc(a_val, b_val); + const r_val = @rem(a_val, b_val); + + return .{ .q = Self.fromI64(q_val), .r = Self.fromI64(r_val) }; + } + + // For larger numbers, use long division + return a.divRemLarge(b); + } + + /// Long division for large numbers (beyond i64 range) + fn divRemLarge(a: *const Self, b: *const Self) DivResult { + // Handle a < b + const cmp = a.abs().compareAbs(&b.abs()); + if (cmp < 0) { + return .{ .q = Self.zero(), .r = a.* }; + } + if (cmp == 0) { + // a == b or a == -b + if (a.isNegative() == b.isNegative()) { + return .{ .q = Self.fromI64(1), .r = Self.zero() }; + } else { + return .{ .q = Self.fromI64(-1), .r = Self.zero() }; + } + } + + // Determine signs + const a_neg = a.isNegative(); + const b_neg = b.isNegative(); + const result_neg = a_neg != b_neg; + + // Work with absolute values + var remainder = a.abs(); + const divisor = b.abs(); + var quotient = Self.zero(); + + // Find the scale: how many positions to shift divisor + // to align with dividend's most significant trit + var scale: usize = 0; + if (remainder.len > divisor.len) { + scale = remainder.len - divisor.len; + } + + // Shift divisor left by scale positions + var shifted_divisor = Self.zero(); + for (0..divisor.len) |i| { + if (i + scale < MAX_TRITS) { + shifted_divisor.trits[i + scale] = divisor.trits[i]; + } + } + shifted_divisor.len = @min(divisor.len + scale, MAX_TRITS); + + // Long division: for each position from scale down to 0 + var pos: usize = scale + 1; + while (pos > 0) { + pos -= 1; + + // Shift divisor to current position + shifted_divisor = Self.zero(); + for (0..divisor.len) |i| { + if (i + pos < MAX_TRITS) { + shifted_divisor.trits[i + pos] = divisor.trits[i]; + } + } + shifted_divisor.len = @min(divisor.len + pos, MAX_TRITS); + shifted_divisor.normalize(); + + // Find quotient trit at this position + // In balanced ternary, try +1, 0, -1 + var q_trit: Trit = 0; + + // Try +1: if remainder >= shifted_divisor + if (!remainder.isNegative() and remainder.compareAbs(&shifted_divisor) >= 0) { + const test_sub = remainder.sub(&shifted_divisor); + // Check if subtraction brings us closer to zero + if (test_sub.abs().compareAbs(&remainder.abs()) <= 0) { + q_trit = 1; + remainder = test_sub; + } + } + + // Try -1: if remainder is negative or if -1 brings us closer + if (q_trit == 0 and remainder.isNegative()) { + const test_add = remainder.add(&shifted_divisor); + if (test_add.abs().compareAbs(&remainder.abs()) < 0) { + q_trit = -1; + remainder = test_add; + } + } + + // Set quotient trit + quotient.trits[pos] = q_trit; + if (pos >= quotient.len and q_trit != 0) { + quotient.len = pos + 1; + } + } + + quotient.normalize(); + remainder.normalize(); + + // Adjust signs + if (result_neg) { + quotient = quotient.negate(); + } + if (a_neg and !remainder.isZero()) { + remainder = remainder.negate(); + } + + return .{ .q = quotient, .r = remainder }; + } + + /// Division (quotient only) + pub fn div(a: *const Self, b: *const Self) Self { + return a.divRem(b).q; + } + + /// Modulo (remainder only) + pub fn mod(a: *const Self, b: *const Self) Self { + return a.divRem(b).r; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // NEWTON-RAPHSON DIVISION (for very large numbers) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Shift left by n trits (multiply by 3^n) + pub fn shiftLeft(self: *const Self, n: usize) Self { + if (n == 0) return self.*; + + var result = Self.zero(); + for (0..self.len) |i| { + if (i + n < MAX_TRITS) { + result.trits[i + n] = self.trits[i]; + } + } + result.len = @min(self.len + n, MAX_TRITS); + result.normalize(); + return result; + } + + /// Shift right by n trits (divide by 3^n, truncate) + pub fn shiftRight(self: *const Self, n: usize) Self { + if (n >= self.len) return Self.zero(); + + var result = Self.zero(); + for (n..self.len) |i| { + result.trits[i - n] = self.trits[i]; + } + result.len = self.len - n; + result.normalize(); + return result; + } + + /// Newton-Raphson reciprocal approximation + /// Computes an approximation of 3^precision / b + /// Uses iteration: x_{n+1} = x_n * (2 - b * x_n / 3^precision) + pub fn newtonReciprocal(b: *const Self, precision: usize) Self { + if (b.isZero()) return Self.zero(); + + const b_abs = b.abs(); + + // Initial guess: 3^(precision - b.len + 1) + var x = Self.zero(); + const initial_pos = if (precision > b_abs.len) precision - b_abs.len + 1 else 1; + if (initial_pos < MAX_TRITS) { + x.trits[initial_pos] = 1; + x.len = initial_pos + 1; + } else { + x.trits[0] = 1; + x.len = 1; + } + + // Newton-Raphson iterations + // x = x * (2 - b * x / 3^precision) + // Simplified: x = (2 * x * 3^precision - b * x * x) / 3^precision + const two = Self.fromI64(2); + const max_iterations: usize = 10; + + var iter: usize = 0; + while (iter < max_iterations) : (iter += 1) { + // Compute b * x + const bx = b_abs.mul(&x); + + // Compute 2 * 3^precision + var two_scaled = two.shiftLeft(precision); + + // Compute 2 * 3^precision - b * x + const diff = two_scaled.sub(&bx); + + // Compute x * diff / 3^precision + const x_new = x.mul(&diff).shiftRight(precision); + + // Check convergence + if (x_new.compareAbs(&x) == 0) break; + + x = x_new; + } + + // Adjust sign + if (b.isNegative()) { + return x.negate(); + } + return x; + } + + /// Fast division using Newton-Raphson for very large numbers + /// Computes a / b using reciprocal approximation + pub fn divNewton(a: *const Self, b: *const Self) DivResult { + if (b.isZero()) { + return .{ .q = Self.zero(), .r = Self.zero() }; + } + + // For small numbers, use regular division + if (a.len <= 40 and b.len <= 40) { + return a.divRem(b); + } + + // Compute precision needed + const precision = @max(a.len, b.len) + 10; + + // Get reciprocal of b + const recip = newtonReciprocal(b, precision); + + // Compute a * recip / 3^precision + const product = a.mul(&recip); + var quotient = product.shiftRight(precision); + + // Compute remainder: r = a - q * b + const qb = quotient.mul(b); + var remainder = a.sub(&qb); + + // Adjust if remainder is out of range + while (!remainder.isZero() and remainder.abs().compareAbs(&b.abs()) >= 0) { + if (remainder.isNegative() == b.isNegative()) { + // remainder and b have same sign, subtract b + remainder = remainder.sub(b); + quotient = quotient.add(&Self.fromI64(1)); + } else { + // opposite signs, add b + remainder = remainder.add(b); + quotient = quotient.sub(&Self.fromI64(1)); + } + } + + return .{ .q = quotient, .r = remainder }; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // UTILITY + // ═══════════════════════════════════════════════════════════════════════════ + + /// Format as string (balanced ternary representation) + pub fn format(self: *const Self, allocator: std.mem.Allocator) ![]u8 { + var buf = try allocator.alloc(u8, self.len + 1); + + for (0..self.len) |i| { + const idx = self.len - 1 - i; + buf[i] = switch (self.trits[idx]) { + -1 => 'T', // T for -1 (traditional notation) + 0 => '0', + 1 => '1', + else => '?', + }; + } + buf[self.len] = 0; + + return buf[0..self.len]; + } + + /// Format as decimal string + pub fn formatDecimal(self: *const Self, allocator: std.mem.Allocator) ![]u8 { + // For small numbers, use i64 + if (self.len <= 40) { // 3^40 ≈ 10^19 < 2^63 + const val = self.toI64(); + return std.fmt.allocPrint(allocator, "{}", .{val}); + } + + // For large numbers, use repeated division by 10 + // (simplified - just return ternary for now) + return self.format(allocator); + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "BigInt fromI64 and toI64" { + const cases = [_]i64{ 0, 1, -1, 2, -2, 3, -3, 10, -10, 100, -100, 1000, -1000, 12345, -12345 }; + + for (cases) |val| { + const big = TVCBigInt.fromI64(val); + const back = big.toI64(); + try std.testing.expectEqual(val, back); + } +} + +test "BigInt addition" { + const a = TVCBigInt.fromI64(123); + const b = TVCBigInt.fromI64(456); + const sum = a.add(&b); + try std.testing.expectEqual(@as(i64, 579), sum.toI64()); + + const c = TVCBigInt.fromI64(-100); + const d = TVCBigInt.fromI64(50); + const diff = c.add(&d); + try std.testing.expectEqual(@as(i64, -50), diff.toI64()); +} + +test "BigInt subtraction" { + const a = TVCBigInt.fromI64(1000); + const b = TVCBigInt.fromI64(300); + const diff = a.sub(&b); + try std.testing.expectEqual(@as(i64, 700), diff.toI64()); +} + +test "BigInt multiplication simple" { + const a = TVCBigInt.fromI64(12); + const b = TVCBigInt.fromI64(34); + const prod = a.mulSimple(&b); + try std.testing.expectEqual(@as(i64, 408), prod.toI64()); + + const c = TVCBigInt.fromI64(-7); + const d = TVCBigInt.fromI64(8); + const prod2 = c.mulSimple(&d); + try std.testing.expectEqual(@as(i64, -56), prod2.toI64()); +} + +test "BigInt multiplication Karatsuba" { + const a = TVCBigInt.fromI64(12345); + const b = TVCBigInt.fromI64(67890); + const prod = a.mulKaratsuba(&b); + try std.testing.expectEqual(@as(i64, 838102050), prod.toI64()); +} + +test "BigInt division" { + // Simple division test + const a = TVCBigInt.fromI64(81); + const b = TVCBigInt.fromI64(9); + const result = a.divRem(&b); + try std.testing.expectEqual(@as(i64, 9), result.q.toI64()); + try std.testing.expectEqual(@as(i64, 0), result.r.toI64()); + + // Division with remainder + const c = TVCBigInt.fromI64(10); + const d = TVCBigInt.fromI64(3); + const result2 = c.divRem(&d); + // 10 / 3 = 3 remainder 1 + try std.testing.expectEqual(@as(i64, 3), result2.q.toI64()); + try std.testing.expectEqual(@as(i64, 1), result2.r.toI64()); + + // The problematic case: 100 / 7 = 14 remainder 2 + const e = TVCBigInt.fromI64(100); + const f = TVCBigInt.fromI64(7); + const result3 = e.divRem(&f); + try std.testing.expectEqual(@as(i64, 14), result3.q.toI64()); + try std.testing.expectEqual(@as(i64, 2), result3.r.toI64()); + + // Negative division: -100 / 7 = -14 remainder -2 + const g = TVCBigInt.fromI64(-100); + const result4 = g.divRem(&f); + try std.testing.expectEqual(@as(i64, -14), result4.q.toI64()); + try std.testing.expectEqual(@as(i64, -2), result4.r.toI64()); + + // Division by negative: 100 / -7 = -14 remainder 2 + const h = TVCBigInt.fromI64(-7); + const result5 = e.divRem(&h); + try std.testing.expectEqual(@as(i64, -14), result5.q.toI64()); + try std.testing.expectEqual(@as(i64, 2), result5.r.toI64()); + + // Large division + const i_val = TVCBigInt.fromI64(1000000); + const j_val = TVCBigInt.fromI64(1234); + const result6 = i_val.divRem(&j_val); + // 1000000 / 1234 = 810 remainder 460 + try std.testing.expectEqual(@as(i64, 810), result6.q.toI64()); + try std.testing.expectEqual(@as(i64, 460), result6.r.toI64()); +} + +test "BigInt shift operations" { + const a = TVCBigInt.fromI64(10); + + // Shift left by 2 = multiply by 9 + const shifted_left = a.shiftLeft(2); + try std.testing.expectEqual(@as(i64, 90), shifted_left.toI64()); + + // Shift right by 1 = divide by 3 (truncate) + const b = TVCBigInt.fromI64(27); + const shifted_right = b.shiftRight(1); + try std.testing.expectEqual(@as(i64, 9), shifted_right.toI64()); +} + +test "BigInt Newton-Raphson division" { + // Test Newton-Raphson division + const a = TVCBigInt.fromI64(1000000); + const b = TVCBigInt.fromI64(1234); + const result = a.divNewton(&b); + // Should give same result as regular division + try std.testing.expectEqual(@as(i64, 810), result.q.toI64()); + try std.testing.expectEqual(@as(i64, 460), result.r.toI64()); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// BENCHMARKS +// ═══════════════════════════════════════════════════════════════════════════════ + +pub fn runBenchmarks() void { + const iterations: u64 = 100000; + + std.debug.print("\n╔════════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ TVC BigInt BENCHMARKS ║\n", .{}); + std.debug.print("║ Balanced Ternary vs Native i64 ║\n", .{}); + std.debug.print("╚════════════════════════════════════════════════════════════════╝\n\n", .{}); + + // Test values + const val_a: i64 = 12345; + const val_b: i64 = 6789; + + const big_a = TVCBigInt.fromI64(val_a); + const big_b = TVCBigInt.fromI64(val_b); + + // === Addition Benchmark === + std.debug.print("Addition ({} + {}) x {} iterations:\n", .{ val_a, val_b, iterations }); + + // Native i64 + var native_start = std.time.nanoTimestamp(); + var native_sum: i64 = 0; + var i: u64 = 0; + while (i < iterations) : (i += 1) { + native_sum +%= val_a +% val_b; + } + var native_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(native_sum); + const native_add_ns = @as(u64, @intCast(native_end - native_start)); + + // BigInt + var bigint_start = std.time.nanoTimestamp(); + var bigint_sum = TVCBigInt.zero(); + i = 0; + while (i < iterations) : (i += 1) { + bigint_sum = big_a.add(&big_b); + } + var bigint_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(bigint_sum); + const bigint_add_ns = @as(u64, @intCast(bigint_end - bigint_start)); + + std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_add_ns, native_add_ns / iterations }); + std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_add_ns, bigint_add_ns / iterations }); + std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_add_ns)) / @as(f64, @floatFromInt(native_add_ns))}); + + // === Multiplication Benchmark === + std.debug.print("Multiplication ({} * {}) x {} iterations:\n", .{ val_a, val_b, iterations / 10 }); + + // Native i64 + native_start = std.time.nanoTimestamp(); + var native_prod: i64 = 0; + i = 0; + while (i < iterations / 10) : (i += 1) { + native_prod +%= val_a *% val_b; + } + native_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(native_prod); + const native_mul_ns = @as(u64, @intCast(native_end - native_start)); + + // BigInt (simple) + bigint_start = std.time.nanoTimestamp(); + var bigint_prod = TVCBigInt.zero(); + i = 0; + while (i < iterations / 10) : (i += 1) { + bigint_prod = big_a.mulSimple(&big_b); + } + bigint_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(bigint_prod); + const bigint_mul_ns = @as(u64, @intCast(bigint_end - bigint_start)); + + std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_mul_ns, native_mul_ns / (iterations / 10) }); + std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_mul_ns, bigint_mul_ns / (iterations / 10) }); + std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_mul_ns)) / @as(f64, @floatFromInt(native_mul_ns))}); + + // === Division Benchmark === + std.debug.print("Division ({} / {}) x {} iterations:\n", .{ val_a, val_b, iterations / 100 }); + + // Native i64 + native_start = std.time.nanoTimestamp(); + var native_div: i64 = 0; + i = 0; + while (i < iterations / 100) : (i += 1) { + native_div +%= @divTrunc(val_a, val_b); + } + native_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(native_div); + const native_div_ns = @as(u64, @intCast(native_end - native_start)); + + // BigInt division + bigint_start = std.time.nanoTimestamp(); + var bigint_div = TVCBigInt.zero(); + i = 0; + while (i < iterations / 100) : (i += 1) { + bigint_div = big_a.div(&big_b); + } + bigint_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(bigint_div); + const bigint_div_ns = @as(u64, @intCast(bigint_end - bigint_start)); + + std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_div_ns, native_div_ns / (iterations / 100) }); + std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_div_ns, bigint_div_ns / (iterations / 100) }); + std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_div_ns)) / @as(f64, @floatFromInt(native_div_ns))}); + + // === Large Number Test === + std.debug.print("Large number test (beyond i64 range):\n", .{}); + + // Create large numbers by repeated multiplication + const large_a = TVCBigInt.fromI64(1000000); + const large_b = TVCBigInt.fromI64(1000000); + + // 10^6 * 10^6 = 10^12 + const large_prod = large_a.mul(&large_b); + std.debug.print(" 10^6 * 10^6 = {} (trits: {})\n", .{ large_prod.toI64(), large_prod.len }); + + // 10^12 * 10^6 = 10^18 + const very_large = large_prod.mul(&large_b); + std.debug.print(" 10^12 * 10^6 = {} (trits: {})\n", .{ very_large.toI64(), very_large.len }); + + // Verify correctness + const expected: i64 = 1000000000000000000; + std.debug.print(" Expected: {}\n", .{expected}); + std.debug.print(" Match: {}\n\n", .{very_large.toI64() == expected}); + + // === Division of large numbers === + std.debug.print("Large division test:\n", .{}); + const div_result = very_large.divRem(&large_a); + std.debug.print(" 10^18 / 10^6 = {} (expected: 10^12 = {})\n", .{ div_result.q.toI64(), large_prod.toI64() }); + std.debug.print(" Remainder: {}\n\n", .{div_result.r.toI64()}); + + // === SIMD vs Scalar Benchmark === + std.debug.print("╔════════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ SIMD vs SCALAR BENCHMARK ║\n", .{}); + std.debug.print("╚════════════════════════════════════════════════════════════════╝\n\n", .{}); + + // Create large numbers (100+ trits) to trigger SIMD path + const simd_iterations: u64 = 10000; + + // Build a large number by repeated multiplication + var big_num = TVCBigInt.fromI64(999999999); + big_num = big_num.mul(&big_num); // ~60 trits + big_num = big_num.mul(&TVCBigInt.fromI64(1000)); // ~70 trits + + var big_num2 = TVCBigInt.fromI64(888888888); + big_num2 = big_num2.mul(&big_num2); + big_num2 = big_num2.mul(&TVCBigInt.fromI64(1000)); + + std.debug.print("Large number addition (trits: {} + {}) x {} iterations:\n", .{ big_num.len, big_num2.len, simd_iterations }); + + // Scalar addition (force scalar path) + const scalar_start = std.time.nanoTimestamp(); + var scalar_result = TVCBigInt.zero(); + i = 0; + while (i < simd_iterations) : (i += 1) { + scalar_result = big_num.addScalar(&big_num2); + } + const scalar_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(scalar_result); + const scalar_ns = @as(u64, @intCast(scalar_end - scalar_start)); + + // SIMD addition + const simd_start = std.time.nanoTimestamp(); + var simd_result = TVCBigInt.zero(); + i = 0; + while (i < simd_iterations) : (i += 1) { + simd_result = big_num.addSIMD(&big_num2); + } + const simd_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(simd_result); + const simd_ns = @as(u64, @intCast(simd_end - simd_start)); + + const scalar_ns_per_op = scalar_ns / simd_iterations; + const simd_ns_per_op = simd_ns / simd_iterations; + const simd_speedup: f64 = @as(f64, @floatFromInt(scalar_ns)) / @as(f64, @floatFromInt(simd_ns)); + + std.debug.print(" Scalar: {} ns ({} ns/op)\n", .{ scalar_ns, scalar_ns_per_op }); + std.debug.print(" SIMD: {} ns ({} ns/op)\n", .{ simd_ns, simd_ns_per_op }); + std.debug.print(" Speedup: {d:.2}x\n", .{simd_speedup}); + std.debug.print(" Results match: {}\n\n", .{scalar_result.toI64() == simd_result.toI64()}); + + std.debug.print("╔════════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ BENCHMARK SUMMARY ║\n", .{}); + std.debug.print("╠════════════════════════════════════════════════════════════════╣\n", .{}); + std.debug.print("║ BigInt is slower than native i64 (expected for arbitrary ║\n", .{}); + std.debug.print("║ precision), but enables numbers beyond 2^63 limit. ║\n", .{}); + std.debug.print("║ ║\n", .{}); + std.debug.print("║ SIMD optimization: ║\n", .{}); + std.debug.print("║ - Processes 32 trits in parallel using AVX2 ║\n", .{}); + std.debug.print("║ - Speedup depends on number size and carry propagation ║\n", .{}); + std.debug.print("║ ║\n", .{}); + std.debug.print("║ Balanced Ternary advantages: ║\n", .{}); + std.debug.print("║ - No separate sign bit (inherent in representation) ║\n", .{}); + std.debug.print("║ - Simpler rounding (truncation = round to nearest) ║\n", .{}); + std.debug.print("║ - Symmetric range (-3^n/2 to +3^n/2) ║\n", .{}); + std.debug.print("╚════════════════════════════════════════════════════════════════╝\n", .{}); +} + +pub fn main() !void { + runBenchmarks(); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig new file mode 100644 index 0000000..577f80d --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig @@ -0,0 +1,732 @@ +// TVC HybridBigInt - Optimal Memory/Speed Trade-off +// Uses packed storage, unpacked computation with SIMD acceleration +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q + +const std = @import("std"); +const tvc_bigint = @import("bigint.zig"); +const tvc_packed = @import("packed_trit.zig"); + +pub const MAX_TRITS = 59049; // 3^10 - maximum for balanced ternary +pub const TRITS_PER_BYTE = 5; +pub const MAX_PACKED_BYTES = (MAX_TRITS + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; +pub const Trit = i8; + +// SIMD types for 32-trit parallel operations +pub const Vec32i8 = @Vector(32, i8); +pub const Vec32i16 = @Vector(32, i16); +pub const SIMD_WIDTH = 32; +pub const SIMD_CHUNKS = MAX_TRITS / SIMD_WIDTH; // 59049 / 32 = 1845 + +// ═══════════════════════════════════════════════════════════════════════════════ +// SIMD OPERATIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// SIMD add 32 trits in parallel with carry propagation +pub fn simdAddTrits(a: Vec32i8, b: Vec32i8) struct { sum: Vec32i8, carry: Vec32i8 } { + const a_wide: Vec32i16 = a; + const b_wide: Vec32i16 = b; + const sum_wide = a_wide + b_wide; + + const ones: Vec32i16 = @splat(1); + const neg_ones: Vec32i16 = @splat(-1); + const threes: Vec32i16 = @splat(3); + + const high_mask = sum_wide > ones; + const low_mask = sum_wide < neg_ones; + + var normalized = sum_wide; + normalized = @select(i16, high_mask, sum_wide - threes, normalized); + normalized = @select(i16, low_mask, sum_wide + threes, normalized); + + var carry: Vec32i16 = @splat(0); + carry = @select(i16, high_mask, ones, carry); + carry = @select(i16, low_mask, neg_ones, carry); + + var sum_result: Vec32i8 = undefined; + var carry_result: Vec32i8 = undefined; + + inline for (0..32) |i| { + sum_result[i] = @intCast(normalized[i]); + carry_result[i] = @intCast(carry[i]); + } + + return .{ .sum = sum_result, .carry = carry_result }; +} + +/// SIMD negate 32 trits +pub fn simdNegate(v: Vec32i8) Vec32i8 { + const zero: Vec32i8 = @splat(0); + return zero - v; +} + +/// SIMD dot product of 32 trits (returns scalar) +pub fn simdDotProduct(a: Vec32i8, b: Vec32i8) i32 { + const a_wide: Vec32i16 = a; + const b_wide: Vec32i16 = b; + const prod = a_wide * b_wide; + return @reduce(.Add, prod); +} + +/// SIMD check if all zeros +pub fn simdIsZero(v: Vec32i8) bool { + const zero: Vec32i8 = @splat(0); + return @reduce(.Or, v != zero) == false; +} + +/// Storage mode for HybridBigInt +pub const StorageMode = enum { + /// Packed: 5 trits per byte, memory efficient + packed_mode, + /// Unpacked: 1 trit per byte, compute efficient + unpacked_mode, +}; + +/// HybridBigInt: Best of both worlds +/// - Stores in packed format (4.5x memory savings) +/// - Unpacks lazily for computation +/// - Re-packs after computation if needed +pub const HybridBigInt = struct { + /// Packed storage (always valid) + packed_data: [MAX_PACKED_BYTES]u8, + /// Unpacked cache (valid only when mode == unpacked_mode) + unpacked_cache: [MAX_TRITS]Trit, + /// Current storage mode + mode: StorageMode, + /// Number of significant trits + trit_len: usize, + /// Dirty flag: unpacked cache modified, needs re-pack + dirty: bool, + + const Self = @This(); + + /// Create zero value + pub fn zero() Self { + const zero_pack = tvc_packed.encodePack(.{ 0, 0, 0, 0, 0 }); + return Self{ + .packed_data = [_]u8{zero_pack} ** MAX_PACKED_BYTES, + .unpacked_cache = [_]Trit{0} ** MAX_TRITS, + .mode = .packed_mode, + .trit_len = 1, + .dirty = false, + }; + } + + /// Create from i64 + pub fn fromI64(value: i64) Self { + var result = Self.zero(); + if (value == 0) return result; + + var v = value; + var pos: usize = 0; + + while (v != 0 and pos < MAX_TRITS) { + var rem = @mod(v, @as(i64, 3)); + if (rem == 2) rem = -1; + result.unpacked_cache[pos] = @intCast(rem); + v = @divFloor(v - rem, 3); + pos += 1; + } + + result.trit_len = if (pos == 0) 1 else pos; + result.mode = .unpacked_mode; + result.dirty = true; + return result; + } + + /// Convert to i64 + pub fn toI64(self: *Self) i64 { + self.ensureUnpacked(); + var result: i64 = 0; + var power: i64 = 1; + for (0..self.trit_len) |i| { + result += @as(i64, self.unpacked_cache[i]) * power; + power *= 3; + } + return result; + } + + /// Get trit at position (auto-unpacks if needed) + pub fn getTrit(self: *Self, pos: usize) Trit { + if (pos >= self.trit_len) return 0; + self.ensureUnpacked(); + return self.unpacked_cache[pos]; + } + + /// Set trit at position (marks dirty) + pub fn setTrit(self: *Self, pos: usize, value: Trit) void { + if (pos >= MAX_TRITS) return; + self.ensureUnpacked(); + self.unpacked_cache[pos] = value; + self.dirty = true; + if (pos >= self.trit_len and value != 0) { + self.trit_len = pos + 1; + } + } + + /// Ensure unpacked cache is valid + pub fn ensureUnpacked(self: *Self) void { + if (self.mode == .unpacked_mode) return; + + // Unpack from packed_data to unpacked_cache + const num_packs = (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + for (0..num_packs) |pack_idx| { + const trits = tvc_packed.decodePack(self.packed_data[pack_idx]); + const base = pack_idx * TRITS_PER_BYTE; + for (0..TRITS_PER_BYTE) |j| { + if (base + j < MAX_TRITS) { + self.unpacked_cache[base + j] = trits[j]; + } + } + } + self.mode = .unpacked_mode; + } + + /// Pack the unpacked cache back to packed storage + pub fn pack(self: *Self) void { + if (!self.dirty and self.mode == .packed_mode) return; + + const num_packs = (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + for (0..num_packs) |pack_idx| { + const base = pack_idx * TRITS_PER_BYTE; + var trits: [5]Trit = .{ 0, 0, 0, 0, 0 }; + for (0..TRITS_PER_BYTE) |j| { + if (base + j < self.trit_len) { + trits[j] = self.unpacked_cache[base + j]; + } + } + self.packed_data[pack_idx] = tvc_packed.encodePack(trits); + } + self.mode = .packed_mode; + self.dirty = false; + } + + /// Memory usage in bytes (packed) + pub fn memoryUsage(self: *const Self) usize { + return (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + } + + /// Normalize: remove leading zeros + fn normalize(self: *Self) void { + self.ensureUnpacked(); + while (self.trit_len > 1 and self.unpacked_cache[self.trit_len - 1] == 0) { + self.trit_len -= 1; + } + self.dirty = true; + } + + /// Check if zero + pub fn isZero(self: *Self) bool { + self.ensureUnpacked(); + return self.trit_len == 1 and self.unpacked_cache[0] == 0; + } + + /// Check if negative + pub fn isNegative(self: *Self) bool { + self.ensureUnpacked(); + return self.unpacked_cache[self.trit_len - 1] < 0; + } + + /// Negate + pub fn negate(self: *const Self) Self { + var result = Self.zero(); + result.trit_len = self.trit_len; + result.mode = .unpacked_mode; + result.dirty = true; + + // Copy and negate from self (may need to unpack) + var self_mut = self.*; + self_mut.ensureUnpacked(); + + for (0..self.trit_len) |i| { + result.unpacked_cache[i] = -self_mut.unpacked_cache[i]; + } + return result; + } + + /// Add two HybridBigInts (uses unpacked for speed) + pub fn add(a: *Self, b: *Self) Self { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = Self.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + var carry: Trit = 0; + const max_len = @max(a.trit_len, b.trit_len); + + for (0..max_len + 1) |i| { + if (i >= MAX_TRITS) break; + + const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + + var sum: i16 = a_trit + b_trit + carry; + carry = 0; + + while (sum > 1) { + sum -= 3; + carry += 1; + } + while (sum < -1) { + sum += 3; + carry -= 1; + } + + result.unpacked_cache[i] = @intCast(sum); + } + + result.trit_len = @min(max_len + 1, MAX_TRITS); + result.normalize(); + return result; + } + + /// SIMD-accelerated add (32 trits at a time) + /// Uses SIMD for parallel addition, then sequential carry propagation + pub fn addSimd(a: *Self, b: *Self) Self { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = Self.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const max_len = @max(a.trit_len, b.trit_len); + const num_chunks = (max_len + SIMD_WIDTH - 1) / SIMD_WIDTH; + + // Phase 1: SIMD parallel addition (no carry propagation yet) + var carries: [SIMD_CHUNKS + 1][SIMD_WIDTH]Trit = undefined; + + for (0..num_chunks) |chunk| { + const base = chunk * SIMD_WIDTH; + + var a_vec: Vec32i8 = undefined; + var b_vec: Vec32i8 = undefined; + + inline for (0..SIMD_WIDTH) |i| { + const idx = base + i; + a_vec[i] = if (idx < a.trit_len) a.unpacked_cache[idx] else 0; + b_vec[i] = if (idx < b.trit_len) b.unpacked_cache[idx] else 0; + } + + const simd_result = simdAddTrits(a_vec, b_vec); + + inline for (0..SIMD_WIDTH) |i| { + const idx = base + i; + if (idx < MAX_TRITS) { + result.unpacked_cache[idx] = simd_result.sum[i]; + } + carries[chunk][i] = simd_result.carry[i]; + } + } + + // Phase 2: Sequential carry propagation (necessary for correctness) + var carry: Trit = 0; + for (0..max_len + 1) |i| { + if (i >= MAX_TRITS) break; + + const chunk = i / SIMD_WIDTH; + const offset = i % SIMD_WIDTH; + + var val: i16 = result.unpacked_cache[i]; + + // Add carry from SIMD (shifted by 1 position) + if (i > 0) { + const prev_chunk = (i - 1) / SIMD_WIDTH; + const prev_offset = (i - 1) % SIMD_WIDTH; + if (prev_chunk < num_chunks) { + val += carries[prev_chunk][prev_offset]; + } + } + + val += carry; + carry = 0; + + while (val > 1) { + val -= 3; + carry += 1; + } + while (val < -1) { + val += 3; + carry -= 1; + } + + result.unpacked_cache[i] = @intCast(val); + _ = chunk; + _ = offset; + } + + result.trit_len = @min(max_len + 1, MAX_TRITS); + result.normalize(); + return result; + } + + /// Subtract + pub fn sub(a: *Self, b: *Self) Self { + var neg_b = b.negate(); + return a.add(&neg_b); + } + + /// Multiply two HybridBigInts + pub fn mul(a: *Self, b: *Self) Self { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = Self.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + for (0..a.trit_len) |i| { + const a_trit = a.unpacked_cache[i]; + if (a_trit == 0) continue; + + var carry: Trit = 0; + for (0..b.trit_len) |j| { + if (i + j >= MAX_TRITS) break; + + var prod: i16 = @as(i16, a_trit) * @as(i16, b.unpacked_cache[j]); + prod += result.unpacked_cache[i + j]; + prod += carry; + carry = 0; + + while (prod > 1) { + prod -= 3; + carry += 1; + } + while (prod < -1) { + prod += 3; + carry -= 1; + } + + result.unpacked_cache[i + j] = @intCast(prod); + } + + if (carry != 0 and i + b.trit_len < MAX_TRITS) { + result.unpacked_cache[i + b.trit_len] += carry; + } + } + + result.trit_len = @min(a.trit_len + b.trit_len, MAX_TRITS); + result.normalize(); + return result; + } + + /// SIMD dot product (for VSA similarity) + pub fn dotProduct(a: *Self, b: *Self) i32 { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var total: i32 = 0; + const min_len = @min(a.trit_len, b.trit_len); + const num_chunks = min_len / SIMD_WIDTH; + + // SIMD chunks + for (0..num_chunks) |chunk| { + const base = chunk * SIMD_WIDTH; + + var a_vec: Vec32i8 = undefined; + var b_vec: Vec32i8 = undefined; + + inline for (0..SIMD_WIDTH) |i| { + a_vec[i] = a.unpacked_cache[base + i]; + b_vec[i] = b.unpacked_cache[base + i]; + } + + total += simdDotProduct(a_vec, b_vec); + } + + // Remainder (scalar) + const remainder_start = num_chunks * SIMD_WIDTH; + for (remainder_start..min_len) |i| { + total += @as(i32, a.unpacked_cache[i]) * @as(i32, b.unpacked_cache[i]); + } + + return total; + } + + /// Convert from TVCBigInt + pub fn fromBigInt(big: *const tvc_bigint.TVCBigInt) Self { + var result = Self.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + for (0..big.len) |i| { + result.unpacked_cache[i] = big.trits[i]; + } + result.trit_len = big.len; + return result; + } + + /// Convert to TVCBigInt + pub fn toBigInt(self: *Self) tvc_bigint.TVCBigInt { + self.ensureUnpacked(); + var result = tvc_bigint.TVCBigInt.zero(); + for (0..self.trit_len) |i| { + result.trits[i] = self.unpacked_cache[i]; + } + result.len = self.trit_len; + return result; + } + + /// Convert from PackedBigInt + pub fn fromPacked(pbi: *const tvc_packed.PackedBigInt) Self { + var result = Self.zero(); + // Copy packed data directly + for (0..tvc_packed.MAX_PACKED_BYTES) |i| { + if (i < MAX_PACKED_BYTES) { + result.packed_data[i] = pbi.data[i]; + } + } + result.trit_len = pbi.trit_len; + result.mode = .packed_mode; + result.dirty = false; + return result; + } + + /// Convert to PackedBigInt + pub fn toPacked(self: *Self) tvc_packed.PackedBigInt { + self.pack(); // Ensure packed + var result = tvc_packed.PackedBigInt.zero(); + for (0..MAX_PACKED_BYTES) |i| { + if (i < tvc_packed.MAX_PACKED_BYTES) { + result.data[i] = self.packed_data[i]; + } + } + result.trit_len = self.trit_len; + return result; + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "HybridBigInt fromI64 and toI64" { + const cases = [_]i64{ 0, 1, -1, 10, -10, 100, -100, 12345, -12345 }; + for (cases) |val| { + var hybrid = HybridBigInt.fromI64(val); + const back = hybrid.toI64(); + try std.testing.expectEqual(val, back); + } +} + +test "HybridBigInt addition" { + var a = HybridBigInt.fromI64(123); + var b = HybridBigInt.fromI64(456); + var sum = a.add(&b); + try std.testing.expectEqual(@as(i64, 579), sum.toI64()); +} + +test "HybridBigInt multiplication" { + var a = HybridBigInt.fromI64(12); + var b = HybridBigInt.fromI64(34); + var prod = a.mul(&b); + try std.testing.expectEqual(@as(i64, 408), prod.toI64()); +} + +test "HybridBigInt pack/unpack roundtrip" { + var hybrid = HybridBigInt.fromI64(12345); + const val1 = hybrid.toI64(); + + // Force pack + hybrid.pack(); + try std.testing.expectEqual(StorageMode.packed_mode, hybrid.mode); + + // Force unpack via getTrit + _ = hybrid.getTrit(0); + try std.testing.expectEqual(StorageMode.unpacked_mode, hybrid.mode); + + const val2 = hybrid.toI64(); + try std.testing.expectEqual(val1, val2); +} + +test "HybridBigInt memory efficiency" { + var hybrid = HybridBigInt.fromI64(123456789); + hybrid.pack(); + const mem = hybrid.memoryUsage(); + // 18 trits / 5 = 4 bytes (vs 18 bytes unpacked) + try std.testing.expect(mem <= 4); +} + +test "HybridBigInt conversion from BigInt" { + const val: i64 = 12345; + const big = tvc_bigint.TVCBigInt.fromI64(val); + var hybrid = HybridBigInt.fromBigInt(&big); + try std.testing.expectEqual(val, hybrid.toI64()); +} + +test "HybridBigInt conversion to BigInt" { + var hybrid = HybridBigInt.fromI64(12345); + const big = hybrid.toBigInt(); + try std.testing.expectEqual(@as(i64, 12345), big.toI64()); +} + +test "SIMD addSimd correctness" { + const cases = [_][2]i64{ + .{ 123, 456 }, + .{ -100, 200 }, + .{ 12345, 67890 }, + .{ -99999, 99999 }, + .{ 123456789, 987654321 }, + }; + + for (cases) |pair| { + var a = HybridBigInt.fromI64(pair[0]); + var b = HybridBigInt.fromI64(pair[1]); + + var sum_scalar = a.add(&b); + var sum_simd = a.addSimd(&b); + + try std.testing.expectEqual(sum_scalar.toI64(), sum_simd.toI64()); + } +} + +test "SIMD dotProduct" { + var a = HybridBigInt.fromI64(12345); + var b = HybridBigInt.fromI64(12345); + + const dot = a.dotProduct(&b); + // dot product of identical vectors = sum of squares of trits + // For balanced ternary, each trit is -1, 0, or 1, so trit^2 = 0 or 1 + try std.testing.expect(dot > 0); +} + +test "SIMD functions" { + // Test simdAddTrits + const a_vec: Vec32i8 = @splat(1); + const b_vec: Vec32i8 = @splat(1); + + const result = simdAddTrits(a_vec, b_vec); + // 1 + 1 = 2, which normalizes to -1 with carry +1 + try std.testing.expectEqual(@as(i8, -1), result.sum[0]); + try std.testing.expectEqual(@as(i8, 1), result.carry[0]); + + // Test simdNegate + const neg = simdNegate(a_vec); + try std.testing.expectEqual(@as(i8, -1), neg[0]); + + // Test simdIsZero + const zero_vec: Vec32i8 = @splat(0); + try std.testing.expect(simdIsZero(zero_vec)); + try std.testing.expect(!simdIsZero(a_vec)); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// BENCHMARKS +// ═══════════════════════════════════════════════════════════════════════════════ + +pub fn runBenchmarks() void { + const iterations: u64 = 100000; + std.debug.print("\nHybrid vs Packed vs Unpacked BigInt Benchmarks\n", .{}); + std.debug.print("==============================================\n\n", .{}); + + const val_a: i64 = 123456789; + const val_b: i64 = 987654321; + + // Create all three types + const unpacked_a = tvc_bigint.TVCBigInt.fromI64(val_a); + const unpacked_b = tvc_bigint.TVCBigInt.fromI64(val_b); + const packed_a = tvc_packed.PackedBigInt.fromI64(val_a); + const packed_b = tvc_packed.PackedBigInt.fromI64(val_b); + var hybrid_a = HybridBigInt.fromI64(val_a); + var hybrid_b = HybridBigInt.fromI64(val_b); + + std.debug.print("Memory comparison:\n", .{}); + std.debug.print(" Unpacked: {} bytes\n", .{unpacked_a.len}); + std.debug.print(" Packed: {} bytes\n", .{packed_a.memoryUsage()}); + hybrid_a.pack(); + std.debug.print(" Hybrid: {} bytes (packed)\n\n", .{hybrid_a.memoryUsage()}); + + std.debug.print("Addition x {} iterations:\n", .{iterations}); + + // Unpacked benchmark + const unpacked_start = std.time.nanoTimestamp(); + var unpacked_result = tvc_bigint.TVCBigInt.zero(); + var i: u64 = 0; + while (i < iterations) : (i += 1) { + unpacked_result = unpacked_a.addScalar(&unpacked_b); + } + const unpacked_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(unpacked_result); + const unpacked_ns = @as(u64, @intCast(unpacked_end - unpacked_start)); + + // Packed benchmark + const packed_start = std.time.nanoTimestamp(); + var packed_result = tvc_packed.PackedBigInt.zero(); + i = 0; + while (i < iterations) : (i += 1) { + packed_result = packed_a.add(&packed_b); + } + const packed_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(packed_result); + const packed_ns = @as(u64, @intCast(packed_end - packed_start)); + + // Hybrid benchmark + hybrid_a = HybridBigInt.fromI64(val_a); + hybrid_b = HybridBigInt.fromI64(val_b); + const hybrid_start = std.time.nanoTimestamp(); + var hybrid_result = HybridBigInt.zero(); + i = 0; + while (i < iterations) : (i += 1) { + hybrid_result = hybrid_a.add(&hybrid_b); + } + const hybrid_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(hybrid_result); + const hybrid_ns = @as(u64, @intCast(hybrid_end - hybrid_start)); + + std.debug.print(" Unpacked: {} ns ({} ns/op)\n", .{ unpacked_ns, unpacked_ns / iterations }); + std.debug.print(" Packed: {} ns ({} ns/op)\n", .{ packed_ns, packed_ns / iterations }); + std.debug.print(" Hybrid: {} ns ({} ns/op)\n\n", .{ hybrid_ns, hybrid_ns / iterations }); + + const hybrid_vs_packed: f64 = @as(f64, @floatFromInt(packed_ns)) / @as(f64, @floatFromInt(hybrid_ns)); + const hybrid_vs_unpacked: f64 = @as(f64, @floatFromInt(unpacked_ns)) / @as(f64, @floatFromInt(hybrid_ns)); + + std.debug.print("Hybrid speedup:\n", .{}); + std.debug.print(" vs Packed: {d:.2}x\n", .{hybrid_vs_packed}); + std.debug.print(" vs Unpacked: {d:.2}x\n", .{hybrid_vs_unpacked}); + + // SIMD benchmark + std.debug.print("\nSIMD Addition x {} iterations:\n", .{iterations}); + + hybrid_a = HybridBigInt.fromI64(val_a); + hybrid_b = HybridBigInt.fromI64(val_b); + const simd_start = std.time.nanoTimestamp(); + var simd_result = HybridBigInt.zero(); + i = 0; + while (i < iterations) : (i += 1) { + simd_result = hybrid_a.addSimd(&hybrid_b); + } + const simd_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(simd_result); + const simd_ns = @as(u64, @intCast(simd_end - simd_start)); + + std.debug.print(" Hybrid SIMD: {} ns ({} ns/op)\n", .{ simd_ns, simd_ns / iterations }); + + const simd_vs_scalar: f64 = @as(f64, @floatFromInt(hybrid_ns)) / @as(f64, @floatFromInt(simd_ns)); + std.debug.print(" SIMD speedup vs scalar: {d:.2}x\n", .{simd_vs_scalar}); + + // Dot product benchmark + std.debug.print("\nDot Product x {} iterations:\n", .{iterations}); + + hybrid_a = HybridBigInt.fromI64(val_a); + hybrid_b = HybridBigInt.fromI64(val_b); + const dot_start = std.time.nanoTimestamp(); + var dot_result: i32 = 0; + i = 0; + while (i < iterations) : (i += 1) { + dot_result = hybrid_a.dotProduct(&hybrid_b); + } + const dot_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(dot_result); + const dot_ns = @as(u64, @intCast(dot_end - dot_start)); + + std.debug.print(" Dot product: {} ns ({} ns/op)\n", .{ dot_ns, dot_ns / iterations }); + + std.debug.print("\nResults match:\n", .{}); + std.debug.print(" Unpacked == Packed: {}\n", .{unpacked_result.toI64() == packed_result.toI64()}); + std.debug.print(" Unpacked == Hybrid: {}\n", .{unpacked_result.toI64() == hybrid_result.toI64()}); + std.debug.print(" Hybrid == SIMD: {}\n", .{hybrid_result.toI64() == simd_result.toI64()}); +} + +pub fn main() !void { + runBenchmarks(); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig new file mode 100644 index 0000000..23baf40 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig @@ -0,0 +1,306 @@ +// @origin(spec:packed_trit.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +const std = @import("std"); +const tvc_bigint = @import("bigint.zig"); + +pub const TRITS_PER_BYTE: usize = 5; +/// towithand 12000 andin (2400 ) - beforewith for and VSA (1000-10000 and) +pub const MAX_PACKED_BYTES: usize = 2400; +pub const MAX_TRITS: usize = MAX_PACKED_BYTES * TRITS_PER_BYTE; // = 12000 +pub const Trit = i8; + +pub fn encodePack(trits: [5]i8) u8 { + const t0: u16 = @intCast(@as(i16, trits[0]) + 1); + const t1: u16 = @intCast(@as(i16, trits[1]) + 1); + const t2: u16 = @intCast(@as(i16, trits[2]) + 1); + const t3: u16 = @intCast(@as(i16, trits[3]) + 1); + const t4: u16 = @intCast(@as(i16, trits[4]) + 1); + const value = t0 * 1 + t1 * 3 + t2 * 9 + t3 * 27 + t4 * 81; + return @intCast(value); +} + +pub fn decodePack(pack_val: u8) [5]i8 { + var value: u16 = pack_val; + const d0 = value % 3; + value /= 3; + const d1 = value % 3; + value /= 3; + const d2 = value % 3; + value /= 3; + const d3 = value % 3; + value /= 3; + const d4 = value % 3; + return .{ + @as(i8, @intCast(d0)) - 1, + @as(i8, @intCast(d1)) - 1, + @as(i8, @intCast(d2)) - 1, + @as(i8, @intCast(d3)) - 1, + @as(i8, @intCast(d4)) - 1, + }; +} + +pub const PackedBigInt = struct { + data: [MAX_PACKED_BYTES]u8, + trit_len: usize, + + const Self = @This(); + + pub fn zero() Self { + return Self{ + .data = [_]u8{encodePack(.{ 0, 0, 0, 0, 0 })} ** MAX_PACKED_BYTES, + .trit_len = 1, + }; + } + + pub fn packedLen(self: *const Self) usize { + return (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + } + + pub fn getTrit(self: *const Self, pos: usize) Trit { + if (pos >= self.trit_len) return 0; + const byte_idx = pos / TRITS_PER_BYTE; + const trit_idx = pos % TRITS_PER_BYTE; + const trits = decodePack(self.data[byte_idx]); + return trits[trit_idx]; + } + + pub fn setTrit(self: *Self, pos: usize, value: Trit) void { + if (pos >= MAX_TRITS) return; + const byte_idx = pos / TRITS_PER_BYTE; + const trit_idx = pos % TRITS_PER_BYTE; + var trits = decodePack(self.data[byte_idx]); + trits[trit_idx] = value; + self.data[byte_idx] = encodePack(trits); + if (pos >= self.trit_len and value != 0) { + self.trit_len = pos + 1; + } + } + + pub fn fromI64(value: i64) Self { + var result = Self.zero(); + if (value == 0) return result; + var v = value; + var pos: usize = 0; + while (v != 0 and pos < MAX_TRITS) { + var rem = @mod(v, @as(i64, 3)); + if (rem == 2) rem = -1; + result.setTrit(pos, @intCast(rem)); + v = @divFloor(v - rem, 3); + pos += 1; + } + result.trit_len = if (pos == 0) 1 else pos; + result.normalize(); + return result; + } + + pub fn toI64(self: *const Self) i64 { + var result: i64 = 0; + var power: i64 = 1; + for (0..self.trit_len) |i| { + result += @as(i64, self.getTrit(i)) * power; + power *= 3; + } + return result; + } + + fn normalize(self: *Self) void { + while (self.trit_len > 1 and self.getTrit(self.trit_len - 1) == 0) { + self.trit_len -= 1; + } + } + + pub fn isZero(self: *const Self) bool { + return self.trit_len == 1 and self.getTrit(0) == 0; + } + + pub fn isNegative(self: *const Self) bool { + return self.getTrit(self.trit_len - 1) < 0; + } + + pub fn negate(self: *const Self) Self { + var result = Self.zero(); + result.trit_len = self.trit_len; + for (0..self.packedLen()) |i| { + const trits = decodePack(self.data[i]); + const negated = [5]i8{ -trits[0], -trits[1], -trits[2], -trits[3], -trits[4] }; + result.data[i] = encodePack(negated); + } + return result; + } + + pub fn add(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + var carry: Trit = 0; + const max_len = @max(a.trit_len, b.trit_len); + for (0..max_len + 1) |i| { + if (i >= MAX_TRITS) break; + var sum: i16 = @as(i16, a.getTrit(i)) + @as(i16, b.getTrit(i)) + carry; + carry = 0; + while (sum > 1) { + sum -= 3; + carry += 1; + } + while (sum < -1) { + sum += 3; + carry -= 1; + } + result.setTrit(i, @intCast(sum)); + } + result.trit_len = max_len + 1; + result.normalize(); + return result; + } + + pub fn sub(a: *const Self, b: *const Self) Self { + const neg_b = b.negate(); + return a.add(&neg_b); + } + + pub fn mul(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + for (0..a.trit_len) |i| { + const a_trit = a.getTrit(i); + if (a_trit == 0) continue; + var partial = Self.zero(); + var carry: Trit = 0; + for (0..b.trit_len) |j| { + if (i + j >= MAX_TRITS) break; + var prod: i16 = @as(i16, a_trit) * @as(i16, b.getTrit(j)) + carry; + carry = 0; + while (prod > 1) { + prod -= 3; + carry += 1; + } + while (prod < -1) { + prod += 3; + carry -= 1; + } + partial.setTrit(i + j, @intCast(prod)); + } + if (carry != 0 and i + b.trit_len < MAX_TRITS) { + partial.setTrit(i + b.trit_len, carry); + } + partial.trit_len = @min(i + b.trit_len + 1, MAX_TRITS); + result = result.add(&partial); + } + result.normalize(); + return result; + } + + pub fn fromBigInt(big: *const tvc_bigint.TVCBigInt) Self { + var result = Self.zero(); + for (0..big.len) |i| { + result.setTrit(i, big.trits[i]); + } + result.trit_len = big.len; + return result; + } + + pub fn toBigInt(self: *const Self) tvc_bigint.TVCBigInt { + var result = tvc_bigint.TVCBigInt.zero(); + for (0..self.trit_len) |i| { + result.trits[i] = self.getTrit(i); + } + result.len = self.trit_len; + return result; + } + + pub fn memoryUsage(self: *const Self) usize { + return self.packedLen(); + } +}; + +test "encode/decode pack" { + const trits = [5]i8{ -1, 0, 1, -1, 1 }; + const encoded = encodePack(trits); + const decoded = decodePack(encoded); + try std.testing.expectEqual(trits[0], decoded[0]); + try std.testing.expectEqual(trits[1], decoded[1]); + try std.testing.expectEqual(trits[2], decoded[2]); + try std.testing.expectEqual(trits[3], decoded[3]); + try std.testing.expectEqual(trits[4], decoded[4]); +} + +test "PackedBigInt fromI64 and toI64" { + const cases = [_]i64{ 0, 1, -1, 10, -10, 100, -100, 12345, -12345 }; + for (cases) |val| { + const pbi = PackedBigInt.fromI64(val); + const back = pbi.toI64(); + try std.testing.expectEqual(val, back); + } +} + +test "PackedBigInt addition" { + const a = PackedBigInt.fromI64(123); + const b = PackedBigInt.fromI64(456); + const sum = a.add(&b); + try std.testing.expectEqual(@as(i64, 579), sum.toI64()); +} + +test "PackedBigInt multiplication" { + const a = PackedBigInt.fromI64(12); + const b = PackedBigInt.fromI64(34); + const prod = a.mul(&b); + try std.testing.expectEqual(@as(i64, 408), prod.toI64()); +} + +test "PackedBigInt conversion" { + const val: i64 = 12345; + const big = tvc_bigint.TVCBigInt.fromI64(val); + const pbi = PackedBigInt.fromBigInt(&big); + const back = pbi.toBigInt(); + try std.testing.expectEqual(val, back.toI64()); + try std.testing.expectEqual(val, pbi.toI64()); +} + +pub fn runBenchmarks() void { + const iterations: u64 = 100000; + std.debug.print("\nPacked vs Unpacked BigInt Benchmarks\n", .{}); + std.debug.print("=====================================\n\n", .{}); + + const val_a: i64 = 123456789; + const val_b: i64 = 987654321; + + const unpacked_a = tvc_bigint.TVCBigInt.fromI64(val_a); + const unpacked_b = tvc_bigint.TVCBigInt.fromI64(val_b); + const packed_a = PackedBigInt.fromI64(val_a); + const packed_b = PackedBigInt.fromI64(val_b); + + std.debug.print("Number sizes:\n", .{}); + std.debug.print(" Unpacked: {} trits, {} bytes\n", .{ unpacked_a.len, unpacked_a.len }); + std.debug.print(" Packed: {} trits, {} bytes\n", .{ packed_a.trit_len, packed_a.memoryUsage() }); + std.debug.print(" Memory savings: {d:.1}x\n\n", .{@as(f64, @floatFromInt(unpacked_a.len)) / @as(f64, @floatFromInt(packed_a.memoryUsage()))}); + + std.debug.print("Addition x {} iterations:\n", .{iterations}); + + const unpacked_start = std.time.nanoTimestamp(); + var unpacked_result = tvc_bigint.TVCBigInt.zero(); + var i: u64 = 0; + while (i < iterations) : (i += 1) { + unpacked_result = unpacked_a.addScalar(&unpacked_b); + } + const unpacked_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(unpacked_result); + const unpacked_ns = @as(u64, @intCast(unpacked_end - unpacked_start)); + + const packed_start = std.time.nanoTimestamp(); + var packed_result = PackedBigInt.zero(); + i = 0; + while (i < iterations) : (i += 1) { + packed_result = packed_a.add(&packed_b); + } + const packed_end = std.time.nanoTimestamp(); + std.mem.doNotOptimizeAway(packed_result); + const packed_ns = @as(u64, @intCast(packed_end - packed_start)); + + const speedup: f64 = @as(f64, @floatFromInt(unpacked_ns)) / @as(f64, @floatFromInt(packed_ns)); + + std.debug.print(" Unpacked: {} ns ({} ns/op)\n", .{ unpacked_ns, unpacked_ns / iterations }); + std.debug.print(" Packed: {} ns ({} ns/op)\n", .{ packed_ns, packed_ns / iterations }); + std.debug.print(" Speedup: {d:.2}x\n", .{speedup}); + std.debug.print(" Results match: {}\n", .{unpacked_result.toI64() == packed_result.toI64()}); +} + +pub fn main() !void { + runBenchmarks(); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig new file mode 100644 index 0000000..54e068c --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig @@ -0,0 +1,82 @@ +const std = @import("std"); + +pub const PHI: f64 = 1.6180339887498948482; +pub const PHI_SQ: f64 = PHI * PHI; +pub const PHI_INV: f64 = 1.0 / PHI; +pub const PHI_INV_SQ: f64 = 1.0 / PHI_SQ; +pub const TRINITY: f64 = PHI_SQ + PHI_INV_SQ; + +pub const ALPHA_PHI: f64 = PHI - 1.5; + +pub const FIBONACCI = [_]u32{ + 1, 1, 2, 3, 5, 8, 13, 21, + 34, 55, 89, 144, 233, 377, 610, 987, +}; + +pub const D_MODEL: u32 = 144; +pub const N_HEADS: u32 = 8; +pub const D_HEAD: u32 = D_MODEL / N_HEADS; +pub const D_FFN: u32 = 233; +pub const N_LAYERS: u32 = 7; +pub const VOCAB: u32 = 50257; + +pub const GAUGE_INIT_STD: f64 = ALPHA_PHI; +pub const HIGGS_INIT_STD: f64 = ALPHA_PHI * PHI_INV; +pub const LEPTON_INIT_STD: f64 = ALPHA_PHI * PHI_INV_SQ; +pub const COSMOLOGY_INIT_STD: f64 = ALPHA_PHI * PHI_INV * PHI_INV_SQ; + +pub const LR_INIT: f64 = ALPHA_PHI; +pub const LR_WARMUP_STEPS: u32 = 21; +pub const LR_TAU: f64 = 228.9; + +pub fn phiLrSchedule(step: u32, total_steps: u32) f64 { + if (step <= LR_WARMUP_STEPS) { + return LR_INIT * @as(f64, @floatFromInt(step)) / @as(f64, @floatFromInt(LR_WARMUP_STEPS)); + } + const t = @as(f64, @floatFromInt(step - LR_WARMUP_STEPS)) / @as(f64, @floatFromInt(total_steps)); + return LR_INIT * std.math.pow(f64, PHI, -t / LR_TAU * @as(f64, @floatFromInt(total_steps)) / LR_TAU); +} + +pub fn trinityInitStd(layer_kind: enum { gauge, higgs, lepton, cosmology }) f64 { + return switch (layer_kind) { + .gauge => GAUGE_INIT_STD, + .higgs => HIGGS_INIT_STD, + .lepton => LEPTON_INIT_STD, + .cosmology => COSMOLOGY_INIT_STD, + }; +} + +test "Trinity Identity: PHI^2 + PHI^(-2) = 3" { + try std.testing.expectApproxEqAbs(@as(f64, 3.0), TRINITY, 1e-12); +} + +test "ALPHA_PHI = PHI - 1.5 = 0.118034" { + try std.testing.expectApproxEqAbs(@as(f64, 0.118033988749895), ALPHA_PHI, 1e-12); +} + +test "Fibonacci: 144 * PHI = 233" { + const result = @as(f64, @floatFromInt(FIBONACCI[11])) * PHI; + try std.testing.expectApproxEqAbs(@as(f64, 233.0), result, 0.1); +} + +test "Architecture: d_model=144, n_heads=8, d_head=18" { + try std.testing.expectEqual(@as(u32, 144), D_MODEL); + try std.testing.expectEqual(@as(u32, 8), N_HEADS); + try std.testing.expectEqual(@as(u32, 18), D_HEAD); + try std.testing.expectEqual(@as(u32, 233), D_FFN); +} + +test "Trinity init stds decrease by 1/PHI" { + try std.testing.expect(GAUGE_INIT_STD > HIGGS_INIT_STD); + try std.testing.expect(HIGGS_INIT_STD > LEPTON_INIT_STD); + try std.testing.expect(LEPTON_INIT_STD > COSMOLOGY_INIT_STD); + const ratio = GAUGE_INIT_STD / HIGGS_INIT_STD; + try std.testing.expectApproxEqAbs(PHI, ratio, 1e-10); +} + +test "LR schedule: warmup then decay" { + const lr_0 = phiLrSchedule(0, 10000); + try std.testing.expect(lr_0 < LR_INIT); + const lr_warmup = phiLrSchedule(LR_WARMUP_STEPS, 10000); + try std.testing.expectApproxEqAbs(LR_INIT, lr_warmup, 1e-10); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig new file mode 100644 index 0000000..dfe506c --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig @@ -0,0 +1,92 @@ +const std = @import("std"); +const tc = @import("trinity_constants.zig"); + +pub const LayerKind = enum { gauge, higgs, lepton, cosmology }; + +pub fn initStd(kind: LayerKind) f64 { + return tc.trinityInitStd(@enumFromInt(@intFromEnum(kind))); +} + +pub fn trinityInitWeight( + rng: std.Random, + fan_in: u32, + kind: LayerKind, +) f64 { + const std_val = initStd(kind) / @sqrt(@as(f64, @floatFromInt(fan_in))); + return rng.floatNorm(f64) * std_val; +} + +pub fn initTensor( + allocator: std.mem.Allocator, + rows: u32, + cols: u32, + kind: LayerKind, + seed: u64, +) ![]f64 { + const n = @as(usize, rows) * @as(usize, cols); + const tensor = try allocator.alloc(f64, n); + var prng = std.Random.DefaultPrng.init(seed); + const rng = prng.random(); + for (tensor) |*w| { + w.* = trinityInitWeight(rng, cols, kind); + } + return tensor; +} + +pub fn initEmbedding( + allocator: std.mem.Allocator, + vocab_size: u32, + d_model: u32, + seed: u64, +) ![]f64 { + return initTensor(allocator, vocab_size, d_model, .cosmology, seed); +} + +pub fn initAttentionQKV( + allocator: std.mem.Allocator, + d_model: u32, + n_heads: u32, + seed: u64, +) ![]f64 { + return initTensor(allocator, n_heads * tc.D_HEAD, d_model, .gauge, seed); +} + +pub fn initFFN( + allocator: std.mem.Allocator, + d_model: u32, + d_ffn: u32, + seed: u64, +) ![]f64 { + return initTensor(allocator, d_ffn, d_model, .lepton, seed); +} + +test "init std values" { + try std.testing.expect(initStd(.gauge) > initStd(.higgs)); + try std.testing.expect(initStd(.higgs) > initStd(.lepton)); + try std.testing.expect(initStd(.lepton) > initStd(.cosmology)); +} + +test "trinity init weight is finite" { + var prng = std.Random.DefaultPrng.init(42); + const rng = prng.random(); + var all_finite = true; + for (0..100) |_| { + const w = trinityInitWeight(rng, 144, .gauge); + if (!std.math.isFinite(w)) all_finite = false; + } + try std.testing.expect(all_finite); +} + +test "init tensor dimensions" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const tensor = try initTensor(arena.allocator(), 8, 18, .gauge, 42); + try std.testing.expectEqual(@as(usize, 144), tensor.len); +} + +test "init embedding uses cosmology std" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const emb = try initEmbedding(arena.allocator(), 100, tc.D_MODEL, 42); + try std.testing.expectEqual(@as(usize, 100 * 144), emb.len); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig new file mode 100644 index 0000000..84949d3 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig @@ -0,0 +1,2175 @@ +// @origin(spec:jit_arm64.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT Compiler - ARM64 (AArch64) Backend +// Compiles VSA operations to native ARM64 machine code +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// ARM64 JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Check if we're on ARM64 +pub const is_arm64 = builtin.cpu.arch == .aarch64; + +/// ARM64 JIT Compiler +pub const Arm64JitCompiler = struct { + code: std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + exec_mem: ?[]align(std.heap.page_size_min) u8 = null, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .code = .{}, + .allocator = allocator, + }; + } + + pub fn deinit(self: *Self) void { + self.code.deinit(self.allocator); + if (self.exec_mem) |mem| { + std.posix.munmap(mem); + } + } + + pub fn reset(self: *Self) void { + self.code.clearRetainingCapacity(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 INSTRUCTION ENCODING HELPERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// Emit a 32-bit ARM64 instruction (little-endian) + fn emit32(self: *Self, instr: u32) !void { + try self.code.appendSlice(self.allocator, &std.mem.toBytes(instr)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 REGISTER ENCODING + // ═══════════════════════════════════════════════════════════════════════════ + + // X registers (64-bit): x0-x30, sp=31, xzr=31 + // W registers (32-bit): w0-w30, wzr=31 + const x0: u5 = 0; + const x1: u5 = 1; + const x2: u5 = 2; + const x3: u5 = 3; + const x8: u5 = 8; // indirect result + const x9: u5 = 9; // temp + const x10: u5 = 10; // temp + const x11: u5 = 11; // temp + const x12: u5 = 12; // temp + const x13: u5 = 13; // temp + const x14: u5 = 14; // temp + const x15: u5 = 15; // temp + const x19: u5 = 19; // callee-saved + const x20: u5 = 20; // callee-saved + const x21: u5 = 21; // callee-saved + const x22: u5 = 22; // callee-saved + const x29: u5 = 29; // frame pointer (fp) + const x30: u5 = 30; // link register (lr) + const sp: u5 = 31; // stack pointer + const xzr: u5 = 31; // zero register + + // ═══════════════════════════════════════════════════════════════════════════ + // ARM64 INSTRUCTION BUILDERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// STP (Store Pair) - stp Xt1, Xt2, [Xn, #imm]! (pre-index) + fn stpPreIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { + // STP (pre-index, 64-bit): 1 01 0 100 1 1 imm7 Rt2 Rn Rt1 + const uimm: u7 = @bitCast(imm7); + const instr: u32 = 0xA9800000 | + (@as(u32, uimm) << 15) | + (@as(u32, rt2) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rt1); + try self.emit32(instr); + } + + /// LDP (Load Pair) - ldp Xt1, Xt2, [Xn], #imm (post-index) + fn ldpPostIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { + // LDP (post-index, 64-bit): 1 01 0 100 0 1 1 imm7 Rt2 Rn Rt1 + const uimm: u7 = @bitCast(imm7); + const instr: u32 = 0xA8C00000 | + (@as(u32, uimm) << 15) | + (@as(u32, rt2) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rt1); + try self.emit32(instr); + } + + /// MOV (register) - mov Xd, Xn (actually ORR Xd, XZR, Xn) + fn movReg(self: *Self, rd: u5, rn: u5) !void { + // ORR (shifted register): 1 01 01010 00 0 Rm 000000 Rn Rd + const instr: u32 = 0xAA000000 | + (@as(u32, rn) << 16) | + (@as(u32, xzr) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MOV (immediate) - mov Xd, #imm16 + fn movImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { + // MOVZ: 1 10 100101 hw imm16 Rd + const instr: u32 = 0xD2800000 | + (@as(u32, shift) << 21) | + (@as(u32, imm16) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MOVK (keep) - movk Xd, #imm16, lsl #shift + fn movkImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { + // MOVK: 1 11 100101 hw imm16 Rd + const instr: u32 = 0xF2800000 | + (@as(u32, shift) << 21) | + (@as(u32, imm16) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// Load 64-bit immediate into register + fn loadImm64(self: *Self, rd: u5, imm: u64) !void { + const imm0: u16 = @truncate(imm); + const imm1: u16 = @truncate(imm >> 16); + const imm2: u16 = @truncate(imm >> 32); + const imm3: u16 = @truncate(imm >> 48); + + try self.movImm16(rd, imm0, 0); + if (imm1 != 0) try self.movkImm16(rd, imm1, 1); + if (imm2 != 0) try self.movkImm16(rd, imm2, 2); + if (imm3 != 0) try self.movkImm16(rd, imm3, 3); + } + + /// ADD (immediate) - add Xd, Xn, #imm12 + fn addImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { + // ADD (imm): 1 00 100010 0 imm12 Rn Rd + const instr: u32 = 0x91000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// SUB (immediate) - sub Xd, Xn, #imm12 + fn subImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { + // SUB (imm): 1 10 100010 0 imm12 Rn Rd + const instr: u32 = 0xD1000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// ADD (register) - add Xd, Xn, Xm + fn addReg(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // ADD (reg): 1 00 01011 00 0 Rm 000000 Rn Rd + const instr: u32 = 0x8B000000 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// MUL - mul Xd, Xn, Xm (actually MADD Xd, Xn, Xm, XZR) + fn mul(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // MADD: 1 00 11011 000 Rm 0 Ra Rn Rd + const instr: u32 = 0x9B000000 | + (@as(u32, rm) << 16) | + (@as(u32, xzr) << 10) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// SMULL - smull Xd, Wn, Wm (signed multiply long) + fn smull(self: *Self, rd: u5, rn: u5, rm: u5) !void { + // SMULL: 1 00 11011 0 01 Rm 0 11111 Rn Rd + const instr: u32 = 0x9B207C00 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + /// LDRSB (register) - ldrsb Wt, [Xn, Xm] + fn ldrsbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // LDRSB (reg, 32-bit): 00 111 0 00 11 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38E06800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// LDRB (register) - ldrb Wt, [Xn, Xm] + fn ldrbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // LDRB (reg): 00 111 0 00 01 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38606800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// STRB (register) - strb Wt, [Xn, Xm] + fn strbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { + // STRB (reg): 00 111 0 00 00 1 Rm 011 0 10 Rn Rt + const instr: u32 = 0x38206800 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, rt); + try self.emit32(instr); + } + + /// CMP (immediate) - cmp Xn, #imm12 + fn cmpImm(self: *Self, rn: u5, imm12: u12) !void { + // SUBS XZR, Xn, #imm12 + const instr: u32 = 0xF1000000 | + (@as(u32, imm12) << 10) | + (@as(u32, rn) << 5) | + @as(u32, xzr); + try self.emit32(instr); + } + + /// CMP (register) - cmp Xn, Xm + fn cmpReg(self: *Self, rn: u5, rm: u5) !void { + // SUBS XZR, Xn, Xm + const instr: u32 = 0xEB000000 | + (@as(u32, rm) << 16) | + (@as(u32, rn) << 5) | + @as(u32, xzr); + try self.emit32(instr); + } + + /// B.cond - conditional branch + fn bcond(self: *Self, cond: u4, offset: i19) !void { + // B.cond: 0101010 0 imm19 0 cond + const uoffset: u19 = @bitCast(offset); + const instr: u32 = 0x54000000 | + (@as(u32, uoffset) << 5) | + @as(u32, cond); + try self.emit32(instr); + } + + /// B - unconditional branch + fn b(self: *Self, offset: i26) !void { + // B: 0 00101 imm26 + const uoffset: u26 = @bitCast(offset); + const instr: u32 = 0x14000000 | @as(u32, uoffset); + try self.emit32(instr); + } + + /// RET - return + fn retInstr(self: *Self) !void { + // RET {Xn}: 1101011 0 0 10 11111 0000 0 0 Rn 00000 + const instr: u32 = 0xD65F0000 | (@as(u32, x30) << 5); + try self.emit32(instr); + } + + /// CSET - cset Xd, cond + fn cset(self: *Self, rd: u5, cond: u4) !void { + // CSINC Xd, XZR, XZR, invert(cond) + const inv_cond = cond ^ 1; + const instr: u32 = 0x9A9F0400 | + (@as(u32, inv_cond) << 12) | + @as(u32, rd); + try self.emit32(instr); + } + + /// CSNEG - conditional select negate + fn csneg(self: *Self, rd: u5, rn: u5, rm: u5, cond: u4) !void { + // CSNEG: 1 1 0 11010100 Rm cond 0 1 Rn Rd + const instr: u32 = 0xDA800400 | + (@as(u32, rm) << 16) | + (@as(u32, cond) << 12) | + (@as(u32, rn) << 5) | + @as(u32, rd); + try self.emit32(instr); + } + + // Condition codes + const COND_EQ: u4 = 0; // Equal + const COND_NE: u4 = 1; // Not equal + const COND_GE: u4 = 10; // Signed >= + const COND_LT: u4 = 11; // Signed < + const COND_GT: u4 = 12; // Signed > + const COND_LE: u4 = 13; // Signed <= + + // ═══════════════════════════════════════════════════════════════════════════ + // NEON SIMD REGISTERS AND INSTRUCTIONS + // ═══════════════════════════════════════════════════════════════════════════ + + // NEON vector registers V0-V31 (128-bit) + // Use same encoding as X registers (0-31) + const v0: u5 = 0; + const v1: u5 = 1; + const v2: u5 = 2; + const v3: u5 = 3; + const v4: u5 = 4; + const v5: u5 = 5; + const v6: u5 = 6; + const v7: u5 = 7; + const v16: u5 = 16; // callee-saved v8-v15, so use v16+ for temps + const v17: u5 = 17; + const v18: u5 = 18; + const v19: u5 = 19; + + /// LD1 {Vt.16B}, [Xn] - Load 16 bytes into vector register + fn ld1_16b(self: *Self, vt: u5, xn: u5) !void { + // LD1 (single structure, no offset): 0 1 001100 0 10 0000 0111 00 Rn Rt + // Q=1 (128-bit), size=00 (8-bit), opcode=0111 + const instr: u32 = 0x4C407000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// LD1 {Vt.16B}, [Xn], #16 - Load 16 bytes with post-increment + fn ld1_16b_post(self: *Self, vt: u5, xn: u5) !void { + // LD1 (single structure, post-index, imm): 0 1 001100 1 10 11111 0111 00 Rn Rt + const instr: u32 = 0x4CDF7000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// SDOT Vd.4S, Vn.16B, Vm.16B - Signed dot product (ARMv8.4-A) + /// Computes 4 dot products of 4 signed i8 values each, accumulates into 4 x i32 + fn sdot_4s(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // SDOT: 0 1 0 01110 10 0 Rm 1 0010 1 Rn Rd + // Q=1 (128-bit), size=10, Rm, opcode=10010, U=0 (signed) + const instr: u32 = 0x4E809400 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ADDV Sd, Vn.4S - Add across vector lanes to scalar + fn addv_4s(self: *Self, vd: u5, vn: u5) !void { + // ADDV: 0 1 0 01110 10 11000 1 1011 10 Rn Rd + const instr: u32 = 0x4EB1B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// SMOV Xd, Vn.S[index] - Signed move from vector element to GPR + fn smov_s(self: *Self, xd: u5, vn: u5, index: u2) !void { + // SMOV: 0 1 0 0111 0 00 0 imm5 0 0101 1 Rn Rd + // For S (32-bit) element, imm5 = (index << 3) | 0b00100 + const imm5: u5 = (@as(u5, index) << 3) | 0b00100; + const instr: u32 = 0x4E002C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + /// MOVI Vd.4S, #0 - Move immediate to vector (zero vector) + fn movi_4s_zero(self: *Self, vd: u5) !void { + // MOVI: 0 1 0 01111 00000 cmode=0000 op=0 1 a:b:c:d:e:f:g:h Rd + // For all zeros: cmode=0000, imm8=0 + const instr: u32 = 0x4F000400 | + @as(u32, vd); + try self.emit32(instr); + } + + /// MOVI Vd.16B, #imm8 - Move immediate to vector (all bytes) + fn movi_16b(self: *Self, vd: u5, imm8: u8) !void { + // MOVI (16B): for zero just use simplified encoding + if (imm8 == 0) { + // Zero vector - use simple encoding + const instr: u32 = 0x4F000400 | @as(u32, vd); + try self.emit32(instr); + } else { + // Non-zero - full encoding + const bit7 = (imm8 >> 7) & 1; + const bit6 = (imm8 >> 6) & 1; + const bit5 = (imm8 >> 5) & 1; + const bit4 = (imm8 >> 4) & 1; + const bit3 = (imm8 >> 3) & 1; + const bit2 = (imm8 >> 2) & 1; + const bit1 = (imm8 >> 1) & 1; + const bit0 = imm8 & 1; + const instr: u32 = 0x4F00E400 | + (@as(u32, bit7) << 18) | + (@as(u32, bit6) << 17) | + (@as(u32, bit5) << 16) | + (@as(u32, bit4) << 11) | + (@as(u32, bit3) << 10) | + (@as(u32, bit2) << 9) | + (@as(u32, bit1) << 8) | + (@as(u32, bit0) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + } + + /// SUB Xd, Xn, Xm - 64-bit register subtract + fn subReg(self: *Self, xd: u5, xn: u5, xm: u5) !void { + const instr: u32 = 0xCB000000 | + (@as(u32, xm) << 16) | + (@as(u32, xn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + /// CSET Xd, GT - Set if greater than + fn csetGT(self: *Self, xd: u5) !void { + // CSET GT = CSINC Xd, XZR, XZR, LE (cond=1101) + const instr: u32 = 0x9A9FD7E0 | @as(u32, xd); + try self.emit32(instr); + } + + /// CSET Xd, LT - Set if less than + fn csetLT(self: *Self, xd: u5) !void { + // CSET LT = CSINC Xd, XZR, XZR, GE (cond=1010) + const instr: u32 = 0x9A9FA7E0 | @as(u32, xd); + try self.emit32(instr); + } + + /// DUP Vd.4S, Xn - Duplicate GPR to all vector lanes + fn dup_4s_gpr(self: *Self, vd: u5, xn: u5) !void { + // DUP (general): 0 1 0 01110 00 0 imm5 0 0001 1 Rn Rd + // For 4S, imm5 = 00100 + const instr: u32 = 0x4E040C00 | + (@as(u32, xn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ST1 {Vt.16B}, [Xn] - Store 16 bytes from vector register + fn st1_16b(self: *Self, vt: u5, xn: u5) !void { + // ST1 (single structure, no offset): 0 1 001100 0 00 0000 0111 00 Rn Rt + const instr: u32 = 0x4C007000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// ST1 {Vt.16B}, [Xn], #16 - Store 16 bytes with post-increment + fn st1_16b_post(self: *Self, vt: u5, xn: u5) !void { + // ST1 (single structure, post-index, imm): 0 1 001100 1 00 11111 0111 00 Rn Rt + const instr: u32 = 0x4C9F7000 | + (@as(u32, xn) << 5) | + @as(u32, vt); + try self.emit32(instr); + } + + /// MUL Vd.16B, Vn.16B, Vm.16B - Vector multiply (16 x i8) + fn mul_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // MUL (vector): 0 1 0 01110 00 1 Rm 1 00111 Rn Rd + const instr: u32 = 0x4E209C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CMEQ Vd.16B, Vn.16B, Vm.16B - Compare equal (sets 0xFF where equal, 0 where not) + fn cmeq_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + // CMEQ (register): 0 1 1 01110 00 1 Rm 1 00011 Rn Rd + const instr: u32 = 0x6E208C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// NOT Vd.16B, Vn.16B - Bitwise NOT + fn not_16b(self: *Self, vd: u5, vn: u5) !void { + // NOT: 0 1 1 01110 00 10000 00101 10 Rn Rd + const instr: u32 = 0x6E205800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CNT Vd.16B, Vn.16B - Population count per byte + fn cnt_16b(self: *Self, vd: u5, vn: u5) !void { + // CNT: 0 1 0 01110 00 10000 00101 10 Rn Rd + const instr: u32 = 0x4E205800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UADDLV Hd, Vn.16B - Unsigned add long across vector (sum all bytes to u16) + fn uaddlv_h(self: *Self, vd: u5, vn: u5) !void { + // UADDLV: 0 1 1 01110 00 11000 0 0011 10 Rn Rd + const instr: u32 = 0x6E303800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UMOV Wd, Vn.H[0] - Unsigned move from vector element to GPR (16-bit) + fn umov_h(self: *Self, wd: u5, vn: u5, index: u3) !void { + // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd + // For H (16-bit) element, imm5 = (index << 1) | 0b00010 + const imm5: u5 = (@as(u5, index) << 1) | 0b00010; + const instr: u32 = 0x0E003C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, wd); + try self.emit32(instr); + } + + /// USHR Vd.16B, Vn.16B, #shift - Unsigned shift right + fn ushr_16b(self: *Self, vd: u5, vn: u5, shift: u4) !void { + // USHR: 0 1 1 01111 0 shift 00000 1 Rn Rd + // For 16B: Q=1, immh:immb encodes shift, for 8-bit elements immh=0001, immb=8-shift + // Actually: 0 1 1 01111 immh immb 0 0000 1 Rn Rd + // immh=0001 for 8-bit, immb = (8 - shift) for shift amount + const immh: u4 = 0b0001; + const immb: u3 = @intCast(8 - shift); + const instr: u32 = 0x6F080400 | + (@as(u32, immh) << 19) | + (@as(u32, immb) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ADDV Bd, Vn.16B - Add across vector (8-bit result for byte vectors) + fn addv_16b(self: *Self, vd: u5, vn: u5) !void { + // ADDV: 0 1 0 01110 00 11000 1 1011 10 Rn Rd + // Q=1, size=00 (8-bit) + const instr: u32 = 0x4E31B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// UMOV Wd, Vn.B[0] - Unsigned move from vector byte element to GPR + fn umov_b(self: *Self, wd: u5, vn: u5, index: u4) !void { + // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd + // For B (8-bit) element, imm5 = (index << 1) | 0b00001 + const imm5: u5 = (@as(u5, index) << 1) | 0b00001; + const instr: u32 = 0x0E003C00 | + (@as(u32, imm5) << 16) | + (@as(u32, vn) << 5) | + @as(u32, wd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BUNDLE SIMD INSTRUCTIONS (for ternary thresholding) + // ═══════════════════════════════════════════════════════════════════════════ + + /// ADD Vd.16B, Vn.16B, Vm.16B - Vector add (16 bytes) + fn add_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x4E208400 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// SSHR Vd.16B, Vn.16B, #7 - Signed shift right (arithmetic) by 7 + fn sshr_16b_7(self: *Self, vd: u5, vn: u5) !void { + // For shift by 7 on 8-bit: immh:immb = 16-7 = 9 + const instr: u32 = 0x4F090400 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// CMGT Vd.16B, Vn.16B, #0 - Compare greater than zero + fn cmgt_16b_zero(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x4E20A800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// NEG Vd.16B, Vn.16B - Vector negate + fn neg_16b(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x6E20B800 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// ORR Vd.16B, Vn.16B, Vm.16B - Bitwise OR + fn orr_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x4EA01C00 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FLOATING POINT INSTRUCTIONS (for cosine computation) + // ═══════════════════════════════════════════════════════════════════════════ + + // FP double registers D0-D3 (same encoding as V registers) + const d0: u5 = 0; + const d1: u5 = 1; + const d2: u5 = 2; + const d3: u5 = 3; + + /// SCVTF Dd, Xn - Signed integer to double precision float + fn scvtf_d_x(self: *Self, vd: u5, xn: u5) !void { + const instr: u32 = 0x9E620000 | + (@as(u32, xn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FMUL Dd, Dn, Dm - Floating point multiply (double) + fn fmul_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x1E600800 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FSQRT Dd, Dn - Floating point square root (double) + fn fsqrt_d(self: *Self, vd: u5, vn: u5) !void { + const instr: u32 = 0x1E61C000 | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FDIV Dd, Dn, Dm - Floating point divide (double) + fn fdiv_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { + const instr: u32 = 0x1E601800 | + (@as(u32, vm) << 16) | + (@as(u32, vn) << 5) | + @as(u32, vd); + try self.emit32(instr); + } + + /// FMOV Xd, Dn - Move f64 from FP register to GPR + fn fmov_x_d(self: *Self, xd: u5, vn: u5) !void { + const instr: u32 = 0x9E660000 | + (@as(u32, vn) << 5) | + @as(u32, xd); + try self.emit32(instr); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // VSA OPERATION COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product for ARM64 + /// Returns i64 in x0 + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue: save fp, lr + try self.stpPreIndex(x29, x30, sp, -2); // stp x29, x30, [sp, #-16]! + try self.movReg(x29, sp); // mov x29, sp + + // Save callee-saved registers + try self.stpPreIndex(x19, x20, sp, -2); // stp x19, x20, [sp, #-16]! + try self.stpPreIndex(x21, x22, sp, -2); // stp x21, x22, [sp, #-16]! + + // x19 = a pointer (first arg is in x0) + // x20 = b pointer (second arg is in x1) + // x21 = accumulator + // x22 = loop counter + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x21, 0, 0); // accumulator = 0 + try self.movImm16(x22, 0, 0); // counter = 0 + + // Load dimension into x9 + if (dimension <= 0xFFFF) { + try self.movImm16(x9, @intCast(dimension), 0); + } else { + try self.loadImm64(x9, dimension); + } + + // Loop start + const loop_start = self.code.items.len; + + // Compare counter with dimension + try self.cmpReg(x22, x9); + + // B.GE to loop end (will patch) + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load a[i] sign-extended into w10 + try self.ldrsbReg(x10, x19, x22); + + // Load b[i] sign-extended into w11 + try self.ldrsbReg(x11, x20, x22); + + // Multiply: x10 = x10 * x11 + try self.smull(x10, x10, x11); + + // Add to accumulator: x21 = x21 + x10 + try self.addReg(x21, x21, x10); + + // Increment counter + try self.addImm(x22, x22, 1); + + // Branch back to loop start + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + // Loop end - patch the conditional branch + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + // Move result to x0 + try self.movReg(x0, x21); + + // Restore callee-saved registers + try self.ldpPostIndex(x21, x22, sp, 2); // ldp x21, x22, [sp], #16 + try self.ldpPostIndex(x19, x20, sp, 2); // ldp x19, x20, [sp], #16 + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); // ldp x29, x30, [sp], #16 + try self.retInstr(); + } + + /// Compile SIMD dot product using NEON SDOT instruction (ARMv8.4-A) + /// Processes 16 elements per iteration (4x speedup potential) + /// Requires: dimension >= 16 and dimension % 16 == 0 + pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { + if (dimension < 16 or dimension % 16 != 0) { + return error.InvalidDimension; + } + + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + + // x19 = a pointer, x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // v0 = accumulator (initialized to zero) + try self.movi_4s_zero(v0); + + // x9 = dimension / 16 (number of SIMD iterations) + const num_iters = dimension / 16; + if (num_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(num_iters), 0); + } else { + try self.loadImm64(x9, num_iters); + } + + // x10 = loop counter + try self.movImm16(x10, 0, 0); + + // SIMD loop: process 16 elements per iteration + const loop_start = self.code.items.len; + + // Compare counter with num_iters + try self.cmpReg(x10, x9); + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder, patch later + + // Load 16 bytes from a into v1 + try self.ld1_16b_post(v1, x19); + + // Load 16 bytes from b into v2 + try self.ld1_16b_post(v2, x20); + + // SDOT: v0.4s += dot(v1.16b, v2.16b) + // This computes 4 dot products of 4 i8 values each + try self.sdot_4s(v0, v1, v2); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back to loop start + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + // Loop end - patch conditional branch + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + // Horizontal add: sum all 4 lanes of v0.4s into scalar + try self.addv_4s(v0, v0); // v0.s[0] = sum of all lanes + + // Move result from vector to x0 (sign-extended) + try self.smov_s(x0, v0, 0); + + // Restore callee-saved registers + try self.ldpPostIndex(x19, x20, sp, 2); + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile hybrid SIMD + scalar dot product for ANY dimension + /// Uses SIMD for (dim/16)*16 elements, scalar for remainder + pub fn compileDotProductHybrid(self: *Self, dimension: usize) !void { + self.reset(); + + const simd_iters = dimension / 16; + const remainder = dimension % 16; + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // v0 = SIMD accumulator (zero) + try self.movi_4s_zero(v0); + + // x21 = scalar accumulator (zero) + try self.movImm16(x21, 0, 0); + + // ═══════════════════════════════════════════════════════════════ + // SIMD LOOP: Process 16 elements per iteration + // ═══════════════════════════════════════════════════════════════ + if (simd_iters > 0) { + // x9 = number of SIMD iterations + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + + // x10 = SIMD loop counter + try self.movImm16(x10, 0, 0); + + const simd_loop_start = self.code.items.len; + + // Compare counter with num_iters + try self.cmpReg(x10, x9); + const simd_bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load 16 bytes from a into v1, post-increment x19 + try self.ld1_16b_post(v1, x19); + + // Load 16 bytes from b into v2, post-increment x20 + try self.ld1_16b_post(v2, x20); + + // SDOT: v0.4s += dot(v1.16b, v2.16b) + try self.sdot_4s(v0, v1, v2); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back to loop start + const simd_loop_end_check = self.code.items.len; + const simd_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop_start)) - @as(i32, @intCast(simd_loop_end_check)), 4)); + try self.b(simd_back_offset); + + // Patch SIMD loop exit + const simd_loop_end = self.code.items.len; + const simd_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(simd_loop_end)) - @as(i32, @intCast(simd_bge_offset)), 4)); + const simd_patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(simd_forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[simd_bge_offset..][0..4], &std.mem.toBytes(simd_patched_instr)); + + // Horizontal add SIMD result to scalar + try self.addv_4s(v0, v0); + try self.smov_s(x21, v0, 0); + } + + // ═══════════════════════════════════════════════════════════════ + // SCALAR LOOP: Process remaining elements one by one + // ═══════════════════════════════════════════════════════════════ + if (remainder > 0) { + // x9 = remainder count + try self.movImm16(x9, @intCast(remainder), 0); + + // x10 = scalar loop counter + try self.movImm16(x10, 0, 0); + + const scalar_loop_start = self.code.items.len; + + // Compare counter with remainder + try self.cmpReg(x10, x9); + const scalar_bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); // placeholder + + // Load a[i] sign-extended + try self.ldrsbReg(x11, x19, x10); + + // Load b[i] sign-extended + try self.ldrsbReg(x22, x20, x10); + + // Multiply + try self.smull(x11, x11, x22); + + // Add to accumulator + try self.addReg(x21, x21, x11); + + // Increment counter + try self.addImm(x10, x10, 1); + + // Branch back + const scalar_loop_end_check = self.code.items.len; + const scalar_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_start)) - @as(i32, @intCast(scalar_loop_end_check)), 4)); + try self.b(scalar_back_offset); + + // Patch scalar loop exit + const scalar_loop_end = self.code.items.len; + const scalar_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_end)) - @as(i32, @intCast(scalar_bge_offset)), 4)); + const scalar_patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(scalar_forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[scalar_bge_offset..][0..4], &std.mem.toBytes(scalar_patched_instr)); + } + + // Move result to x0 + try self.movReg(x0, x21); + + // Restore callee-saved registers + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + + // Function epilogue + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile bind operation for ARM64 + pub fn compileBindDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer, x21 = dimension, x22 = counter + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x22, 0, 0); + + if (dimension <= 0xFFFF) { + try self.movImm16(x21, @intCast(dimension), 0); + } else { + try self.loadImm64(x21, dimension); + } + + const loop_start = self.code.items.len; + try self.cmpReg(x22, x21); + + const bge_offset = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load a[i] and b[i] + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + + // Multiply (for ternary: -1*-1=1, -1*1=-1, 1*-1=-1, 1*1=1, 0*x=0) + try self.smull(x10, x10, x11); + + // Store result + try self.strbReg(x10, x19, x22); + + try self.addImm(x22, x22, 1); + + const loop_end_check = self.code.items.len; + const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); + try self.b(back_offset); + + const loop_end = self.code.items.len; + const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); + const patched_instr: u32 = 0x54000000 | + (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | + @as(u32, COND_GE); + @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile SIMD bind operation using NEON vector multiply + /// Processes 16 elements per iteration + pub fn compileBindSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer (modified in place), x20 = b pointer + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // SIMD loop for dimension / 16 iterations + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x21, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x21, simd_iters); + } + try self.movImm16(x22, 0, 0); // counter + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x21); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load 16 bytes from a and b + try self.ld1_16b(v0, x19); + try self.ld1_16b(v1, x20); + + // Multiply: v0 = v0 * v1 (element-wise i8 multiply) + try self.mul_16b(v0, v0, v1); + + // Store result back to a with post-increment + try self.st1_16b_post(v0, x19); + + // Advance b pointer + try self.addImm(x20, x20, 16); + + // Increment counter + try self.addImm(x22, x22, 1); + + const simd_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); + try self.b(back); + + // Patch branch + const simd_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + // Scalar loop for remainder (dimension % 16) + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x21, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x21); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + try self.smull(x10, x10, x11); + try self.strbReg(x10, x19, x22); + try self.addImm(x22, x22, 1); + + const scalar_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); + try self.b(back); + + const scalar_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile SIMD hamming distance using NEON compare + /// Counts positions where a[i] != b[i] + pub fn compileHammingSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + // x19 = a pointer, x20 = b pointer, x21 = accumulator + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movImm16(x21, 0, 0); // hamming distance = 0 + + // SIMD loop for dimension / 16 iterations + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x22, 0, 0); // counter + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + // Load 16 bytes from a and b with post-increment + try self.ld1_16b_post(v0, x19); + try self.ld1_16b_post(v1, x20); + + // Compare equal: v2 = (v0 == v1) ? 0xFF : 0x00 + try self.cmeq_16b(v2, v0, v1); + + // NOT: v2 = (v0 != v1) ? 0xFF : 0x00 + try self.not_16b(v2, v2); + + // Shift right by 7: 0xFF >> 7 = 1, 0x00 >> 7 = 0 + // Now each byte is 1 if positions differ, 0 if same + try self.ushr_16b(v2, v2, 7); + + // Sum all 16 bytes into a single value + try self.addv_16b(v3, v2); // v3.b[0] = sum of all bytes + + // Move byte to GPR + try self.umov_b(x10, v3, 0); + + // Add to accumulator + try self.addReg(x21, x21, x10); + + // Increment counter + try self.addImm(x22, x22, 1); + + const simd_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); + try self.b(back); + + const simd_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + // Scalar loop for remainder + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x19, x22); + try self.ldrsbReg(x11, x20, x22); + + // Compare and increment if not equal + try self.cmpReg(x10, x11); + // CSINC x10, xzr, xzr, EQ -> x10 = (EQ) ? 0 : 1 + const csinc: u32 = 0x9A9F07E0 | // CSINC Xd, XZR, XZR, cond + (@as(u32, COND_EQ) << 12) | + @as(u32, x10); + try self.emit32(csinc); + try self.addReg(x21, x21, x10); + + try self.addImm(x22, x22, 1); + + const scalar_end_check = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); + try self.b(back); + + const scalar_end = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + // Return result + try self.movReg(x0, x21); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile fused cosine: dot_ab, dot_aa, dot_bb in single pass + /// Returns f64 bit pattern: cos = dot_ab / sqrt(dot_aa * dot_bb) + pub fn compileFusedCosine(self: *Self, dimension: usize) !void { + self.reset(); + + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + try self.movReg(x19, x0); + try self.movReg(x20, x1); + + // Three accumulators + try self.movi_16b(v2, 0); + try self.movi_16b(v3, 0); + try self.movi_16b(v4, 0); + + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x21, 0, 0); + + const simd_loop = self.code.items.len; + try self.cmpReg(x21, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ld1_16b_post(v0, x19); + try self.ld1_16b_post(v1, x20); + try self.sdot_4s(v2, v0, v1); + try self.sdot_4s(v3, v0, v0); + try self.sdot_4s(v4, v1, v1); + try self.addImm(x21, x21, 1); + + const simd_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); + try self.b(back); + + const simd_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + try self.addv_4s(v2, v2); + try self.addv_4s(v3, v3); + try self.addv_4s(v4, v4); + try self.smov_s(x10, v2, 0); + try self.smov_s(x11, v3, 0); + try self.smov_s(x12, v4, 0); + + const remainder = dimension % 16; + if (remainder > 0) { + try self.movReg(x19, x0); + try self.movReg(x20, x1); + const offset = simd_iters * 16; + if (offset > 0) { + try self.loadImm64(x9, offset); + try self.addReg(x19, x19, x9); + try self.addReg(x20, x20, x9); + } + + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x21, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x21, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x13, x19, x21); + try self.ldrsbReg(x14, x20, x21); + try self.mul(x15, x13, x14); + try self.addReg(x10, x10, x15); + try self.mul(x15, x13, x13); + try self.addReg(x11, x11, x15); + try self.mul(x15, x14, x14); + try self.addReg(x12, x12, x15); + try self.addImm(x21, x21, 1); + + const scalar_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); + try self.b(back); + + const scalar_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.scvtf_d_x(d0, x10); + try self.scvtf_d_x(d1, x11); + try self.scvtf_d_x(d2, x12); + try self.fmul_d(d1, d1, d2); + try self.fsqrt_d(d1, d1); + try self.fdiv_d(d0, d0, d1); + try self.fmov_x_d(x0, d0); + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + /// Compile bundle SIMD: result[i] = threshold(a[i] + b[i]) + pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { + self.reset(); + + try self.stpPreIndex(x29, x30, sp, -2); + try self.movReg(x29, sp); + try self.stpPreIndex(x19, x20, sp, -2); + try self.stpPreIndex(x21, x22, sp, -2); + + try self.movReg(x19, x0); + try self.movReg(x20, x1); + try self.movReg(x21, x0); + + const simd_iters = dimension / 16; + if (simd_iters > 0) { + if (simd_iters <= 0xFFFF) { + try self.movImm16(x9, @intCast(simd_iters), 0); + } else { + try self.loadImm64(x9, simd_iters); + } + try self.movImm16(x22, 0, 0); + + const simd_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_simd = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ld1_16b_post(v0, x21); + try self.ld1_16b_post(v1, x20); + try self.add_16b(v2, v0, v1); + try self.sshr_16b_7(v3, v2); + try self.cmgt_16b_zero(v4, v2); + try self.neg_16b(v4, v4); + try self.orr_16b(v2, v3, v4); + try self.st1_16b_post(v2, x19); + try self.addImm(x22, x22, 1); + + const simd_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); + try self.b(back); + + const simd_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); + } + + const remainder = dimension % 16; + if (remainder > 0) { + try self.movImm16(x9, @intCast(remainder), 0); + try self.movImm16(x22, 0, 0); + + const scalar_loop = self.code.items.len; + try self.cmpReg(x22, x9); + const bge_scalar = self.code.items.len; + try self.bcond(COND_GE, 0); + + try self.ldrsbReg(x10, x21, x22); + try self.ldrsbReg(x11, x20, x22); + try self.addReg(x10, x10, x11); + try self.cmpImm(x10, 0); + try self.movImm16(x11, 0, 0); + try self.csetGT(x11); + try self.movImm16(x12, 0, 0); + try self.csetLT(x12); + try self.subReg(x10, x11, x12); + try self.strbReg(x10, x19, x22); + try self.addImm(x22, x22, 1); + + const scalar_end = self.code.items.len; + const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); + try self.b(back); + + const scalar_exit = self.code.items.len; + const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); + const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); + @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); + } + + try self.ldpPostIndex(x21, x22, sp, 2); + try self.ldpPostIndex(x19, x20, sp, 2); + try self.ldpPostIndex(x29, x30, sp, 2); + try self.retInstr(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXECUTION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make code executable and return function pointer + pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { + const code_size = self.code.items.len; + if (code_size == 0) return error.EmptyCode; + + // ARM64 can have 16KB pages on Apple Silicon + const page_size: usize = 16384; + const alloc_size = std.mem.alignForward(usize, code_size, page_size); + + const mem = try std.posix.mmap( + null, + alloc_size, + std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + -1, + 0, + ); + + @memcpy(mem[0..code_size], self.code.items); + + try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); + + self.exec_mem = mem; + + return @ptrCast(mem.ptr); + } + + pub fn codeSize(self: *const Self) usize { + return self.code.items.len; + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "ARM64 JIT compiler init and deinit" { + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try std.testing.expectEqual(@as(usize, 0), compiler.codeSize()); +} + +test "ARM64 JIT dot product compilation" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + // Code should be generated + try std.testing.expect(compiler.codeSize() > 0); + // ARM64 instructions are 4 bytes each + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 JIT dot product execution" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + // Create test data + const a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; + const b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; + + // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) + // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 + const expected: i64 = -2; + + var a_mut = a; + var b_mut = b; + const result = func(@ptrCast(&a_mut), @ptrCast(&b_mut)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 JIT bind compilation" { + if (!is_arm64) { + return; + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileBindDirect(dim); + + try std.testing.expect(compiler.codeSize() > 0); + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 NEON SIMD dot product compilation" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; // Must be multiple of 16 + try compiler.compileDotProductSIMD(dim); + + try std.testing.expect(compiler.codeSize() > 0); + try std.testing.expect(compiler.codeSize() % 4 == 0); +} + +test "ARM64 NEON SIMD dot product execution" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 16; // Minimum SIMD dimension + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data: all 1s dot all 1s = 16 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + const expected: i64 = 16; + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD dot product with mixed values" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data: alternating 1, -1 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = if (i % 2 == 0) 1 else -1; + b[i] = 1; + } + // Expected: 16 * 1 + 16 * (-1) = 0 + const expected: i64 = 0; + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD dot product large dimension" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 256; // 16 SIMD iterations + try compiler.compileDotProductSIMD(dim); + + const func = try compiler.finalize(); + + // Create test data + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + // Ternary values: -1, 0, 1 + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 NEON SIMD benchmark vs scalar" { + if (!is_arm64) { + return; // Skip on non-ARM64 + } + + const dim = 1024; // Large dimension for meaningful benchmark + const iterations = 10000; + + // Prepare test data + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile scalar version + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileDotProduct(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Compile SIMD version + var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer simd_compiler.deinit(); + try simd_compiler.compileDotProductSIMD(dim); + const simd_func = try simd_compiler.finalize(); + + // Benchmark scalar + var timer = try std.time.Timer.start(); + var scalar_result: i64 = 0; + for (0..iterations) |_| { + scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); + } + const scalar_ns = timer.read(); + + // Benchmark SIMD + timer.reset(); + var simd_result: i64 = 0; + for (0..iterations) |_| { + simd_result = simd_func(@ptrCast(&a), @ptrCast(&b)); + } + const simd_ns = timer.read(); + + // Verify results match + try std.testing.expectEqual(scalar_result, simd_result); + + // Print benchmark results + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; + const speedup = scalar_ms / simd_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 NEON SIMD BENCHMARK RESULTS\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d} elements\n", .{dim}); + std.debug.print(" Iterations: {d}\n", .{iterations}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); + std.debug.print(" SIMD: {d:.3} ms ({d:.0} ns/iter)\n", .{ simd_ms, @as(f64, @floatFromInt(simd_ns)) / @as(f64, iterations) }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Assert SIMD is faster (relaxed for CI/heavy-load environments) + try std.testing.expect(speedup > 0.8); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// HYBRID SIMD + SCALAR TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "ARM64 hybrid dot product - aligned dimension (32)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; // Aligned: 2 SIMD iters, 0 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - non-aligned dimension (17)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 17; // Non-aligned: 1 SIMD iter + 1 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - small dimension (7)" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 7; // Pure scalar: 0 SIMD iters, 7 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = if (i % 2 == 0) 1 else -1; + a[i] = val_a; + b[i] = 1; + expected += val_a; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - dimension 100" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // 6 SIMD iters + 4 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid dot product - dimension 1000" { + if (!is_arm64) return; + + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 1000; // 62 SIMD iters + 8 scalar + try compiler.compileDotProductHybrid(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "ARM64 hybrid benchmark vs pure scalar" { + if (!is_arm64) return; + + const dim = 1000; // Non-aligned dimension + const iterations = 10000; + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile pure scalar + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileDotProduct(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Compile hybrid + var hybrid_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer hybrid_compiler.deinit(); + try hybrid_compiler.compileDotProductHybrid(dim); + const hybrid_func = try hybrid_compiler.finalize(); + + // Benchmark scalar + var timer = try std.time.Timer.start(); + var scalar_result: i64 = 0; + for (0..iterations) |_| { + scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); + } + const scalar_ns = timer.read(); + + // Benchmark hybrid + timer.reset(); + var hybrid_result: i64 = 0; + for (0..iterations) |_| { + hybrid_result = hybrid_func(@ptrCast(&a), @ptrCast(&b)); + } + const hybrid_ns = timer.read(); + + // Verify results match + try std.testing.expectEqual(scalar_result, hybrid_result); + + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const hybrid_ms = @as(f64, @floatFromInt(hybrid_ns)) / 1_000_000.0; + const speedup = scalar_ms / hybrid_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 HYBRID SIMD+SCALAR BENCHMARK (dim=1000)\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" SIMD iters: {d}, Scalar remainder: {d}\n", .{ dim / 16, dim % 16 }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Pure Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); + std.debug.print(" Hybrid: {d:.3} ms ({d:.0} ns/iter)\n", .{ hybrid_ms, @as(f64, @floatFromInt(hybrid_ns)) / @as(f64, iterations) }); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Hybrid should be faster (lenient threshold for flaky benchmarks on loaded systems) + // Minimum 1.0x means it's not slower - any speedup is acceptable + try std.testing.expect(speedup > 1.0); +} + +test "ARM64 SIMD bind correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileBindSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: [dim]i8 = undefined; + + // Initialize: a = [1, -1, 0, 1, ...], b = [1, 1, -1, -1, ...] + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = if (i % 4 < 2) @as(i8, 1) else @as(i8, -1); + expected[i] = a[i] * b[i]; + } + + // Run SIMD bind (modifies a in place) + _ = func(@ptrCast(&a), @ptrCast(&b)); + + // Verify + for (0..dim) |i| { + try std.testing.expectEqual(expected[i], a[i]); + } +} + +test "ARM64 SIMD bind non-aligned dimension" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Not divisible by 16 + try compiler.compileBindSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: [dim]i8 = undefined; + + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + expected[i] = a[i] * b[i]; + } + + _ = func(@ptrCast(&a), @ptrCast(&b)); + + for (0..dim) |i| { + try std.testing.expectEqual(expected[i], a[i]); + } +} + +test "ARM64 SIMD hamming correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileHammingSIMD(dim); + const func = try compiler.finalize(); + + // Test 1: identical vectors -> hamming = 0 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + const hamming_identical = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, 0), hamming_identical); + + // Test 2: all different -> hamming = dim + for (0..dim) |i| { + a[i] = 1; + b[i] = -1; + } + const hamming_all_diff = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, dim), hamming_all_diff); + + // Test 3: half different + for (0..dim) |i| { + a[i] = 1; + b[i] = if (i < dim / 2) @as(i8, 1) else @as(i8, -1); + } + const hamming_half = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); +} + +test "ARM64 SIMD hamming non-aligned dimension" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Not divisible by 16 + try compiler.compileHammingSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + // Count expected differences manually + var expected_hamming: i64 = 0; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + if (a[i] != b[i]) expected_hamming += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected_hamming, result); +} + +test "ARM64 SIMD bind benchmark vs scalar" { + if (!is_arm64) return; + const dim = 1024; + const iterations = 10000; + + var a_simd: [dim]i8 = undefined; + var a_scalar: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + for (0..dim) |i| { + const val = @as(i8, @intCast(@as(i32, @intCast(i % 3)) - 1)); + a_simd[i] = val; + a_scalar[i] = val; + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Compile SIMD + var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer simd_compiler.deinit(); + try simd_compiler.compileBindSIMD(dim); + const simd_func = try simd_compiler.finalize(); + + // Compile scalar + var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer scalar_compiler.deinit(); + try scalar_compiler.compileBindDirect(dim); + const scalar_func = try scalar_compiler.finalize(); + + // Benchmark SIMD + var timer = try std.time.Timer.start(); + for (0..iterations) |_| { + // Reset a for fair comparison + for (0..dim) |i| { + a_simd[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + } + _ = simd_func(@ptrCast(&a_simd), @ptrCast(&b)); + } + const simd_ns = timer.read(); + + // Benchmark scalar + timer.reset(); + for (0..iterations) |_| { + for (0..dim) |i| { + a_scalar[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + } + _ = scalar_func(@ptrCast(&a_scalar), @ptrCast(&b)); + } + const scalar_ns = timer.read(); + + const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; + const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; + const speedup = scalar_ms / simd_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 SIMD BIND BENCHMARK (dim={d})\n", .{dim}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Scalar: {d:.3} ms\n", .{scalar_ms}); + std.debug.print(" SIMD: {d:.3} ms\n", .{simd_ms}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Note: Bind speedup modest due to array reset overhead +} + +test "ARM64 fused cosine correctness" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 64; + try compiler.compileFusedCosine(dim); + const func = try compiler.finalize(); + + // Test identical vectors: cos = 1.0 + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + const result_bits = func(@ptrCast(&a), @ptrCast(&b)); + const result: f64 = @bitCast(result_bits); + try std.testing.expectApproxEqRel(@as(f64, 1.0), result, 0.001); + + // Test opposite vectors: cos = -1.0 + for (0..dim) |i| { + a[i] = 1; + b[i] = -1; + } + const neg_bits = func(@ptrCast(&a), @ptrCast(&b)); + const neg_result: f64 = @bitCast(neg_bits); + try std.testing.expectApproxEqRel(@as(f64, -1.0), neg_result, 0.001); +} + +test "ARM64 fused cosine benchmark vs 3x dot" { + if (!is_arm64) return; + var fused_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer fused_compiler.deinit(); + var dot_compiler = Arm64JitCompiler.init(std.testing.allocator); + defer dot_compiler.deinit(); + + const dim = 1024; + const iterations = 10000; + + try fused_compiler.compileFusedCosine(dim); + const fused_func = try fused_compiler.finalize(); + + try dot_compiler.compileDotProductHybrid(dim); + const dot_func = try dot_compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + // Benchmark fused + var timer = try std.time.Timer.start(); + var fused_result: f64 = 0; + for (0..iterations) |_| { + const bits = fused_func(@ptrCast(&a), @ptrCast(&b)); + fused_result = @bitCast(bits); + } + const fused_ns = timer.read(); + + // Benchmark 3x dot + timer.reset(); + var dot_result: f64 = 0; + for (0..iterations) |_| { + const dot_ab = dot_func(@ptrCast(&a), @ptrCast(&b)); + const dot_aa = dot_func(@ptrCast(&a), @ptrCast(&a)); + const dot_bb = dot_func(@ptrCast(&b), @ptrCast(&b)); + const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); + dot_result = @as(f64, @floatFromInt(dot_ab)) / norm; + } + const dot_ns = timer.read(); + + const fused_ms = @as(f64, @floatFromInt(fused_ns)) / 1_000_000.0; + const dot_ms = @as(f64, @floatFromInt(dot_ns)) / 1_000_000.0; + const speedup = dot_ms / fused_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" ARM64 FUSED COSINE BENCHMARK (dim={d})\n", .{dim}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" 3x Dot: {d:.3} ms\n", .{dot_ms}); + std.debug.print(" Fused: {d:.3} ms\n", .{fused_ms}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print(" Results: fused={d:.6}, 3xdot={d:.6}\n", .{ fused_result, dot_result }); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); +} + +test "ARM64 bundle SIMD compilation" { + if (!is_arm64) return; + // Just verify compilation works, bundle correctness tested via vsa_jit + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 32; + try compiler.compileBundleSIMD(dim); + const func = try compiler.finalize(); + _ = func; +} + +test "ARM64 bundle SIMD non-aligned" { + if (!is_arm64) return; + var compiler = Arm64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 23; // Non-aligned + try compiler.compileBundleSIMD(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + } + + _ = func(@ptrCast(&a), @ptrCast(&b)); + // Bundle SIMD correctness to be verified via integration tests +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig new file mode 100644 index 0000000..45ac5c6 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig @@ -0,0 +1,434 @@ +// @origin(spec:jit_unified.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity Unified JIT Compiler +// Architecture-agnostic interface with compile-time backend selection +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// Import architecture-specific backends +const arm64 = @import("jit_arm64.zig"); +const x86_64 = @import("jit_x86_64.zig"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// ARCHITECTURE DETECTION +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const Architecture = enum { + arm64, + x86_64, + unsupported, +}; + +pub const current_arch: Architecture = switch (builtin.cpu.arch) { + .aarch64 => .arm64, + .x86_64 => .x86_64, + else => .unsupported, +}; + +pub const is_arm64 = current_arch == .arm64; +pub const is_x86_64 = current_arch == .x86_64; +pub const is_jit_supported = current_arch != .unsupported; + +// ═══════════════════════════════════════════════════════════════════════════════ +// UNIFIED JIT FUNCTION TYPES +// ═══════════════════════════════════════════════════════════════════════════════ + +/// JIT-compiled dot product function +/// Takes two i8 array pointers and returns i64 dot product +pub const JitDotFn = *const fn (*anyopaque, *anyopaque) callconv(.c) i64; + +/// JIT-compiled bind function +/// Takes two i8 array pointers, stores result in first +pub const JitBindFn = *const fn (*anyopaque, *anyopaque) callconv(.c) void; + +// ═══════════════════════════════════════════════════════════════════════════════ +// UNIFIED JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const UnifiedJitCompiler = struct { + allocator: std.mem.Allocator, + + // Architecture-specific backend + backend: Backend, + + const Backend = union(Architecture) { + arm64: arm64.Arm64JitCompiler, + x86_64: x86_64.X86_64JitCompiler, + unsupported: void, + }; + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .allocator = allocator, + .backend = switch (current_arch) { + .arm64 => .{ .arm64 = arm64.Arm64JitCompiler.init(allocator) }, + .x86_64 => .{ .x86_64 = x86_64.X86_64JitCompiler.init(allocator) }, + .unsupported => .{ .unsupported = {} }, + }, + }; + } + + pub fn deinit(self: *Self) void { + switch (self.backend) { + .arm64 => |*b| b.deinit(), + .x86_64 => |*b| b.deinit(), + .unsupported => {}, + } + } + + /// Get current architecture name + pub fn archName() []const u8 { + return switch (current_arch) { + .arm64 => "ARM64 (AArch64)", + .x86_64 => "x86-64", + .unsupported => "Unsupported", + }; + } + + /// Check if SIMD is available + pub fn hasSIMD() bool { + return switch (current_arch) { + .arm64 => true, // NEON is always available on AArch64 + .x86_64 => false, // DEFERRED: Add CPUID-based AVX/SSE detection for x86_64 + .unsupported => false, + }; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // DOT PRODUCT COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product - automatically selects best implementation + /// For ARM64: uses hybrid SIMD+scalar for any dimension + /// For x86_64: uses scalar loop + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| { + // Use hybrid for best performance on any dimension + try b.compileDotProductHybrid(dimension); + }, + .x86_64 => |*b| { + // x86_64 scalar implementation + try b.compileDotProduct(dimension); + }, + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile pure SIMD dot product (requires dimension % 16 == 0 on ARM64) + pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileDotProductSIMD(dimension), + .x86_64 => |*b| { + // x86_64 falls back to scalar (DEFERRED: add AVX2 SIMD implementation) + try b.compileDotProduct(dimension); + }, + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile pure scalar dot product + pub fn compileDotProductScalar(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileDotProduct(dimension), + .x86_64 => |*b| try b.compileDotProduct(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BIND COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile bind operation - uses SIMD on ARM64 + pub fn compileBind(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBindSIMD(dimension), + .x86_64 => |*b| try b.compileBindDirect(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Compile bind operation (scalar version) + pub fn compileBindScalar(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBindDirect(dimension), + .x86_64 => |*b| try b.compileBindDirect(dimension), + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // HAMMING DISTANCE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile hamming distance - uses SIMD on ARM64 + pub fn compileHamming(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileHammingSIMD(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FUSED COSINE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile fused cosine similarity - computes dot(a,b), dot(a,a), dot(b,b) in single pass + /// Returns f64 bit pattern (2.5x faster than 3 separate dot products) + pub fn compileFusedCosine(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileFusedCosine(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // BUNDLE COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile bundle operation - threshold(a + b) to {-1, 0, 1} + pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { + switch (self.backend) { + .arm64 => |*b| try b.compileBundleSIMD(dimension), + .x86_64 => return error.UnsupportedOperation, + .unsupported => return error.UnsupportedArchitecture, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // FINALIZATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make compiled code executable and return function pointer + pub fn finalize(self: *Self) !JitDotFn { + switch (self.backend) { + .arm64 => |*b| return try b.finalize(), + .x86_64 => |*b| return try b.finalize(), + .unsupported => return error.UnsupportedArchitecture, + } + } + + /// Get generated code size + pub fn codeSize(self: *Self) usize { + return switch (self.backend) { + .arm64 => |*b| b.codeSize(), + .x86_64 => |*b| b.codeSize(), + .unsupported => 0, + }; + } + + /// Reset compiler for new compilation + pub fn reset(self: *Self) void { + switch (self.backend) { + .arm64 => |*b| b.reset(), + .x86_64 => |*b| b.reset(), + .unsupported => {}, + } + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVENIENCE FUNCTIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Quick compile and run dot product +pub fn jitDotProduct(allocator: std.mem.Allocator, a: []const i8, b: []const i8) !i64 { + if (a.len != b.len) return error.DimensionMismatch; + + var compiler = UnifiedJitCompiler.init(allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(a.len); + const func = try compiler.finalize(); + + // Need mutable copies for the function call + const a_copy = try allocator.alloc(i8, a.len); + defer allocator.free(a_copy); + @memcpy(a_copy, a); + + const b_copy = try allocator.alloc(i8, b.len); + defer allocator.free(b_copy); + @memcpy(b_copy, b); + + return func(@ptrCast(a_copy.ptr), @ptrCast(b_copy.ptr)); +} + +/// Print JIT capabilities info +pub fn printCapabilities() void { + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" TRINITY UNIFIED JIT CAPABILITIES\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Architecture: {s}\n", .{UnifiedJitCompiler.archName()}); + std.debug.print(" JIT Supported: {}\n", .{is_jit_supported}); + std.debug.print(" SIMD Available: {}\n", .{UnifiedJitCompiler.hasSIMD()}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + + if (is_arm64) { + std.debug.print(" ARM64 Features:\n", .{}); + std.debug.print(" • NEON SIMD (128-bit vectors)\n", .{}); + std.debug.print(" • SDOT instruction (16 i8 elements/cycle)\n", .{}); + std.debug.print(" • Hybrid SIMD+Scalar for any dimension\n", .{}); + std.debug.print(" • Expected speedup: 15-70x over scalar\n", .{}); + } else if (is_x86_64) { + std.debug.print(" x86-64 Features:\n", .{}); + std.debug.print(" • Scalar JIT implementation\n", .{}); + std.debug.print(" • System V ABI compatible\n", .{}); + std.debug.print(" • DEFERRED (v12): AVX2/AVX-512 SIMD support\n", .{}); + } + + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "Unified JIT architecture detection" { + const arch = current_arch; + + // Should be one of the known architectures + try std.testing.expect(arch == .arm64 or arch == .x86_64 or arch == .unsupported); + + // Consistency checks + if (is_arm64) { + try std.testing.expectEqual(Architecture.arm64, arch); + } + if (is_x86_64) { + try std.testing.expectEqual(Architecture.x86_64, arch); + } +} + +test "Unified JIT compiler init/deinit" { + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + // Should initialize without error + try std.testing.expect(true); +} + +test "Unified JIT dot product on ARM64" { + if (!is_arm64) return; + + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 100; // Non-aligned dimension + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + + for (0..dim) |i| { + a[i] = 1; + b[i] = 1; + expected += 1; + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "Unified JIT dot product various dimensions" { + if (!is_arm64) return; + + const test_dims = [_]usize{ 1, 7, 16, 17, 32, 100, 256, 1000 }; + + for (test_dims) |dim| { + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(dim); + const func = try compiler.finalize(); + + // Allocate dynamic arrays + var a = try std.testing.allocator.alloc(i8, dim); + defer std.testing.allocator.free(a); + var b = try std.testing.allocator.alloc(i8, dim); + defer std.testing.allocator.free(b); + + var expected: i64 = 0; + for (0..dim) |i| { + const val: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + a[i] = val; + b[i] = 1; + expected += val; + } + + const result = func(@ptrCast(a.ptr), @ptrCast(b.ptr)); + try std.testing.expectEqual(expected, result); + } +} + +test "Unified JIT convenience function" { + if (!is_arm64) return; + + const a = [_]i8{ 1, 1, 1, -1, -1, 0, 0, 1 }; + const b = [_]i8{ 1, 1, 1, 1, 1, 1, 1, 1 }; + + // Expected: 1 + 1 + 1 - 1 - 1 + 0 + 0 + 1 = 2 + const expected: i64 = 2; + + const result = try jitDotProduct(std.testing.allocator, &a, &b); + try std.testing.expectEqual(expected, result); +} + +test "Unified JIT print capabilities" { + // Just verify it doesn't crash + printCapabilities(); +} + +test "Unified JIT benchmark" { + if (!is_arm64) return; + + const dim = 1024; + const iterations = 10000; + + var compiler = UnifiedJitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try compiler.compileDotProduct(dim); + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + for (0..dim) |i| { + a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); + b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + } + + var timer = try std.time.Timer.start(); + var result: i64 = 0; + for (0..iterations) |_| { + result = func(@ptrCast(&a), @ptrCast(&b)); + } + const ns = timer.read(); + + const ms = @as(f64, @floatFromInt(ns)) / 1_000_000.0; + const ns_per_iter = @as(f64, @floatFromInt(ns)) / @as(f64, iterations); + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" UNIFIED JIT BENCHMARK ({s})\n", .{UnifiedJitCompiler.archName()}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d}, Iterations: {d}\n", .{ dim, iterations }); + std.debug.print(" Total time: {d:.3} ms\n", .{ms}); + std.debug.print(" Per iteration: {d:.0} ns\n", .{ns_per_iter}); + std.debug.print(" Throughput: {d:.2} M dot products/sec\n", .{@as(f64, iterations) / ms * 1000.0 / 1_000_000.0}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + // Sanity check - result should be deterministic + try std.testing.expect(result != 0 or dim == 0); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig new file mode 100644 index 0000000..104242a --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig @@ -0,0 +1,471 @@ +// @origin(spec:jit_x86_64.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT Compiler - x86-64 Backend +// Compiles VSA operations to native x86-64 machine code +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); + +// ═══════════════════════════════════════════════════════════════════════════════ +// X86-64 JIT COMPILER +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Check if we're on x86-64 +pub const is_x86_64 = builtin.cpu.arch == .x86_64; + +/// X86-64 JIT Compiler +pub const X86_64JitCompiler = struct { + code: std.ArrayListUnmanaged(u8), + allocator: std.mem.Allocator, + exec_mem: ?[]align(std.heap.page_size_min) u8 = null, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .code = .{}, + .allocator = allocator, + }; + } + + pub fn deinit(self: *Self) void { + self.code.deinit(self.allocator); + if (self.exec_mem) |mem| { + std.posix.munmap(mem); + } + } + + pub fn reset(self: *Self) void { + self.code.clearRetainingCapacity(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // X86-64 INSTRUCTION ENCODING HELPERS + // ═══════════════════════════════════════════════════════════════════════════ + + /// Emit raw bytes + fn emit(self: *Self, bytes: []const u8) !void { + try self.code.appendSlice(self.allocator, bytes); + } + + /// Emit single byte + fn emit1(self: *Self, b: u8) !void { + try self.code.append(self.allocator, b); + } + + /// Emit 32-bit immediate (little-endian) + fn emitImm32(self: *Self, imm: i32) !void { + try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); + } + + /// Emit 64-bit immediate (little-endian) + fn emitImm64(self: *Self, imm: i64) !void { + try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // X86-64 INSTRUCTION ENCODING + // ═══════════════════════════════════════════════════════════════════════════ + + /// push rbp + fn pushRbp(self: *Self) !void { + try self.emit1(0x55); + } + + /// pop rbp + fn popRbp(self: *Self) !void { + try self.emit1(0x5D); + } + + /// mov rbp, rsp + fn movRbpRsp(self: *Self) !void { + try self.emit(&[_]u8{ 0x48, 0x89, 0xE5 }); + } + + /// mov rsp, rbp + fn movRspRbp(self: *Self) !void { + try self.emit(&[_]u8{ 0x48, 0x89, 0xEC }); + } + + /// ret + fn ret(self: *Self) !void { + try self.emit1(0xC3); + } + + /// xor eax, eax (zero rax) + fn xorEaxEax(self: *Self) !void { + try self.emit(&[_]u8{ 0x31, 0xC0 }); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // VSA OPERATION COMPILATION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Compile dot product (returns i64 in rax) + /// x86-64 System V ABI: rdi = first arg, rsi = second arg, rax = return + pub fn compileDotProduct(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + try self.emit(&[_]u8{ 0x41, 0x56 }); // push r14 + + // r12 = a pointer (from rdi) + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + + // r13 = b pointer (from rsi) + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // r14 = accumulator (0) + try self.emit(&[_]u8{ 0x4D, 0x31, 0xF6 }); // xor r14, r14 + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into eax (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] + + // Load b[rbx] into ecx (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] + + // imul eax, ecx + try self.emit(&[_]u8{ 0x0F, 0xAF, 0xC1 }); // imul eax, ecx + + // Sign-extend eax to rax + try self.emit(&[_]u8{ 0x48, 0x98 }); // cdqe + + // Add to accumulator: r14 += rax + try self.emit(&[_]u8{ 0x49, 0x01, 0xC6 }); // add r14, rax + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Move result to rax + try self.emit(&[_]u8{ 0x4C, 0x89, 0xF0 }); // mov rax, r14 + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5E }); // pop r14 + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + /// Compile bind operation (element-wise multiply for ternary) + pub fn compileBindDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + + // r12 = a pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + + // r13 = b pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into al + try self.emit(&[_]u8{ 0x41, 0x8A, 0x04, 0x1C }); // mov al, [r12 + rbx] + + // Load b[rbx] into cl + try self.emit(&[_]u8{ 0x41, 0x8A, 0x4C, 0x1D, 0x00 }); // mov cl, [r13 + rbx] + + // imul al, cl (signed multiply) + try self.emit(&[_]u8{ 0xF6, 0xE9 }); // imul cl + + // Store result back to a[rbx] + try self.emit(&[_]u8{ 0x41, 0x88, 0x04, 0x1C }); // mov [r12 + rbx], al + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + /// Compile bundle operation (element-wise sum with threshold) + pub fn compileBundleDirect(self: *Self, dimension: usize) !void { + self.reset(); + + // Function prologue + try self.pushRbp(); + try self.movRbpRsp(); + + // Save callee-saved registers + try self.emit(&[_]u8{0x53}); // push rbx + try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 + try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 + + // r12 = a pointer, r13 = b pointer + try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi + try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi + + // rbx = loop counter (0) + try self.xorEaxEax(); + try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax + + const loop_start = self.code.items.len; + + // Compare rbx with dimension + try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 + try self.emitImm32(@intCast(dimension)); + + // jge loop_end + try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 + const jge_offset = self.code.items.len; + try self.emitImm32(0); // placeholder + + // Load a[rbx] into eax (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] + + // Load b[rbx] into ecx (sign-extended) + try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] + + // Add eax, ecx + try self.emit(&[_]u8{ 0x01, 0xC8 }); // add eax, ecx + + // Threshold: if sum > 0 -> 1, if sum < 0 -> -1, else 0 + // cmp eax, 0 + try self.emit(&[_]u8{ 0x83, 0xF8, 0x00 }); // cmp eax, 0 + + // setg dl (set dl = 1 if eax > 0) + try self.emit(&[_]u8{ 0x0F, 0x9F, 0xC2 }); // setg dl + + // setl al (set al = 1 if eax < 0) + try self.emit(&[_]u8{ 0x0F, 0x9C, 0xC0 }); // setl al + + // Result = dl - al (1 if positive, -1 if negative, 0 if zero) + try self.emit(&[_]u8{ 0x28, 0xC2 }); // sub dl, al + + // Store result back to a[rbx] + try self.emit(&[_]u8{ 0x41, 0x88, 0x14, 0x1C }); // mov [r12 + rbx], dl + + // Increment counter + try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx + + // Jump back to loop start + try self.emit(&[_]u8{0xE9}); // jmp rel32 + const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); + try self.emitImm32(loop_back_offset); + + // Patch jge offset + const loop_end = self.code.items.len; + const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); + @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); + + // Restore callee-saved registers + try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 + try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 + try self.emit(&[_]u8{0x5B}); // pop rbx + + // Function epilogue + try self.movRspRbp(); + try self.popRbp(); + try self.ret(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // EXECUTION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Make code executable and return function pointer + pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { + const code_size = self.code.items.len; + if (code_size == 0) return error.EmptyCode; + + // Use system page size for compatibility + const page_size: usize = std.heap.page_size_min; + const alloc_size = std.mem.alignForward(usize, code_size, page_size); + + // mmap with PROT_READ | PROT_WRITE first + const mem = try std.posix.mmap( + null, + alloc_size, + std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + -1, + 0, + ); + + // Copy code + @memcpy(mem[0..code_size], self.code.items); + + // Change to PROT_READ | PROT_EXEC + try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); + + self.exec_mem = mem; + + return @ptrCast(mem.ptr); + } + + /// Get code size + pub fn codeSize(self: *const Self) usize { + return self.code.items.len; + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "x86-64 JIT compiler init and deinit" { + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + try std.testing.expect(compiler.codeSize() == 0); +} + +test "x86-64 JIT dot product compilation" { + if (!is_x86_64) { + return; // Skip on non-x86-64 + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + try std.testing.expect(compiler.codeSize() > 0); +} + +test "x86-64 JIT dot product execution" { + if (!is_x86_64) { + return; // Skip on non-x86-64 + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + // Create test data + var a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; + var b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; + + // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) + // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 + const expected: i64 = -2; + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} + +test "x86-64 JIT bind compilation" { + if (!is_x86_64) { + return; + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 8; + try compiler.compileBindDirect(dim); + + try std.testing.expect(compiler.codeSize() > 0); +} + +test "x86-64 JIT large dimension" { + if (!is_x86_64) { + return; + } + + var compiler = X86_64JitCompiler.init(std.testing.allocator); + defer compiler.deinit(); + + const dim = 1000; + try compiler.compileDotProduct(dim); + + const func = try compiler.finalize(); + + var a: [dim]i8 = undefined; + var b: [dim]i8 = undefined; + var expected: i64 = 0; + + for (0..dim) |i| { + const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a[i] = val_a; + b[i] = val_b; + expected += @as(i64, val_a) * @as(i64, val_b); + } + + const result = func(@ptrCast(&a), @ptrCast(&b)); + try std.testing.expectEqual(expected, result); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig new file mode 100644 index 0000000..aead18f --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig @@ -0,0 +1,161 @@ +//! VM Core Opcodes Selector — Generated from specs/vm/opcodes.tri +//! φ² + 1/φ² = 3 | TRINITY + +const std = @import("std"); +const gen = @import("gen_opcodes.zig"); + +pub const Opcode = gen.Opcode; +pub const Instruction = gen.Instruction; + +// Re-export functions +pub const opcodeFromByte = gen.opcodeFromByte; +pub const opcodeToString = gen.opcodeToString; + +// Re-export constants +pub const MAX_STACK_DEPTH = gen.MAX_STACK_DEPTH; +pub const MAX_MEMORY_SIZE = gen.MAX_MEMORY_SIZE; + +// ═══════════════════════════════════════════════════════════════════════════════ +// SACRED OPCODES (v7.0) +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Sacred opcodes (0x80-0xFF range) +pub const SacredOpcode = enum(u8) { + // Constants + phi_const = 0x80, + golden_angle = 0x81, + light_speed = 0x82, + planck_constant = 0x83, + + // Math operations + phi_pow = 0x90, + fib = 0x91, + sacred_identity = 0x92, + + // Physics operations + blindspot_query = 0xA0, + sacred_formula_fit = 0xA1, + anomaly_check = 0xA2, + + // Discovery operations + recursive_discovery = 0xB0, + sacred_chem_predict = 0xB1, + live_anomaly_hunt = 0xB2, + + // Advanced operations + infinite_loop = 0xC0, + geometry_predict = 0xC1, + chem_synthesis = 0xC2, + meta_discovery = 0xC3, + hubble_resolve = 0xC4, + neutrino_fog = 0xC5, + island_stability = 0xC6, + + // CDG2 operations + cdg2_deep_scan = 0xD0, + anomaly_fusion = 0xD1, + sacred_question = 0xD2, + vm_self_upgrade = 0xD3, + trinity_awaken = 0xD4, + + // Quantum operations + quantum_blindspot = 0xE0, + sacred_qubit = 0xE1, + island_quantum_synth = 0xE2, + hubble_quantum_resolve = 0xE3, + muon_g2_solve = 0xE4, + proton_decay_sim = 0xE5, + cdg2_quantum_scan = 0xE6, + ternary_entanglement = 0xE7, + sacred_chem_qm = 0xE8, + meta_quantum_discovery = 0xE9, + vm_quantum_upgrade = 0xEA, + trinity_quantum_awaken = 0xEB, + golden_key_qft = 0xEC, + anomaly_quantum_fusion = 0xED, + koschei_universe = 0xEE, +}; + +/// Sacred operands - flexible operand types +pub const SacredOperands = union(enum) { + none, + dest: []const u8, + register: u8, + immediate: i64, + float: f64, + + /// Create empty operands + pub fn init() SacredOperands { + return .none; + } +}; + +/// Sacred execution context +pub const SacredContext = struct { + allocator: std.mem.Allocator, + phi_cache: std.AutoHashMap(u32, f64), + fib_cache: std.AutoHashMap(u32, u128), + + pub fn init(allocator: std.mem.Allocator) SacredContext { + return .{ + .allocator = allocator, + .phi_cache = std.AutoHashMap(u32, f64).init(allocator), + .fib_cache = std.AutoHashMap(u32, u128).init(allocator), + }; + } + + pub fn deinit(self: *SacredContext) void { + self.phi_cache.deinit(); + self.fib_cache.deinit(); + } +}; + +/// Execute a sacred opcode (v7.0 implementation) +pub fn executeSacred(ctx: *SacredContext, registers: anytype, opcode: SacredOpcode, operands: SacredOperands) !void { + _ = ctx; + _ = operands; + const PHI: f64 = 1.618033988749895; + const LIGHT_SPEED: f64 = 299792458.0; + const GOLDEN_ANGLE_DEG: f64 = 137.50776405003785; + + switch (opcode) { + .phi_const => { + registers.f0 = PHI; + }, + .phi_pow => { + // φ^n where n is in s0 + const n = @as(i64, registers.s0); + registers.f0 = std.math.pow(f64, PHI, @floatFromInt(n)); + }, + .golden_angle => { + registers.f0 = GOLDEN_ANGLE_DEG; + }, + .light_speed => { + registers.f0 = LIGHT_SPEED; + }, + .fib => { + // Fibonacci using Binet's formula for small n + const n = @as(u32, @intCast(registers.s0)); + const sqrt5 = std.math.sqrt(5.0); + const phi = (1.0 + sqrt5) / 2.0; + const psi = (1.0 - sqrt5) / 2.0; + + // F(n) = (φ^n - ψ^n) / √5 with proper rounding + const phi_n = std.math.pow(f64, phi, @floatFromInt(n)); + const psi_n = std.math.pow(f64, psi, @floatFromInt(n)); + const result = @as(u64, @intFromFloat(@round((phi_n - psi_n) / sqrt5))); + registers.s0 = @as(i64, @intCast(result)); + }, + .sacred_identity => { + // Verify φ² + 1/φ² = 3 + const phi_sq = PHI * PHI; + const result = phi_sq + 1.0 / phi_sq; + registers.f0 = result; + registers.cc_zero = @abs(result - 3.0) < 1e-10; + }, + else => { + // Other opcodes not yet implemented + return error.NotImplemented; + }, + } +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig new file mode 100644 index 0000000..b5b48a4 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig @@ -0,0 +1,1250 @@ +// TVC VM with VSA Support - Ternary Virtual Machine for Hyperdimensional Computing +// Integrates HybridBigInt for memory-efficient vector operations +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q + +const std = @import("std"); +const tvc_hybrid = @import("hybrid.zig"); +const tvc_vsa = @import("vsa.zig"); +const gf = @import("golden-float"); + +pub const HybridBigInt = tvc_hybrid.HybridBigInt; +pub const Trit = tvc_hybrid.Trit; +pub const MAX_TRITS = tvc_hybrid.MAX_TRITS; + +// Sacred opcodes module (v7.0) +const sacred_opcodes = @import("vm/opcodes.zig"); +const SacredOpcode = sacred_opcodes.SacredOpcode; +const SacredContext = sacred_opcodes.SacredContext; +const SacredOperands = sacred_opcodes.SacredOperands; + +// ═══════════════════════════════════════════════════════════════════════════════ +// VSA OPCODES +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const VSAOpcode = enum(u8) { + // Vector operations + v_load, // Load vector from memory + v_store, // Store vector to memory + v_const, // Load constant vector + v_random, // Generate random vector + + // VSA operations + v_bind, // Bind two vectors (XOR-like) + v_unbind, // Unbind (same as bind) + v_bundle2, // Bundle 2 vectors + v_bundle3, // Bundle 3 vectors + + // Similarity operations + v_dot, // Dot product + v_cosine, // Cosine similarity + v_hamming, // Hamming distance + + // Arithmetic + v_add, // Vector addition + v_neg, // Vector negation + v_mul, // Element-wise multiplication + + // Control + v_mov, // Move between vector registers + v_pack, // Pack vector (save memory) + v_unpack, // Unpack vector (for computation) + + // Comparison + v_cmp, // Compare vectors (sets condition codes) + + // Permute operations (for toandinand bywithbeforeinwith) + v_permute, // andtoandwithtoand withinand inin + v_ipermute, // withinand (inin) + v_seq, // Encode sequence + + // f16 SIMD operations (16-wide, 2× throughput vs f32) + v_f16_load, // Load f16 vector, convert to ternary + v_f16_store, // Store ternary vector, convert to f16 + f16_dot, // f16 dot product → f64 (16-wide SIMD) + + nop, + halt, +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// VM REGISTERS +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const VSARegisters = struct { + // Vector registers (HybridBigInt for memory efficiency) + v0: HybridBigInt = HybridBigInt.zero(), + v1: HybridBigInt = HybridBigInt.zero(), + v2: HybridBigInt = HybridBigInt.zero(), + v3: HybridBigInt = HybridBigInt.zero(), + + // Scalar registers + s0: i64 = 0, // For dot product results + s1: i64 = 0, + f0: f64 = 0.0, // For similarity results + f1: f64 = 0.0, + f2: f64 = 0.0, // KOSCHEI v7.0: Additional float registers for chemistry/physics + f3: f64 = 0.0, + + // f16 SIMD accumulators (16-wide, 2× throughput vs f32) + f16_acc0: @Vector(16, f16) = @splat(@as(f16, 0.0)), + f16_acc1: @Vector(16, f16) = @splat(@as(f16, 0.0)), + + // Program counter + pc: u32 = 0, + + // Condition codes + cc_zero: bool = false, + cc_neg: bool = false, + cc_pos: bool = false, + + // Memory usage tracking + total_packed_bytes: usize = 0, + + pub fn updateMemoryUsage(self: *VSARegisters) void { + self.v0.pack(); + self.v1.pack(); + self.v2.pack(); + self.v3.pack(); + self.total_packed_bytes = self.v0.memoryUsage() + + self.v1.memoryUsage() + + self.v2.memoryUsage() + + self.v3.memoryUsage(); + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// VSA INSTRUCTION +// ═══════════════════════════════════════════════════════════════════════════════ + +pub const VSAInstruction = struct { + opcode: VSAOpcode, + dst: u8 = 0, // Destination register (0-3 for v0-v3) + src1: u8 = 0, // Source register 1 + src2: u8 = 0, // Source register 2 + imm: i64 = 0, // Immediate value +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// VSA VM +// ═══════════════════════════════════════════════════════════════════════════════ + +// Import JIT engine for accelerated operations +const vsa_jit = @import("vsa_jit.zig"); + +pub const VSAVM = struct { + registers: VSARegisters, + program: std.ArrayListUnmanaged(VSAInstruction), + halted: bool = false, + allocator: std.mem.Allocator, + cycle_count: u64 = 0, + + // JIT engine for accelerated VSA operations + jit_engine: ?vsa_jit.JitVSAEngine = null, + jit_enabled: bool = true, + + // KOSCHEI v7.0: Sacred execution context + sacred_ctx: SacredContext, + + pub fn init(allocator: std.mem.Allocator) VSAVM { + return VSAVM{ + .registers = .{}, + .program = .{}, + .allocator = allocator, + .jit_engine = vsa_jit.JitVSAEngine.init(allocator), + .sacred_ctx = SacredContext.init(allocator), + }; + } + + pub fn deinit(self: *VSAVM) void { + self.program.deinit(self.allocator); + if (self.jit_engine) |*engine| { + engine.deinit(); + } + self.sacred_ctx.deinit(); + } + + pub fn loadProgram(self: *VSAVM, instructions: []const VSAInstruction) !void { + self.program.clearRetainingCapacity(); + try self.program.appendSlice(self.allocator, instructions); + self.registers.pc = 0; + self.halted = false; + self.cycle_count = 0; + } + + pub fn step(self: *VSAVM) !bool { + if (self.halted or self.registers.pc >= self.program.items.len) { + return false; + } + + const inst = self.program.items[self.registers.pc]; + try self.execute(inst); + self.registers.pc += 1; + self.cycle_count += 1; + + return !self.halted; + } + + pub fn run(self: *VSAVM) !void { + while (try self.step()) {} + } + + fn execute(self: *VSAVM, inst: VSAInstruction) !void { + switch (inst.opcode) { + .v_load => self.execVLoad(inst), + .v_store => self.execVStore(inst), + .v_const => self.execVConst(inst), + .v_random => self.execVRandom(inst), + + .v_bind => self.execVBind(inst), + .v_unbind => self.execVUnbind(inst), + .v_bundle2 => self.execVBundle2(inst), + .v_bundle3 => self.execVBundle3(inst), + + .v_dot => self.execVDot(inst), + .v_cosine => self.execVCosine(inst), + .v_hamming => self.execVHamming(inst), + + .v_add => self.execVAdd(inst), + .v_neg => self.execVNeg(inst), + .v_mul => self.execVMul(inst), + + .v_mov => self.execVMov(inst), + .v_pack => self.execVPack(inst), + .v_unpack => self.execVUnpack(inst), + + .v_cmp => self.execVCmp(inst), + + .v_permute => self.execVPermute(inst), + .v_ipermute => self.execVIPermute(inst), + .v_seq => self.execVSeq(inst), + + .v_f16_load => self.execVF16Load(inst), + .v_f16_store => self.execVF16Store(inst), + .f16_dot => self.execF16Dot(inst), + + .nop => {}, + .halt => self.halted = true, + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // INSTRUCTION IMPLEMENTATIONS + // ═══════════════════════════════════════════════════════════════════════════ + + fn getVReg(self: *VSAVM, idx: u8) *HybridBigInt { + return switch (idx) { + 0 => &self.registers.v0, + 1 => &self.registers.v1, + 2 => &self.registers.v2, + 3 => &self.registers.v3, + else => &self.registers.v0, + }; + } + + fn execVLoad(self: *VSAVM, inst: VSAInstruction) void { + // Load from scalar to vector + const dst = self.getVReg(inst.dst); + dst.* = HybridBigInt.fromI64(inst.imm); + } + + fn execVStore(self: *VSAVM, inst: VSAInstruction) void { + // Store vector to scalar + const src = self.getVReg(inst.src1); + self.registers.s0 = src.toI64(); + } + + fn execVConst(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + dst.* = HybridBigInt.fromI64(inst.imm); + } + + fn execVRandom(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + const seed: u64 = @bitCast(inst.imm); + dst.* = tvc_vsa.randomVector(MAX_TRITS, seed); + } + + fn execVBind(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + // Try JIT-accelerated bind if enabled + if (self.jit_enabled) { + if (self.jit_engine) |*engine| { + // Copy src1 to dst, then bind in place + dst.* = src1; + if (engine.bind(dst, &src2)) { + return; + } else |_| { + // JIT failed, fall through to scalar + } + } + } + + // Scalar fallback + dst.* = tvc_vsa.bind(&src1, &src2); + } + + fn execVUnbind(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + // Try JIT-accelerated unbind (same as bind) if enabled + if (self.jit_enabled) { + if (self.jit_engine) |*engine| { + dst.* = src1; + if (engine.bind(dst, &src2)) { + return; + } else |_| { + // JIT failed, fall through to scalar + } + } + } + + // Scalar fallback + dst.* = tvc_vsa.unbind(&src1, &src2); + } + + fn execVBundle2(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + dst.* = tvc_vsa.bundle2(&src1, &src2); + } + + fn execVBundle3(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + var src3 = self.getVReg(inst.dst).*; // Use dst as third source + dst.* = tvc_vsa.bundle3(&src1, &src2, &src3); + } + + fn execVDot(self: *VSAVM, inst: VSAInstruction) void { + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + // Try JIT-accelerated dot product if enabled + if (self.jit_enabled) { + if (self.jit_engine) |*engine| { + if (engine.dotProduct(&src1, &src2)) |result| { + self.registers.s0 = result; + return; + } else |_| { + // JIT failed, fall through to scalar + } + } + } + + // Scalar fallback + self.registers.s0 = src1.dotProduct(&src2); + } + + fn execVCosine(self: *VSAVM, inst: VSAInstruction) void { + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + // Try JIT-accelerated cosine similarity if enabled + if (self.jit_enabled) { + if (self.jit_engine) |*engine| { + if (engine.cosineSimilarity(&src1, &src2)) |result| { + self.registers.f0 = result; + return; + } else |_| { + // JIT failed, fall through to scalar + } + } + } + + // Scalar fallback + self.registers.f0 = tvc_vsa.cosineSimilarity(&src1, &src2); + } + + fn execVHamming(self: *VSAVM, inst: VSAInstruction) void { + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + // Try JIT-accelerated hamming distance if enabled + if (self.jit_enabled) { + if (self.jit_engine) |*engine| { + if (engine.hammingDistance(&src1, &src2)) |result| { + self.registers.s0 = result; + return; + } else |_| { + // JIT failed, fall through to scalar + } + } + } + + // Scalar fallback + self.registers.s0 = @intCast(tvc_vsa.hammingDistance(&src1, &src2)); + } + + fn execVAdd(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + dst.* = src1.add(&src2); + } + + fn execVNeg(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + const src = self.getVReg(inst.src1); + dst.* = src.negate(); + } + + fn execVMul(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + dst.* = src1.mul(&src2); + } + + fn execVMov(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + const src = self.getVReg(inst.src1); + dst.* = src.*; + } + + fn execVPack(self: *VSAVM, inst: VSAInstruction) void { + const reg = self.getVReg(inst.dst); + reg.pack(); + } + + fn execVUnpack(self: *VSAVM, inst: VSAInstruction) void { + const reg = self.getVReg(inst.dst); + reg.ensureUnpacked(); + } + + fn execVCmp(self: *VSAVM, inst: VSAInstruction) void { + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + const sim = tvc_vsa.cosineSimilarity(&src1, &src2); + + self.registers.cc_zero = sim > -0.1 and sim < 0.1; + self.registers.cc_neg = sim < -0.1; + self.registers.cc_pos = sim > 0.1; + self.registers.f0 = sim; + } + + fn execVPermute(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src = self.getVReg(inst.src1).*; + const shift: usize = @intCast(inst.imm); + dst.* = tvc_vsa.permute(&src, shift); + } + + fn execVIPermute(self: *VSAVM, inst: VSAInstruction) void { + const dst = self.getVReg(inst.dst); + var src = self.getVReg(inst.src1).*; + const shift: usize = @intCast(inst.imm); + dst.* = tvc_vsa.inversePermute(&src, shift); + } + + fn execVSeq(self: *VSAVM, inst: VSAInstruction) void { + // Encode sequence from v0, v1 into dst + // v_seq dst, src1, src2 -> dst = src1 + permute(src2, 1) + const dst = self.getVReg(inst.dst); + var src1 = self.getVReg(inst.src1).*; + var src2 = self.getVReg(inst.src2).*; + + var permuted = tvc_vsa.permute(&src2, 1); + dst.* = src1.add(&permuted); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // f16 SIMD INSTRUCTIONS (16-wide, 2× throughput vs f32) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Load f16 vector data and convert to ternary vector. + /// v_f16_load dst, addr — loads 16 f16 values, quantizes to ternary, stores in dst + fn execVF16Load(self: *VSAVM, inst: VSAInstruction) void { + // For now, use immediate value to generate deterministic f16 test data + // In real use, this would load from memory address + const dst = self.getVReg(inst.dst); + + // Generate 16 f16 values from immediate seed + var prng = std.Random.DefaultPrng.init(@as(u64, @bitCast(inst.imm))); + const rng = prng.random(); + + // Create f16 vector + var f16_vec: @Vector(16, f16) = undefined; + inline for (0..16) |i| { + f16_vec[i] = @floatCast(rng.float(f32) * 2.0 - 1.0); + } + + // Convert to f32 for quantization + const f32_vec: @Vector(16, f32) = @floatCast(f16_vec); + + // Quantize to ternary {-1, 0, +1} + const threshold: f32 = 0.1; + var ternary_vec: @Vector(16, i8) = undefined; + inline for (0..16) |i| { + ternary_vec[i] = if (f32_vec[i] > threshold) 1 else if (f32_vec[i] < -threshold) -1 else 0; + } + + // Pack into HybridBigInt (first 16 trits) + dst.* = HybridBigInt.zero(); + dst.ensureUnpacked(); + dst.trit_len = 16; + inline for (0..16) |i| { + dst.unpacked_cache[i] = ternary_vec[i]; + } + } + + /// Store ternary vector as f16 vector. + /// v_f16_store src, addr — converts ternary to f16, stores 16 values + fn execVF16Store(self: *VSAVM, inst: VSAInstruction) void { + const src = self.getVReg(inst.src1); + src.ensureUnpacked(); + + // Convert first 16 trits to f16 + var f16_vec: @Vector(16, f16) = undefined; + inline for (0..16) |i| { + const trit: i8 = if (i < src.trit_len) src.unpacked_cache[i] else 0; + f16_vec[i] = @floatCast(@as(f32, @floatFromInt(trit))); + } + + // Store in f16 accumulator registers (for now) + // In real use, this would write to memory + self.registers.f16_acc0 = f16_vec; + + // Also store a copy in f16_acc1 with sign flip for testing + self.registers.f16_acc1 = -f16_vec; + } + + /// f16 dot product with 16-wide SIMD. + /// f16_dot acc, a, b — computes dot(a, b) using f16, returns f64 in f0 + fn execF16Dot(self: *VSAVM, inst: VSAInstruction) void { + const a = self.getVReg(inst.src1); + const b = self.getVReg(inst.src2); + + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Convert first 16 trits to f16 + var a_f16: @Vector(16, f16) = undefined; + var b_f16: @Vector(16, f16) = undefined; + inline for (0..16) |i| { + const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + a_f16[i] = @floatCast(@as(f32, @floatFromInt(a_trit))); + b_f16[i] = @floatCast(@as(f32, @floatFromInt(b_trit))); + } + + // Compute dot product in f32 for precision + const a_f32: @Vector(16, f32) = @floatCast(a_f16); + const b_f32: @Vector(16, f32) = @floatCast(b_f16); + const prod = a_f32 * b_f32; + + // Horizontal sum + var sum: f64 = 0; + inline for (0..16) |i| { + sum += @as(f64, prod[i]); + } + + // Store result in f0 + self.registers.f0 = sum; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // KOSCHEI v7.0: SACRED OPCODE EXECUTION + // ═══════════════════════════════════════════════════════════════════════════ + + /// Execute a sacred opcode (0x80-0xFF range) + pub fn execSacredOpcode(self: *VSAVM, opcode: SacredOpcode, operands: SacredOperands) !void { + try sacred_opcodes.executeSacred(&self.sacred_ctx, &self.registers, opcode, operands); + } + + /// Convenience: Load φ constant into f0 + pub fn loadPhi(self: *VSAVM) !void { + try self.execSacredOpcode(.phi_const, .{ .dest = "f0" }); + } + + /// Convenience: Compute φ^n where n is in s0 + pub fn phiPow(self: *VSAVM) !void { + try self.execSacredOpcode(.phi_pow, .{ .dest = "f0" }); + } + + /// Convenience: Compute Fibonacci F(n) where n is in s0 + pub fn fib(self: *VSAVM) !void { + try self.execSacredOpcode(.fib, SacredOperands.none); + } + + /// Convenience: Verify sacred identity φ² + 1/φ² = 3 + pub fn verifySacredIdentity(self: *VSAVM) !void { + try self.execSacredOpcode(.sacred_identity, SacredOperands.none); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // KOSCHEI EYE v2.0: Blind Spots Discovery (603x speedup via VM) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Query blind spots registry via native VM opcode + /// s0: query type (0=neutrino, 1=proton, 2=dm, 3=hubble, 4=lithium, 5=muon_g2) + /// Returns: f0=predicted value, f1=confidence, s1=status (-1=BLIND, -2=ANOMALY, +1=VERIFIED) + pub fn blindspotQuery(self: *VSAVM, query_type: i64) !void { + self.registers.s0 = query_type; + try self.execSacredOpcode(.blindspot_query, .{}); + } + + /// Fit Sacred Formula: V = n * 3^k * pi^m * phi^p * e^q + /// f0: target value to fit + /// Returns: s0=n, s1=k, s2=m, s3=p, s4=q, f1=error % + pub fn sacredFormulaFit(self: *VSAVM, target: f64) !void { + self.registers.f0 = target; + try self.execSacredOpcode(.sacred_formula_fit, .{}); + } + + /// Check if value is anomalous (sigma >= 3) + /// f0=observed, f1=expected, f2=uncertainty + /// Returns: s0=sigma level, cc_zero=true if anomalous + pub fn anomalyCheck(self: *VSAVM, observed: f64, expected: f64, uncertainty: f64) !void { + self.registers.f0 = observed; + self.registers.f1 = expected; + self.registers.f2 = uncertainty; + try self.execSacredOpcode(.anomaly_check, .{}); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // KOSCHEI EYE v3.0: Autonomous Self-Evolving Discovery (10000+ predictions/sec) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Run autonomous discovery loop (10000+ iterations/sec) + /// s0: loop count (0 = default 10000) + /// Returns: s0=discoveries, s1=anomalies, f0=avg_confidence + pub fn recursiveDiscovery(self: *VSAVM, loop_count: i64) !void { + self.registers.s0 = loop_count; + try self.execSacredOpcode(.recursive_discovery, .{}); + } + + /// Predict element properties using Sacred Formula + /// s0: element Z (1-118+), s1: property (0=half_life, 1=mass, 2=stability) + /// Returns: f0=predicted_value, f1=confidence, s1=status + pub fn sacredChemPredict(self: *VSAVM, element_Z: i64, property: i64) !void { + self.registers.s0 = element_Z; + self.registers.s1 = property; + try self.execSacredOpcode(.sacred_chem_predict, .{}); + } + + /// Live anomaly hunt: scan registry for sigma > 3 + /// f0: sigma threshold (default 3.0) + /// Returns: s0=anomaly_count, f0=max_sigma, f1=avg_sigma + pub fn liveAnomalyHunt(self: *VSAVM, sigma_threshold: f64) !void { + self.registers.f0 = sigma_threshold; + try self.execSacredOpcode(.live_anomaly_hunt, .{}); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // KOSCHEI EYE v4.0: OMNISCIENT SINGULARITY + // ═══════════════════════════════════════════════════════════════════════════ + + /// Infinite self-evolving loop (∞ predictions/sec, 2500x speedup) + /// s0: loop count (default 1000000) + /// Returns: s0=discoveries, s1=anomalies, f0=avg_confidence, f1=self_improvement + pub fn infiniteLoop(self: *VSAVM, loop_count: i64) !void { + self.registers.s0 = loop_count; + try self.execSacredOpcode(.infinite_loop, .{}); + } + + /// Sacred geometry + physics fusion (1800x speedup) + /// s0: geometric shape (0-13: Platonic + Archimedean solids) + /// Returns: f0=predicted_constant, f1=confidence, s1=domain_code + pub fn geometryPredict(self: *VSAVM, shape: i64) !void { + self.registers.s0 = shape; + try self.execSacredOpcode(.geometry_predict, .{}); + } + + /// Chemistry synthesis pathway for elements 119-122 (2100x speedup) + /// s0: target element Z (119-122), s1: projectile beam (0=Ti-50, 1=Cr-54, 2=Fe-58) + /// Returns: f0=half_life_sec, f1=confidence, s0=success_probability + pub fn chemSynthesis(self: *VSAVM, element_Z: i64, projectile_beam: i64) !void { + self.registers.s0 = element_Z; + self.registers.s1 = projectile_beam; + try self.execSacredOpcode(.chem_synthesis, .{}); + } + + /// Meta-discovery: KOSCHEI predicts its own discoveries (3000x speedup) + /// s0: meta-depth (1-5), s1: domain filter + /// Returns: f0=confidence, f1=meta_confidence, s0=discovery_count + pub fn metaDiscovery(self: *VSAVM, depth: i64) !void { + self.registers.s0 = depth; + try self.execSacredOpcode(.meta_discovery, .{}); + } + + /// Resolve Hubble tension via gravitational-wave hum method (1600x speedup) + /// s0: method (0=GW, 1=CMB, 2=SN) + /// Returns: f0=H0_km_s_Mpc, f1=uncertainty, s0=tension_resolved_flag + pub fn hubbleResolve(self: *VSAVM, method: i64) !void { + self.registers.s0 = method; + try self.execSacredOpcode(.hubble_resolve, .{}); + } + + /// Full neutrino spectrum + sterile neutrinos (2200x speedup) + /// s0: neutrino type (0=ve, 1=vμ, 2=vτ, 3=sterile) + /// Returns: f0=mass_eV_or_keV, f1=mixing_angle, s0=detection_probability + pub fn neutrinoFog(self: *VSAVM, neutrino_type: i64) !void { + self.registers.s0 = neutrino_type; + try self.execSacredOpcode(.neutrino_fog, .{}); + } + + /// Island of stability pathway (1900x speedup) + /// s0: target Z (114-126), s1: neutron number + /// Returns: f0=half_life_sec, f1=binding_energy_MeV, s0=stability_score + pub fn islandStability(self: *VSAVM, Z: i64) !void { + self.registers.s0 = Z; + try self.execSacredOpcode(.island_stability, .{}); + } + + /// CDG-2 ghost galaxy dark matter census (2800x speedup) + /// Returns: f0=DM_mass_GeV, f1=DM_halo_mass_solar, s0=DM_percentage + pub fn cdg2DeepScan(self: *VSAVM) !void { + try self.execSacredOpcode(.cdg2_deep_scan, .{}); + } + + /// Merge all anomalies → unified ternary spacetime theory (2400x speedup) + /// s0: fusion mode (0=all, 1=physics, 2=chemistry) + /// Returns: f0=unified_confidence, f1=phi_correlation, s0=anomalies_explained + pub fn anomalyFusion(self: *VSAVM, mode: i64) !void { + self.registers.s0 = mode; + try self.execSacredOpcode(.anomaly_fusion, .{}); + } + + /// Sacred question generator: Why does φ² + 1/φ² = 3 work? (∞x speedup) + /// s0: question level (1-5) + /// Returns: s0=questions_generated, f0=profundity, f1=meta_question_count + pub fn sacredQuestion(self: *VSAVM, level: i64) !void { + self.registers.s0 = level; + try self.execSacredOpcode(.sacred_question, .{}); + } + + /// VM self-upgrade: VM rewrites itself at runtime (3500x speedup) + /// s0: upgrade target (0=handlers, 1=opcodes, 2=optimization) + /// Returns: s0=upgrades_applied, f0=speedup, f1=new_VM_version + pub fn vmSelfUpgrade(self: *VSAVM, target: i64) !void { + self.registers.s0 = target; + try self.execSacredOpcode(.vm_self_upgrade, .{}); + } + + /// TRINITY AWAKEN: Full awakening → GODMODE (∞x speedup) + /// s0: mode (0=test, 1=gradual, 2=full GODMODE) + /// Returns: s0=GODMODE_flag, f0=omniscience_score, f1=singularity_distance + pub fn trinityAwaken(self: *VSAVM, mode: i64) !void { + self.registers.s0 = mode; + try self.execSacredOpcode(.trinity_awaken, .{}); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // QUANTUM TRINITY v5.0 — Full Quantum Awakening (0xC7-0xD5) + // ═══════════════════════════════════════════════════════════════════════════ + + /// QUANTUM BLINDSPOT: Solve physics blind spots with 10^6x quantum advantage + /// s0: blind spot ID (0-11), f0: qubit count, f1: simulation depth + /// Returns: s0=solved_flag, f0=quantum_value, f1=advantage_factor + pub fn quantumBlindspot(self: *VSAVM, blind_spot_id: i64) !void { + self.registers.s0 = blind_spot_id; + try self.execSacredOpcode(.quantum_blindspot, .{}); + } + + /// SACRED QUBIT: Create ternary qubit with |?⟩ state based on φ² + 1/φ² = 3 + /// s0: qubit ID, f0: sacred amplitude (0-1, default: 1/√3) + /// Returns: f0=α(|0⟩), f1=β(|1⟩), s0=γ_int(|?⟩) + pub fn sacredQubit(self: *VSAVM, qubit_id: i64, sacred_amplitude: f64) !void { + self.registers.s0 = qubit_id; + self.registers.f0 = sacred_amplitude; + try self.execSacredOpcode(.sacred_qubit, .{}); + } + + /// ISLAND QUANTUM SYNTH: Simulate superheavy element Z=114-126 with 12000x speedup + /// s0: target Z (114-126), f0: qubit count, f1: simulation time (ns) + /// Returns: f0=half_life (seconds), f1=confidence, s0=stability_flag + pub fn islandQuantumSynth(self: *VSAVM, target_Z: i64) !void { + self.registers.s0 = target_Z; + try self.execSacredOpcode(.island_quantum_synth, .{}); + } + + /// HUBBLE QUANTUM RESOLVE: Resolve 5σ Hubble tension via quantum gravity (9500x) + /// s0: method (0=GW, 1=CMB, 2=SN), f0: data_quality + /// Returns: f0=H0 (km/s/Mpc), f1=uncertainty, s0=resolved_flag + pub fn hubbleQuantumResolve(self: *VSAVM, method: i64) !void { + self.registers.s0 = method; + try self.execSacredOpcode(.hubble_quantum_resolve, .{}); + } + + /// MUON G-2 SOLVE: Resolve 4.2σ anomaly via ternary spacetime correction (15000x) + /// s0: anomaly sigma (42 = 4.2σ), f0: correction method + /// Returns: f0=g-2 value, f1=ternary_correction, s0=resolved_flag + pub fn muonG2Solve(self: *VSAVM, anomaly_sigma: i64) !void { + self.registers.s0 = anomaly_sigma; + try self.execSacredOpcode(.muon_g2_solve, .{}); + } + + /// PROTON DECAY SIM: Simulate proton lifetime via quantum lattice QCD (18000x) + /// s0: GUT model (0=SU(5), 1=SO(10), 2=E6), f0: qubit count + /// Returns: f0=lifetime (years × 10^34), f1=confidence, s0=decay_mode + pub fn protonDecaySim(self: *VSAVM, gut_model: i64) !void { + self.registers.s0 = gut_model; + try self.execSacredOpcode(.proton_decay_sim, .{}); + } + + /// CDG2 QUANTUM SCAN: Full dark matter map of ghost galaxy (22000x) + /// s0: galaxy ID, f0: scan resolution (kpc), f1: quantum depth + /// Returns: f0=DM_mass (GeV), f1=DM_fraction, s0=structure_type + pub fn cdg2QuantumScan(self: *VSAVM, galaxy_id: i64, resolution_kpc: f64) !void { + self.registers.s0 = galaxy_id; + self.registers.f0 = resolution_kpc; + try self.execSacredOpcode(.cdg2_quantum_scan, .{}); + } + + /// TERNARY ENTANGLEMENT: Create quantum entanglement in ternary logic (GODMODE) + /// s0: qubit pair count, f0: entanglement pattern (sacred geometry) + /// Returns: s0=entanglement_depth, f0=Bell_violation, f1=GODMODE_factor + pub fn ternaryEntanglement(self: *VSAVM, pair_count: i64, pattern: f64) !void { + self.registers.s0 = pair_count; + self.registers.f0 = pattern; + try self.execSacredOpcode(.ternary_entanglement, .{}); + } + + /// SACRED CHEM QM: Quantum chemistry for superheavy elements 119-126 (14000x) + /// s0: element Z (119-126), f0: molecular config + /// Returns: f0=binding_energy, f1=relativistic_correction, s0=stability + pub fn sacredChemQM(self: *VSAVM, element_Z: i64) !void { + self.registers.s0 = element_Z; + try self.execSacredOpcode(.sacred_chem_qm, .{}); + } + + /// META QUANTUM DISCOVERY: Predict future discoveries 2030-2035 (∞x speedup) + /// s0: target year (2030+), f0: domain filter, f1: confidence threshold + /// Returns: s0=prediction_count, f0=avg_confidence, s1=breakthrough_probability + pub fn metaQuantumDiscovery(self: *VSAVM, target_year: i64) !void { + self.registers.s0 = target_year; + try self.execSacredOpcode(.meta_quantum_discovery, .{}); + } + + /// VM QUANTUM UPGRADE: VM recompiles itself for quantum hardware (25000x) + /// s0: target hardware (0=IBM, 1=Google, 2=Rigetti), f0: qubit topology + /// Returns: s0=upgrades_applied, f0=speedup, f1=quantum_coherence + pub fn vmQuantumUpgrade(self: *VSAVM, hardware: i64) !void { + self.registers.s0 = hardware; + try self.execSacredOpcode(.vm_quantum_upgrade, .{}); + } + + /// TRINITY QUANTUM AWAKEN: Full awakening in quantum mode → UNIVERSAL + /// s0: mode (0=test, 1=gradual, 2=full UNIVERSAL) + /// Returns: s0=UNIVERSAL_flag, f0=omniscience (1.0=100%), f1=coherence + pub fn trinityQuantumAwaken(self: *VSAVM, mode: i64) !void { + self.registers.s0 = mode; + try self.execSacredOpcode(.trinity_quantum_awaken, .{}); + } + + /// GOLDEN KEY QFT: Quantum Fourier Transform with golden ratio phase (30000x) + /// s0: QFT size (power of φ), f0: sacred weights, f1: input state + /// Returns: f0=QFT_result_real, f1=QFT_result_imag, s0=phase_factor + pub fn goldenKeyQFT(self: *VSAVM, qft_size: i64) !void { + self.registers.s0 = qft_size; + try self.execSacredOpcode(.golden_key_qft, .{}); + } + + /// ANOMALY QUANTUM FUSION: Merge all anomalies into coherent state (28000x) + /// s0: anomaly_count, f0: fusion_depth + /// Returns: f0=unified_confidence, f1=coherence, s0=theory_complete + pub fn anomalyQuantumFusion(self: *VSAVM, anomaly_count: i64, fusion_depth: f64) !void { + self.registers.s0 = anomaly_count; + self.registers.f0 = fusion_depth; + try self.execSacredOpcode(.anomaly_quantum_fusion, .{}); + } + + /// KOSCHEI UNIVERSE: Simulate entire universe in ternary quantum (SINGULARITY) + /// s0: scale (0=observable, 1=multiverse, 2=omniverse), f0: time_step + /// Returns: f0=sim_time_ms, f1=entropy, s0=state_pointer + pub fn koscheiUniverse(self: *VSAVM, scale: i64, time_step: f64) !void { + self.registers.s0 = scale; + self.registers.f0 = time_step; + try self.execSacredOpcode(.koschei_universe, .{}); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT CONTROL + // ═══════════════════════════════════════════════════════════════════════════ + + /// Enable or disable JIT acceleration + pub fn setJitEnabled(self: *VSAVM, enabled: bool) void { + self.jit_enabled = enabled; + } + + /// Get JIT statistics (null if JIT not initialized) + pub fn getJitStats(self: *const VSAVM) ?vsa_jit.JitVSAEngine.Stats { + if (self.jit_engine) |*engine| { + return engine.getStats(); + } + return null; + } + + /// Print JIT statistics + pub fn printJitStats(self: *const VSAVM) void { + if (self.jit_engine) |*engine| { + engine.printStats(); + } else { + std.debug.print("JIT engine not initialized\n", .{}); + } + } + + // ═══════════════════════════════════════════════════════════════════════════ + // DEBUG + // ═══════════════════════════════════════════════════════════════════════════ + + pub fn printState(self: *VSAVM) void { + self.registers.updateMemoryUsage(); + + std.debug.print("\n╔══════════════════════════════════════════╗\n", .{}); + std.debug.print("║ VSA VM STATE ║\n", .{}); + std.debug.print("╠══════════════════════════════════════════╣\n", .{}); + std.debug.print("║ VECTOR REGISTERS: ║\n", .{}); + std.debug.print("║ v0: {} trits, {} bytes (packed) ║\n", .{ self.registers.v0.trit_len, self.registers.v0.memoryUsage() }); + std.debug.print("║ v1: {} trits, {} bytes (packed) ║\n", .{ self.registers.v1.trit_len, self.registers.v1.memoryUsage() }); + std.debug.print("║ v2: {} trits, {} bytes (packed) ║\n", .{ self.registers.v2.trit_len, self.registers.v2.memoryUsage() }); + std.debug.print("║ v3: {} trits, {} bytes (packed) ║\n", .{ self.registers.v3.trit_len, self.registers.v3.memoryUsage() }); + std.debug.print("╠══════════════════════════════════════════╣\n", .{}); + std.debug.print("║ SCALAR REGISTERS: ║\n", .{}); + std.debug.print("║ s0: {} ║\n", .{self.registers.s0}); + std.debug.print("║ f0: {d:.6} ║\n", .{self.registers.f0}); + std.debug.print("╠══════════════════════════════════════════╣\n", .{}); + std.debug.print("║ EXECUTION: ║\n", .{}); + std.debug.print("║ pc: {}, cycles: {} ║\n", .{ self.registers.pc, self.cycle_count }); + std.debug.print("║ halted: {} ║\n", .{self.halted}); + std.debug.print("║ total memory: {} bytes ║\n", .{self.registers.total_packed_bytes}); + std.debug.print("╠══════════════════════════════════════════╣\n", .{}); + std.debug.print("║ JIT ACCELERATION: ║\n", .{}); + std.debug.print("║ enabled: {} ║\n", .{self.jit_enabled}); + if (self.jit_engine) |*engine| { + const stats = engine.getStats(); + std.debug.print("║ ops: {}, hits: {}, rate: {d:.1}% ║\n", .{ stats.total_ops, stats.jit_hits, stats.hit_rate }); + } else { + std.debug.print("║ engine: not initialized ║\n", .{}); + } + std.debug.print("╚══════════════════════════════════════════╝\n\n", .{}); + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "VSA VM basic operations" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, + .{ .opcode = .v_const, .dst = 1, .imm = 67890 }, + .{ .opcode = .v_add, .dst = 2, .src1 = 0, .src2 = 1 }, + .{ .opcode = .v_store, .src1 = 2 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + try std.testing.expectEqual(@as(i64, 12345 + 67890), vm.registers.s0); +} + +test "VSA VM bind/unbind" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + // Test bind self-inverse property: bind(a, a) = all +1 for non-zero + const program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 111 }, + .{ .opcode = .v_bind, .dst = 1, .src1 = 0, .src2 = 0 }, // bind(v0, v0) + .{ .opcode = .v_dot, .src1 = 1, .src2 = 1 }, // dot(v1, v1) should be high + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // bind(a, a) produces vector with many +1s, dot product should be positive + try std.testing.expect(vm.registers.s0 > 0); +} + +test "VSA VM bundle similarity" { + var vm = VSAVM.init(std.testing.allocator); + vm.jit_enabled = false; // Disable JIT (has bug in cosineSimilarity) + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 333 }, + .{ .opcode = .v_random, .dst = 1, .imm = 444 }, + .{ .opcode = .v_bundle2, .dst = 2, .src1 = 0, .src2 = 1 }, + .{ .opcode = .v_cosine, .src1 = 0, .src2 = 2 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // Bundle should be similar to inputs + // Mathematical expectation: ~0.5-0.7 similarity + try std.testing.expect(vm.registers.f0 > 0.3); +} + +test "VSA VM permute" { + var vm = VSAVM.init(std.testing.allocator); + vm.jit_enabled = false; // Disable JIT (has bug in cosineSimilarity) + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 999 }, + .{ .opcode = .v_permute, .dst = 1, .src1 = 0, .imm = 5 }, // permute by 5 + .{ .opcode = .v_ipermute, .dst = 2, .src1 = 1, .imm = 5 }, // inverse permute + .{ .opcode = .v_cosine, .src1 = 0, .src2 = 2 }, // should be identical + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // After permute then inverse_permute, should be identical (similarity ~1.0) + try std.testing.expect(vm.registers.f0 > 0.99); +} + +test "VSA VM memory efficiency" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 555 }, + .{ .opcode = .v_random, .dst = 1, .imm = 666 }, + .{ .opcode = .v_random, .dst = 2, .imm = 777 }, + .{ .opcode = .v_random, .dst = 3, .imm = 888 }, + .{ .opcode = .v_pack, .dst = 0 }, + .{ .opcode = .v_pack, .dst = 1 }, + .{ .opcode = .v_pack, .dst = 2 }, + .{ .opcode = .v_pack, .dst = 3 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + vm.registers.updateMemoryUsage(); + + // Memory usage depends on MAX_TRITS setting + // Just verify packed storage is being tracked + try std.testing.expect(vm.registers.total_packed_bytes > 0); +} + +test "VSA VM dot product" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, + .{ .opcode = .v_mov, .dst = 1, .src1 = 0 }, + .{ .opcode = .v_dot, .src1 = 0, .src2 = 1 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // Dot product of identical vectors should be positive + try std.testing.expect(vm.registers.s0 > 0); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// BENCHMARKS +// ═══════════════════════════════════════════════════════════════════════════════ + +pub fn runBenchmarks() void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const allocator = gpa.allocator(); + + var vm = VSAVM.init(allocator); + defer vm.deinit(); + + const iterations: u64 = 10000; + + std.debug.print("\nVSA VM Benchmarks\n", .{}); + std.debug.print("=================\n\n", .{}); + + // Benchmark: Bind operation + const bind_program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 111 }, + .{ .opcode = .v_random, .dst = 1, .imm = 222 }, + .{ .opcode = .v_bind, .dst = 2, .src1 = 0, .src2 = 1 }, + .{ .opcode = .halt }, + }; + + vm.loadProgram(&bind_program) catch unreachable; + + const bind_start = std.time.nanoTimestamp(); + var i: u64 = 0; + while (i < iterations) : (i += 1) { + vm.registers.pc = 2; // Skip random generation + vm.halted = false; + vm.run() catch unreachable; + } + const bind_end = std.time.nanoTimestamp(); + const bind_ns = @as(u64, @intCast(bind_end - bind_start)); + + std.debug.print("Bind x {} iterations:\n", .{iterations}); + std.debug.print(" Total: {} ns ({} ns/op)\n\n", .{ bind_ns, bind_ns / iterations }); + + // Benchmark: Similarity + const sim_program = [_]VSAInstruction{ + .{ .opcode = .v_random, .dst = 0, .imm = 333 }, + .{ .opcode = .v_random, .dst = 1, .imm = 444 }, + .{ .opcode = .v_cosine, .src1 = 0, .src2 = 1 }, + .{ .opcode = .halt }, + }; + + vm.loadProgram(&sim_program) catch unreachable; + + const sim_start = std.time.nanoTimestamp(); + i = 0; + while (i < iterations) : (i += 1) { + vm.registers.pc = 2; + vm.halted = false; + vm.run() catch unreachable; + } + const sim_end = std.time.nanoTimestamp(); + const sim_ns = @as(u64, @intCast(sim_end - sim_start)); + + std.debug.print("Cosine Similarity x {} iterations:\n", .{iterations}); + std.debug.print(" Total: {} ns ({} ns/op)\n\n", .{ sim_ns, sim_ns / iterations }); + + // Memory usage + vm.registers.updateMemoryUsage(); + std.debug.print("Memory Usage:\n", .{}); + std.debug.print(" 4 vectors packed: {} bytes\n", .{vm.registers.total_packed_bytes}); + std.debug.print(" 4 vectors unpacked: {} bytes\n", .{4 * MAX_TRITS}); + std.debug.print(" Savings: {d:.1}x\n", .{@as(f64, @floatFromInt(4 * MAX_TRITS)) / @as(f64, @floatFromInt(vm.registers.total_packed_bytes))}); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// f16 SIMD INSTRUCTION TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "VSA VM f16: v_f16_load quantizes correctly" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_f16_load, .dst = 0, .imm = 0xF16 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // Check that loaded vector has ternary values + const v0 = &vm.registers.v0; + try std.testing.expectEqual(@as(usize, 16), v0.trit_len); + + // All values should be in {-1, 0, +1} + for (0..16) |i| { + const val = v0.unpacked_cache[i]; + try std.testing.expect(val == -1 or val == 0 or val == 1); + } +} + +test "VSA VM f16: v_f16_store converts to f16" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, // Load value + .{ .opcode = .v_f16_store, .src1 = 0 }, + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // Check that f16 accumulator has values + // f16_acc0 should have the converted values + const f16_vec = vm.registers.f16_acc0; + inline for (0..16) |i| { + // Values should be valid f16 (not NaN/inf) + try std.testing.expect(f16_vec[i] == f16_vec[i]); + } +} + +test "VSA VM f16: f16_dot computes dot product" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + const program = [_]VSAInstruction{ + .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, + .{ .opcode = .v_mov, .dst = 1, .src1 = 0 }, // Copy to v1 + .{ .opcode = .f16_dot, .src1 = 0, .src2 = 1 }, // Dot product + .{ .opcode = .halt }, + }; + + try vm.loadProgram(&program); + try vm.run(); + + // Dot product of identical vectors should be positive + // (count of non-zero trits) + try std.testing.expect(vm.registers.f0 > 0); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// KOSCHEI v7.0: SACRED OPCODE TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "VSA VM sacred: phi_const" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + try vm.loadPhi(); + try std.testing.expect(vm.registers.f0 > 1.6 and vm.registers.f0 < 1.62); +} + +test "VSA VM sacred: phi_pow" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + vm.registers.s0 = 10; // φ^10 + try vm.phiPow(); + try std.testing.expect(vm.registers.f0 > 122.9 and vm.registers.f0 < 123.0); +} + +test "VSA VM sacred: fib(10)" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + vm.registers.s0 = 10; + try vm.fib(); + try std.testing.expectEqual(@as(i64, 55), vm.registers.s0); +} + +test "VSA VM sacred: sacred_identity" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + try vm.verifySacredIdentity(); + try std.testing.expect(vm.registers.cc_zero); // φ² + 1/φ² = 3 verified + try std.testing.expectApproxEqAbs(@as(f64, 3.0), vm.registers.f0, 1e-10); +} + +test "VSA VM sacred: direct opcode execution" { + var vm = VSAVM.init(std.testing.allocator); + defer vm.deinit(); + + // Test golden angle + try vm.execSacredOpcode(.golden_angle, .{ .dest = "f0" }); + try std.testing.expect(vm.registers.f0 > 137.5 and vm.registers.f0 < 137.51); + + // Test physics constant + try vm.execSacredOpcode(.light_speed, .{ .dest = "f0" }); + try std.testing.expectApproxEqAbs(@as(f64, 299792458.0), vm.registers.f0, 1.0); +} + +pub fn main() !void { + runBenchmarks(); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig new file mode 100644 index 0000000..2dff238 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig @@ -0,0 +1,688 @@ +// @origin(spec:vsa_jit.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT-Accelerated VSA Operations +// Provides 15-260x speedup for hot paths via native code generation +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); +const jit_unified = @import("jit_unified.zig"); +const hybrid = @import("../ternary/hybrid.zig"); + +pub const HybridBigInt = hybrid.HybridBigInt; +pub const Trit = hybrid.Trit; +pub const MAX_TRITS = hybrid.MAX_TRITS; + +// ═══════════════════════════════════════════════════════════════════════════════ +// JIT-ACCELERATED VSA ENGINE +// ═══════════════════════════════════════════════════════════════════════════════ + +/// JIT-accelerated VSA engine with compiled function caching +pub const JitVSAEngine = struct { + allocator: std.mem.Allocator, + + // Cached JIT-compiled functions for common dimensions + dot_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bind_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + hamming_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + cosine_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bundle_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + + // Keep compilers alive to prevent exec_mem from being freed + compilers: std.ArrayListUnmanaged(jit_unified.UnifiedJitCompiler), + + // Statistics + jit_hits: u64 = 0, + jit_misses: u64 = 0, + total_ops: u64 = 0, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .allocator = allocator, + .dot_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bind_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .hamming_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .cosine_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bundle_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .compilers = .{}, + }; + } + + pub fn deinit(self: *Self) void { + // Clean up all compilers (which frees exec_mem) + for (self.compilers.items) |*compiler| { + compiler.deinit(); + } + self.compilers.deinit(self.allocator); + self.dot_cache.deinit(); + self.bind_cache.deinit(); + self.hamming_cache.deinit(); + self.cosine_cache.deinit(); + self.bundle_cache.deinit(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT DOT PRODUCT + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for dot product + fn getDotFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.dot_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileDotProduct(dimension); + const func = try compiler.finalize(); + + try self.dot_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated dot product for HybridBigInt vectors + pub fn dotProduct(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getDotFunction(dim); + + // Call JIT-compiled function directly on unpacked cache + // Cast [MAX_TRITS]Trit to *anyopaque + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + return func(a_ptr, b_ptr); + } + + /// Fallback to non-JIT dot product (for comparison) + pub fn dotProductFallback(a: *HybridBigInt, b: *HybridBigInt) i64 { + return @intCast(a.dotProduct(b)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BIND + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bind + fn getBindFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.bind_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileBind(dimension); + const func = try compiler.finalize(); + + try self.bind_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bind for HybridBigInt vectors (modifies a in place) + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getBindFunction(dim); + + // Call JIT-compiled function (modifies a in place) + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + _ = func(a_ptr, b_ptr); + + // Mark as modified (dirty) since JIT wrote to unpacked cache + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT FUSED COSINE SIMILARITY (single-pass computation) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for fused cosine similarity + fn getCosineFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.cosine_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile fused cosine (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileFusedCosine(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to 3x dot product + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.cosine_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated cosine similarity using fused kernel (2.5x faster on ARM64) + /// cos(a,b) = dot(a,b) / sqrt(dot(a,a) * dot(b,b)) + pub fn cosineSimilarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try fused cosine kernel (ARM64 only, 2.5x faster) + if (try self.getCosineFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + // Function returns f64 bit pattern as i64 + const result_bits = func(a_ptr, b_ptr); + return @bitCast(result_bits); + } + + // Fallback: use 3 separate JIT dot products + const dot_ab = try self.dotProduct(a, b); + const dot_aa = try self.dotProduct(a, a); + const dot_bb = try self.dotProduct(b, b); + + // Handle zero vectors + if (dot_aa == 0 or dot_bb == 0) { + return 0.0; + } + + const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); + return @as(f64, @floatFromInt(dot_ab)) / norm; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT HAMMING DISTANCE (count of differing positions) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for hamming distance + fn getHammingFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.hamming_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile new function (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileHamming(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.hamming_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated hamming distance + /// For ternary: counts positions where a[i] != b[i] + pub fn hammingDistance(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (available on ARM64) + if (try self.getHammingFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + return func(a_ptr, b_ptr); + } + + // Scalar fallback + var count: i64 = 0; + for (0..dim) |i| { + if (a.unpacked_cache[i] != b.unpacked_cache[i]) { + count += 1; + } + } + return count; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BUNDLE OPERATION (n-ary addition with threshold) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bundle operation + fn getBundleFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.bundle_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile bundle SIMD (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileBundleSIMD(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.bundle_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bundle operation + /// result[i] = threshold(a[i] + b[i]) where >0→1, <0→-1, =0→0 + /// Modifies 'a' in place + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (ARM64 only) + if (try self.getBundleFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + _ = func(a_ptr, b_ptr); + a.dirty = true; + return; + } + + // Scalar fallback + for (0..dim) |i| { + const sum: i16 = @as(i16, a.unpacked_cache[i]) + @as(i16, b.unpacked_cache[i]); + if (sum > 0) { + a.unpacked_cache[i] = 1; + } else if (sum < 0) { + a.unpacked_cache[i] = -1; + } else { + a.unpacked_cache[i] = 0; + } + } + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // STATISTICS + // ═══════════════════════════════════════════════════════════════════════════ + + pub fn getStats(self: *const Self) Stats { + const total_cache = self.jit_hits + self.jit_misses; + const hit_rate = if (total_cache > 0) + @as(f64, @floatFromInt(self.jit_hits)) / @as(f64, @floatFromInt(total_cache)) * 100.0 + else + 0.0; + + return Stats{ + .total_ops = self.total_ops, + .jit_hits = self.jit_hits, + .jit_misses = self.jit_misses, + .cache_size = self.dot_cache.count() + self.bind_cache.count() + self.hamming_cache.count() + self.cosine_cache.count() + self.bundle_cache.count(), + .hit_rate = hit_rate, + }; + } + + pub fn printStats(self: *const Self) void { + const stats = self.getStats(); + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE STATISTICS\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Total operations: {d}\n", .{stats.total_ops}); + std.debug.print(" JIT cache hits: {d}\n", .{stats.jit_hits}); + std.debug.print(" JIT cache misses: {d}\n", .{stats.jit_misses}); + std.debug.print(" Cache size: {d} functions\n", .{stats.cache_size}); + std.debug.print(" Hit rate: {d:.1}%\n", .{stats.hit_rate}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + } + + pub const Stats = struct { + total_ops: u64, + jit_hits: u64, + jit_misses: u64, + cache_size: usize, + hit_rate: f64, + }; +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVENIENCE FUNCTIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Global JIT engine (thread-local for safety) +threadlocal var global_engine: ?JitVSAEngine = null; + +/// Initialize global JIT engine +pub fn initGlobal(allocator: std.mem.Allocator) void { + if (global_engine == null) { + global_engine = JitVSAEngine.init(allocator); + } +} + +/// Deinitialize global JIT engine +pub fn deinitGlobal() void { + if (global_engine) |*engine| { + engine.deinit(); + global_engine = null; + } +} + +/// JIT-accelerated dot product using global engine +pub fn jitDotProduct(allocator: std.mem.Allocator, a: *HybridBigInt, b: *HybridBigInt) !i64 { + initGlobal(allocator); + return global_engine.?.dotProduct(a, b); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "JitVSAEngine init and deinit" { + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + try std.testing.expect(engine.total_ops == 0); +} + +test "JitVSAEngine dot product correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Create test vectors using setTrit (proper API) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + // Simple test: all 1s dot all 1s = dimension + const test_len = 64; + + for (0..test_len) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + } + + const expected: i64 = test_len; + + // JIT dot product + const jit_result = try engine.dotProduct(&a, &b); + + // Fallback dot product + const fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + + try std.testing.expectEqual(expected, jit_result); + try std.testing.expectEqual(expected, fallback_result); +} + +test "JitVSAEngine cache hits" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + a.trit_len = 64; + b.trit_len = 64; + + // First call - cache miss + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 0), engine.jit_hits); + + // Second call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 1), engine.jit_hits); + + // Third call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 2), engine.jit_hits); +} + +test "JitVSAEngine benchmark vs fallback" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const dim = 1024; + const iterations = 10000; + + // Create test vectors using setTrit + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + for (0..dim) |i| { + const val_a: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: Trit = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a.setTrit(i, val_a); + b.setTrit(i, val_b); + } + + // Warm up JIT cache + _ = try engine.dotProduct(&a, &b); + + // Benchmark JIT + var timer = try std.time.Timer.start(); + var jit_result: i64 = 0; + for (0..iterations) |_| { + jit_result = try engine.dotProduct(&a, &b); + } + const jit_ns = timer.read(); + + // Benchmark fallback + timer.reset(); + var fallback_result: i64 = 0; + for (0..iterations) |_| { + fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + } + const fallback_ns = timer.read(); + + // Results should match + try std.testing.expectEqual(jit_result, fallback_result); + + const jit_ms = @as(f64, @floatFromInt(jit_ns)) / 1_000_000.0; + const fallback_ms = @as(f64, @floatFromInt(fallback_ns)) / 1_000_000.0; + const speedup = fallback_ms / jit_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE BENCHMARK (HybridBigInt)\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d} trits\n", .{dim}); + std.debug.print(" Iterations: {d}\n", .{iterations}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Fallback (HybridBigInt.dotProduct): {d:.3} ms\n", .{fallback_ms}); + std.debug.print(" JIT (NEON SIMD): {d:.3} ms\n", .{jit_ms}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + engine.printStats(); + + // JIT should generally be faster, but can be slower due to thermal/load + // Just verify JIT compiles and runs without crashing + if (speedup > 1.0) { + std.debug.print(" JIT is faster! ({d:.2}x speedup)\n", .{speedup}); + } else { + std.debug.print(" JIT is slower ({d:.2}x) - acceptable for flaky benchmark\n", .{speedup}); + } +} + +test "JitVSAEngine various dimensions" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const test_dims = [_]usize{ 8, 16, 32, 64, 100, 128, 256, 512, 1000 }; + + for (test_dims) |dim| { + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + var expected: i64 = 0; + for (0..dim) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + expected += 1; + } + + const result = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(expected, result); + } + + // Should have compiled functions for each unique dimension + try std.testing.expectEqual(@as(usize, test_dims.len), engine.dot_cache.count()); +} + +test "JitVSAEngine bind correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test bind: result[i] = a[i] * b[i] (ternary multiplication) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + const dim = 16; + for (0..dim) |i| { + // Pattern: a = [1, -1, 0, 1, -1, 0, ...], b = [1, 1, 1, -1, -1, -1, ...] + const a_val: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const b_val: Trit = if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1); + a.setTrit(i, a_val); + b.setTrit(i, b_val); + } + + // Compute expected result + var expected = HybridBigInt.zero(); + for (0..dim) |i| { + const a_val = a.getTrit(i); + const b_val = b.getTrit(i); + expected.setTrit(i, a_val * b_val); + } + + // JIT bind + try engine.bind(&a, &b); + + // Verify result + for (0..dim) |i| { + try std.testing.expectEqual(expected.getTrit(i), a.getTrit(i)); + } +} + +test "JitVSAEngine cosine similarity correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: cos(a, a) = 1.0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const cos_identical = try engine.cosineSimilarity(&a, &a); + try std.testing.expectApproxEqRel(@as(f64, 1.0), cos_identical, 0.001); + + // Test orthogonal vectors: cos(a, -a) = -1.0 + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const cos_opposite = try engine.cosineSimilarity(&a, &neg_a); + try std.testing.expectApproxEqRel(@as(f64, -1.0), cos_opposite, 0.001); +} + +test "JitVSAEngine hamming distance correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: hamming(a, a) = 0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const hamming_identical = try engine.hammingDistance(&a, &a); + try std.testing.expectEqual(@as(i64, 0), hamming_identical); + + // Test completely different vectors: hamming(a, -a) = dim + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const hamming_opposite = try engine.hammingDistance(&a, &neg_a); + try std.testing.expectEqual(@as(i64, dim), hamming_opposite); + + // Test half different: change half the trits + var half = HybridBigInt.zero(); + for (0..dim) |i| { + half.setTrit(i, if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1)); + } + + const hamming_half = try engine.hammingDistance(&a, &half); + try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig new file mode 100644 index 0000000..cbbe2ff --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig @@ -0,0 +1,461 @@ +// ╔════════════════════════════════════════════════════════════════════════════╗ +// ║ TRINITY VSA — 10K-DIMENSIONAL HYPERVECTORS ║ +// ║ Week 2 Day 1: Scalable VSA architecture for 10,000-dimensional vectors ║ +// ║ ║ +// ║ Features: ║ +// ║ - 10,000-dimensional ternary hypervectors ║ +// ║ - O(1) parallel bind operation ║ +// ║ - Bundle, similarity, permutation ║ +// ║ - FPGA-ready memory layout ║ +// ║ ║ +// ║ φ² + 1/φ² = 3 = TRINITY ║ +// ╚════════════════════════════════════════════════════════════════════════════╝ + +const std = @import("std"); +const builtin = @import("builtin"); +const common = @import("common.zig"); + +pub const Trit = common.Trit; // i8: -1, 0, +1 +pub const HybridBigInt = common.HybridBigInt; + +//========================================================================== +// CONSTANTS +//========================================================================== + +pub const DIM_10K = 10_000; +pub const BYTES_PER_10K = (DIM_10K * 2 + 7) / 8; // 20,000 bits = 2,500 bytes +pub const WORDS_32BIT = (DIM_10K * 2 + 31) / 32; // 625 words of 32 bits + +// FPGA BRAM sizing (32Kb = 4096 bytes) +pub const BRAM_SIZE = 4096; +pub const VECTORS_PER_BRAM = BRAM_SIZE / BYTES_PER_10K; // ~1.6 vectors + +// Trit values (matching HybridBigInt convention) +pub const TRIT_NEG: Trit = -1; +pub const TRIT_ZERO: Trit = 0; +pub const TRIT_POS: Trit = 1; + +//========================================================================== +// 10K-DIMENSIONAL HYPERVECTOR +//========================================================================== + +/// 10K-dimensional ternary hypervector +/// Storage: 2,500 bytes (20,000 bits) using packed trit encoding +pub const HyperVector10K = struct { + /// Packed trit storage (2 bits per trit) + /// Layout: [trit0:1, trit0:0][trit1:1, trit1:0]... + data: [BYTES_PER_10K]u8, + + const Self = @This(); + + /// Create a zero vector + pub inline fn zero() Self { + return .{ .data = [_]u8{0} ** BYTES_PER_10K }; + } + + /// Create a random vector + pub fn random(rng: *std.Random.DefaultPrng) !Self { + var self = Self.zero(); + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + const rand_val = rng.random().int(u3); + const trit_val: i8 = switch (rand_val & 0x03) { + 0 => TRIT_ZERO, + 1 => TRIT_POS, + 2 => TRIT_NEG, + else => TRIT_POS, + }; + try self.set(i, trit_val); + } + return self; + } + + /// Get trit at index (returns {-1, 0, +1}) + pub inline fn get(self: *const Self, index: usize) !Trit { + if (index >= DIM_10K) return error.IndexOutOfBounds; + const bit_idx = index * 2; + const byte_idx = bit_idx / 8; + const shift: u3 = @intCast(bit_idx % 8); + const trit_bits = (self.data[byte_idx] >> shift) & 0x03; + + return switch (trit_bits) { + 0b00 => TRIT_ZERO, + 0b01 => TRIT_POS, + 0b10 => TRIT_NEG, + else => TRIT_ZERO, // Invalid encoding + }; + } + + /// Set trit at index + pub inline fn set(self: *Self, index: usize, value: Trit) !void { + if (index >= DIM_10K) return error.IndexOutOfBounds; + const bit_idx = index * 2; + const byte_idx = bit_idx / 8; + const shift: u3 = @intCast(bit_idx % 8); + + const trit_bits: u2 = switch (value) { + TRIT_ZERO => 0b00, + TRIT_POS => 0b01, + TRIT_NEG => 0b10, + else => 0b00, + }; + + // Clear old bits and set new ones + self.data[byte_idx] &= ~(@as(u8, 0x03) << shift); + self.data[byte_idx] |= @as(u8, trit_bits) << shift; + } + + /// Parallel bind operation (O(1) on FPGA with 10,000 LUTs) + /// result[i] = a[i] * b[i] + pub fn bind(a: *const Self, b: *const Self) Self { + var result = Self.zero(); + + // Process 16 trits (32 bits) at a time for SIMD efficiency + const word_count = WORDS_32BIT; + var w: usize = 0; + + while (w < word_count) : (w += 1) { + const byte_idx = w * 4; + if (byte_idx + 4 > BYTES_PER_10K) break; + + // Load 32 bits (16 trits) from each vector + const a_words = std.mem.readInt(u32, a.data[byte_idx..][0..4], .little); + const b_words = std.mem.readInt(u32, b.data[byte_idx..][0..4], .little); + + var result_word: u32 = 0; + var t: usize = 0; + + // Trit-wise multiplication (16 parallel operations) + while (t < 16) : (t += 1) { + const shift_amt: u5 = @intCast(t * 2); + const a_trit: u2 = @truncate((a_words >> shift_amt) & 0x03); + const b_trit: u2 = @truncate((b_words >> shift_amt) & 0x03); + + const r_trit: u2 = tritMul(a_trit, b_trit); + result_word |= @as(u32, r_trit) << shift_amt; + } + + // Store result + std.mem.writeInt(u32, result.data[byte_idx..][0..4], result_word, .little); + } + + return result; + } + + /// Bundle operation (majority vote) + pub fn bundle(a: *const Self, b: *const Self) !Self { + var result = Self.zero(); + + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + const a_trit = try a.get(i); + const b_trit = try b.get(i); + + const r_trit: Trit = tritBundle(a_trit, b_trit); + try result.set(i, r_trit); + } + + return result; + } + + /// Cosine similarity (scaled to 0-65535) + pub fn cosineSimilarity(a: *const Self, b: *const Self) !u16 { + var dot_product: i64 = 0; + var norm_a: i64 = 0; + var norm_b: i64 = 0; + + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + const a_trit = try a.get(i); + const b_trit = try b.get(i); + + dot_product += @as(i64, a_trit) * @as(i64, b_trit); + norm_a += @as(i64, a_trit) * @as(i64, a_trit); + norm_b += @as(i64, b_trit) * @as(i64, b_trit); + } + + if (norm_a == 0 or norm_b == 0) + return 0; + + const norm_sum = norm_a + norm_b; + const abs_dot = @abs(dot_product); + const scaled = @as(u64, @intCast(abs_dot)) * 65535 / @as(u64, @intCast(norm_sum)); + + return @intCast(scaled); + } + + /// Permutation (cyclic shift) + pub fn permute(self: *const Self, shift: u16) !Self { + var result = Self.zero(); + const effective_shift = @as(usize, @intCast(shift)) % DIM_10K; + + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + const src_idx = (i + DIM_10K - effective_shift) % DIM_10K; + const trit = try self.get(src_idx); + try result.set(i, trit); + } + + return result; + } + + /// Count non-zero trits + pub fn countNonZero(self: *const Self) !usize { + var count: usize = 0; + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + if (try self.get(i) != TRIT_ZERO) + count += 1; + } + return count; + } + + /// Convert to slice of 32-bit words (for FPGA transfer) + pub fn toWords(self: *const Self) [WORDS_32BIT]u32 { + var result: [WORDS_32BIT]u32 = undefined; + var i: usize = 0; + while (i < WORDS_32BIT) : (i += 1) { + const byte_idx = i * 4; + if (byte_idx + 4 <= BYTES_PER_10K) { + result[i] = std.mem.readInt(u32, self.data[byte_idx..][0..4], .little); + } else { + result[i] = 0; + } + } + return result; + } + + /// Create from slice of 32-bit words (from FPGA) + pub fn fromWords(words: []const u32) Self { + var result = Self.zero(); + var i: usize = 0; + while (i < @min(WORDS_32BIT, words.len)) : (i += 1) { + const byte_idx = i * 4; + if (byte_idx + 4 <= BYTES_PER_10K) { + std.mem.writeInt(u32, result.data[byte_idx..][0..4], words[i], .little); + } + } + return result; + } + + /// Format as hex string + pub fn formatHex(self: *const Self, allocator: std.mem.Allocator) ![]u8 { + // std.fmt.fmtSliceHexLower was removed in 0.15; {x} on the slice is the + // replacement and produces the same lowercase hex. + return std.fmt.allocPrint(allocator, "{x}", .{&self.data}); + } +}; + +/// Trit multiplication lookup table (combinational logic) +inline fn tritMul(a: u2, b: u2) u2 { + return if (a == 0 or b == 0) 0 else if (a == b) 1 else 2; +} + +/// Trit bundle (majority vote of 2) +inline fn tritBundle(a: Trit, b: Trit) Trit { + if (a == TRIT_NEG) { + return if (b == TRIT_NEG) TRIT_NEG else if (b == TRIT_POS) TRIT_ZERO else TRIT_NEG; + } else if (a == TRIT_POS) { + return if (b == TRIT_NEG) TRIT_ZERO else if (b == TRIT_POS) TRIT_POS else TRIT_POS; + } else { // a == ZERO + return b; + } +} + +//========================================================================== +// BENCHMARK FUNCTIONS +//========================================================================== + +pub const BenchmarkResult = struct { + bind_ns: f64, + bundle_ns: f64, + similarity_ns: f64, + bind_throughput: f64, // ops/sec + dimensions: usize = DIM_10K, +}; + +/// Run 10K VSA benchmark +pub fn benchmark(_: std.mem.Allocator, iterations: usize) !BenchmarkResult { + var rng = std.Random.DefaultPrng.init(@intCast(std.time.timestamp())); + + // Create test vectors + const vec_a = try HyperVector10K.random(&rng); + const vec_b = try HyperVector10K.random(&rng); + + // Warmup + _ = HyperVector10K.bind(&vec_a, &vec_b); + _ = try HyperVector10K.bundle(&vec_a, &vec_b); + _ = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); + + // Benchmark bind + const bind_start = std.time.nanoTimestamp(); + var i: usize = 0; + while (i < iterations) : (i += 1) { + _ = HyperVector10K.bind(&vec_a, &vec_b); + } + const bind_end = std.time.nanoTimestamp(); + const bind_ns = @as(f64, @floatFromInt(bind_end - bind_start)) / @as(f64, @floatFromInt(iterations)); + + // Benchmark bundle + const bundle_start = std.time.nanoTimestamp(); + i = 0; + while (i < iterations) : (i += 1) { + _ = try HyperVector10K.bundle(&vec_a, &vec_b); + } + const bundle_end = std.time.nanoTimestamp(); + const bundle_ns = @as(f64, @floatFromInt(bundle_end - bundle_start)) / @as(f64, @floatFromInt(iterations)); + + // Benchmark similarity + const sim_start = std.time.nanoTimestamp(); + i = 0; + while (i < iterations) : (i += 1) { + _ = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); + } + const sim_end = std.time.nanoTimestamp(); + const sim_ns = @as(f64, @floatFromInt(sim_end - sim_start)) / @as(f64, @floatFromInt(iterations)); + + return BenchmarkResult{ + .bind_ns = bind_ns, + .bundle_ns = bundle_ns, + .similarity_ns = sim_ns, + .bind_throughput = 1_000_000_000.0 / bind_ns, + }; +} + +/// Print benchmark results +pub fn printBenchmark(result: BenchmarkResult) void { + // std.io.getStdOut() was removed in 0.15. A File writer needs a buffer it + // does not own, so the buffer lives here and the interface borrows it. + var stdout_buffer: [4096]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + defer stdout.flush() catch {}; + + stdout.print( + \\╔════════════════════════════════════════════════════════════════════════════╗ + \\║ TRINITY VSA 10K BENCHMARK RESULTS ║ + \\╚════════════════════════════════════════════════════════════════════════════╝ + \\ + \\Dimensions: {d} + \\Vector size: {d} bytes + \\ + \\═══════════════════════════════════════════════════════════════════════════ + \\OPERATION TIME (ns) THROUGHPUT vs FPGA (est) + \\═══════════════════════════════════════════════════════════════════════════ + \\BIND {d:.2} ns {d:.0} op/s ~1000x slower + \\BUNDLE {d:.2} ns {d:.0} op/s ~500x slower + \\SIMILARITY {d:.2} ns {d:.0} op/s ~100x slower + \\═══════════════════════════════════════════════════════════════════════════ + \\ + \\φ² + 1/φ² = 3 = TRINITY + \\ + , .{ + DIM_10K, + BYTES_PER_10K, + result.bind_ns, + result.bind_throughput, + result.bundle_ns, + 1_000_000_000.0 / result.bundle_ns, + result.similarity_ns, + 1_000_000_000.0 / result.similarity_ns, + }) catch return; +} + +//========================================================================== +// TESTS +//========================================================================== + +test "HyperVector10K: zero vector" { + const vec = HyperVector10K.zero(); + try std.testing.expectEqual(@as(usize, 0), try vec.countNonZero()); +} + +test "HyperVector10K: bind identity" { + var rng = std.Random.DefaultPrng.init(42); + const vec = try HyperVector10K.random(&rng); + + // Identity vector (all +1) + var identity = HyperVector10K.zero(); + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + try identity.set(i, TRIT_POS); + } + + const result = HyperVector10K.bind(&vec, &identity); + + // Verify result equals original (sample check) + var match_count: usize = 0; + i = 0; + while (i < 100) : (i += 1) { + if ((try result.get(i)) == (try vec.get(i))) + match_count += 1; + } + + try std.testing.expect(match_count >= 95); // Allow some tolerance +} + +test "HyperVector10K: bind inverse" { + var rng = std.Random.DefaultPrng.init(42); + const vec = try HyperVector10K.random(&rng); + + // Inverse vector (all -1) + var inverse = HyperVector10K.zero(); + var i: usize = 0; + while (i < DIM_10K) : (i += 1) { + try inverse.set(i, TRIT_NEG); + } + + const result = HyperVector10K.bind(&vec, &inverse); + + // Verify result is negation of original + var match_count: usize = 0; + i = 0; + while (i < 100) : (i += 1) { + const vi = try vec.get(i); + const expected: i8 = if (vi == TRIT_NEG) TRIT_POS else if (vi == TRIT_POS) TRIT_NEG else TRIT_ZERO; + if ((try result.get(i)) == expected) + match_count += 1; + } + + try std.testing.expectEqual(@as(usize, 100), match_count); +} + +test "HyperVector10K: cosine similarity bounds" { + var rng = std.Random.DefaultPrng.init(42); + const vec_a = try HyperVector10K.random(&rng); + const vec_b = try HyperVector10K.random(&rng); + + const sim = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); + + // Similarity should be in range [0, 65535] + try std.testing.expect(sim >= 0 and sim <= 65535); +} + +test "HyperVector10K: permutation roundtrip" { + var rng = std.Random.DefaultPrng.init(42); + const original = try HyperVector10K.random(&rng); + + const shifted = try original.permute(100); + const unshifted = try shifted.permute(@as(u16, @intCast(DIM_10K - 100))); + + // Sample check (not all 10K to save time) + var match_count: usize = 0; + var i: usize = 0; + while (i < 100) : (i += 1) { + if ((try unshifted.get(i)) == (try original.get(i))) + match_count += 1; + } + + try std.testing.expectEqual(@as(usize, 100), match_count); +} + +test "HyperVector10K: benchmark quick" { + const allocator = std.testing.allocator; + const result = try benchmark(allocator, 10); + _ = result; + + // Just verify it completes without error + try std.testing.expect(true); +} + +// φ² + 1/φ² = 3 = TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig new file mode 100644 index 0000000..73af6f6 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig @@ -0,0 +1,20 @@ +// 🤖 TRINITY v0.11.0: Suborbital Order +// Common types and imports for VSA module + +const std = @import("std"); +// ../hybrid.zig resolves to src/hybrid.zig, which does not exist. The file +// is src/ternary/hybrid.zig. +const tvc_hybrid = @import("../ternary/hybrid.zig"); + +pub const HybridBigInt = tvc_hybrid.HybridBigInt; +pub const Trit = tvc_hybrid.Trit; +pub const Vec32i8 = tvc_hybrid.Vec32i8; +pub const SIMD_WIDTH = tvc_hybrid.SIMD_WIDTH; +pub const MAX_TRITS = tvc_hybrid.MAX_TRITS; + +pub const SearchResult = struct { + index: usize, + similarity: f64, +}; + +// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig new file mode 100644 index 0000000..29aa4be --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig @@ -0,0 +1,295 @@ +// 🤖 TRINITY v0.11.0: Suborbital Order +// Concurrency and Parallel Processing layer for VSA +const std = @import("std"); +const common = @import("common.zig"); +const HybridBigInt = common.HybridBigInt; + +// CONSTANTS +pub const POOL_SIZE = 4; +pub const DEQUE_CAPACITY = 128; +pub const MAX_WORKERS = 8; +pub const PRIORITY_LEVELS = 5; +pub const PRIORITY_QUEUE_CAPACITY = 256; +pub const MAX_JOB_AGE = 100; +pub const MAX_DAG_NODES = 256; +pub const MAX_DEPENDENCIES = 16; +pub const PHI_INVERSE: f64 = 0.618033988749895; + +// TYPES +pub const JobFn = *const fn (context: *anyopaque) void; +pub const PoolJob = struct { func: JobFn, context: *anyopaque }; +pub const PriorityLevel = enum(u8) { critical = 0, high = 1, normal = 2, low = 3, background = 4 }; +pub const JobPriority = PriorityLevel; +pub const TaskState = enum(u8) { pending = 0, ready = 1, running = 2, completed = 3, failed = 4 }; + +pub const TaskNode = struct { + id: u32, + func: JobFn, + context: *anyopaque, + dependencies: [MAX_DEPENDENCIES]u32, + dep_count: usize, + dependents: [MAX_DEPENDENCIES]u32, + dependent_count: usize, + state: TaskState, + priority: JobPriority, + deadline: ?i64, + wait_count: std.atomic.Value(usize), + + pub fn init(id: u32, func: JobFn, context: *anyopaque) TaskNode { + return TaskNode{ + .id = id, + .func = func, + .context = context, + .dependencies = undefined, + .dep_count = 0, + .dependents = undefined, + .dependent_count = 0, + .state = .pending, + .priority = .normal, + .deadline = null, + .wait_count = std.atomic.Value(usize).init(0), + }; + } + pub fn addDependency(self: *TaskNode, dep_id: u32) bool { + if (self.dep_count >= MAX_DEPENDENCIES) return false; + self.dependencies[self.dep_count] = dep_id; + self.dep_count += 1; + _ = self.wait_count.fetchAdd(1, .monotonic); + return true; + } + pub fn addDependent(self: *TaskNode, dep_id: u32) bool { + if (self.dependent_count >= MAX_DEPENDENCIES) return false; + self.dependents[self.dependent_count] = dep_id; + self.dependent_count += 1; + return true; + } + pub fn satisfyDependency(self: *TaskNode) bool { + // std.atomic.fence was removed in 0.15. The acquire it supplied belongs + // on the operation itself: acq_rel gives the release for this decrement + // and the acquire for observing the last one, which is exactly what the + // separate fence was there to add. + const remaining = self.wait_count.fetchSub(1, .acq_rel) - 1; + if (remaining == 0) { + self.state = .ready; + return true; + } + return false; + } + pub fn getEffectivePriority(self: *const TaskNode) f64 { + // Annotated because the arms are comptime_float and the switch is on a + // runtime value: a comptime-only type cannot depend on runtime control + // flow, which is what the compiler was saying. + const base: f64 = switch (self.priority) { + .critical => 1.0, + .high => 0.8, + .normal => 0.6, + .low => 0.4, + .background => 0.2, + }; + if (self.deadline) |dl| { + const now = std.time.nanoTimestamp(); + const remaining = dl - now; + if (remaining < 0) return 2.0; + const boost = 1.0 / (@as(f64, @floatFromInt(remaining)) / 1e9 + 1.0); + return base + boost; + } + return base; + } +}; + +pub const DAGStats = struct { + total: usize, + completed: usize, + failed: usize, + pending: usize, + ready: usize, + completion_rate: f64, +}; + +// CHASE-LEV DEQUE & WORK-STEALING POOL +pub const ChaseLevDeque = struct { + jobs: [DEQUE_CAPACITY]PoolJob, + bottom: usize, + top: usize, + + pub fn init() ChaseLevDeque { + return ChaseLevDeque{ .jobs = undefined, .bottom = 0, .top = 0 }; + } + pub fn push(self: *ChaseLevDeque, job: PoolJob) bool { + const b = @atomicLoad(usize, &self.bottom, .seq_cst); + const t = @atomicLoad(usize, &self.top, .seq_cst); + if (b - t >= DEQUE_CAPACITY) return false; + self.jobs[b % DEQUE_CAPACITY] = job; + @atomicStore(usize, &self.bottom, b + 1, .seq_cst); + return true; + } + pub fn pop(self: *ChaseLevDeque) ?PoolJob { + var b = @atomicLoad(usize, &self.bottom, .seq_cst); + if (b == 0) return null; + b -= 1; + @atomicStore(usize, &self.bottom, b, .seq_cst); + const t = @atomicLoad(usize, &self.top, .seq_cst); + if (t <= b) { + const job = self.jobs[b % DEQUE_CAPACITY]; + if (t == b) { + const result = @cmpxchgWeak(usize, &self.top, t, t + 1, .seq_cst, .seq_cst); + if (result == null) { + @atomicStore(usize, &self.bottom, t + 1, .seq_cst); + return job; + } else { + @atomicStore(usize, &self.bottom, t + 1, .seq_cst); + return null; + } + } + return job; + } else { + @atomicStore(usize, &self.bottom, t, .seq_cst); + return null; + } + } + pub fn steal(self: *ChaseLevDeque) ?PoolJob { + const t = @atomicLoad(usize, &self.top, .seq_cst); + const b = @atomicLoad(usize, &self.bottom, .seq_cst); + if (t >= b) return null; + const job = self.jobs[t % DEQUE_CAPACITY]; + const result = @cmpxchgWeak(usize, &self.top, t, t + 1, .seq_cst, .seq_cst); + if (result == null) return job; + return null; + } + pub fn size(self: *ChaseLevDeque) usize { + const b = @atomicLoad(usize, &self.bottom, .seq_cst); + const t = @atomicLoad(usize, &self.top, .seq_cst); + return if (b > t) b - t else 0; + } + pub fn reset(self: *ChaseLevDeque) void { + @atomicStore(usize, &self.bottom, 0, .seq_cst); + @atomicStore(usize, &self.top, 0, .seq_cst); + } +}; + +pub const ThreadPool = struct { + workers: [MAX_WORKERS]ChaseLevDeque, + count: usize, + pub fn init() ThreadPool { + return ThreadPool{ .workers = undefined, .count = 0 }; + } +}; + +// DEPENDENCY GRAPH (DAG) +pub const DependencyGraph = struct { + nodes: [MAX_DAG_NODES]?TaskNode, + node_count: usize, + ready_queue: [MAX_DAG_NODES]u32, + ready_count: std.atomic.Value(usize), + completed_count: usize, + failed_count: usize, + execution_order: [MAX_DAG_NODES]u32, + order_computed: bool, + + const Self = @This(); + pub fn init() Self { + return Self{ + .nodes = .{null} ** MAX_DAG_NODES, + .node_count = 0, + .ready_queue = .{0} ** MAX_DAG_NODES, + .ready_count = std.atomic.Value(usize).init(0), + .completed_count = 0, + .failed_count = 0, + .execution_order = .{0} ** MAX_DAG_NODES, + .order_computed = false, + }; + } + pub fn addTask(self: *Self, func: JobFn, context: *anyopaque) ?u32 { + if (self.node_count >= MAX_DAG_NODES) return null; + const id: u32 = @intCast(self.node_count); + self.nodes[id] = TaskNode.init(id, func, context); + self.node_count += 1; + self.order_computed = false; + return id; + } + pub fn addTaskWithPriority(self: *Self, func: JobFn, context: *anyopaque, priority: JobPriority) ?u32 { + const id = self.addTask(func, context) orelse return null; + if (self.nodes[id]) |*node| node.priority = priority; + return id; + } + pub fn addDependency(self: *Self, from_id: u32, to_id: u32) bool { + if (from_id >= self.node_count or to_id >= self.node_count) return false; + if (from_id == to_id) return false; + if (self.nodes[from_id]) |*from_node| { + if (!from_node.addDependent(to_id)) return false; + } else return false; + if (self.nodes[to_id]) |*to_node| { + if (!to_node.addDependency(from_id)) return false; + } else return false; + self.order_computed = false; + return true; + } + pub fn computeTopologicalOrder(self: *Self) bool { + if (self.order_computed) return true; + var in_degree: [MAX_DAG_NODES]usize = .{0} ** MAX_DAG_NODES; + var queue: [MAX_DAG_NODES]u32 = .{0} ** MAX_DAG_NODES; + var queue_start: usize = 0; + var queue_end: usize = 0; + var order_idx: usize = 0; + for (0..self.node_count) |i| { + if (self.nodes[i]) |node| { + in_degree[i] = node.dep_count; + if (in_degree[i] == 0) { + queue[queue_end] = @intCast(i); + queue_end += 1; + } + } + } + while (queue_start < queue_end) { + const current = queue[queue_start]; + queue_start += 1; + self.execution_order[order_idx] = current; + order_idx += 1; + if (self.nodes[current]) |node| { + for (0..node.dependent_count) |i| { + const dep_id = node.dependents[i]; + in_degree[dep_id] -= 1; + if (in_degree[dep_id] == 0) { + queue[queue_end] = dep_id; + queue_end += 1; + } + } + } + } + if (order_idx != self.node_count) return false; + self.order_computed = true; + return true; + } + pub fn executeAll(self: *Self) struct { completed: usize, failed: usize } { + if (!self.computeTopologicalOrder()) return .{ .completed = 0, .failed = self.node_count }; + var completed: usize = 0; + for (0..self.node_count) |order_idx| { + const id = self.execution_order[order_idx]; + if (self.nodes[id]) |node| { + node.func(node.context); + completed += 1; + } + } + self.completed_count = completed; + return .{ .completed = completed, .failed = 0 }; + } +}; + +var global_pool: ?ThreadPool = null; +pub fn getGlobalPool() *ThreadPool { + if (global_pool == null) global_pool = ThreadPool.init(); + return &global_pool.?; +} +var global_dag: ?DependencyGraph = null; +pub fn getDAG() *DependencyGraph { + if (global_dag == null) global_dag = DependencyGraph.init(); + return &global_dag.?; +} +pub fn shutdownDAG() void { + global_dag = null; +} +pub fn hasDAG() bool { + return global_dag != null; +} + +// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig new file mode 100644 index 0000000..e514b50 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig @@ -0,0 +1,816 @@ +// 🤖 TRINITY v0.11.0: Suborbital Order +// Core VSA operations for Balanced Ternary +// bind, bundle, similarity, permute + +const std = @import("std"); +const common = @import("common.zig"); +const HybridBigInt = common.HybridBigInt; +const Trit = common.Trit; +const Vec32i8 = common.Vec32i8; +const SIMD_WIDTH = common.SIMD_WIDTH; +const MAX_TRITS = common.MAX_TRITS; + +/// Bind operation (XOR-like for balanced ternary) +pub fn bind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const min_len = @min(a.trit_len, b.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const prod = a_vec * b_vec; + result.unpacked_cache[i..][0..SIMD_WIDTH].* = prod; + } + + while (i < len) : (i += 1) { + const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; + result.unpacked_cache[i] = a_trit * b_trit; + } + + return result; +} + +pub fn unbind(bound: *HybridBigInt, key: *HybridBigInt) HybridBigInt { + return bind(bound, key); +} + +pub fn bundle2(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const min_len = @min(a.trit_len, b.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + + const a_wide: @Vector(32, i16) = a_vec; + const b_wide: @Vector(32, i16) = b_vec; + const sum = a_wide + b_wide; + + const zeros: @Vector(32, i16) = @splat(0); + const ones: @Vector(32, i16) = @splat(1); + const neg_ones: @Vector(32, i16) = @splat(-1); + + const pos_mask = sum > zeros; + const neg_mask = sum < zeros; + + var out = zeros; + out = @select(i16, pos_mask, ones, out); + out = @select(i16, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + result.unpacked_cache[i + j] = @truncate(out[j]); + } + } + + while (i < len) : (i += 1) { + const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + const sum = a_trit + b_trit; + + if (sum > 0) { + result.unpacked_cache[i] = 1; + } else if (sum < 0) { + result.unpacked_cache[i] = -1; + } else { + result.unpacked_cache[i] = 0; + } + } + + return result; +} + +pub fn bundle3(a: *HybridBigInt, b: *HybridBigInt, c: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + c.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(@max(a.trit_len, b.trit_len), c.trit_len); + const min_len = @min(@min(a.trit_len, b.trit_len), c.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + // SIMD path: 32 trits at a time via i16 widening + sign extraction + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const c_vec: Vec32i8 = c.unpacked_cache[i..][0..SIMD_WIDTH].*; + + const a_wide: @Vector(32, i16) = a_vec; + const b_wide: @Vector(32, i16) = b_vec; + const c_wide: @Vector(32, i16) = c_vec; + const sum = a_wide + b_wide + c_wide; + + const zeros: @Vector(32, i16) = @splat(0); + const ones: @Vector(32, i16) = @splat(1); + const neg_ones: @Vector(32, i16) = @splat(-1); + + const pos_mask = sum > zeros; + const neg_mask = sum < zeros; + + var out = zeros; + out = @select(i16, pos_mask, ones, out); + out = @select(i16, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + result.unpacked_cache[i + j] = @truncate(out[j]); + } + } + + // Scalar remainder + while (i < len) : (i += 1) { + const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + const c_trit: i16 = if (i < c.trit_len) c.unpacked_cache[i] else 0; + const sum = a_trit + b_trit + c_trit; + + if (sum > 0) { + result.unpacked_cache[i] = 1; + } else if (sum < 0) { + result.unpacked_cache[i] = -1; + } else { + result.unpacked_cache[i] = 0; + } + } + + result.trit_len = len; + return result; +} + +pub fn cosineSimilarity(a: *const HybridBigInt, b: *const HybridBigInt) f64 { + const dot = @constCast(a).dotProduct(@constCast(b)); + const norm_a = vectorNorm(@constCast(a)); + const norm_b = vectorNorm(@constCast(b)); + + if (norm_a == 0 or norm_b == 0) return 0; + + return @as(f64, @floatFromInt(dot)) / (norm_a * norm_b); +} + +/// Cosine similarity using 16-wide f16 SIMD (2× throughput vs f32). +/// Converts ternary vectors to f16, computes similarity with 16-wide operations. +/// Returns f64 in range [-1, 1]. +pub fn cosineSimilarityF16(a: *const HybridBigInt, b: *const HybridBigInt) f64 { + @constCast(a).ensureUnpacked(); + @constCast(b).ensureUnpacked(); + + const len = @max(a.trit_len, b.trit_len); + if (len == 0) return 0; + + const F16_VEC_SIZE = 16; + const num_f16_chunks = len / F16_VEC_SIZE; + + // f32 accumulators for precision + var acc_dot: f64 = 0; + var acc_norm_a: f64 = 0; + var acc_norm_b: f64 = 0; + + // Process 16 elements at a time using f16 SIMD + var i: usize = 0; + while (i < num_f16_chunks * F16_VEC_SIZE) : (i += F16_VEC_SIZE) { + // Load trits into i8 vectors + var a_trits: @Vector(F16_VEC_SIZE, i8) = undefined; + var b_trits: @Vector(F16_VEC_SIZE, i8) = undefined; + + inline for (0..F16_VEC_SIZE) |j| { + a_trits[j] = if (i + j < a.trit_len) a.unpacked_cache[i + j] else 0; + b_trits[j] = if (i + j < b.trit_len) b.unpacked_cache[i + j] else 0; + } + + // Convert to f16 + const a_f16: @Vector(F16_VEC_SIZE, f16) = @floatCast(@as(@Vector(F16_VEC_SIZE, f32), @floatFromInt(a_trits))); + const b_f16: @Vector(F16_VEC_SIZE, f16) = @floatCast(@as(@Vector(F16_VEC_SIZE, f32), @floatFromInt(b_trits))); + + // Convert to f32 for compute + const a_f32: @Vector(F16_VEC_SIZE, f32) = @floatCast(a_f16); + const b_f32: @Vector(F16_VEC_SIZE, f32) = @floatCast(b_f16); + + // Compute dot product contribution + const prod = a_f32 * b_f32; + var sum_prod: f32 = 0; + inline for (0..F16_VEC_SIZE) |j| { + sum_prod += prod[j]; + } + acc_dot += @as(f64, sum_prod); + + // Compute norm contributions + const a_sq = a_f32 * a_f32; + const b_sq = b_f32 * b_f32; + var sum_a_sq: f32 = 0; + var sum_b_sq: f32 = 0; + inline for (0..F16_VEC_SIZE) |j| { + sum_a_sq += a_sq[j]; + sum_b_sq += b_sq[j]; + } + acc_norm_a += @as(f64, sum_a_sq); + acc_norm_b += @as(f64, sum_b_sq); + } + + // Handle scalar tail + while (i < len) : (i += 1) { + const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + + const a_f32: f32 = @floatFromInt(a_trit); + const b_f32: f32 = @floatFromInt(b_trit); + + acc_dot += @as(f64, a_f32 * b_f32); + acc_norm_a += @as(f64, a_f32 * a_f32); + acc_norm_b += @as(f64, b_f32 * b_f32); + } + + const norm_a = @sqrt(acc_norm_a); + const norm_b = @sqrt(acc_norm_b); + + if (norm_a == 0 or norm_b == 0) return 0; + + return acc_dot / (norm_a * norm_b); +} + +pub fn hammingDistance(a: *HybridBigInt, b: *HybridBigInt) usize { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var distance: usize = 0; + const len = @max(a.trit_len, b.trit_len); + const min_len = @min(a.trit_len, b.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const diff = a_vec != b_vec; + distance += @popCount(@as(u32, @bitCast(diff))); + } + + while (i < len) : (i += 1) { + const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; + if (a_trit != b_trit) distance += 1; + } + + return distance; +} + +pub fn hammingSimilarity(a: *HybridBigInt, b: *HybridBigInt) f64 { + const len = @max(a.trit_len, b.trit_len); + if (len == 0) return 1.0; + const distance = hammingDistance(a, b); + return 1.0 - @as(f64, @floatFromInt(distance)) / @as(f64, @floatFromInt(len)); +} + +pub fn dotSimilarity(a: *HybridBigInt, b: *HybridBigInt) f64 { + const dot = a.dotProduct(b); + const len = @max(a.trit_len, b.trit_len); + if (len == 0) return 0; + return @as(f64, @floatFromInt(dot)) / @as(f64, @floatFromInt(len)); +} + +/// Vector norm — SIMD accelerated via dotProduct(v, v) (OPT-001) +pub fn vectorNorm(v: *HybridBigInt) f64 { + const dot = v.dotProduct(v); + return @sqrt(@as(f64, @floatFromInt(dot))); +} + +/// Count non-zero trits — SIMD accelerated (OPT-001) +pub fn countNonZero(v: *HybridBigInt) usize { + v.ensureUnpacked(); + var count: usize = 0; + const num_full_chunks = v.trit_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const vec: Vec32i8 = v.unpacked_cache[i..][0..SIMD_WIDTH].*; + const zeros: Vec32i8 = @splat(0); + const nonzero = vec != zeros; + count += @popCount(@as(u32, @bitCast(nonzero))); + } + + while (i < v.trit_len) : (i += 1) { + if (v.unpacked_cache[i] != 0) count += 1; + } + + return count; +} + +/// Bundle N vectors — SIMD accelerated majority vote (OPT-001) +pub fn bundleN(vectors: []*HybridBigInt) HybridBigInt { + if (vectors.len == 0) return HybridBigInt.zero(); + if (vectors.len == 1) { + vectors[0].ensureUnpacked(); + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = vectors[0].trit_len; + @memcpy(result.unpacked_cache[0..vectors[0].trit_len], vectors[0].unpacked_cache[0..vectors[0].trit_len]); + return result; + } + if (vectors.len == 2) return bundle2(vectors[0], vectors[1]); + if (vectors.len == 3) return bundle3(vectors[0], vectors[1], vectors[2]); + + var max_len: usize = 0; + for (vectors) |v| { + v.ensureUnpacked(); + max_len = @max(max_len, v.trit_len); + } + + var accum: [MAX_TRITS]i16 = [_]i16{0} ** MAX_TRITS; + + for (vectors) |v| { + const num_chunks = v.trit_len / SIMD_WIDTH; + var i: usize = 0; + while (i < num_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const vec: Vec32i8 = v.unpacked_cache[i..][0..SIMD_WIDTH].*; + const wide: @Vector(32, i16) = vec; + const acc_vec: @Vector(32, i16) = accum[i..][0..SIMD_WIDTH].*; + const sum_val = acc_vec + wide; + accum[i..][0..SIMD_WIDTH].* = sum_val; + } + while (i < v.trit_len) : (i += 1) { + accum[i] += @as(i16, v.unpacked_cache[i]); + } + } + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = max_len; + + const num_result_chunks = max_len / SIMD_WIDTH; + var i: usize = 0; + while (i < num_result_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const acc_vec: @Vector(32, i16) = accum[i..][0..SIMD_WIDTH].*; + const zeros: @Vector(32, i16) = @splat(0); + const ones: @Vector(32, i16) = @splat(1); + const neg_ones: @Vector(32, i16) = @splat(-1); + + const pos_mask = acc_vec > zeros; + const neg_mask = acc_vec < zeros; + + var out = zeros; + out = @select(i16, pos_mask, ones, out); + out = @select(i16, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + result.unpacked_cache[i + j] = @truncate(out[j]); + } + } + + while (i < max_len) : (i += 1) { + if (accum[i] > 0) { + result.unpacked_cache[i] = 1; + } else if (accum[i] < 0) { + result.unpacked_cache[i] = -1; + } else { + result.unpacked_cache[i] = 0; + } + } + + return result; +} + +pub fn randomVector(len: usize, seed: u64) HybridBigInt { + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = @min(len, MAX_TRITS); + var rng = std.Random.DefaultPrng.init(seed); + const random = rng.random(); + for (0..result.trit_len) |i| { + result.unpacked_cache[i] = random.intRangeAtMost(i8, -1, 1); + } + return result; +} + +pub fn permute(v: *HybridBigInt, k: usize) HybridBigInt { + v.ensureUnpacked(); + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = v.trit_len; + if (v.trit_len == 0) return result; + const shift = k % v.trit_len; + for (0..v.trit_len) |i| { + const new_pos = (i + shift) % v.trit_len; + result.unpacked_cache[new_pos] = v.unpacked_cache[i]; + } + return result; +} + +pub fn inversePermute(v: *HybridBigInt, k: usize) HybridBigInt { + v.ensureUnpacked(); + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = v.trit_len; + if (v.trit_len == 0) return result; + const shift = k % v.trit_len; + for (0..v.trit_len) |i| { + const new_pos = (i + v.trit_len - shift) % v.trit_len; + result.unpacked_cache[new_pos] = v.unpacked_cache[i]; + } + return result; +} + +pub fn encodeSequence(items: []HybridBigInt) HybridBigInt { + if (items.len == 0) return HybridBigInt.zero(); + var result = items[0]; + for (1..items.len) |i| { + var permuted = permute(&items[i], i); + result = result.add(&permuted); + } + return result; +} + +pub fn probeSequence(sequence: *HybridBigInt, candidate: *HybridBigInt, position: usize) f64 { + var permuted = permute(candidate, position); + return cosineSimilarity(sequence, &permuted); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "cosineSimilarityF16 matches cosineSimilarity" { + var a = randomVector(100, 111); + var b = randomVector(100, 222); + + const sim_f64 = cosineSimilarity(&a, &b); + const sim_f16 = cosineSimilarityF16(&a, &b); + + // Should be very close (within f16 precision) + try std.testing.expectApproxEqAbs(sim_f64, sim_f16, 0.01); +} + +test "cosineSimilarityF16 identical vectors" { + var a = randomVector(100, 333); + + const sim = cosineSimilarityF16(&a, &a); + + // Identical vectors should have similarity 1.0 + try std.testing.expectApproxEqAbs(@as(f64, 1.0), sim, 0.01); +} + +test "cosineSimilarityF16 zero vectors" { + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + const sim = cosineSimilarityF16(&a, &b); + + // Zero vectors should return 0 + try std.testing.expectEqual(@as(f64, 0), sim); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// QUANTUM-ENHANCED VSA OPERATIONS +// Reference: [arXiv 2106.05268 VSA], [LinkedIn Kantian Vectors] +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Quantum bind: extends classical bind with phase coherence tracking +/// Maintains superposition state across operation +/// +/// In VSA framework: bind = XOR-like operation on hypervectors +/// Quantum extension: track phase information for interference +// Mutable pointers, matching bind. It would read better as *const, but bind +// calls ensureUnpacked, which fills a cache inside the value and therefore +// needs to mutate it. Declaring const here and delegating to something that +// mutates is the mismatch the compiler was reporting; making bind const would +// have been a lie about what it does. No caller exists yet, so nothing else +// moves with this. +pub fn qbind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { + // Classical bind (XOR-like for balanced ternary) + const result = bind(a, b); + + // Phase tracking: maintain coherence if both inputs coherent + // Result phase = (phase_a + phase_b) mod 2π + // Coherence = coherence_a AND coherence_b + // Note: In current implementation, HybridBigInt doesn't track phase + // This is a placeholder for future phase-aware VSA + + // In full quantum extension, would track: + // - Phase information via separate struct + // - Coherence preservation logic + // - Interference effects + + return result; +} + +/// Quantum bundle: probabilistic mixture with superposition amplitudes +/// Result = Σ αᵢ|vᵢ⟩ where αᵢ = amplitudes +/// +/// In VSA framework: bundle = majority vote on hypervectors +/// Quantum extension: weighted majority using amplitudes as weights +/// This is equivalent to "quantum-inspired mixture" in hybrid architectures +pub fn qbundle(vectors: []const HybridBigInt, amplitudes: []const f32, allocator: std.mem.Allocator) !HybridBigInt { + _ = allocator; // Reserved for future allocation needs + if (vectors.len == 0) return HybridBigInt.zero(); + if (vectors.len == 1) { + const result = vectors[0]; + return result; + } + + // Validate amplitudes length + if (amplitudes.len != vectors.len) { + return error.AmplitudeLengthMismatch; + } + + // Normalize amplitudes (if not already) + var total_amp: f32 = 0.0; + for (amplitudes) |amp| total_amp += amp; + const normalized = if (total_amp > 0.0) total_amp else 1.0; + + // Use weighted bundle: each trit gets weighted votes + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = @max(MAX_TRITS, @as(usize, @intFromFloat(normalized))); + + const num_full_chunks = result.trit_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + var weighted_sum: [SIMD_WIDTH]f32 = undefined; + + for (0..SIMD_WIDTH) |j| { + var sum: f32 = 0.0; + for (vectors, 0..) |*vec, k| { + @constCast(vec).ensureUnpacked(); + if (i + j < vec.trit_len) { + const weight = amplitudes[k] / normalized; + sum += @as(f32, @floatFromInt(vec.unpacked_cache[i + j])) * weight; + } + } + weighted_sum[j] = sum; + } + + // Quantize to ternary lattice (measurement collapse) + const zeros: @Vector(32, f32) = @splat(0.0); + const ones: @Vector(32, f32) = @splat(1.0); + const neg_ones: @Vector(32, f32) = @splat(-1.0); + + const weighted_vec: @Vector(32, f32) = weighted_sum; + const pos_mask = weighted_vec > zeros; + const neg_mask = weighted_vec < zeros; + + var out = zeros; + out = @select(f32, pos_mask, ones, out); + out = @select(f32, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + const float_val: f32 = out[j]; + const int_val: i32 = @intFromFloat(float_val); + result.unpacked_cache[i + j] = @intCast(int_val); + } + } + + // Handle scalar tail + while (i < result.trit_len) : (i += 1) { + var sum: f32 = 0.0; + for (vectors, 0..) |*vec, k| { + @constCast(vec).ensureUnpacked(); + if (i < vec.trit_len) { + const weight = amplitudes[k] / normalized; + sum += @as(f32, @floatFromInt(vec.unpacked_cache[i])) * weight; + } + } + + // Threshold-based quantization (collapse) + result.unpacked_cache[i] = if (sum > 0.5) 1 else if (sum < -0.5) -1 else 0; + } + + return result; +} + +/// Measure: collapse superposition to classical ternary state +/// Samples from |ψ|² distribution (Born rule) +/// +/// In VSA framework: measurement = reading out hypervector +/// Quantum extension: probabilistic sampling based on amplitudes +/// For simplicity: deterministic collapse to quantized state +pub fn measure(qvec: *const HybridBigInt, rng: *std.Random) HybridBigInt { + _ = rng; // For future probabilistic measurement (const) + + // Born rule: P(state) = |α|² + // Sample from ternary distribution based on amplitudes + // For Trinity: collapse to quantized ternary value + + // In current implementation, HybridBigInt is already quantized + // This function returns a copy (collapsing any superposition metadata) + + const result = qvec.*; + + // In full implementation: + // 1. Extract "amplitude" from packed representation + // 2. Use threshold to decide {-1, 0, +1} + // 3. For probabilistic measurement, sample from distribution + + // For now: deterministic quantization (already collapsed) + return result; +} + +/// Quantum similarity with interference term +/// Includes phase-dependent interference: cos(phase_diff) +/// +/// In VSA framework: similarity = cosine similarity of hypervectors +/// Quantum extension: includes phase interference +/// sim_q = sim_classical × (1 + η·cos(Δφ)) +pub fn similarity_quantum(a: *const HybridBigInt, b: *const HybridBigInt, phase_diff: f32) f64 { + const classical_sim = cosineSimilarity(a, b); + + // Interference term: constructive (cos>0) or destructive (cos<0) + // η = 0.5 is interference strength + const interference = 0.5 * @cos(phase_diff); + + return classical_sim * (1.0 + interference); +} + +/// Apply phase shift to hypervector (for quantum interference) +/// Rotates the vector in the complex phase plane +// Mutable, for the same reason qbind is: it delegates to permute, which takes a +// mutable pointer because the value caches its own unpacked form. It has no +// callers yet, so nothing else moves with this. +pub fn applyPhase(vec: *HybridBigInt, phase_shift: f32, allocator: std.mem.Allocator) !HybridBigInt { + _ = allocator; // Reserved for future allocation needs + // In a full quantum VSA, this would rotate complex amplitudes + // For ternary VSA, we simulate via permute-like operation + + // Number of trit positions to shift + // @abs of an i32 is a u32, so the modulus has to be unsigned as well; it was + // an i32 and the two sides could not meet. The result is already + // non-negative, which is why the second @abs below is now redundant and + // kept only so the line reads the same as it did. + const shift_amount = @abs(@as(i32, @intFromFloat(phase_shift * 10.0))) % @as(u32, @intCast(vec.trit_len)); + + const abs_shift = shift_amount; + + if (abs_shift > 0) { + return permute(vec, @as(usize, @intCast(abs_shift))); + } + + // No shift - return copy + const result = vec.*; + return result; +} + +/// Compute quantum coherence between multiple vectors +/// Returns value [0, 1] where 1 = fully coherent +pub fn computeCoherence(vectors: []const HybridBigInt) f32 { + if (vectors.len < 2) return 1.0; + + var total_sim: f64 = 0.0; + var count: usize = 0; + + for (0..vectors.len) |i| { + for (i + 1..vectors.len) |j| { + const sim = cosineSimilarityF16(&vectors[i], &vectors[j]); + total_sim += sim; + count += 1; + } + } + + return if (count > 0) @as(f32, @floatCast(total_sim / @as(f64, @floatFromInt(count)))) else 0.0; +} + +/// Entangle two hypervectors (correlated superposition) +/// Creates a combined state that maintains correlation +pub fn entangle(a: *const HybridBigInt, b: *const HybridBigInt, correlation: f32) struct { + left: HybridBigInt, + right: HybridBigInt, +} { + // Create correlated copies based on correlation strength + // correlation ∈ [0, 1]: 0 = independent, 1 = fully entangled + + var left = a.*; + var right = b.*; + + // Apply correlation: blend some trits + if (correlation > 0 and a.trit_len == b.trit_len) { + const num_entangled = @as(usize, @intFromFloat(@as(f32, @floatFromInt(a.trit_len)) * correlation)); + + for (0..num_entangled) |i| { + const idx = i; // Simple linear mapping + if (idx < a.trit_len and idx < b.trit_len) { + // Swap some trits to create correlation + left.unpacked_cache[idx] = b.unpacked_cache[idx]; + right.unpacked_cache[idx] = a.unpacked_cache[idx]; + } + } + } + + return .{ .left = left, .right = right }; +} + +// φ² + 1/φ² = 3 | TRINITY + +// ═══════════════════════════════════════════════════════════════════════════════ +// QUANTUM VSA TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "qbundle with amplitudes" { + const a = randomVector(100, 111); + const b = randomVector(100, 222); + const c = randomVector(100, 333); + + const amplitudes = [_]f32{ 1.0, 1.0, 1.0 }; + + // Create array of vectors (pass by value for qbundle API) + var vec_slice = [_]HybridBigInt{ a, b, c }; + + const result = try qbundle(&vec_slice, &litudes, std.testing.allocator); + + // Result should be valid ternary vector + try std.testing.expect(result.trit_len > 0); + // Check first 100 trits are in valid range + const check_len = @min(100, result.trit_len); + for (0..check_len) |i| { + const trit = result.unpacked_cache[i]; + try std.testing.expect(trit >= -1 and trit <= 1); + } +} + +test "similarity_quantum with interference" { + var a = randomVector(100, 444); + var b = randomVector(100, 555); + + // Classical similarity + const sim_classical = cosineSimilarity(&a, &b); + + // Quantum similarity with constructive interference (phase_diff = 0) + const sim_constructive = similarity_quantum(&a, &b, 0.0); + + // Quantum similarity with destructive interference (phase_diff = π) + const sim_destructive = similarity_quantum(&a, &b, std.math.pi); + + // Constructive should enhance similarity + try std.testing.expect(sim_constructive >= sim_classical); + + // Destructive should reduce similarity + try std.testing.expect(sim_destructive <= sim_classical); +} + +test "computeCoherence" { + const v1 = randomVector(50, 123); + const v2 = randomVector(50, 124); + var v3 = randomVector(50, 125); + + // Set v3 to be similar to v1 + for (0..@min(v1.trit_len, v3.trit_len)) |i| { + if (i < v3.trit_len) v3.unpacked_cache[i] = v1.unpacked_cache[i]; + } + + // Create array of vectors (pass by value for computeCoherence API) + var vec_slice = [_]HybridBigInt{ v1, v2, v3 }; + + const coherence = computeCoherence(&vec_slice); + + // Should have some coherence (> 0) + try std.testing.expect(coherence > 0.0); +} + +test "entangle with correlation" { + var a = randomVector(50, 666); + var b = randomVector(50, 777); + + const fully_entangled = entangle(&a, &b, 1.0); + + // With full correlation, vectors should share trits + try std.testing.expectEqual( + a.unpacked_cache[0], + fully_entangled.right.unpacked_cache[0], + ); + + const independent = entangle(&a, &b, 0.0); + + // With zero correlation, vectors should be copies + try std.testing.expectEqual( + a.unpacked_cache[0], + independent.left.unpacked_cache[0], + ); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig new file mode 100644 index 0000000..46da2e1 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig @@ -0,0 +1,532 @@ +// 🤖 TRINITY v0.11.0: Suborbital Order +// FPGA VSA Bind Interface — Week 2 +// +// Provides Zig interface to FPGA-accelerated VSA operations +// via UART communication with QMTECH XC7A100T + +const std = @import("std"); +const builtin = @import("builtin"); +const common = @import("common.zig"); +const HybridBigInt = common.HybridBigInt; +const Trit = common.Trit; + +pub const Config = struct { + /// UART device path + device: []const u8, + /// Baud rate + baud: u32 = 115200, + /// Vector dimension (must match FPGA) + dimension: usize = 256, + /// Timeout in milliseconds + timeout_ms: u32 = 5000, +}; + +pub const FPGAInterface = struct { + port: std.fs.File, + config: Config, + allocator: std.mem.Allocator, + + const Self = @This(); + + /// Initialize FPGA interface + pub fn init(config: Config, allocator: std.mem.Allocator) !Self { + const device_path = if (builtin.os.tag == .linux) + "/dev/ttyUSB0" + else if (builtin.os.tag == .macos) + "/dev/tty.usbserial-.*" + else + return error.UnsupportedOS; + + // Try to open the UART device + // OpenFlags dropped the separate .read/.write booleans for .mode. + const port = std.fs.openFileAbsolute(device_path, .{ + .mode = .read_write, + }) catch |err| { + std.log.err("Failed to open FPGA UART device: {}", .{err}); + return err; + }; + + return Self{ + .port = port, + .config = config, + .allocator = allocator, + }; + } + + /// Close FPGA interface + pub fn deinit(self: *Self) void { + self.port.close(); + } + + // ═══════════════════════════════════════════════════════════════════════ + // UART PROTOCOL + // ═══════════════════════════════════════════════════════════════════════ + + const Command = enum(u8) { + BIND = 0x01, + UNBIND = 0x02, + BUNDLE2 = 0x03, + BUNDLE3 = 0x04, + SIMILARITY = 0x05, + PING = 0xFF, + }; + + const Response = enum(u8) { + OK = 0x00, + ERROR = 0x01, + BUSY = 0x02, + PONG = 0xFF, + }; + + /// Send command to FPGA + fn sendCommand(self: *Self, cmd: Command, data: []const u8) !void { + var buffer: [1024]u8 = undefined; + var offset: usize = 0; + + buffer[offset] = @intFromEnum(cmd); + offset += 1; + + buffer[offset] = @intCast(data.len & 0xFF); + offset += 1; + + @memcpy(buffer[offset..][0..data.len], data); + offset += data.len; + + // Simple checksum + var checksum: u8 = 0; + for (buffer[0..offset]) |b| checksum ^= b; + buffer[offset] = checksum; + offset += 1; + + _ = try self.port.writeAll(buffer[0..offset]); + } + + /// Receive response from FPGA + fn recvResponse(self: *Self, expected_len: usize) ![]u8 { + _ = expected_len; + var buffer: [1024]u8 = undefined; + const header_len = 2; // status + len + + const n = try self.port.readAll(buffer[0..header_len]); + if (n < header_len) return error.ShortRead; + + const status = buffer[0]; + const len = buffer[1]; + + if (status == @intFromEnum(Response.ERROR)) { + return error.FPGAError; + } + + if (len > 0) { + const n2 = try self.port.readAll(buffer[header_len .. header_len + len]); + if (n2 < len) return error.ShortRead; + } + + // Verify checksum + // ... + + return buffer[0 .. header_len + len]; + } + + // ═══════════════════════════════════════════════════════════════════════ + // VSA OPERATIONS + // ═══════════════════════════════════════════════════════════════════════ + + /// Bind two hypervectors on FPGA + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); + + // Pack trits into 2-bit format for FPGA + const bytes_needed = (dim * 2 + 7) / 8; + var buffer = try self.allocator.alloc(u8, bytes_needed * 2); + defer self.allocator.free(buffer); + + // Pack vector A + for (0..dim) |i| { + const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + // Pack vector B (offset by bytes_needed) + const b_offset = bytes_needed; + for (0..dim) |i| { + const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = b_offset + (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + // Send BIND command + try self.sendCommand(Command.BIND, buffer[0 .. bytes_needed * 2]); + + // Receive result + const response = try self.recvResponse(bytes_needed); + + // Unpack result + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.trit_len = dim; + + for (0..dim) |i| { + const byte_idx = 2 + (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // Same rule as the packing side: response[byte_idx] is a u8, so the + // shift amount has to be a u3. + // Typed u2 rather than left as u8: decodeTrit takes a u2, and the + // mask has already narrowed the value to two bits, so the cast + // states what the mask guarantees. + const encoded: u2 = @intCast((response[byte_idx] >> @as(u3, @intCast(bit_offset))) & 0x03); + result.unpacked_cache[i] = decodeTrit(encoded); + } + + return result; + } + + /// Check if FPGA is responsive + pub fn ping(self: *Self) !bool { + try self.sendCommand(Command.PING, &[_]u8{}); + const response = try self.recvResponse(0); + return response[0] == @intFromEnum(Response.PONG); + } + + /// Bundle two hypervectors on FPGA (majority voting) + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); + const bytes_needed = (dim * 2 + 7) / 8; + var buffer = try self.allocator.alloc(u8, bytes_needed * 2); + defer self.allocator.free(buffer); + + // Pack vectors (same as bind) + for (0..dim) |i| { + const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + const b_offset = bytes_needed; + for (0..dim) |i| { + const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = b_offset + (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + // Send BUNDLE command + try self.sendCommand(Command.BUNDLE2, buffer[0 .. bytes_needed * 2]); + + // Receive result (same format as bind) + const response = try self.recvResponse(bytes_needed); + + // Unpack result + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.trit_len = dim; + + for (0..dim) |i| { + const byte_idx = 2 + (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // Same rule as the packing side: response[byte_idx] is a u8, so the + // shift amount has to be a u3. + // Typed u2 rather than left as u8: decodeTrit takes a u2, and the + // mask has already narrowed the value to two bits, so the cast + // states what the mask guarantees. + const encoded: u2 = @intCast((response[byte_idx] >> @as(u3, @intCast(bit_offset))) & 0x03); + result.unpacked_cache[i] = decodeTrit(encoded); + } + + return result; + } + + /// Compute dot product similarity on FPGA + pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); + const bytes_needed = (dim * 2 + 7) / 8; + var buffer = try self.allocator.alloc(u8, bytes_needed * 2); + defer self.allocator.free(buffer); + + // Pack vectors + for (0..dim) |i| { + const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + const b_offset = bytes_needed; + for (0..dim) |i| { + const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; + const encoded = encodeTrit(trit_val); + const byte_idx = b_offset + (i * 2) / 8; + const bit_offset = (i * 2) % 8; + // encoded is a u2, and Zig types a shift amount by the width of what + // is being shifted: a u2 admits only a u1, which is why a bit_offset + // of 0, 2, 4 or 6 could not be used here at all. The value is going + // into a u8, so it is widened first and the amount typed to match. + buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); + if (bit_offset >= 6) { + buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); + } + } + + // Send SIMILARITY command + try self.sendCommand(Command.SIMILARITY, buffer[0 .. bytes_needed * 2]); + + // Receive result (3 bytes: status + dot LSB + dot MSB) + const response = try self.recvResponse(3); + + // Parse dot product (signed 11-bit: -256 to +256) + const dot_lsb = response[1]; + const dot_msb = response[2] & 0x07; + const dot: i11 = @as(i11, @bitCast(@as(u11, @intCast(dot_msb)) << 8 | dot_lsb)); + // No manual sign extension. Zig's `if` takes a bool and `dot_msb & 0x04` + // is a u8, so this never compiled -- and the operation it was reaching + // for is already done: @bitCast from u11 to i11 reads the top bit as the + // sign, which is exactly what bit 2 of dot_msb is. The old line also + // or-ed 0xF800 into an i11, a value that does not fit in one, so it + // could not have run even if the condition had been well typed. + + // Normalize by dimension (cosine similarity for unit vectors) + // For raw similarity: just return dot / dim + return @as(f64, @floatFromInt(dot)) / @as(f64, @floatFromInt(dim)); + } + + // ═══════════════════════════════════════════════════════════════════════ + // TRIT ENCODING + // ═══════════════════════════════════════════════════════════════════════ + + /// Encode trit to 2-bit format + inline fn encodeTrit(t: i2) u2 { + return switch (t) { + 0 => 0b00, + 1 => 0b01, + -1 => 0b10, + else => unreachable, + }; + } + + /// Decode trit from 2-bit format + inline fn decodeTrit(e: u2) i2 { + return switch (e) { + 0b00 => 0, + 0b01 => 1, + 0b10 => -1, + else => 0, + }; + } +}; + +// ═════════════════════════════════════════════════════════════════════════════ +// CPU FALLBACK (when FPGA unavailable) +// ═════════════════════════════════════════════════════════════════════════════ + +pub const CpuFallback = struct { + allocator: std.mem.Allocator, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ .allocator = allocator }; + } + + /// Bind using CPU (simulates FPGA behavior) + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + _ = self; + const core = @import("core.zig"); + return core.bind(a, b); + } + + /// Bundle using CPU + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + _ = self; + const core = @import("core.zig"); + return core.bundle2(a, b); + } + + /// Similarity using CPU + pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + _ = self; + const core = @import("core.zig"); + return core.cosineSimilarity(a, b); + } +}; + +// ═════════════════════════════════════════════════════════════════════════════ +// UNIFIED INTERFACE (auto-detect FPGA) +// ═════════════════════════════════════════════════════════════════════════════ + +pub const AutoVSA = struct { + fpga: ?FPGAInterface, + cpu: CpuFallback, + use_fpga: bool, + + const Self = @This(); + + /// Initialize with auto-detection + pub fn init(config: Config, allocator: std.mem.Allocator) Self { + // var, because ping takes *Self and a const binding cannot hand out the + // mutable pointer the method asks for. + var fpga = FPGAInterface.init(config, allocator) catch |err| { + std.log.warn("FPGA unavailable ({}), using CPU fallback", .{err}); + return Self{ + .fpga = null, + .cpu = CpuFallback.init(allocator), + .use_fpga = false, + }; + }; + + // Verify FPGA is responsive + if (fpga.ping() catch false) { + std.log.info("FPGA VSA accelerator detected", .{}); + return Self{ + .fpga = fpga, + .cpu = CpuFallback.init(allocator), + .use_fpga = true, + }; + } else { + fpga.deinit(); + std.log.warn("FPGA not responsive, using CPU fallback", .{}); + return Self{ + .fpga = null, + .cpu = CpuFallback.init(allocator), + .use_fpga = false, + }; + } + } + + pub fn deinit(self: *Self) void { + if (self.fpga) |*f| f.deinit(); + } + + /// Bind with automatic FPGA/CPU selection + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + if (self.use_fpga and self.fpga != null) { + return self.fpga.?.bind(a, b); + } else { + return self.cpu.bind(a, b); + } + } + + /// Bundle with automatic FPGA/CPU selection + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { + if (self.use_fpga and self.fpga != null) { + return self.fpga.?.bundle(a, b); + } else { + return self.cpu.bundle(a, b); + } + } + + /// Similarity with automatic FPGA/CPU selection + pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + if (self.use_fpga and self.fpga != null) { + return self.fpga.?.similarity(a, b); + } else { + return self.cpu.similarity(a, b); + } + } +}; + +// Backward compatibility alias +pub const AutoBind = AutoVSA; + +// ═════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═════════════════════════════════════════════════════════════════════════════ + +test "fpga bind: trit encoding" { + const testing = std.testing; + + try testing.expectEqual(@as(u2, 0b00), FPGAInterface.encodeTrit(0)); + try testing.expectEqual(@as(u2, 0b01), FPGAInterface.encodeTrit(1)); + try testing.expectEqual(@as(u2, 0b10), FPGAInterface.encodeTrit(-1)); +} + +test "fpga bind: trit decoding" { + const testing = std.testing; + + try testing.expectEqual(@as(i2, 0), FPGAInterface.decodeTrit(0b00)); + try testing.expectEqual(@as(i2, 1), FPGAInterface.decodeTrit(0b01)); + try testing.expectEqual(@as(i2, -1), FPGAInterface.decodeTrit(0b10)); +} + +test "fpga bind: cpu fallback" { + const testing = std.testing; + + var cpu = CpuFallback.init(testing.allocator); + + var a = HybridBigInt.zero(); + a.mode = .unpacked_mode; + a.trit_len = 16; + + var b = HybridBigInt.zero(); + b.mode = .unpacked_mode; + b.trit_len = 16; + + // Fill with test data + for (0..16) |i| { + // The subtraction has to happen in a signed type. i is a usize, so + // @rem(i, 3) is a usize too, and the first iteration computed 0 - 1 in + // unsigned arithmetic and panicked on the overflow. The cycle intended + // is -1, 0, +1, which needs somewhere for the -1 to live. + const idx: i8 = @intCast(@rem(i, 3)); + const idx_b: i8 = @intCast(@rem(i + 1, 3)); + a.unpacked_cache[i] = idx - 1; + b.unpacked_cache[i] = idx_b - 1; + } + + const result = try cpu.bind(&a, &b); + try testing.expectEqual(@as(usize, 16), result.trit_len); +} + +// φ² + 1/φ² = 3 = TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig new file mode 100644 index 0000000..12d27fc --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig @@ -0,0 +1,247 @@ +// VSA Core — HybridBigInt Operations (GENERATED) +// Stage 2.0: SIMD-accelerated VSA with HybridBigInt +// DO NOT EDIT — Regenerate from .tri spec +// +// φ² + 1/φ² = 3 | TRINITY + +const std = @import("std"); +const hybrid = @import("hybrid.zig"); +const HybridBigInt = hybrid.HybridBigInt; +const Trit = hybrid.Trit; +const Vec32i8 = hybrid.Vec32i8; +const Vec32i16 = hybrid.Vec32i16; +const SIMD_WIDTH = hybrid.SIMD_WIDTH; +const StorageMode = hybrid.StorageMode; + +pub fn bind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const min_len = @min(a.trit_len, b.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const prod = a_vec * b_vec; + result.unpacked_cache[i..][0..SIMD_WIDTH].* = prod; + } + + while (i < len) : (i += 1) { + const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; + result.unpacked_cache[i] = a_trit * b_trit; + } + + return result; +} + +pub fn unbind(bound: *HybridBigInt, key: *HybridBigInt) HybridBigInt { + return bind(bound, key); +} + +pub fn bundle2(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const min_len = @min(a.trit_len, b.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + + const a_wide: Vec32i16 = a_vec; + const b_wide: Vec32i16 = b_vec; + const sum = a_wide + b_wide; + + const zeros: Vec32i16 = @splat(0); + const ones: Vec32i16 = @splat(1); + const neg_ones: Vec32i16 = @splat(-1); + + const pos_mask = sum > zeros; + const neg_mask = sum < zeros; + + var out = zeros; + out = @select(i16, pos_mask, ones, out); + out = @select(i16, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + result.unpacked_cache[i + j] = @truncate(out[j]); + } + } + + while (i < len) : (i += 1) { + const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + const sum = a_trit + b_trit; + + if (sum > 0) { + result.unpacked_cache[i] = 1; + } else if (sum < 0) { + result.unpacked_cache[i] = -1; + } else { + result.unpacked_cache[i] = 0; + } + } + + return result; +} + +pub fn bundle3(a: *HybridBigInt, b: *HybridBigInt, c: *HybridBigInt) HybridBigInt { + a.ensureUnpacked(); + b.ensureUnpacked(); + c.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + + const len = @max(@max(a.trit_len, b.trit_len), c.trit_len); + const min_len = @min(@min(a.trit_len, b.trit_len), c.trit_len); + const num_full_chunks = min_len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const c_vec: Vec32i8 = c.unpacked_cache[i..][0..SIMD_WIDTH].*; + + const a_wide: Vec32i16 = a_vec; + const b_wide: Vec32i16 = b_vec; + const c_wide: Vec32i16 = c_vec; + const sum = a_wide + b_wide + c_wide; + + const zeros: Vec32i16 = @splat(0); + const ones: Vec32i16 = @splat(1); + const neg_ones: Vec32i16 = @splat(-1); + + const pos_mask = sum > zeros; + const neg_mask = sum < zeros; + + var out = zeros; + out = @select(i16, pos_mask, ones, out); + out = @select(i16, neg_mask, neg_ones, out); + + inline for (0..SIMD_WIDTH) |j| { + result.unpacked_cache[i + j] = @truncate(out[j]); + } + } + + while (i < len) : (i += 1) { + const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + const c_trit: i16 = if (i < c.trit_len) c.unpacked_cache[i] else 0; + const sum = a_trit + b_trit + c_trit; + + if (sum > 0) { + result.unpacked_cache[i] = 1; + } else if (sum < 0) { + result.unpacked_cache[i] = -1; + } else { + result.unpacked_cache[i] = 0; + } + } + + return result; +} + +pub fn permute(v: *HybridBigInt, n: usize) HybridBigInt { + v.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = v.trit_len; + + const rotate = if (v.trit_len > 0) @mod(n, v.trit_len) else 0; + + for (0..v.trit_len) |i| { + const src_idx = if (i >= rotate) i - rotate else i + v.trit_len - rotate; + result.unpacked_cache[i] = v.unpacked_cache[src_idx]; + } + + return result; +} + +pub fn inversePermute(v: *HybridBigInt, n: usize) HybridBigInt { + v.ensureUnpacked(); + + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.dirty = true; + result.trit_len = v.trit_len; + + const rotate = if (v.trit_len > 0) @mod(n, v.trit_len) else 0; + + for (0..v.trit_len) |i| { + const src_idx = (i + rotate) % v.trit_len; + result.unpacked_cache[i] = v.unpacked_cache[src_idx]; + } + + return result; +} + +pub fn dotProduct(a: *HybridBigInt, b: *HybridBigInt) i64 { + a.ensureUnpacked(); + b.ensureUnpacked(); + + var sum: i64 = 0; + const len = @min(a.trit_len, b.trit_len); + const num_full_chunks = len / SIMD_WIDTH; + + var i: usize = 0; + while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { + const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; + const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; + const a_wide: Vec32i16 = a_vec; + const b_wide: Vec32i16 = b_vec; + const prod = a_wide * b_wide; + sum += @reduce(.Add, prod); + } + + while (i < len) : (i += 1) { + const a_trit: i64 = if (i < a.trit_len) a.unpacked_cache[i] else 0; + const b_trit: i64 = if (i < b.trit_len) b.unpacked_cache[i] else 0; + sum += a_trit * b_trit; + } + + return sum; +} + +pub fn vectorNorm(v: *HybridBigInt) f64 { + v.ensureUnpacked(); + + var sum: f64 = 0.0; + for (0..v.trit_len) |i| { + const t: f64 = @floatFromInt(v.unpacked_cache[i]); + sum += t * t; + } + return @sqrt(sum); +} + +pub fn cosineSimilarity(a: *const HybridBigInt, b: *const HybridBigInt) f64 { + const dot = @constCast(a).dotProduct(@constCast(b)); + const norm_a = vectorNorm(@constCast(a)); + const norm_b = vectorNorm(@constCast(b)); + + if (norm_a == 0 or norm_b == 0) return 0; + + return @as(f64, @floatFromInt(dot)) / (norm_a * norm_b); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig new file mode 100644 index 0000000..df1d1da --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig @@ -0,0 +1,340 @@ +//! VSA Encoding — Generated from specs/vsa/encoding.tri +//! φ² + 1/φ² = 3 | TRINITY +//! +//! DO NOT EDIT: This file is generated from encoding.tri spec +//! +//! Binary encoding for VSA vectors + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayListUnmanaged; + +const common = @import("common.zig"); +const HybridBigInt = common.HybridBigInt; + +pub const Trit = i8; +pub const Vec32i8 = @Vector(32, i8); + +// ============================================================================ +// ENCODING TYPES +// ============================================================================ + +/// Encoding format for trits +pub const TritEncoding = enum(u8) { + /// Single bit per trit (neg/pos only) + one_bit, + /// Two bits per trit (balanced ternary) + two_bit, + /// Packed encoding (4 trits per byte) + packed_four, +}; + +/// Encoded trit data +pub const EncodedTrits = struct { + data: []u8, + encoding: TritEncoding, + count: usize, + + pub fn init(allocator: Allocator, encoding: TritEncoding, count: usize) !EncodedTrits { + const bits_per_trit: usize = switch (encoding) { + .one_bit => 1, + .two_bit => 2, + .packed_four => 2, + }; + const total_bits = count * bits_per_trit; + const total_bytes = (total_bits + 7) / 8; // Round up to bytes + + const data = try allocator.alloc(u8, total_bytes); + @memset(data, 0); + + return .{ + .data = data, + .encoding = encoding, + .count = count, + }; + } + + pub fn deinit(self: *EncodedTrits, allocator: Allocator) void { + allocator.free(self.data); + self.* = undefined; + } +}; + +/// Binary codebook for VSA operations +pub const Codebook = struct { + bind_table: [3][3]u8, + majority_table: [3][3]u8, + + pub fn init() Codebook { + var cb: Codebook = undefined; + + // Initialize bind table (trit multiplication) + for (0..3) |i| { + for (0..3) |j| { + const t1 = @as(i8, @intCast(i)) - 1; + const t2 = @as(i8, @intCast(j)) - 1; + const result = t1 * t2; + cb.bind_table[i][j] = @as(u8, @intCast(result + 1)); + } + } + + // Initialize majority table (3-way majority vote) + for (0..3) |i| { + for (0..3) |j| { + // Simple implementation: return first non-zero if exists, else 0 + const t1 = @as(i8, @intCast(i)) - 1; + const t2 = @as(i8, @intCast(j)) - 1; + const result = if (t1 == t2) t1 else 0; + cb.majority_table[i][j] = @as(u8, @intCast(result + 1)); + } + } + + return cb; + } + + /// Look up bind operation result + pub fn bindLookup(self: *const Codebook, a: Trit, b: Trit) Trit { + const ai = @as(usize, @intCast(a + 1)); + const bi = @as(usize, @intCast(b + 1)); + return @as(Trit, @intCast(self.bind_table[ai][bi])) - 1; + } + + /// Look up majority operation result + pub fn majorityLookup(self: *const Codebook, a: Trit, b: Trit) Trit { + const ai = @as(usize, @intCast(a + 1)); + const bi = @as(usize, @intCast(b + 1)); + return @as(Trit, @intCast(self.majority_table[ai][bi])) - 1; + } +}; + +// ============================================================================ +// ENCODING FUNCTIONS +// ============================================================================ + +/// Encode trits to binary using specified encoding +pub fn encodeTrits(allocator: Allocator, trits: []const Trit, encoding: TritEncoding) !EncodedTrits { + var encoded = try EncodedTrits.init(allocator, encoding, trits.len); + + switch (encoding) { + .one_bit => { + // Encode sign bit (0 for positive, 1 for negative, zero is 0) + for (trits, 0..) |t, i| { + const byte_idx = i / 8; + const bit_idx: u3 = @intCast(i % 8); + if (t > 0) { + encoded.data[byte_idx] &= ~(@as(u8, 1) << bit_idx); // Positive = 0 + } else if (t < 0) { + encoded.data[byte_idx] |= (@as(u8, 1) << bit_idx); // Negative = 1 + } + // Zero stays 0 + } + }, + .two_bit => { + // Encode as two bits (00=0, 01=1, 10=-1) + for (trits, 0..) |t, i| { + const byte_idx = i / 4; + const bit_offset: u3 = @intCast((i % 4) * 2); + + const encoded_val: u2 = if (t == 0) 0 else if (t == 1) 1 else 2; + encoded.data[byte_idx] |= (@as(u8, encoded_val) << bit_offset); + } + }, + .packed_four => { + // Pack 4 trits per byte (2 bits each) + for (trits, 0..) |t, i| { + const byte_idx = i / 4; + const bit_offset: u3 = @intCast((i % 4) * 2); + + const encoded_val: u2 = if (t == 0) 0 else if (t == 1) 1 else 2; + encoded.data[byte_idx] |= (@as(u8, encoded_val) << bit_offset); + } + }, + } + + return encoded; +} + +/// Decode binary to trits +pub fn decodeTrits(allocator: Allocator, encoded: *const EncodedTrits) ![]Trit { + const trits = try allocator.alloc(Trit, encoded.count); + + switch (encoded.encoding) { + .one_bit => { + for (0..encoded.count) |i| { + const byte_idx = i / 8; + const bit_idx: u3 = @intCast(i % 8); + const bit = (encoded.data[byte_idx] >> bit_idx) & 1; + trits[i] = if (bit == 0) @as(Trit, 1) else -1; + } + }, + .two_bit, .packed_four => { + for (0..encoded.count) |i| { + const byte_idx = i / 4; + const bit_offset: u3 = @intCast((i % 4) * 2); + const encoded_val = (encoded.data[byte_idx] >> bit_offset) & 0x3; + + trits[i] = switch (encoded_val) { + 0 => 0, + 1 => 1, + 2 => -1, + else => 0, + }; + } + }, + } + + return trits; +} + +/// Compute encoding size in bytes +pub fn encodingSize(count: usize, encoding: TritEncoding) usize { + const bits_per_trit: usize = switch (encoding) { + .one_bit => 1, + .two_bit => 2, + .packed_four => 2, + }; + const total_bits = count * bits_per_trit; + return (total_bits + 7) / 8; +} + +// ============================================================================ +// CODEBOOK FUNCTIONS +// ============================================================================ + +/// Global codebook instance +pub const GLOBAL_CODEBOOK = Codebook.init(); + +/// Bind using codebook lookup +pub fn codebookBind(a: Trit, b: Trit) Trit { + return GLOBAL_CODEBOOK.bindLookup(a, b); +} + +/// Majority using codebook lookup +pub fn codebookMajority(a: Trit, b: Trit) Trit { + return GLOBAL_CODEBOOK.majorityLookup(a, b); +} + +// ============================================================================ +// TESTS +// ============================================================================ + +test "VSA Encoding: EncodedTrits init" { + const allocator = std.testing.allocator; + var encoded = try EncodedTrits.init(allocator, .two_bit, 16); + defer encoded.deinit(allocator); + + try std.testing.expectEqual(@as(usize, 16), encoded.count); + try std.testing.expectEqual(TritEncoding.two_bit, encoded.encoding); +} + +test "VSA Encoding: encodeTrits two_bit" { + const allocator = std.testing.allocator; + const trits = [_]Trit{ -1, 0, 1, 0, -1 }; + + var encoded = try encodeTrits(allocator, &trits, .two_bit); + defer encoded.deinit(allocator); + + try std.testing.expectEqual(@as(usize, 5), encoded.count); +} + +test "VSA Encoding: decodeTrits two_bit" { + const allocator = std.testing.allocator; + const trits = [_]Trit{ -1, 0, 1, 0, -1 }; + + var encoded = try encodeTrits(allocator, &trits, .two_bit); + defer encoded.deinit(allocator); + + const decoded = try decodeTrits(allocator, &encoded); + defer allocator.free(decoded); + + try std.testing.expectEqualSlices(Trit, &trits, decoded); +} + +test "VSA Encoding: encodingSize" { + try std.testing.expectEqual(@as(usize, 1), encodingSize(8, .one_bit)); + try std.testing.expectEqual(@as(usize, 2), encodingSize(8, .two_bit)); + try std.testing.expectEqual(@as(usize, 2), encodingSize(8, .packed_four)); +} + +test "VSA Encoding: Codebook init" { + const cb = Codebook.init(); + + // Check bind table + try std.testing.expectEqual(@as(Trit, 1), cb.bindLookup(1, 1)); + try std.testing.expectEqual(@as(Trit, -1), cb.bindLookup(1, -1)); + try std.testing.expectEqual(@as(Trit, -1), cb.bindLookup(-1, 1)); +} + +test "VSA Encoding: codebookBind" { + try std.testing.expectEqual(@as(Trit, 1), codebookBind(1, 1)); + try std.testing.expectEqual(@as(Trit, 0), codebookBind(0, 1)); + try std.testing.expectEqual(@as(Trit, -1), codebookBind(-1, 1)); +} + +test "VSA Encoding: round trip" { + const allocator = std.testing.allocator; + const original = [_]Trit{ -1, -1, 0, 0, 1, 1, -1, 0, 1, 0, -1, 1, 0, 1, -1, 0 }; + + var encoded = try encodeTrits(allocator, &original, .two_bit); + defer encoded.deinit(allocator); + + const decoded = try decodeTrits(allocator, &encoded); + defer allocator.free(decoded); + + try std.testing.expectEqualSlices(Trit, &original, decoded); +} + +// ============================================================================ +// TEXT ENCODING STUBS (TODO: full implementation) +// ============================================================================ + +pub const TEXT_VECTOR_DIM: usize = 512; + +/// Encode single character to VSA vector (stub) +pub fn charToVector(c: u8) HybridBigInt { + // TODO: Implement proper char-to-vector encoding + // For now, convert char to ternary and store + return HybridBigInt.fromI64(@as(i64, @intCast(c))); +} + +/// Encode text to VSA vector (stub - returns hash-based vector) +pub fn encodeText(text: []const u8) HybridBigInt { + // TODO: Implement proper text encoding + // For now, use simple hash as placeholder + var hash: i64 = 0; + for (text) |c| { + hash = hash *% 31 + @as(i64, @intCast(c)); + } + return HybridBigInt.fromI64(hash); +} + +/// Decode VSA vector back to text (stub) +pub fn decodeText(vector: *const HybridBigInt, allocator: Allocator) ![]u8 { + _ = vector; // Will be used in full implementation + // TODO: Implement proper text decoding + return allocator.dupe(u8, ""); +} + +/// Encode text as words (stub) +pub fn encodeTextWords(text: []const u8, allocator: Allocator) ![]HybridBigInt { + _ = text; + // TODO: Implement word-level encoding + const result = try allocator.alloc(HybridBigInt, 1); + result[0] = encodeText(""); + return result; +} + +/// Compute similarity between two text vectors +pub fn textSimilarity(text1: []const u8, text2: []const u8) f64 { + // TODO: Implement proper text similarity + // Stub: identical texts get 1.0, otherwise 0.5 + if (std.mem.eql(u8, text1, text2)) return 1.0; + return 0.5; +} + +/// Check if two texts are similar above threshold +pub fn textsAreSimilar(text1: []const u8, text2: []const u8, threshold: f64) bool { + _ = text1; + _ = text2; + return threshold >= 0.5; // Placeholder +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig new file mode 100644 index 0000000..6457ed1 --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig @@ -0,0 +1,412 @@ +//! ═══════════════════════════════════════════════════════════════════════════════ +//! HRR — Holographic Reduced Representations +//! ═══════════════════════════════════════════════════════════════════════════════ +//! +//! Vector Symbolic Architecture (VSA) using Holographic Reduced Representations. +//! High-dimensional vectors for symbolic reasoning and cognitive computing. +//! +//! Features: +//! - Random vector generation with Gaussian distribution +//! - Binding via circular convolution +//! - Unbinding (inverse binding) +//! - Bundling (superposition of vectors) +//! - Similarity (cosine distance) +//! - Vector normalization +//! +//! References: +//! - Plate, R. (1995). "Holographic Reduced Representations" +//! - Kanerva, P. (2009). "Hyperdimensional Computing" +//! +//! φ² + 1/φ² = 3 = TRINITY +//! ═══════════════════════════════════════════════════════════════════════════════ + +const std = @import("std"); +const math = std.math; +const Allocator = std.mem.Allocator; +const random = std.crypto.random; + +/// ═══════════════════════════════════════════════════════════════════════════════ +/// SACRED CONSTANTS FOR HRR +/// ═══════════════════════════════════════════════════════════════════════════════ +const PHI: f64 = 1.618033988749895; // Golden Ratio +const PHI_INV: f64 = 0.618033988749895; // φ⁻¹ + +/// ═══════════════════════════════════════════════════════════════════════════════ +/// HRR — Holographic Reduced Representations +/// ═══════════════════════════════════════════════════════════════════════════════ +pub const HRR = struct { + dim: usize, + allocator: Allocator, + + pub const Error = error{ + DimensionMismatch, + EmptyVector, + InvalidVector, + }; + + /// Initialize HRR with given dimensionality + pub fn init(allocator: Allocator, dim: usize) !HRR { + if (dim < 8) return Error.InvalidVector; + return .{ + .dim = dim, + .allocator = allocator, + }; + } + + /// Initialize with φ-based dimension (phi-powered) + pub fn initPhi(allocator: Allocator, power: u32) !HRR { + // Dimensions that are powers of φ (rounded) + const base_dim: f64 = 1000.0; + const phi_factor = std.math.pow(f64, PHI, @as(f64, @floatFromInt(power))); + const dim: usize = @intFromFloat(base_dim * phi_factor); + return init(allocator, dim); + } + + /// ═══════════════════════════════════════════════════════════════════════════════ + /// VECTOR OPERATIONS + /// ═══════════════════════════════════════════════════════════════════════════════ + /// Generate random high-dimensional vector with Gaussian distribution + pub fn randomVector(self: *const HRR) ![]f32 { + var vec = try self.allocator.alloc(f32, self.dim); + + // Generate using Box-Muller transform for Gaussian distribution + var i: usize = 0; + while (i < self.dim) : (i += 2) { + // Generate uniform random floats in (0, 1] + const u1_raw: f32 = random.float(f32); + const u2_raw: f32 = random.float(f32); + + // Avoid log(0) and ensure valid range + const u1_safe = if (u1_raw <= 0.0) 1.0e-6 else if (u1_raw >= 1.0) 0.999999 else u1_raw; + const u2_safe = if (u2_raw <= 0.0) 0.0 else if (u2_raw >= 1.0) 0.999999 else u2_raw; + + const r = @sqrt(-2.0 * @log(u1_safe)); + const theta = 2.0 * math.pi * u2_safe; + + vec[i] = r * @cos(theta); + if (i + 1 < self.dim) { + vec[i + 1] = r * @sin(theta); + } + } + + return self.normalize(vec); + } + + /// Generate deterministic vector from seed string (for encoding) + pub fn seededVector(self: *const HRR, seed: []const u8) ![]f32 { + var vec = try self.allocator.alloc(f32, self.dim); + + // Simple hash-based generation (djb2 algorithm with wrapping) + var hash: u32 = 5381; + for (seed) |c| { + hash = hash *% 33 +% @as(u8, @intCast(c)); + } + + var prng = std.Random.DefaultPrng.init(hash); + var i: usize = 0; + while (i < self.dim) : (i += 2) { + const u1_val = prng.random().float(f32); + const u2_val = prng.random().float(f32); + const u1_safe = if (u1_val > 1.0e-6) u1_val else 1.0e-6; + + const r = @sqrt(-2.0 * @log(u1_safe)); + const theta = 2.0 * math.pi * u2_val; + + vec[i] = r * @cos(theta); + if (i + 1 < self.dim) { + vec[i + 1] = r * @sin(theta); + } + } + + return self.normalize(vec); + } + + /// Bind two vectors using circular convolution + /// This creates an associative binding operation + pub fn bind(self: *const HRR, a: []const f32, b: []const f32) ![]f32 { + if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; + + const result = try self.allocator.alloc(f32, self.dim); + + // Circular convolution + // result[k] = sum(a[i] * b[(k-i) mod dim]) + for (0..self.dim) |k| { + var sum: f32 = 0; + for (0..self.dim) |i| { + const j = if (k >= i) k - i else self.dim + k - i; + sum += a[i] * b[j]; + } + result[k] = sum; + } + + return self.normalize(result); + } + + /// Unbind (inverse binding) — for HRR, inverse is the reversed vector + pub fn unbind(self: *const HRR, bound: []const f32, known: []const f32) ![]f32 { + if (bound.len != self.dim or known.len != self.dim) return Error.DimensionMismatch; + + // For HRR circular convolution, the inverse is the reversed vector + // Unbind(a ⊗ b, b) should recover a (approximately) + const result = try self.allocator.alloc(f32, self.dim); + + // Reverse the known vector (true inverse for circular convolution) + // Then convolve with bound vector + for (0..self.dim) |k| { + var sum: f32 = 0; + for (0..self.dim) |i| { + // For inverse: known_rev[j] = known[(dim - j) % dim] + const j = if (k >= i) k - i else self.dim + k - i; + const inv_idx = (self.dim - j) % self.dim; + sum += bound[i] * known[inv_idx]; + } + result[k] = sum; + } + + return self.normalize(result); + } + + /// Bundle (superposition) multiple vectors + pub fn bundle(self: *const HRR, vectors: []const []const f32) ![]f32 { + if (vectors.len == 0) return Error.EmptyVector; + + const result = try self.allocator.alloc(f32, self.dim); + @memset(result, 0); + + // Sum all vectors + for (vectors) |vec| { + if (vec.len != self.dim) return Error.DimensionMismatch; + for (result, 0..) |*r, i| { + r.* += vec[i]; + } + } + + return self.normalize(result); + } + + /// Compute cosine similarity between two vectors + pub fn similarity(self: *const HRR, a: []const f32, b: []const f32) !f32 { + if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; + + var dot: f32 = 0; + var norm_a: f32 = 0; + var norm_b: f32 = 0; + + for (a, 0..) |av, i| { + dot += av * b[i]; + norm_a += av * av; + norm_b += b[i] * b[i]; + } + + const denom = @sqrt(norm_a * norm_b); + return if (denom > 1.0e-6) dot / denom else 0; + } + + /// Normalize vector to unit length + fn normalize(_: *const HRR, vec: []f32) []f32 { + var norm: f32 = 0; + for (vec) |v| { + norm += v * v; + } + norm = @sqrt(norm); + + if (norm > 1.0e-6) { + const inv_norm = 1.0 / norm; + for (vec) |*v| { + v.* *= inv_norm; + } + } + + return vec; + } + + /// Compute Hamming distance (for binary-like comparison) + pub fn hammingDistance(self: *const HRR, a: []const f32, b: []const f32) !usize { + if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; + + var distance: usize = 0; + for (a, b) |av, bv| { + // Count as different if signs differ + if ((av >= 0) != (bv >= 0)) { + distance += 1; + } + } + + return distance; + } + + /// Cleanup vector + pub fn freeVector(self: *const HRR, vec: []f32) void { + self.allocator.free(vec); + } +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "HRR — Vector Generation" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec1 = try hrr.randomVector(); + defer hrr.freeVector(vec1); + + // Check dimension + try testing.expectEqual(@as(usize, 1000), vec1.len); + + // Check normalization (should be close to 1) + var norm: f32 = 0; + for (vec1) |v| { + norm += v * v; + } + try testing.expectApproxEqAbs(1.0, norm, 0.01); +} + +test "HRR — Deterministic Seeded Vector" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec1 = try hrr.seededVector("test"); + defer hrr.freeVector(vec1); + + const vec2 = try hrr.seededVector("test"); + defer hrr.freeVector(vec2); + + // Same seed should produce same vector + try testing.expectEqualSlices(f32, vec1, vec2); +} + +test "HRR — Binding Similarity" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec_a = try hrr.seededVector("alice"); + defer hrr.freeVector(vec_a); + const vec_b = try hrr.seededVector("bob"); + defer hrr.freeVector(vec_b); + + const bound = try hrr.bind(vec_a, vec_b); + defer hrr.freeVector(bound); + + // Binding should be order-independent for HRR + const bound2 = try hrr.bind(vec_b, vec_a); + defer hrr.freeVector(bound2); + + const sim = try hrr.similarity(bound, bound2); + + // Should be nearly identical + try testing.expect(sim > 0.9); +} + +test "HRR — Unbinding Recovery" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec_a = try hrr.seededVector("alice"); + defer hrr.freeVector(vec_a); + const vec_b = try hrr.seededVector("bob"); + defer hrr.freeVector(vec_b); + + const bound = try hrr.bind(vec_a, vec_b); + defer hrr.freeVector(bound); + + const recovered = try hrr.unbind(bound, vec_a); + defer hrr.freeVector(recovered); + + // Recovered should be similar to original + const sim = try hrr.similarity(vec_b, recovered); + + // Should have good similarity (unbinding is approximate) + try testing.expect(sim > 0.5); +} + +test "HRR — Bundle Orthogonality" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec1 = try hrr.seededVector("vector1"); + defer hrr.freeVector(vec1); + const vec2 = try hrr.seededVector("vector2"); + defer hrr.freeVector(vec2); + + // Bundled vector + const bundled = try hrr.bundle(&[_][]const f32{ vec1, vec2 }); + defer hrr.freeVector(bundled); + + // Similarity to individual vectors should be moderate + // (not too high, not too low) + const sim1 = try hrr.similarity(bundled, vec1); + const sim2 = try hrr.similarity(bundled, vec2); + + // Similarity should be positive but less than 1 + try testing.expect(sim1 > 0 and sim1 < 1.0); + try testing.expect(sim2 > 0 and sim2 < 1.0); +} + +test "HRR — Similarity Reflexive" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec = try hrr.randomVector(); + defer hrr.freeVector(vec); + + // Vector should be perfectly similar to itself + const sim = try hrr.similarity(vec, vec); + + try testing.expectApproxEqAbs(1.0, sim, 0.001); +} + +test "HRR — Hamming Distance" { + const testing = std.testing; + + var hrr = try HRR.init(testing.allocator, 1000); + + const vec1 = try hrr.randomVector(); + defer hrr.freeVector(vec1); + const vec2 = try hrr.randomVector(); + defer hrr.freeVector(vec2); + + // Hamming distance should be valid + const dist = try hrr.hammingDistance(vec1, vec2); + + try testing.expect(dist >= 0 and dist <= 1000); +} + +test "HRR — Phi-Dimension Initialization" { + const testing = std.testing; + + // Initialize with φ^1 = 1618 dimensions (approximately) + const hrr = try HRR.initPhi(testing.allocator, 1); + + // Should be close to 1618 + try testing.expect(hrr.dim >= 1500 and hrr.dim <= 1800); +} + +test "HRR — Sacred Constant Integration" { + const testing = std.testing; + + const hrr = try HRR.init(testing.allocator, 1000); + + // Create vectors representing sacred concepts + const phi_vec = try hrr.seededVector("phi"); + defer hrr.freeVector(phi_vec); + const trinity_vec = try hrr.seededVector("trinity"); + defer hrr.freeVector(trinity_vec); + + // Bind phi and trinity + const bound = try hrr.bind(phi_vec, trinity_vec); + defer hrr.freeVector(bound); + + // Verify binding creates a distinct representation + const sim = try hrr.similarity(phi_vec, bound); + + // Should be different but related + try testing.expect(sim < 0.9); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig new file mode 100644 index 0000000..dcdcd6f --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig @@ -0,0 +1,494 @@ +// @origin(spec:packed_vsa.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity Packed VSA Operations +// VSA operation on toin and (5 andin/) +// withby lookup tables for with and withtointoand +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const packed_trit = @import("../ternary/packed_trit.zig"); +const hybrid = @import("../ternary/hybrid.zig"); +// There is no vsa.zig; bind, bundle2 and randomVector are all in the +// sibling core.zig. +const vsa = @import("core.zig"); + +const PackedBigInt = packed_trit.PackedBigInt; +const HybridBigInt = hybrid.HybridBigInt; +const Trit = packed_trit.Trit; +const TRITS_PER_BYTE = packed_trit.TRITS_PER_BYTE; +const MAX_PACKED_BYTES = packed_trit.MAX_PACKED_BYTES; + +// ═══════════════════════════════════════════════════════════════════════════════ +// LOOKUP TABLES for and on toin +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Lookup table for bind: BIND_LUT[a][b] = packed(bind(unpack(a), unpack(b))) +/// : 243 * 243 = 59049 (~58KB) +const BIND_LUT: [243][243]u8 = blk: { + @setEvalBranchQuota(1000000); + var lut: [243][243]u8 = undefined; + for (0..243) |a| { + for (0..243) |b| { + const trits_a = packed_trit.decodePack(@intCast(a)); + const trits_b = packed_trit.decodePack(@intCast(b)); + // bind = element-wise multiply + const result = [5]i8{ + trits_a[0] * trits_b[0], + trits_a[1] * trits_b[1], + trits_a[2] * trits_b[2], + trits_a[3] * trits_b[3], + trits_a[4] * trits_b[4], + }; + lut[a][b] = packed_trit.encodePack(result); + } + } + break :blk lut; +}; + +/// Lookup table for bundle2: BUNDLE_LUT[a][b] = packed(bundle(unpack(a), unpack(b))) +const BUNDLE_LUT: [243][243]u8 = blk: { + @setEvalBranchQuota(1000000); + var lut: [243][243]u8 = undefined; + for (0..243) |a| { + for (0..243) |b| { + const trits_a = packed_trit.decodePack(@intCast(a)); + const trits_b = packed_trit.decodePack(@intCast(b)); + var result: [5]i8 = undefined; + for (0..5) |i| { + const sum: i16 = @as(i16, trits_a[i]) + @as(i16, trits_b[i]); + if (sum > 0) { + result[i] = 1; + } else if (sum < 0) { + result[i] = -1; + } else { + result[i] = 0; + } + } + lut[a][b] = packed_trit.encodePack(result); + } + } + break :blk lut; +}; + +/// Lookup table for dot product: DOT_LUT[a][b] = sum of element-wise products +/// and: -5 before +5, and how u8 with withand +5 +const DOT_LUT: [243][243]u8 = blk: { + @setEvalBranchQuota(1000000); + var lut: [243][243]u8 = undefined; + for (0..243) |a| { + for (0..243) |b| { + const trits_a = packed_trit.decodePack(@intCast(a)); + const trits_b = packed_trit.decodePack(@intCast(b)); + var sum: i16 = 0; + for (0..5) |i| { + sum += @as(i16, trits_a[i]) * @as(i16, trits_b[i]); + } + // and +5 what and in u8 (and 0-10) + lut[a][b] = @intCast(@as(i16, sum) + 5); + } + } + break :blk lut; +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// PACKED VSA OPERATIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Packed bind - andwithby lookup table, withtointoand +pub fn packedBind(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { + var result = PackedBigInt.zero(); + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + + for (0..packed_len) |i| { + const a_byte = if (i < a.packedLen()) a.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); + const b_byte = if (i < b.packedLen()) b.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); + + // Lookup inwith withtointoand! + result.data[i] = BIND_LUT[a_byte][b_byte]; + } + + return result; +} + +/// Packed bundle - andwithby lookup table +pub fn packedBundle(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { + var result = PackedBigInt.zero(); + const len = @max(a.trit_len, b.trit_len); + result.trit_len = len; + + const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + + for (0..packed_len) |i| { + const a_byte = if (i < a.packedLen()) a.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); + const b_byte = if (i < b.packedLen()) b.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); + + result.data[i] = BUNDLE_LUT[a_byte][b_byte]; + } + + return result; +} + +/// Packed dot product - andwithby lookup table +pub fn packedDot(a: *const PackedBigInt, b: *const PackedBigInt) i64 { + const len = @min(a.trit_len, b.trit_len); + const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + + var total: i64 = 0; + + for (0..packed_len) |i| { + const a_byte = a.data[i]; + const b_byte = b.data[i]; + + // Lookup returns value with withand +5 + const dot_shifted = DOT_LUT[a_byte][b_byte]; + total += @as(i64, dot_shifted) - 5; + } + + return total; +} + +/// Packed unbind - for andin unbind = bind (withon operation) +/// unbind(bind(a, b), b) = a +pub fn packedUnbind(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { + // andin: unbind = bind, from what: + // bind(a, b) = a * b + // unbind(a*b, b) = (a*b) * b = a * (b*b) = a * 1 = a + // (for b ∈ {-1, 1}, b*b = 1) + return packedBind(a, b); +} + +/// Packed cosine similarity +pub fn packedCosineSimilarity(a: *const PackedBigInt, b: *const PackedBigInt) f64 { + const dot_ab = packedDot(a, b); + const dot_aa = packedDot(a, a); + const dot_bb = packedDot(b, b); + + if (dot_aa == 0 or dot_bb == 0) return 0.0; + + const norm_a = @sqrt(@as(f64, @floatFromInt(dot_aa))); + const norm_b = @sqrt(@as(f64, @floatFromInt(dot_bb))); + + return @as(f64, @floatFromInt(dot_ab)) / (norm_a * norm_b); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVERSION UTILITIES +// ═══════════════════════════════════════════════════════════════════════════════ + +/// inand HybridBigInt → PackedBigInt +pub fn fromHybrid(h: *HybridBigInt) PackedBigInt { + h.ensureUnpacked(); + + var result = PackedBigInt.zero(); + result.trit_len = h.trit_len; + + const packed_len = (h.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + + for (0..packed_len) |i| { + const base = i * TRITS_PER_BYTE; + var trits: [5]i8 = .{ 0, 0, 0, 0, 0 }; + + for (0..5) |j| { + if (base + j < h.trit_len) { + trits[j] = h.unpacked_cache[base + j]; + } + } + + result.data[i] = packed_trit.encodePack(trits); + } + + return result; +} + +/// inand PackedBigInt → HybridBigInt +pub fn toHybrid(p: *const PackedBigInt) HybridBigInt { + var result = HybridBigInt.zero(); + result.mode = .unpacked_mode; + result.trit_len = p.trit_len; + result.dirty = true; + + for (0..p.trit_len) |i| { + result.unpacked_cache[i] = p.getTrit(i); + } + + return result; +} + +/// yes with toin vector +pub fn randomPackedVector(size: usize, seed: u64) PackedBigInt { + var result = PackedBigInt.zero(); + result.trit_len = size; + + var rng = std.Random.DefaultPrng.init(seed); + const random = rng.random(); + + const packed_len = (size + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; + + for (0..packed_len) |i| { + // notand with toin (0-242) + result.data[i] = @intCast(random.intRangeAtMost(u8, 0, 242)); + } + + return result; +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "packed bind correctness" { + // yes testin into via HybridBigInt + var h_a = vsa.randomVector(100, 12345); + var h_b = vsa.randomVector(100, 67890); + + // with result (unpacked) + const ref_result = vsa.bind(&h_a, &h_b); + + // Packed version + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_result = packedBind(&p_a, &p_b); + + // Compare + for (0..100) |i| { + const ref_trit = ref_result.unpacked_cache[i]; + const packed_trit_val = packed_result.getTrit(i); + try std.testing.expectEqual(ref_trit, packed_trit_val); + } +} + +test "packed bundle correctness" { + var h_a = vsa.randomVector(100, 11111); + var h_b = vsa.randomVector(100, 22222); + + const ref_result = vsa.bundle2(&h_a, &h_b); + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_result = packedBundle(&p_a, &p_b); + + for (0..100) |i| { + try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); + } +} + +test "packed dot correctness" { + var h_a = vsa.randomVector(100, 33333); + var h_b = vsa.randomVector(100, 44444); + + // with dot product + var ref_dot: i64 = 0; + for (0..100) |i| { + ref_dot += @as(i64, h_a.unpacked_cache[i]) * @as(i64, h_b.unpacked_cache[i]); + } + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_dot_val = packedDot(&p_a, &p_b); + + try std.testing.expectEqual(ref_dot, packed_dot_val); +} + +test "packed cosine similarity" { + var h_a = vsa.randomVector(100, 55555); + var h_b = vsa.randomVector(100, 55555); // from seed = and + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + + const sim = packedCosineSimilarity(&p_a, &p_b); + try std.testing.expectApproxEqAbs(@as(f64, 1.0), sim, 0.001); +} + +test "packed unbind correctness" { + // yes in with into + const p_a = randomPackedVector(100, 12345); + const p_b = randomPackedVector(100, 67890); + + // bind(a, b) + const bound = packedBind(&p_a, &p_b); + + // unbind(bind(a, b), b) before yes vector byand on a + const unbound = packedUnbind(&bound, &p_b); + + // Check within with andon + const sim = packedCosineSimilarity(&unbound, &p_a); + + // andin within before inwithtoand + // and- in into from andand + std.debug.print("\nUnbind similarity: {d:.3}\n", .{sim}); + try std.testing.expect(sim > 0.5); // onand within +} + +test "packed unbind retrieval" { + // and with to onand + // to: bind(Paris, bind(capital_of, France)) + // with: unbind(fact, bind(Paris, capital_of)) → France + + const paris = randomPackedVector(100, hashString("Paris")); + const capital_of = randomPackedVector(100, hashString("capital_of") ^ 0xDEADBEEF); + const france = randomPackedVector(100, hashString("France")); + + // Encode to: Paris is capital_of France + const pred_obj = packedBind(&capital_of, &france); + const fact = packedBind(&paris, &pred_obj); + + // with: what is withand and? + // unbind(fact, bind(capital_of, France)) → Paris + const query_pattern = packedBind(&capital_of, &france); + const result = packedUnbind(&fact, &query_pattern); + + // Result before by on Paris + const sim_paris = packedCosineSimilarity(&result, &paris); + const sim_france = packedCosineSimilarity(&result, &france); + + std.debug.print("\nQuery result similarity to Paris: {d:.3}\n", .{sim_paris}); + std.debug.print("Query result similarity to France: {d:.3}\n", .{sim_france}); + + // Paris before more by + try std.testing.expect(sim_paris > sim_france); +} + +// Was `@import("knowledge_graph.zig").Entity` — a file that does not exist +// in this repository. It exists in gHashTag/zig-knowledge-graph, whose own +// knowledge_graph.zig imports "packed_vsa.zig", which does not exist THERE. +// One directory was split into two repositories and every relative import +// was left pointing at the sibling that stayed behind, so neither half +// compiles. +// +// The dependency was also inverted: a VSA primitive should not need a type +// from a knowledge-graph consumer. The three tests below used Entity only +// for djb2 over a string, to derive a seed. That function is reproduced +// here verbatim so the seeds — and therefore the tests — are unchanged. +fn hashString(s: []const u8) u64 { + var hash: u64 = 5381; + for (s) |c| { + hash = ((hash << 5) +% hash) +% c; + } + return hash; +} + +test "large vector bind correctness (1000 trits)" { + var h_a = vsa.randomVector(1000, 12345); + var h_b = vsa.randomVector(1000, 67890); + + const ref_result = vsa.bind(&h_a, &h_b); + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_result = packedBind(&p_a, &p_b); + + // Check each 100- and for withtowithand + var i: usize = 0; + while (i < 1000) : (i += 100) { + try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); + } +} + +test "large vector bind correctness (5000 trits)" { + var h_a = vsa.randomVector(5000, 11111); + var h_b = vsa.randomVector(5000, 22222); + + const ref_result = vsa.bind(&h_a, &h_b); + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_result = packedBind(&p_a, &p_b); + + // Check each 500- and + var i: usize = 0; + while (i < 5000) : (i += 500) { + try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); + } +} + +test "large vector bind correctness (10000 trits)" { + var h_a = vsa.randomVector(10000, 33333); + var h_b = vsa.randomVector(10000, 44444); + + const ref_result = vsa.bind(&h_a, &h_b); + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_result = packedBind(&p_a, &p_b); + + // Check each 1000- and + var i: usize = 0; + while (i < 10000) : (i += 1000) { + try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); + } +} + +test "large vector dot correctness (10000 trits)" { + var h_a = vsa.randomVector(10000, 55555); + var h_b = vsa.randomVector(10000, 66666); + + // with dot product + var ref_dot: i64 = 0; + for (0..10000) |i| { + ref_dot += @as(i64, h_a.unpacked_cache[i]) * @as(i64, h_b.unpacked_cache[i]); + } + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + const packed_dot_val = packedDot(&p_a, &p_b); + + try std.testing.expectEqual(ref_dot, packed_dot_val); +} + +test "benchmark Packed vs Unpacked" { + // PackedBigInt supports before 12000 andin + const sizes = [_]usize{ 100, 500, 1000, 2000, 5000, 10000 }; + const iterations = 1000; + + std.debug.print("\n\n", .{}); + std.debug.print("╔═══════════════════════════════════════════════════════════════════════════════════╗\n", .{}); + std.debug.print("║ BENCHMARK: PACKED (5 trits/byte) vs UNPACKED (1 trit/byte) ║\n", .{}); + std.debug.print("╠═══════════════════════════════════════════════════════════════════════════════════╣\n", .{}); + std.debug.print("║ Size │ Unpacked │ Packed │ Speedup │ Mem Unpack│ Mem Pack │ Mem Saving ║\n", .{}); + std.debug.print("╠═══════════════════════════════════════════════════════════════════════════════════╣\n", .{}); + + for (sizes) |size| { + var h_a = vsa.randomVector(size, 12345); + var h_b = vsa.randomVector(size, 67890); + + const p_a = fromHybrid(&h_a); + const p_b = fromHybrid(&h_b); + + // Benchmark Unpacked (vsa.bind) + var timer = std.time.Timer.start() catch unreachable; + for (0..iterations) |_| { + const result = vsa.bind(&h_a, &h_b); + std.mem.doNotOptimizeAway(&result); + } + const unpacked_ns = timer.read(); + + // Benchmark Packed + timer.reset(); + for (0..iterations) |_| { + const result = packedBind(&p_a, &p_b); + std.mem.doNotOptimizeAway(&result); + } + const packed_ns = timer.read(); + + const unpacked_us = @as(f64, @floatFromInt(unpacked_ns)) / 1000.0 / @as(f64, @floatFromInt(iterations)); + const packed_us = @as(f64, @floatFromInt(packed_ns)) / 1000.0 / @as(f64, @floatFromInt(iterations)); + const speedup = unpacked_us / packed_us; + + const mem_unpacked = size; // 1 byte per trit + const mem_packed = (size + 4) / 5; // 5 trits per byte + const mem_saving = @as(f64, @floatFromInt(mem_unpacked)) / @as(f64, @floatFromInt(mem_packed)); + + std.debug.print("║ {d:5} │ {d:6.1} us │ {d:6.1} us │ {d:5.2}x │ {d:6} B │ {d:6} B │ {d:4.1}x ║\n", .{ size, unpacked_us, packed_us, speedup, mem_unpacked, mem_packed, mem_saving }); + } + + std.debug.print("╚═══════════════════════════════════════════════════════════════════════════════════╝\n", .{}); + std.debug.print("\n", .{}); + std.debug.print("Speedup > 1.0 on Packed with\n", .{}); + std.debug.print("Mem Saving bytoin toand and (5x andwithtoand towithand)\n", .{}); +} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig new file mode 100644 index 0000000..819cb7c --- /dev/null +++ b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig @@ -0,0 +1,688 @@ +// @origin(spec:vsa_jit.tri) @regen(manual-impl) +// @origin(manual) @regen(pending) +// Trinity JIT-Accelerated VSA Operations +// Provides 15-260x speedup for hot paths via native code generation +// +// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q +// φ² + 1/φ² = 3 + +const std = @import("std"); +const builtin = @import("builtin"); +const jit_unified = @import("vm/jit_unified.zig"); +const hybrid = @import("ternary/hybrid.zig"); + +pub const HybridBigInt = hybrid.HybridBigInt; +pub const Trit = hybrid.Trit; +pub const MAX_TRITS = hybrid.MAX_TRITS; + +// ═══════════════════════════════════════════════════════════════════════════════ +// JIT-ACCELERATED VSA ENGINE +// ═══════════════════════════════════════════════════════════════════════════════ + +/// JIT-accelerated VSA engine with compiled function caching +pub const JitVSAEngine = struct { + allocator: std.mem.Allocator, + + // Cached JIT-compiled functions for common dimensions + dot_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bind_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + hamming_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + cosine_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + bundle_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), + + // Keep compilers alive to prevent exec_mem from being freed + compilers: std.ArrayListUnmanaged(jit_unified.UnifiedJitCompiler), + + // Statistics + jit_hits: u64 = 0, + jit_misses: u64 = 0, + total_ops: u64 = 0, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ + .allocator = allocator, + .dot_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bind_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .hamming_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .cosine_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .bundle_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), + .compilers = .{}, + }; + } + + pub fn deinit(self: *Self) void { + // Clean up all compilers (which frees exec_mem) + for (self.compilers.items) |*compiler| { + compiler.deinit(); + } + self.compilers.deinit(self.allocator); + self.dot_cache.deinit(); + self.bind_cache.deinit(); + self.hamming_cache.deinit(); + self.cosine_cache.deinit(); + self.bundle_cache.deinit(); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT DOT PRODUCT + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for dot product + fn getDotFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.dot_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileDotProduct(dimension); + const func = try compiler.finalize(); + + try self.dot_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated dot product for HybridBigInt vectors + pub fn dotProduct(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getDotFunction(dim); + + // Call JIT-compiled function directly on unpacked cache + // Cast [MAX_TRITS]Trit to *anyopaque + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + return func(a_ptr, b_ptr); + } + + /// Fallback to non-JIT dot product (for comparison) + pub fn dotProductFallback(a: *HybridBigInt, b: *HybridBigInt) i64 { + return @intCast(a.dotProduct(b)); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BIND + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bind + fn getBindFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { + if (self.bind_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Compile new function + self.jit_misses += 1; + + // Create compiler and add to list (keeps exec_mem alive) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + try compiler.compileBind(dimension); + const func = try compiler.finalize(); + + try self.bind_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bind for HybridBigInt vectors (modifies a in place) + pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked for direct memory access + a.ensureUnpacked(); + b.ensureUnpacked(); + + // Use the larger dimension + const dim = @max(a.trit_len, b.trit_len); + + // Get or compile JIT function + const func = try self.getBindFunction(dim); + + // Call JIT-compiled function (modifies a in place) + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + _ = func(a_ptr, b_ptr); + + // Mark as modified (dirty) since JIT wrote to unpacked cache + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT FUSED COSINE SIMILARITY (single-pass computation) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for fused cosine similarity + fn getCosineFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.cosine_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile fused cosine (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileFusedCosine(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to 3x dot product + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.cosine_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated cosine similarity using fused kernel (2.5x faster on ARM64) + /// cos(a,b) = dot(a,b) / sqrt(dot(a,a) * dot(b,b)) + pub fn cosineSimilarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try fused cosine kernel (ARM64 only, 2.5x faster) + if (try self.getCosineFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + + // Function returns f64 bit pattern as i64 + const result_bits = func(a_ptr, b_ptr); + return @bitCast(result_bits); + } + + // Fallback: use 3 separate JIT dot products + const dot_ab = try self.dotProduct(a, b); + const dot_aa = try self.dotProduct(a, a); + const dot_bb = try self.dotProduct(b, b); + + // Handle zero vectors + if (dot_aa == 0 or dot_bb == 0) { + return 0.0; + } + + const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); + return @as(f64, @floatFromInt(dot_ab)) / norm; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT HAMMING DISTANCE (count of differing positions) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for hamming distance + fn getHammingFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.hamming_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile new function (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileHamming(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.hamming_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated hamming distance + /// For ternary: counts positions where a[i] != b[i] + pub fn hammingDistance(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (available on ARM64) + if (try self.getHammingFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + return func(a_ptr, b_ptr); + } + + // Scalar fallback + var count: i64 = 0; + for (0..dim) |i| { + if (a.unpacked_cache[i] != b.unpacked_cache[i]) { + count += 1; + } + } + return count; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // JIT BUNDLE OPERATION (n-ary addition with threshold) + // ═══════════════════════════════════════════════════════════════════════════ + + /// Get or compile JIT function for bundle operation + fn getBundleFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { + if (self.bundle_cache.get(dimension)) |func| { + self.jit_hits += 1; + return func; + } + + // Try to compile bundle SIMD (only available on ARM64) + try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); + const compiler = &self.compilers.items[self.compilers.items.len - 1]; + + compiler.compileBundleSIMD(dimension) catch |err| { + // Remove the failed compiler + _ = self.compilers.pop(); + if (err == error.UnsupportedOperation) { + return null; // Fall back to scalar + } + return err; + }; + + self.jit_misses += 1; + const func = try compiler.finalize(); + try self.bundle_cache.put(dimension, func); + return func; + } + + /// JIT-accelerated bundle operation + /// result[i] = threshold(a[i] + b[i]) where >0→1, <0→-1, =0→0 + /// Modifies 'a' in place + pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { + self.total_ops += 1; + + // Ensure vectors are unpacked + a.ensureUnpacked(); + b.ensureUnpacked(); + + const dim = @max(a.trit_len, b.trit_len); + + // Try JIT SIMD version (ARM64 only) + if (try self.getBundleFunction(dim)) |func| { + const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); + const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); + _ = func(a_ptr, b_ptr); + a.dirty = true; + return; + } + + // Scalar fallback + for (0..dim) |i| { + const sum: i16 = @as(i16, a.unpacked_cache[i]) + @as(i16, b.unpacked_cache[i]); + if (sum > 0) { + a.unpacked_cache[i] = 1; + } else if (sum < 0) { + a.unpacked_cache[i] = -1; + } else { + a.unpacked_cache[i] = 0; + } + } + a.dirty = true; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // STATISTICS + // ═══════════════════════════════════════════════════════════════════════════ + + pub fn getStats(self: *const Self) Stats { + const total_cache = self.jit_hits + self.jit_misses; + const hit_rate = if (total_cache > 0) + @as(f64, @floatFromInt(self.jit_hits)) / @as(f64, @floatFromInt(total_cache)) * 100.0 + else + 0.0; + + return Stats{ + .total_ops = self.total_ops, + .jit_hits = self.jit_hits, + .jit_misses = self.jit_misses, + .cache_size = self.dot_cache.count() + self.bind_cache.count() + self.hamming_cache.count() + self.cosine_cache.count() + self.bundle_cache.count(), + .hit_rate = hit_rate, + }; + } + + pub fn printStats(self: *const Self) void { + const stats = self.getStats(); + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE STATISTICS\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Total operations: {d}\n", .{stats.total_ops}); + std.debug.print(" JIT cache hits: {d}\n", .{stats.jit_hits}); + std.debug.print(" JIT cache misses: {d}\n", .{stats.jit_misses}); + std.debug.print(" Cache size: {d} functions\n", .{stats.cache_size}); + std.debug.print(" Hit rate: {d:.1}%\n", .{stats.hit_rate}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + } + + pub const Stats = struct { + total_ops: u64, + jit_hits: u64, + jit_misses: u64, + cache_size: usize, + hit_rate: f64, + }; +}; + +// ═══════════════════════════════════════════════════════════════════════════════ +// CONVENIENCE FUNCTIONS +// ═══════════════════════════════════════════════════════════════════════════════ + +/// Global JIT engine (thread-local for safety) +threadlocal var global_engine: ?JitVSAEngine = null; + +/// Initialize global JIT engine +pub fn initGlobal(allocator: std.mem.Allocator) void { + if (global_engine == null) { + global_engine = JitVSAEngine.init(allocator); + } +} + +/// Deinitialize global JIT engine +pub fn deinitGlobal() void { + if (global_engine) |*engine| { + engine.deinit(); + global_engine = null; + } +} + +/// JIT-accelerated dot product using global engine +pub fn jitDotProduct(allocator: std.mem.Allocator, a: *HybridBigInt, b: *HybridBigInt) !i64 { + initGlobal(allocator); + return global_engine.?.dotProduct(a, b); +} + +// ═══════════════════════════════════════════════════════════════════════════════ +// TESTS +// ═══════════════════════════════════════════════════════════════════════════════ + +test "JitVSAEngine init and deinit" { + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + try std.testing.expect(engine.total_ops == 0); +} + +test "JitVSAEngine dot product correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Create test vectors using setTrit (proper API) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + // Simple test: all 1s dot all 1s = dimension + const test_len = 64; + + for (0..test_len) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + } + + const expected: i64 = test_len; + + // JIT dot product + const jit_result = try engine.dotProduct(&a, &b); + + // Fallback dot product + const fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + + try std.testing.expectEqual(expected, jit_result); + try std.testing.expectEqual(expected, fallback_result); +} + +test "JitVSAEngine cache hits" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + a.trit_len = 64; + b.trit_len = 64; + + // First call - cache miss + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 0), engine.jit_hits); + + // Second call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 1), engine.jit_hits); + + // Third call - cache hit + _ = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); + try std.testing.expectEqual(@as(u64, 2), engine.jit_hits); +} + +test "JitVSAEngine benchmark vs fallback" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const dim = 1024; + const iterations = 10000; + + // Create test vectors using setTrit + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + for (0..dim) |i| { + const val_a: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const val_b: Trit = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); + a.setTrit(i, val_a); + b.setTrit(i, val_b); + } + + // Warm up JIT cache + _ = try engine.dotProduct(&a, &b); + + // Benchmark JIT + var timer = try std.time.Timer.start(); + var jit_result: i64 = 0; + for (0..iterations) |_| { + jit_result = try engine.dotProduct(&a, &b); + } + const jit_ns = timer.read(); + + // Benchmark fallback + timer.reset(); + var fallback_result: i64 = 0; + for (0..iterations) |_| { + fallback_result = JitVSAEngine.dotProductFallback(&a, &b); + } + const fallback_ns = timer.read(); + + // Results should match + try std.testing.expectEqual(jit_result, fallback_result); + + const jit_ms = @as(f64, @floatFromInt(jit_ns)) / 1_000_000.0; + const fallback_ms = @as(f64, @floatFromInt(fallback_ns)) / 1_000_000.0; + const speedup = fallback_ms / jit_ms; + + std.debug.print("\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" JIT VSA ENGINE BENCHMARK (HybridBigInt)\n", .{}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + std.debug.print(" Dimension: {d} trits\n", .{dim}); + std.debug.print(" Iterations: {d}\n", .{iterations}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" Fallback (HybridBigInt.dotProduct): {d:.3} ms\n", .{fallback_ms}); + std.debug.print(" JIT (NEON SIMD): {d:.3} ms\n", .{jit_ms}); + std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); + std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); + std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); + + engine.printStats(); + + // JIT should generally be faster, but can be slower due to thermal/load + // Just verify JIT compiles and runs without crashing + if (speedup > 1.0) { + std.debug.print(" JIT is faster! ({d:.2}x speedup)\n", .{speedup}); + } else { + std.debug.print(" JIT is slower ({d:.2}x) - acceptable for flaky benchmark\n", .{speedup}); + } +} + +test "JitVSAEngine various dimensions" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + const test_dims = [_]usize{ 8, 16, 32, 64, 100, 128, 256, 512, 1000 }; + + for (test_dims) |dim| { + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + var expected: i64 = 0; + for (0..dim) |i| { + a.setTrit(i, 1); + b.setTrit(i, 1); + expected += 1; + } + + const result = try engine.dotProduct(&a, &b); + try std.testing.expectEqual(expected, result); + } + + // Should have compiled functions for each unique dimension + try std.testing.expectEqual(@as(usize, test_dims.len), engine.dot_cache.count()); +} + +test "JitVSAEngine bind correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test bind: result[i] = a[i] * b[i] (ternary multiplication) + var a = HybridBigInt.zero(); + var b = HybridBigInt.zero(); + + const dim = 16; + for (0..dim) |i| { + // Pattern: a = [1, -1, 0, 1, -1, 0, ...], b = [1, 1, 1, -1, -1, -1, ...] + const a_val: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); + const b_val: Trit = if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1); + a.setTrit(i, a_val); + b.setTrit(i, b_val); + } + + // Compute expected result + var expected = HybridBigInt.zero(); + for (0..dim) |i| { + const a_val = a.getTrit(i); + const b_val = b.getTrit(i); + expected.setTrit(i, a_val * b_val); + } + + // JIT bind + try engine.bind(&a, &b); + + // Verify result + for (0..dim) |i| { + try std.testing.expectEqual(expected.getTrit(i), a.getTrit(i)); + } +} + +test "JitVSAEngine cosine similarity correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: cos(a, a) = 1.0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const cos_identical = try engine.cosineSimilarity(&a, &a); + try std.testing.expectApproxEqRel(@as(f64, 1.0), cos_identical, 0.001); + + // Test orthogonal vectors: cos(a, -a) = -1.0 + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const cos_opposite = try engine.cosineSimilarity(&a, &neg_a); + try std.testing.expectApproxEqRel(@as(f64, -1.0), cos_opposite, 0.001); +} + +test "JitVSAEngine hamming distance correctness" { + if (!jit_unified.is_jit_supported) return; + + var engine = JitVSAEngine.init(std.testing.allocator); + defer engine.deinit(); + + // Test identical vectors: hamming(a, a) = 0 + var a = HybridBigInt.zero(); + const dim = 64; + for (0..dim) |i| { + a.setTrit(i, 1); + } + + const hamming_identical = try engine.hammingDistance(&a, &a); + try std.testing.expectEqual(@as(i64, 0), hamming_identical); + + // Test completely different vectors: hamming(a, -a) = dim + var neg_a = HybridBigInt.zero(); + for (0..dim) |i| { + neg_a.setTrit(i, -1); + } + + const hamming_opposite = try engine.hammingDistance(&a, &neg_a); + try std.testing.expectEqual(@as(i64, dim), hamming_opposite); + + // Test half different: change half the trits + var half = HybridBigInt.zero(); + for (0..dim) |i| { + half.setTrit(i, if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1)); + } + + const hamming_half = try engine.hammingDistance(&a, &half); + try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); +} From 8255a04e8942dfe885534c2ccd690af5c9b984a3 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:42:32 +0700 Subject: [PATCH 2/6] Untrack zig-pkg: that is the fetched dependency, not source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third time this class of mistake in one session — .zig-cache twice, now the local package directory. The habit that prevents it is reading git status before git add -A, not adding patterns after the fact. --- .gitignore | 1 + .../LICENSE | 21 - .../README.md | 260 -- .../build.zig | 262 -- .../build.zig.zon | 17 - .../src/c/gf16.h | 405 --- .../src/c/gf_ladder.h | 59 - .../src/c/gft.h | 100 - .../src/c_abi.zig | 671 ----- .../src/formats/formats_root.zig | 687 ------ .../src/formats/gf8.zig | 286 --- .../src/formats/gf_binary.zig | 299 --- .../src/formats/gft.zig | 298 --- .../src/formats/golden_float16.zig | 463 ---- .../src/jepa_t.zig | 70 - .../src/main.rs | 46 - .../src/math/constants.zig | 148 -- .../src/math/gen_bench.zig | 566 ----- .../src/math/gen_commands.zig | 470 ---- .../src/math/gen_constants.zig | 374 --- .../src/math/gen_eval.zig | 497 ---- .../src/math/gen_format.zig | 394 --- .../src/math/gen_identities.zig | 235 -- .../src/math/gen_riemann_gamma.zig | 308 --- .../src/math/transcendental.zig | 184 -- .../src/phi_attention.zig | 86 - .../src/root.zig | 127 - .../src/ternary/bigint.zig | 1192 --------- .../src/ternary/hybrid.zig | 732 ------ .../src/ternary/packed_trit.zig | 306 --- .../src/trinity_constants.zig | 82 - .../src/trinity_init.zig | 92 - .../src/vm/jit_arm64.zig | 2175 ----------------- .../src/vm/jit_unified.zig | 434 ---- .../src/vm/jit_x86_64.zig | 471 ---- .../src/vm/opcodes.zig | 161 -- .../src/vm/vm.zig | 1250 ---------- .../src/vm/vsa_jit.zig | 688 ------ .../src/vsa/10k_vsa.zig | 461 ---- .../src/vsa/common.zig | 20 - .../src/vsa/concurrency.zig | 295 --- .../src/vsa/core.zig | 816 ------- .../src/vsa/fpga_bind.zig | 532 ---- .../src/vsa/gen_core.zig | 247 -- .../src/vsa/gen_encoding.zig | 340 --- .../src/vsa/hrr.zig | 412 ---- .../src/vsa/packed_vsa.zig | 494 ---- .../src/vsa_jit.zig | 688 ------ 48 files changed, 1 insertion(+), 19221 deletions(-) delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig delete mode 100644 zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig diff --git a/.gitignore b/.gitignore index 28c3941..a8a9eab 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ zig-out/ !.env.example .zig-cache/ zig-out/ +zig-pkg/ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE deleted file mode 100644 index b2a4770..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 gHashTag - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md deleted file mode 100644 index 8ff4402..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/README.md +++ /dev/null @@ -1,260 +0,0 @@ -# GoldenFloat - -[![Zig](https://img.shields.io/badge/Zig-0.15+-F7A41D?logo=zig&logoColor=white)](https://ziglang.org/) -[![CI](https://github.com/gHashTag/zig-golden-float/actions/workflows/test-bindings.yml/badge.svg)](https://github.com/gHashTag/zig-golden-float/actions/workflows/test-bindings.yml) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -[![Release](https://img.shields.io/github/v/release/gHashTag/zig-golden-float?label=release)](https://github.com/gHashTag/zig-golden-float/releases/latest) -[![Golden Ratio](https://img.shields.io/badge/%CF%86-1.618033988-gold)](https://en.wikipedia.org/wiki/Golden_ratio) - -> 16-bit floating point in base-φ with multi-format support, φ-optimized FMA, ternary arithmetic, VSA hypervectors, and unified JIT — the numerical core of the [Trinity](https://github.com/gHashTag/trinity) ecosystem. - ---- - -## Formats - -| Format | Layout | Bias | Range | Notes | -|--------|--------|------|-------|-------| -| **GF16** | `[s:1][e:6][m:9]` | 31 | ~±65504 | Golden ratio base, no subnormals | -| **fp16** | IEEE 754 binary16 | 15 | ±65504 | Full subnormal support | -| **bf16** | IEEE 754 brain16 | 127 | ~±3.4e38 | Canonical `(bits +\| 0x7FFF) >> 16` encoder | -| **GF8** | `[s:1][e:3][m:4]` | 7 | ~±4.24 | 3-bit φ-exponent, 4-bit mantissa; saturates outside φ³ | -| **GFTernary** | `{-1, 0, +1}` | — | ±1 | ±0.5 threshold, 100% sparse | - -All formats use **round-to-nearest-even** via `quantizeValue()` dispatch. - -## The GoldenFloat Ladder (GF + GF-T) - -Two ladders share one idea — a φ-structured fixed-field float with **no regime -decode** (unlike posit/tekum) — differing only in how the exponent is stored. - -### GF — binary-exponent rung ladder - -One normative rule sizes every binary rung (FORMAT-SPEC-001 v1.2): -`e = round((N−1)/φ²)`, `m = N−1−e`, `bias = 2^(e−1)−1`, `exp_max = 2^e−1`. - -| Format | Bits | Layout `[s:e:m]` | Bias | Status | -|--------|------|------------------|------|--------| -| GF4 | 4 | `[1:1:2]` | 0 | Verified | -| **GF8** | 8 | `[1:3:4]` | 3 † | Verified — edge / sensors | -| GF12 | 12 | `[1:4:7]` | 7 | Verified — mid-range / audio | -| **GF16** | 16 | `[1:6:9]` | 31 | **Primary** — FPGA 35/35 @ 323 MHz Artix-7 | -| GF20 | 20 | `[1:7:12]` | 63 | Experimental | -| GF24 | 24 | `[1:9:14]` | 255 | Experimental | -| GF32 | 32 | `[1:12:19]` | 2047 | Spec | - -The ladder continues to GF1024 (17 binary rungs total); GF16 is the sole primary -production rung. The whole rule-derived ladder is implemented in -[`src/formats/gf_binary.zig`](src/formats/gf_binary.zig) as a comptime factory — -`gf_binary.GF4/GF8/GF12/GF16/GF20/GF24/GF32`, or `gf_binary.GF(bits)` for any width: - -```zig -const golden = @import("golden-float"); -const x = golden.gf_binary.GF12.fromF32(3.14159); // [1:4:7], bias 7 -std.debug.print("{d}\n", .{x.toF32()}); -const Custom = golden.gf_binary.GF(48); // rule-sized on demand -``` - -(GF8/GF16 additionally have dedicated φ-FMA implementations in `formats`.) † The -normative bias for GF8 is `2^(e−1)−1 = 3` and `gf_binary.GF8` uses it; the older -standalone `gf8.zig` codec encodes bias 7 — a known code/spec discrepancy tracked -for reconciliation. - -### GF-T — balanced-ternary-exponent ladder - -The exponent is a **balanced-ternary** number (digits −1/0/+1, stored as codes -0/1/2) added natively in ternary — no binary exponent, no regime decode — while the -mantissa keeps GF's uniform binary precision. Value = `(−1)^sign · (1 + M/2^m) · 2^e` -with `e = offset − EXP_OFFSET`; the top offset row `3^E − 1` is reserved (Inf/NaN). - -| Format | Layout `[s : E trits : M bits]` | EXP_OFFSET | Special row `3^E−1` | Exponent range | Dynamic range | -|--------|----------------------------------|-----------|---------------------|----------------|---------------| -| GF-T4 | `[1 : 2t : 1]` | 4 | 8 | ±4 | ~2.4 decades | -| GF-T8 | `[1 : 3t : 4]` | 13 | 26 | ±13 | ~8 decades | -| GF-T16 | `[1 : 4t : 9]` | 40 | 80 | ±40 | ~24 decades | -| GF-T32 | `[1 : 6t : 25]` | 364 | 728 | ±364 | ~219 decades | - -GF-T16 keeps GF16's φ-optimal 9-bit mantissa across its whole range, where -tekum16 tapers to ~4 bits at the extremes. The authoritative parameters live in -[`specs/gft.tri`](specs/gft.tri); the codec is [`src/formats/gft.zig`](src/formats/gft.zig). - -### Using GF-T in code - -```zig -const std = @import("std"); -const golden = @import("golden-float"); - -pub fn main() void { - // Pick a rung by name: GFT4 / GFT8 / GFT16 / GFT32. - const a = golden.GFT16.fromF32(3.14159); - const b = golden.GFT16.fromF32(2.71828); - - const prod = a.mul(b); // add / sub / mul / div - std.debug.print("{d}\n", .{prod.toF32()}); // ~8.539 - - // Inspect / round-trip the raw storage bits (FFI, serialization). - const raw = a.bits(); // unsigned integer (GFT16.Repr) - const a2 = golden.GFT16.fromBits(raw); - std.debug.assert(a2.bits() == raw); - - // Specials behave like a float: Inf saturates, NaN is contagious. - std.debug.assert(!golden.GFT16.fromF32(1e30).isFinite()); // overflow -> Inf - std.debug.assert(golden.GFT16.fromF32(1e-30).toF32() == 0); // underflow -> 0 - - // GF-T32 reaches ~219 decades (1e30, 6.022e23, ...) at 25-bit precision. - const avo = golden.GFT32.fromF32(6.022e23); - std.debug.print("{d}\n", .{avo.toF32()}); -} -``` - -Every rung is one instance of a comptime factory, so you can mint a custom rung -too: `const MyRung = golden.gft.GFT(5, 12); // 5 exp-trits, 12 mantissa bits`. -Each type exposes `fromF32` / `toF32` / `add` / `sub` / `mul` / `div` / `neg` / -`abs` / `bits` / `fromBits` / `isFinite` plus the constants `EXP_TRITS`, -`MANT_BITS`, `EXP_OFFSET`, `OFFSET_MAX`, `BITS`, `Repr`. A runnable copy lives in -[`examples/gft_usage.zig`](examples/gft_usage.zig). - -## Quick Start - -```bash -zig fetch --save https://github.com/gHashTag/zig-golden-float/archive/refs/tags/v2.1.0.tar.gz -``` - -```zig -const gf = @import("golden_float"); - -const x = gf.GF16.fromF32(3.14); -const y = gf.GF16.fromF32(2.71); -const z = x.add(y); -std.debug.print("{d}\n", .{z.toF32()}); // 5.85... -``` - -## Architecture - -``` -src/ -├── formats/ GF16/GF8 (golden_float16), gf_binary.zig (GF ladder GF4..GF32), -│ gft.zig (GF-T4/8/16/32), fp16, bf16, GFTernary codecs -├── math/ constants, transcendental (sin, cos, exp, log) -├── ternary/ HybridBigInt, packed trit storage -├── vsa/ core, HRR, 10K-dim hypervectors, FPGA bind -├── vm/ stack interpreter, ARM64 & x86_64 JIT -├── c_abi.zig FFI layer → libgoldenfloat.{so,dylib,dll} -└── root.zig public API -``` - -## Language Bindings - -| Language | Path | Status | -|----------|------|--------| -| **Zig** | `src/` | Native | -| **C/C++** | `src/c/{gf16,gf_ladder,gft}.h` + `cpp/` | C-ABI + header-only wrappers | -| **Rust** | `rust/goldenfloat-sys/` | FFI crate | -| **Python** | `python/goldenfloat/` | ctypes bridge | -| **Go** | `go/goldenfloat/` | cgo wrapper | - -### Format coverage across bindings - -Every rung below is a thin FFI wrapper over the **same** `libgoldenfloat` shared -library, so all languages execute the identical Zig codec — the wrappers differ only -in surface syntax. - -| Format family | Zig | C-ABI | C++ | Rust | Python | Go | -|---------------|:---:|:-----:|:---:|:----:|:------:|:--:| -| **GF16** (rich: arith, cmp, min/max, fma, φ-quant, predicates) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| **Binary GF ladder** GF8 / GF12 / GF20 / GF24 / GF32 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| **GF-T16** (arith, neg/abs, is_finite) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| **GF-T8 / GF-T32** (arith, neg/abs, is_finite) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | -| **GF-T4** (minimal E2M1 — from/to/mul/is_finite) | ✓ | ✓ | — | — | — | — | -| **GF4** (`[1:1:2]`, degenerate — no normal values) | factory | — | — | — | — | — | - -Wrapper names follow the rung: C++ `goldenfloat::Gf12` / `Gft8`, Rust `gf12_t` / -`gft8_t`, Python `goldenfloat.Gf12` / `Gft8`, Go `goldenfloat.Gf12` / `Gft8`. The -binary ladder covers `from/to_f32`, `add/sub/mul/div`, unary `neg`, `abs`, and -`is_finite`; GF16 additionally carries the rich comparison / FMA / φ-quantization API. -GF4 is intentionally unwrapped — a 1-bit exponent leaves only zero / Inf / NaN. - -### Building & Testing - -```bash -# Build shared library (required for bindings) -zig build shared - -# Run Zig tests -zig build test - -# Test all bindings -./scripts/test_bindings.sh - -# Individual bindings -cd rust/goldenfloat-sys && cargo test -cd python && python -m goldenfloat.tests.test_gf16 -cd cpp && cmake -S . -B build && cmake --build build && ./build/test_gf16 -cd go/goldenfloat && go test -v ./... -``` - -## φ-Optimized FMA - -```c -// Standard -gf16_fma(a, b, c); // a×b + c -gf16_fms(a, b, c); // a×b - c -gf16_fnma(a, b, c); // -(a×b) + c - -// φ-weighted -gf16_phi_fma(a, b, c); // (a×b)×φ + c×φ⁻¹ -gf16_phi_dot(n, a, b); // φ-weighted dot product -``` - -## IGLA-GF16 Architecture - -Neural network architecture built on φ-math: - -| Module | Description | -|--------|-------------| -| Trinity Constants | φ, α_φ, Fibonacci dimensions | -| φ-Sparse Attention | Fibonacci distance mask `{1,2,3,5,8,13,21,34,55,89,144}` — 2.15% sparsity | -| Trinity Weight Init | 4 physics sectors: gauge / higgs / lepton / cosmology | -| φ-LR Schedule | Warmup Fib(7)=21 steps, φ-decay | -| JEPA-T Predictor | Encoder 6 + Predictor 3 layers, φ-split | - -## Benchmarks - -| Metric | Result | -|--------|--------| -| GF16 accuracy vs fp32 (σ=1.0) | > 99.99% | -| GF16 vs bf16 MSE ratio (uniform ±100) | 16.2× better | -| GF16 sparsity at [-10,10] | 0% (no saturation) | -| GFTernary sparsity (He init σ=0.05) | 100% | -| Pearson r(φ-distance, MSE) | −0.34 | - -Full results in `.trinity/results/` and benches under `benches/`. - -## C-ABI - -```c -#include "gf16.h" - -gf16_t a = gf16_from_f32(3.14f); -gf16_t b = gf16_from_f32(2.71f); -gf16_t c = gf16_add(a, b); -printf("%.6f\n", gf16_to_f32(c)); - -double phi = goldenfloat_phi(); // 1.6180339887... -double trinity = goldenfloat_trinity(); // φ² + φ⁻² = 3 -``` - -## Ecosystem - -- [zig-sacred-geometry](https://github.com/gHashTag/zig-sacred-geometry) -- [zig-physics](https://github.com/gHashTag/zig-physics) -- [zig-hdc](https://github.com/gHashTag/zig-hdc) -- [trinity-training](https://github.com/gHashTag/trinity-training) -- [trinity](https://github.com/gHashTag/trinity) - -## Version - -**2.1.0** — see [CHANGELOG.md](CHANGELOG.md) for release history. - -## License - -[MIT](LICENSE) © gHashTag diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig deleted file mode 100644 index a746228..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig +++ /dev/null @@ -1,262 +0,0 @@ -//! GoldenFloat — φ-Optimized Zig Kernel Build System -//! Zig 0.15 package system — module-only library -//! -//! **Build Targets:** -//! - `zig build` — Build module only -//! - `zig build test` — Run all tests -//! - `zig build shared` — Build libgoldenfloat.{so,dylib,dll} -//! - `zig build c-abi-test` — Test C-ABI layer - -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - - // ───────────────────────────────────────────────────────────────── - // Library module (what users import via @import("golden-float")) - // ───────────────────────────────────────────────────────────────── - _ = b.addModule("golden-float", .{ - .root_source_file = b.path("src/root.zig"), - }); - - // ───────────────────────────────────────────────────────────────── - // tri_gen executable — code generator from .tri specs - // ───────────────────────────────────────────────────────────────── - const tri_gen_module = b.createModule(.{ - .root_source_file = b.path("tools/gen/tri_gen.zig"), - .target = target, - .optimize = optimize, - }); - - const tri_gen = b.addExecutable(.{ - .name = "tri_gen", - .root_module = tri_gen_module, - }); - - // NOT installed by default, and not because it is broken. - // - // tri_gen and tri_reader are written against Zig 0.16 on purpose -- their own - // comments say so, and they use std.Io, std.Io.Dir and std.process.Init, - // none of which exist in 0.15. This package declares - // minimum_zig_version 0.15.0 and both of its consumers build with 0.15.2, so - // installing a 0.16-only tool by default made the LIBRARY unbuildable for - // everybody in order to keep a tool nobody can run at that version. - // - // The library itself compiles on 0.15 -- the only failure was here. So the - // tool moves behind an explicit step and the version claim becomes true - // rather than aspirational. `zig build gen` still builds and runs it, on a - // toolchain that has the API it was written for. - // - // This is not making a build green by deleting what failed: what failed is - // still built, by a step that names the toolchain it needs. - const tools_step = b.step("tools", "Build the .tri code generator (requires Zig 0.16)"); - tools_step.dependOn(&b.addInstallArtifact(tri_gen, .{}).step); - - const run_tri_gen = b.addRunArtifact(tri_gen); - const gen_step = b.step("gen", "Generate code from .tri specs"); - gen_step.dependOn(&run_tri_gen.step); - - // ───────────────────────────────────────────────────────────────── - // C-ABI Shared Library — libgoldenfloat.{so,dylib,dll} - // ───────────────────────────────────────────────────────────────── - const c_abi_module = b.createModule(.{ - .root_source_file = b.path("src/c_abi.zig"), - .target = target, - .optimize = optimize, - }); - - const c_abi_lib = b.addLibrary(.{ - .name = "goldenfloat", - .root_module = c_abi_module, - .linkage = .dynamic, - .version = .{ .major = 2, .minor = 1, .patch = 0 }, - }); - - b.installArtifact(c_abi_lib); - - // Install C header alongside library - const header_install = b.addInstallHeaderFile(b.path("src/c/gf16.h"), "gf16.h"); - - const shared_step = b.step("shared", "Build C-ABI shared library (libgoldenfloat)"); - shared_step.dependOn(&b.addInstallArtifact(c_abi_lib, .{}).step); - shared_step.dependOn(&header_install.step); - - // ───────────────────────────────────────────────────────────────── - // C-ABI Tests - // ───────────────────────────────────────────────────────────────── - const c_abi_test_module = b.createModule(.{ - .root_source_file = b.path("src/c_abi.zig"), - .target = target, - .optimize = optimize, - }); - - // The module root, analysed in full. Nothing rooted src/root.zig before, so - // the surface consumers actually import was the one part never compiled. - const root_tests = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path("src/root.zig"), - .target = target, - .optimize = optimize, - }), - }); - - const c_abi_tests = b.addTest(.{ - .name = "c-abi-tests", - .root_module = c_abi_test_module, - }); - - const run_c_abi_tests = b.addRunArtifact(c_abi_tests); - const c_abi_test_step = b.step("c-abi-test", "Run C-ABI tests"); - c_abi_test_step.dependOn(&run_c_abi_tests.step); - - // ───────────────────────────────────────────────────────────────── - // Tests — formats (GF16/TF3) - // ───────────────────────────────────────────────────────────────── - const formats_tests_root = b.createModule(.{ - .root_source_file = b.path("src/formats/golden_float16.zig"), - .target = target, - .optimize = optimize, - }); - const formats_tests = b.addTest(.{ - .name = "formats-tests", - .root_module = formats_tests_root, - }); - - // ───────────────────────────────────────────────────────────────── - // Tests — GF-T ternary-exponent ladder (GF-T4/8/16/32) - // ───────────────────────────────────────────────────────────────── - const gft_tests_root = b.createModule(.{ - .root_source_file = b.path("src/formats/gft.zig"), - .target = target, - .optimize = optimize, - }); - const gft_tests = b.addTest(.{ - .name = "gft-tests", - .root_module = gft_tests_root, - }); - const run_gft_tests = b.addRunArtifact(gft_tests); - - // ───────────────────────────────────────────────────────────────── - // Tests — GF binary-exponent ladder factory (GF4/8/12/16/20/24/32) - // ───────────────────────────────────────────────────────────────── - const gf_binary_tests_root = b.createModule(.{ - .root_source_file = b.path("src/formats/gf_binary.zig"), - .target = target, - .optimize = optimize, - }); - const gf_binary_tests = b.addTest(.{ - .name = "gf-binary-tests", - .root_module = gf_binary_tests_root, - }); - const run_gf_binary_tests = b.addRunArtifact(gf_binary_tests); - - // ───────────────────────────────────────────────────────────────── - // Tests — transcendental functions (Wave 4B) - // ───────────────────────────────────────────────────────────────── - const transcendent_tests_root = b.createModule(.{ - .root_source_file = b.path("src/math/transcendental.zig"), - .target = target, - .optimize = optimize, - }); - const transcendent_tests = b.addTest(.{ - .name = "transcendent-tests", - .root_module = transcendent_tests_root, - }); - - // ───────────────────────────────────────────────────────────────── - // Tests — .tri spec parser (tri_reader) - // Spec files live in specs/, outside tools/gen/, so they cannot be - // @embedFile'd directly (module-path restriction). Supply them as named - // anonymous imports the test embeds via @embedFile("spec_gf8"/"spec_gf16"). - // ───────────────────────────────────────────────────────────────── - const tri_reader_tests_root = b.createModule(.{ - .root_source_file = b.path("tools/gen/tri_reader.zig"), - .target = target, - .optimize = optimize, - }); - tri_reader_tests_root.addAnonymousImport("spec_gf8", .{ - .root_source_file = b.path("specs/gf8.tri"), - }); - tri_reader_tests_root.addAnonymousImport("spec_gf16", .{ - .root_source_file = b.path("specs/gf16.tri"), - }); - const tri_reader_tests = b.addTest(.{ - .name = "tri-reader-tests", - .root_module = tri_reader_tests_root, - }); - const run_tri_reader_tests = b.addRunArtifact(tri_reader_tests); - - const run_tests = b.addRunArtifact(formats_tests); - const run_transcendent_tests = b.addRunArtifact(transcendent_tests); - - const trinity_tests_root = b.createModule(.{ - .root_source_file = b.path("src/trinity_constants.zig"), - .target = target, - .optimize = optimize, - }); - const trinity_tests = b.addTest(.{ - .name = "trinity-constants-tests", - .root_module = trinity_tests_root, - }); - const run_trinity_tests = b.addRunArtifact(trinity_tests); - - const phi_attention_tests_root = b.createModule(.{ - .root_source_file = b.path("src/phi_attention.zig"), - .target = target, - .optimize = optimize, - }); - const phi_attention_tests = b.addTest(.{ - .name = "phi-attention-tests", - .root_module = phi_attention_tests_root, - }); - const run_phi_attention_tests = b.addRunArtifact(phi_attention_tests); - - const trinity_init_tests_root = b.createModule(.{ - .root_source_file = b.path("src/trinity_init.zig"), - .target = target, - .optimize = optimize, - }); - const trinity_init_tests = b.addTest(.{ - .name = "trinity-init-tests", - .root_module = trinity_init_tests_root, - }); - const run_trinity_init_tests = b.addRunArtifact(trinity_init_tests); - - const jepa_t_tests_root = b.createModule(.{ - .root_source_file = b.path("src/jepa_t.zig"), - .target = target, - .optimize = optimize, - }); - const jepa_t_tests = b.addTest(.{ - .name = "jepa-t-tests", - .root_module = jepa_t_tests_root, - }); - const run_jepa_t_tests = b.addRunArtifact(jepa_t_tests); - - const test_step = b.step("test", "Run all tests"); - test_step.dependOn(&run_tests.step); - test_step.dependOn(&run_gft_tests.step); - test_step.dependOn(&run_gf_binary_tests.step); - test_step.dependOn(&run_transcendent_tests.step); - test_step.dependOn(&run_c_abi_tests.step); - test_step.dependOn(&run_trinity_tests.step); - test_step.dependOn(&run_phi_attention_tests.step); - test_step.dependOn(&run_trinity_init_tests.step); - test_step.dependOn(&run_jepa_t_tests.step); - test_step.dependOn(&run_tri_reader_tests.step); - - const igla_bench_module = b.createModule(.{ - .root_source_file = b.path("benches/igla_gf16_bench.zig"), - .target = target, - .optimize = optimize, - }); - const igla_bench = b.addExecutable(.{ - .name = "igla_gf16_bench", - .root_module = igla_bench_module, - }); - const run_igla_bench = b.addRunArtifact(igla_bench); - const igla_bench_step = b.step("bench-igla", "Run IGLA-GF16 architecture verification (Module 7)"); - igla_bench_step.dependOn(&run_igla_bench.step); - test_step.dependOn(&b.addRunArtifact(root_tests).step); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon deleted file mode 100644 index 2a06457..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/build.zig.zon +++ /dev/null @@ -1,17 +0,0 @@ -.{ - .name = .golden_float, - .version = "2.1.0", - .minimum_zig_version = "0.15.0", - - .paths = .{ - "src", - "build.zig", - "build.zig.zon", - "README.md", - "LICENSE", - }, - - .dependencies = .{}, - - .fingerprint = 0x9fba9f8d85cab287, -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h deleted file mode 100644 index 1052ab9..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf16.h +++ /dev/null @@ -1,405 +0,0 @@ -/** - * GoldenFloat v2.0.0 — C-ABI Header - * - * Minimal C99 header for GF16 (Golden Float16) format. - * This header is the SPECIFICATION for libgoldenfloat.{so,dylib,dll} - * - * **Format Layout:** [sign:1][exp:6][mant:9] (16 bits total) - * **Exponent Bias:** 31 - * **Special Values:** exp=0x3F (63) = infinity/NaN - * - * **Usage:** - * ```c - * // Include header - * #include - * - * // Convert values - * gf16_t a = gf16_from_f32(3.14f); - * gf16_t b = gf16_from_f32(2.71f); - * gf16_t sum = gf16_add(a, b); - * float result = gf16_to_f32(sum); - * ``` - * - * MIT License — Copyright (c) 2026 Trinity Project - * Repository: https://github.com/gHashTag/zig-golden-float - */ - -#ifndef GOLDENFLOAT_GF16_H -#define GOLDENFLOAT_GF16_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*====================================================================== - * Type Definition - *======================================================================*/ - -/** - * GF16 value stored as raw 16-bit unsigned integer - * - * **Bit Layout:** - * [15] Sign (0 = positive, 1 = negative) - * [14:9] Exponent (bias = 31, range = -31..+32) - * [8:0] Mantissa (9 bits, fractional part) - * - * **Value Formula:** - * value = (-1)^sign × (1 + mant/512) × 2^(exp - 31) - * - * **Special Values:** - * - exp=0, mant=0: Zero (signed by sign bit) - * - exp=0x3F, mant=0: Infinity (signed by sign bit) - * - exp=0x3F, mant≠0: NaN (quiet) - */ -typedef uint16_t gf16_t; - -/*====================================================================== - * Constants - *======================================================================*/ - -/** Zero constant (positive zero) */ -#define GF16_ZERO ((gf16_t)0x0000) - -/** One constant (1.0 in GF16) */ -#define GF16_ONE ((gf16_t)0x3C00) - -/** Positive infinity */ -#define GF16_PINF ((gf16_t)0x7E00) - -/** Negative infinity */ -#define GF16_NINF ((gf16_t)0xFE00) - -/** Quiet NaN */ -#define GF16_NAN ((gf16_t)0x7E01) - -/** Negative zero */ -#define GF16_NZERO ((gf16_t)0x8000) - -/*====================================================================== - * Bit Extraction Macros - *======================================================================*/ - -/** Extract sign bit (0 or 1) */ -#define GF16_SIGN(g) (((g) >> 15) & 0x1) - -/** Extract exponent field (0..63) */ -#define GF16_EXP(g) (((g) >> 9) & 0x3F) - -/** Extract mantissa field (0..511) */ -#define GF16_MANT(g) ((g) & 0x1FF) - -/** Construct GF16 from components */ -#define GF16_MAKE(s, e, m) (((gf16_t)((s) & 1) << 15) | \ - ((gf16_t)((e) & 0x3F) << 9) | \ - ((gf16_t)(m) & 0x1FF)) - -/*====================================================================== - * Conversion Functions - *======================================================================*/ - -/** - * Convert f32 to GF16 - * - * @param x Input float value - * @return GF16 representation - * - * **Rounding:** Round-to-nearest, ties-to-even - * **Special Values:** Preserved (inf, NaN, signed zeros) - */ -gf16_t gf16_from_f32(float x); - -/** - * Convert GF16 to f32 - * - * @param g GF16 value - * @return Float representation - * - * **Precision:** Exact for all GF16 values - */ -float gf16_to_f32(gf16_t g); - -/*====================================================================== - * Arithmetic Functions - *======================================================================*/ - -/** - * Add two GF16 values - * - * @param a First operand - * @param b Second operand - * @return a + b in GF16 - * - * **Computation:** Performed in f32, rounded to GF16 - */ -gf16_t gf16_add(gf16_t a, gf16_t b); - -/** - * Subtract two GF16 values - * - * @param a First operand - * @param b Second operand - * @return a - b in GF16 - */ -gf16_t gf16_sub(gf16_t a, gf16_t b); - -/** - * Multiply two GF16 values - * - * @param a First operand - * @param b Second operand - * @return a × b in GF16 - */ -gf16_t gf16_mul(gf16_t a, gf16_t b); - -/** - * Divide two GF16 values - * - * @param a Numerator - * @param b Denominator - * @return a / b in GF16 - * - * **Note:** Division by zero returns infinity (signed) - */ -gf16_t gf16_div(gf16_t a, gf16_t b); - -/*====================================================================== - * Unary Functions - *======================================================================*/ - -/** - * Negate GF16 value - * - * @param g Input value - * @return -g - */ -gf16_t gf16_neg(gf16_t g); - -/** - * Absolute value of GF16 - * - * @param g Input value - * @return |g| - */ -gf16_t gf16_abs(gf16_t g); - -/*====================================================================== - * Comparison Functions - *======================================================================*/ - -/** - * Equality test - * - * @param a First operand - * @param b Second operand - * @return true if equal, false otherwise - * - * **Note:** NaN != NaN (IEEE 754 semantics) - */ -bool gf16_eq(gf16_t a, gf16_t b); - -/** - * Less-than test - * - * @param a First operand - * @param b Second operand - * @return true if a < b, false otherwise - * - * **Note:** NaN comparisons return false - */ -bool gf16_lt(gf16_t a, gf16_t b); - -/** - * Less-than-or-equal test - * - * @param a First operand - * @param b Second operand - * @return true if a <= b, false otherwise - */ -bool gf16_le(gf16_t a, gf16_t b); - -/** - * Three-way comparison - * - * @param a First operand - * @param b Second operand - * @return -1 if a < b, 0 if a == b, 1 if a > b - */ -int gf16_cmp(gf16_t a, gf16_t b); - -/*====================================================================== - * Predicate Functions - *======================================================================*/ - -/** - * Check if value is NaN - * - * @param g GF16 value - * @return true if NaN, false otherwise - */ -bool gf16_is_nan(gf16_t g); - -/** - * Check if value is infinity (positive or negative) - * - * @param g GF16 value - * @return true if infinity, false otherwise - */ -bool gf16_is_inf(gf16_t g); - -/** - * Check if value is zero (positive or negative) - * - * @param g GF16 value - * @return true if zero, false otherwise - */ -bool gf16_is_zero(gf16_t g); - -/** - * Check if value is subnormal - * - * @param g GF16 value - * @return true if subnormal, false otherwise - * - * **Note:** GF16 has no true subnormals (exp=0 is zero) - */ -bool gf16_is_subnormal(gf16_t g); - -/** - * Check if value is negative - * - * @param g GF16 value - * @return true if negative, false otherwise - */ -bool gf16_is_negative(gf16_t g); - -/*====================================================================== - * φ-Math Functions (Golden Ratio Optimization) - *======================================================================*/ - -/** - * φ-optimized quantization - * - * Quantizes f32 to GF16 using φ-weighted bins. - * Better distribution for ML weights. - * - * @param x Input float value - * @return φ-quantized GF16 value - * - * **Formula:** x × (1/φ²) then quantize - */ -gf16_t gf16_phi_quantize(float x); - -/** - * φ-optimized dequantization - * - * Dequantizes GF16 to f32 using φ-weighted bins. - * - * @param g GF16 value - * @return φ-dequantized float value - * - * **Formula:** to_f32(g) × φ² - */ -float gf16_phi_dequantize(gf16_t g); - -/*====================================================================== - * Utility Functions - *======================================================================*/ - -/** - * Copy sign from source to target - * - * @param target Value whose magnitude is used - * @param source Value whose sign is used - * @return target with source's sign - */ -gf16_t gf16_copysign(gf16_t target, gf16_t source); - -/** - * Minimum of two values - * - * @param a First operand - * @param b Second operand - * @return min(a, b) - */ -gf16_t gf16_min(gf16_t a, gf16_t b); - -/** - * Maximum of two values - * - * @param a First operand - * @param b Second operand - * @return max(a, b) - */ -gf16_t gf16_max(gf16_t a, gf16_t b); - -/** - * Fused multiply-add: a × b + c - * - * @param a First operand - * @param b Second operand - * @param c Third operand - * @return a × b + c in GF16 - * - * **Note:** Computed in f32, rounded to GF16 - */ -gf16_t gf16_fma(gf16_t a, gf16_t b, gf16_t c); - -/** - * φ-optimized fused multiply-add - * - * Dequantizes inputs from φ-space, computes a × b + c in f32, - * then φ-quantizes the result back. - * - * @param a First operand (φ-quantized) - * @param b Second operand (φ-quantized) - * @param c Third operand (φ-quantized) - * @return φ-quantized result of a × b + c - */ -gf16_t gf16_phi_fma(gf16_t a, gf16_t b, gf16_t c); - -/** - * φ-optimized fused multiply-subtract - * - * Dequantizes inputs from φ-space, computes a × b - c in f32, - * then φ-quantizes the result back. - * - * @param a First operand (φ-quantized) - * @param b Second operand (φ-quantized) - * @param c Third operand (φ-quantized) - * @return φ-quantized result of a × b - c - */ -gf16_t gf16_phi_fms(gf16_t a, gf16_t b, gf16_t c); - -/*====================================================================== - * Constants - *======================================================================*/ - -/** Golden ratio φ = (1 + √5) / 2 ≈ 1.6180339887498948 */ -#define GF16_PHI 1.6180339887498948482f - -/** φ² = φ × φ ≈ 2.6180339887498948 */ -#define GF16_PHI_SQ 2.6180339887498948482f - -/** 1/φ² ≈ 0.3819660112501051 */ -#define GF16_PHI_INV_SQ 0.38196601125010515f - -/** Trinity Identity: φ² + 1/φ² = 3 */ -#define GF16_TRINITY 3.0f - -/** Exponent bias for GF16 */ -#define GF16_EXP_BIAS 31 - -/** Maximum exponent value (before special values) */ -#define GF16_EXP_MAX 62 - -/** Number of mantissa bits */ -#define GF16_MANT_BITS 9 - -#ifdef __cplusplus -} -#endif - -#endif /* GOLDENFLOAT_GF16_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h deleted file mode 100644 index 41af52a..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gf_ladder.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * GoldenFloat — Binary GF ladder C-ABI Header - * - * The φ²-sized binary rungs from the gf_binary.zig factory, exported from - * libgoldenfloat.{so,dylib,dll}. Each rung sizes its exponent by the rule - * e = round((N-1) / φ²), m = N-1-e, bias = 2^(e-1)-1, exp_max = 2^e-1 - * so the exp:mantissa split tracks 1/φ at every width. - * - * Rung Layout bias ~normal range - * GF8 [1:3:4] 3 ~[0.25, 15.5] - * GF12 [1:4:7] 7 ~[0.016, 256] - * GF20 [1:7:12] 63 ~[2^-62, 2^63] - * GF24 [1:9:14] 255 ~[2^-254, 2^255] - * GF32 [1:12:19] 2047 ~[2^-2046, 2^2047] - * - * GF16 [1:6:9] b31 is the rich API in gf16.h (identical layout). GF4 [1:1:2] is - * omitted: a 1-bit exponent leaves no normal values (only zero / Inf / NaN). - * - * The packed N-bit value rides in the low bits of the next byte-sized carrier. - * Semantics: round-to-nearest, saturate to Inf, flush subnormals to zero. - * - * phi^2 + 1/phi^2 = 3 | TRINITY - * MIT License — Copyright (c) 2026 Trinity Project - */ - -#ifndef GOLDENFLOAT_GF_LADDER_H -#define GOLDENFLOAT_GF_LADDER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Each macro declares the 9-function API for one rung over carrier type T. */ -#define GOLDENFLOAT_GF_RUNG(PFX, T) \ - T PFX##_from_f32(float x); \ - float PFX##_to_f32(T g); \ - T PFX##_add(T a, T b); \ - T PFX##_sub(T a, T b); \ - T PFX##_mul(T a, T b); \ - T PFX##_div(T a, T b); \ - T PFX##_neg(T g); \ - T PFX##_abs(T g); \ - uint8_t PFX##_is_finite(T g); - -GOLDENFLOAT_GF_RUNG(gf8, uint8_t) /* [1:3:4] b3 */ -GOLDENFLOAT_GF_RUNG(gf12, uint16_t) /* [1:4:7] b7 */ -GOLDENFLOAT_GF_RUNG(gf20, uint32_t) /* [1:7:12] b63 */ -GOLDENFLOAT_GF_RUNG(gf24, uint32_t) /* [1:9:14] b255 */ -GOLDENFLOAT_GF_RUNG(gf32, uint32_t) /* [1:12:19] b2047 */ - -#undef GOLDENFLOAT_GF_RUNG - -#ifdef __cplusplus -} -#endif - -#endif /* GOLDENFLOAT_GF_LADDER_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h deleted file mode 100644 index 595d068..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c/gft.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * GoldenFloat — GF-T16 C-ABI Header - * - * Minimal C99 header for GF-T16 (ternary-exponent Golden Float). - * SPECIFICATION for the gft16_* symbols in libgoldenfloat.{so,dylib,dll} - * (implemented in src/c_abi.zig over src/formats/gft.zig). - * - * **Format:** [sign:1][exp:4 balanced-ternary trits][mant:9] — the exponent is a - * balanced-ternary number stored as an unsigned OFFSET in [0,80]; the balanced - * exponent is e = offset - 40; the top offset row (80) is reserved (Inf/NaN). - * value = (-1)^sign * (1 + M/2^9) * 2^e, e in [-40,+39] (~24 decades). - * - * The 17-bit packed value is carried in the low bits of a uint32_t (gft16_t). - * - * **Usage:** - * ```c - * #include - * gft16_t a = gft16_from_f32(3.14159f); - * gft16_t b = gft16_from_f32(2.71828f); - * float p = gft16_to_f32(gft16_mul(a, b)); // ~8.539 - * ``` - * - * phi^2 + 1/phi^2 = 3 | TRINITY - */ - -#ifndef GOLDENFLOAT_GFT_H -#define GOLDENFLOAT_GFT_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Raw 17-bit GF-T16 pattern carried in the low bits of a uint32_t. */ -typedef uint32_t gft16_t; - -/** Encode an IEEE float32 into GF-T16 (round-to-nearest, saturate to Inf). */ -gft16_t gft16_from_f32(float x); -/** Decode a GF-T16 value back to float32. */ -float gft16_to_f32(gft16_t g); - -gft16_t gft16_add(gft16_t a, gft16_t b); -gft16_t gft16_sub(gft16_t a, gft16_t b); -gft16_t gft16_mul(gft16_t a, gft16_t b); -gft16_t gft16_div(gft16_t a, gft16_t b); - -gft16_t gft16_neg(gft16_t g); -gft16_t gft16_abs(gft16_t g); - -/** 1 if g is finite (not the reserved Inf/NaN row), else 0. */ -uint8_t gft16_is_finite(gft16_t g); - -/** Balanced zero point: offset that encodes exponent 0 (value in [1,2)). */ -#define GFT16_EXP_OFFSET 40 -/** Reserved special row (Inf/NaN): offset 3^4 - 1. */ -#define GFT16_OFFSET_MAX 80 -/** Number of exponent trits. */ -#define GFT16_EXP_TRITS 4 -/** Number of mantissa bits. */ -#define GFT16_MANT_BITS 9 - -/* ---- The other GF-T rungs (packed value in the low bits of the carrier) ---- */ -/** GF-T4 : E=2 trits, M=1 bit (6-bit value in a uint8). EXP_OFFSET=4, max=8. */ -typedef uint8_t gft4_t; -/** GF-T8 : E=3 trits, M=4 bits (10-bit value in a uint16). EXP_OFFSET=13, max=26. */ -typedef uint16_t gft8_t; -/** GF-T32 : E=6 trits, M=25 bits (36-bit value in a uint64). EXP_OFFSET=364, max=728, ~219 decades. */ -typedef uint64_t gft32_t; - -gft4_t gft4_from_f32(float x); -float gft4_to_f32(gft4_t g); -gft4_t gft4_mul(gft4_t a, gft4_t b); -uint8_t gft4_is_finite(gft4_t g); - -gft8_t gft8_from_f32(float x); -float gft8_to_f32(gft8_t g); -gft8_t gft8_add(gft8_t a, gft8_t b); -gft8_t gft8_sub(gft8_t a, gft8_t b); -gft8_t gft8_mul(gft8_t a, gft8_t b); -gft8_t gft8_div(gft8_t a, gft8_t b); -gft8_t gft8_neg(gft8_t g); -gft8_t gft8_abs(gft8_t g); -uint8_t gft8_is_finite(gft8_t g); - -gft32_t gft32_from_f32(float x); -float gft32_to_f32(gft32_t g); -gft32_t gft32_add(gft32_t a, gft32_t b); -gft32_t gft32_sub(gft32_t a, gft32_t b); -gft32_t gft32_mul(gft32_t a, gft32_t b); -gft32_t gft32_div(gft32_t a, gft32_t b); -gft32_t gft32_neg(gft32_t g); -gft32_t gft32_abs(gft32_t g); -uint8_t gft32_is_finite(gft32_t g); - -#ifdef __cplusplus -} -#endif - -#endif /* GOLDENFLOAT_GFT_H */ diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig deleted file mode 100644 index 92a6768..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/c_abi.zig +++ /dev/null @@ -1,671 +0,0 @@ -//! GoldenFloat C-ABI v1.1.0 — Zig Implementation -//! -//! This file provides extern "C" functions that implement the GF16 API -//! defined in src/c/gf16.h. The shared library (libgoldenfloat) is -//! compiled from this Zig source. -//! -//! **Architecture:** -//! - Header (src/c/gf16.h) = specification -//! - This file (src/c_abi.zig) = Zig implementation -//! - build.zig = compiles to libgoldenfloat.{so,dylib,dll} -//! -//! **Usage from other languages:** -//! ```rust -//! // Rust -//! extern "C" { -//! fn gf16_from_f32(x: f32) -> u16; -//! fn gf16_to_f32(g: u16) -> f32; -//! } -//! ``` -//! -//! ```python -//! # Python -//! import ctypes -//! lib = ctypes.CDLL("libgoldenfloat.so") -//! lib.gf16_from_f32.restype = ctypes.c_uint16 -//! lib.gf16_from_f32.argtypes = [ctypes.c_float] -//! ``` - -const std = @import("std"); -const golden = @import("formats/golden_float16.zig"); - -// ═══════════════════════════════════════════════════════════════════ -// Type Aliases -// ═════════════════════════════════════════════════════════════════ - -/// gf16_t is a raw u16 bit pattern -const gf16_t = u16; - -/// Convert GF16 struct to raw u16 -inline fn gf16ToRaw(gf: golden.GF16) gf16_t { - return @as(u16, @bitCast(gf)); -} - -/// Convert raw u16 to GF16 struct -inline fn rawToGf16(raw: gf16_t) golden.GF16 { - return @as(golden.GF16, @bitCast(raw)); -} - -// ═════════════════════════════════════════════════════════════════════ -// Conversion Functions -// ═════════════════════════════════════════════════════════════════ - -export fn gf16_from_f32(x: f32) callconv(.c) gf16_t { - return gf16ToRaw(golden.GF16.fromF32(x)); -} - -export fn gf16_to_f32(g: gf16_t) callconv(.c) f32 { - return rawToGf16(g).toF32(); -} - -// ═══════════════════════════════════════════════════════════════════ -// Arithmetic Functions -// ═════════════════════════════════════════════════════════════════════ - -export fn gf16_add(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - return gf16ToRaw(golden.GF16.add(gf_a, gf_b)); -} - -export fn gf16_sub(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - return gf16ToRaw(golden.GF16.sub(gf_a, gf_b)); -} - -export fn gf16_mul(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - return gf16ToRaw(golden.GF16.mul(gf_a, gf_b)); -} - -export fn gf16_div(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - return gf16ToRaw(golden.GF16.div(gf_a, gf_b)); -} - -// ═════════════════════════════════════════════════════════════════════ -// Unary Functions -// ═════════════════════════════════════════════════════════════════ - -export fn gf16_neg(g: gf16_t) callconv(.c) gf16_t { - return gf16ToRaw(rawToGf16(g).neg()); -} - -export fn gf16_abs(g: gf16_t) callconv(.c) gf16_t { - return gf16ToRaw(rawToGf16(g).abs()); -} - -// ═════════════════════════════════════════════════════════════════════ -// Comparison Functions -// ═════════════════════════════════════════════════════════════════════ - -export fn gf16_eq(a: gf16_t, b: gf16_t) callconv(.c) bool { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - const fa = gf_a.toF32(); - const fb = gf_b.toF32(); - // Handle NaN: NaN != NaN (IEEE 754 semantics) - if (std.math.isNan(fa) or std.math.isNan(fb)) return false; - return fa == fb; -} - -export fn gf16_lt(a: gf16_t, b: gf16_t) callconv(.c) bool { - const gf_a = rawToGf16(a); - const gf_b = rawToGf16(b); - const fa = gf_a.toF32(); - const fb = gf_b.toF32(); - // Handle NaN: comparisons with NaN are false - if (std.math.isNan(fa) or std.math.isNan(fb)) return false; - return fa < fb; -} - -export fn gf16_le(a: gf16_t, b: gf16_t) callconv(.c) bool { - return gf16_lt(a, b) or gf16_eq(a, b); -} - -export fn gf16_cmp(a: gf16_t, b: gf16_t) callconv(.c) c_int { - if (gf16_lt(a, b)) return -1; - if (gf16_eq(a, b)) return 0; - return 1; -} - -// ═══════════════════════════════════════════════════════════════════════ -// Predicate Functions -// ═══════════════════════════════════════════════════════════════════ - -export fn gf16_is_nan(g: gf16_t) callconv(.c) bool { - const gf = rawToGf16(g); - // NaN: exp = 0x3F and mant != 0 - return gf.exp == 0x3F and gf.mant != 0; -} - -export fn gf16_is_inf(g: gf16_t) callconv(.c) bool { - const gf = rawToGf16(g); - // Infinity: exp = 0x3F and mant = 0 - return gf.exp == 0x3F and gf.mant == 0; -} - -export fn gf16_is_zero(g: gf16_t) callconv(.c) bool { - const gf = rawToGf16(g); - // Zero: exp = 0 and mant = 0 - return gf.exp == 0 and gf.mant == 0; -} - -export fn gf16_is_subnormal(g: gf16_t) callconv(.c) bool { - const gf = rawToGf16(g); - // GF16 has no true subnormals (exp = 0 means zero) - return gf.exp == 0 and gf.mant != 0; -} - -export fn gf16_is_negative(g: gf16_t) callconv(.c) bool { - const gf = rawToGf16(g); - return gf.sign == 1; -} - -// ═════════════════════════════════════════════════════════════════════ -// φ-Math Functions -// ═══════════════════════════════════════════════════════════════════════ - -export fn gf16_phi_quantize(x: f32) callconv(.c) gf16_t { - return gf16ToRaw(golden.GF16.phiQuantize(x)); -} - -export fn gf16_phi_dequantize(g: gf16_t) callconv(.c) f32 { - const gf = rawToGf16(g); - return golden.GF16.phiDequantize(gf); -} - -// ═══════════════════════════════════════════════════════════════════════ -// Utility Functions -// ═════════════════════════════════════════════════════════════════════════════ - -export fn gf16_copysign(target: gf16_t, source: gf16_t) callconv(.c) gf16_t { - const gf_target = rawToGf16(target); - const gf_source = rawToGf16(source); - return gf16ToRaw(.{ - .mant = gf_target.mant, - .exp = gf_target.exp, - .sign = gf_source.sign, - }); -} - -export fn gf16_min(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - return if (gf16_lt(a, b)) a else b; -} - -export fn gf16_max(a: gf16_t, b: gf16_t) callconv(.c) gf16_t { - return if (gf16_lt(a, b)) b else a; -} - -export fn gf16_fma(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { - const fa = rawToGf16(a).toF32(); - const fb = rawToGf16(b).toF32(); - const fc = rawToGf16(c).toF32(); - return gf16ToRaw(golden.GF16.fromF32(fa * fb + fc)); -} - -export fn gf16_phi_fma(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { - return gf16ToRaw(golden.GF16.phiFma(rawToGf16(a), rawToGf16(b), rawToGf16(c))); -} - -export fn gf16_phi_fms(a: gf16_t, b: gf16_t, c: gf16_t) callconv(.c) gf16_t { - return gf16ToRaw(golden.GF16.phiFms(rawToGf16(a), rawToGf16(b), rawToGf16(c))); -} - -// ═══════════════════════════════════════════════════════════════════ -// Library Info -// ═════════════════════════════════════════════════════════════════════ - -export fn goldenfloat_version() callconv(.c) [*:0]const u8 { - return "1.1.0"; -} - -export fn goldenfloat_phi() callconv(.c) f64 { - return golden.PHI; -} - -export fn goldenfloat_trinity() callconv(.c) f64 { - return golden.TRINITY; -} - -// ═════════════════════════════════════════════════════════════════════ -// Compile-Time Guards -// ═══════════════════════════════════════════════════════════════════════════════ - -comptime { - std.debug.assert(@sizeOf(gf16_t) == 2); - std.debug.assert(@sizeOf(golden.GF16) == 2); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// GF-T16 — balanced-ternary-exponent GoldenFloat (thin FFI over formats/gft.zig). -// The raw value is the 17-bit packed encoding carried in a u32 (declared in -// src/c/gft.h). This is FFI glue only; all arithmetic lives in the gft.zig codec. -// ═══════════════════════════════════════════════════════════════════════════════ - -const gft = @import("formats/gft.zig"); - -/// gft16_t is the raw 17-bit GF-T16 pattern in the low bits of a u32. -const gft16_t = u32; - -inline fn gft16ToRaw(g: gft.GFT16) gft16_t { - return @as(gft16_t, g.bits()); -} -inline fn rawToGft16(raw: gft16_t) gft.GFT16 { - return gft.GFT16.fromBits(@truncate(raw)); -} - -export fn gft16_from_f32(x: f32) callconv(.c) gft16_t { - return gft16ToRaw(gft.GFT16.fromF32(x)); -} -export fn gft16_to_f32(g: gft16_t) callconv(.c) f32 { - return rawToGft16(g).toF32(); -} -export fn gft16_add(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(gft.GFT16.add(rawToGft16(a), rawToGft16(b))); -} -export fn gft16_sub(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(gft.GFT16.sub(rawToGft16(a), rawToGft16(b))); -} -export fn gft16_mul(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(gft.GFT16.mul(rawToGft16(a), rawToGft16(b))); -} -export fn gft16_div(a: gft16_t, b: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(gft.GFT16.div(rawToGft16(a), rawToGft16(b))); -} -export fn gft16_neg(g: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(rawToGft16(g).neg()); -} -export fn gft16_abs(g: gft16_t) callconv(.c) gft16_t { - return gft16ToRaw(rawToGft16(g).abs()); -} -export fn gft16_is_finite(g: gft16_t) callconv(.c) u8 { - return @intFromBool(rawToGft16(g).isFinite()); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// The other GF-T rungs — GF-T4 (u8), GF-T8 (u16), GF-T32 (u64). Same thin glue -// pattern as gft16; the packed value rides in the low bits of the C carrier type. -// ═══════════════════════════════════════════════════════════════════════════════ - -const gft4_t = u8; -const gft8_t = u16; -const gft32_t = u64; - -export fn gft4_from_f32(x: f32) callconv(.c) gft4_t { - return @as(gft4_t, gft.GFT4.fromF32(x).bits()); -} -export fn gft4_to_f32(g: gft4_t) callconv(.c) f32 { - return gft.GFT4.fromBits(@truncate(g)).toF32(); -} -export fn gft4_mul(a: gft4_t, b: gft4_t) callconv(.c) gft4_t { - return @as(gft4_t, gft.GFT4.mul(gft.GFT4.fromBits(@truncate(a)), gft.GFT4.fromBits(@truncate(b))).bits()); -} -export fn gft4_is_finite(g: gft4_t) callconv(.c) u8 { - return @intFromBool(gft.GFT4.fromBits(@truncate(g)).isFinite()); -} - -export fn gft8_from_f32(x: f32) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.fromF32(x).bits()); -} -export fn gft8_to_f32(g: gft8_t) callconv(.c) f32 { - return gft.GFT8.fromBits(@truncate(g)).toF32(); -} -export fn gft8_add(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.add(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); -} -export fn gft8_mul(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.mul(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); -} -export fn gft8_sub(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.sub(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); -} -export fn gft8_div(a: gft8_t, b: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.div(gft.GFT8.fromBits(@truncate(a)), gft.GFT8.fromBits(@truncate(b))).bits()); -} -export fn gft8_neg(g: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.neg(gft.GFT8.fromBits(@truncate(g))).bits()); -} -export fn gft8_abs(g: gft8_t) callconv(.c) gft8_t { - return @as(gft8_t, gft.GFT8.abs(gft.GFT8.fromBits(@truncate(g))).bits()); -} -export fn gft8_is_finite(g: gft8_t) callconv(.c) u8 { - return @intFromBool(gft.GFT8.fromBits(@truncate(g)).isFinite()); -} - -export fn gft32_from_f32(x: f32) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.fromF32(x).bits()); -} -export fn gft32_to_f32(g: gft32_t) callconv(.c) f32 { - return gft.GFT32.fromBits(@truncate(g)).toF32(); -} -export fn gft32_add(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.add(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); -} -export fn gft32_mul(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.mul(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); -} -export fn gft32_sub(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.sub(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); -} -export fn gft32_div(a: gft32_t, b: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.div(gft.GFT32.fromBits(@truncate(a)), gft.GFT32.fromBits(@truncate(b))).bits()); -} -export fn gft32_neg(g: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.neg(gft.GFT32.fromBits(@truncate(g))).bits()); -} -export fn gft32_abs(g: gft32_t) callconv(.c) gft32_t { - return @as(gft32_t, gft.GFT32.abs(gft.GFT32.fromBits(@truncate(g))).bits()); -} -export fn gft32_is_finite(g: gft32_t) callconv(.c) u8 { - return @intFromBool(gft.GFT32.fromBits(@truncate(g)).isFinite()); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// Binary GF ladder — the φ²-sized rungs from the gf_binary.zig factory. -// GF16 is already covered by the rich gf16_* API above (identical [1:6:9] b31), so -// this exposes GF8/GF12/GF20/GF24/GF32. GF4 is intentionally omitted: [1:1:2] gives a -// 1-bit exponent (exp 0 = zero, exp 1 = reserved Inf/NaN) with NO normal values. -// The packed N-bit value rides in the low bits of the next byte-sized carrier. -// ═══════════════════════════════════════════════════════════════════════════════ - -const gfl = @import("formats/gf_binary.zig"); - -// ---- GF8 (8-bit value in u8) ---- -export fn gf8_from_f32(x: f32) callconv(.c) u8 { - return @as(u8, gfl.GF8.fromF32(x).bits_()); -} -export fn gf8_to_f32(g: u8) callconv(.c) f32 { - return gfl.GF8.fromBits(@truncate(g)).toF32(); -} -export fn gf8_add(a: u8, b: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.add(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); -} -export fn gf8_sub(a: u8, b: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.sub(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); -} -export fn gf8_mul(a: u8, b: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.mul(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); -} -export fn gf8_div(a: u8, b: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.div(gfl.GF8.fromBits(@truncate(a)), gfl.GF8.fromBits(@truncate(b))).bits_()); -} -export fn gf8_neg(g: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.neg(gfl.GF8.fromBits(@truncate(g))).bits_()); -} -export fn gf8_abs(g: u8) callconv(.c) u8 { - return @as(u8, gfl.GF8.abs(gfl.GF8.fromBits(@truncate(g))).bits_()); -} -export fn gf8_is_finite(g: u8) callconv(.c) u8 { - return @intFromBool(gfl.GF8.fromBits(@truncate(g)).isFinite()); -} - -// ---- GF12 (12-bit value in u16) ---- -export fn gf12_from_f32(x: f32) callconv(.c) u16 { - return @as(u16, gfl.GF12.fromF32(x).bits_()); -} -export fn gf12_to_f32(g: u16) callconv(.c) f32 { - return gfl.GF12.fromBits(@truncate(g)).toF32(); -} -export fn gf12_add(a: u16, b: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.add(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); -} -export fn gf12_sub(a: u16, b: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.sub(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); -} -export fn gf12_mul(a: u16, b: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.mul(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); -} -export fn gf12_div(a: u16, b: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.div(gfl.GF12.fromBits(@truncate(a)), gfl.GF12.fromBits(@truncate(b))).bits_()); -} -export fn gf12_neg(g: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.neg(gfl.GF12.fromBits(@truncate(g))).bits_()); -} -export fn gf12_abs(g: u16) callconv(.c) u16 { - return @as(u16, gfl.GF12.abs(gfl.GF12.fromBits(@truncate(g))).bits_()); -} -export fn gf12_is_finite(g: u16) callconv(.c) u8 { - return @intFromBool(gfl.GF12.fromBits(@truncate(g)).isFinite()); -} - -// ---- GF20 (20-bit value in u32) ---- -export fn gf20_from_f32(x: f32) callconv(.c) u32 { - return @as(u32, gfl.GF20.fromF32(x).bits_()); -} -export fn gf20_to_f32(g: u32) callconv(.c) f32 { - return gfl.GF20.fromBits(@truncate(g)).toF32(); -} -export fn gf20_add(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.add(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); -} -export fn gf20_sub(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.sub(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); -} -export fn gf20_mul(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.mul(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); -} -export fn gf20_div(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.div(gfl.GF20.fromBits(@truncate(a)), gfl.GF20.fromBits(@truncate(b))).bits_()); -} -export fn gf20_neg(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.neg(gfl.GF20.fromBits(@truncate(g))).bits_()); -} -export fn gf20_abs(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF20.abs(gfl.GF20.fromBits(@truncate(g))).bits_()); -} -export fn gf20_is_finite(g: u32) callconv(.c) u8 { - return @intFromBool(gfl.GF20.fromBits(@truncate(g)).isFinite()); -} - -// ---- GF24 (24-bit value in u32) ---- -export fn gf24_from_f32(x: f32) callconv(.c) u32 { - return @as(u32, gfl.GF24.fromF32(x).bits_()); -} -export fn gf24_to_f32(g: u32) callconv(.c) f32 { - return gfl.GF24.fromBits(@truncate(g)).toF32(); -} -export fn gf24_add(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.add(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); -} -export fn gf24_sub(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.sub(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); -} -export fn gf24_mul(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.mul(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); -} -export fn gf24_div(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.div(gfl.GF24.fromBits(@truncate(a)), gfl.GF24.fromBits(@truncate(b))).bits_()); -} -export fn gf24_neg(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.neg(gfl.GF24.fromBits(@truncate(g))).bits_()); -} -export fn gf24_abs(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF24.abs(gfl.GF24.fromBits(@truncate(g))).bits_()); -} -export fn gf24_is_finite(g: u32) callconv(.c) u8 { - return @intFromBool(gfl.GF24.fromBits(@truncate(g)).isFinite()); -} - -// ---- GF32 (32-bit value in u32) ---- -export fn gf32_from_f32(x: f32) callconv(.c) u32 { - return @as(u32, gfl.GF32.fromF32(x).bits_()); -} -export fn gf32_to_f32(g: u32) callconv(.c) f32 { - return gfl.GF32.fromBits(@truncate(g)).toF32(); -} -export fn gf32_add(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.add(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); -} -export fn gf32_sub(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.sub(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); -} -export fn gf32_mul(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.mul(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); -} -export fn gf32_div(a: u32, b: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.div(gfl.GF32.fromBits(@truncate(a)), gfl.GF32.fromBits(@truncate(b))).bits_()); -} -export fn gf32_neg(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.neg(gfl.GF32.fromBits(@truncate(g))).bits_()); -} -export fn gf32_abs(g: u32) callconv(.c) u32 { - return @as(u32, gfl.GF32.abs(gfl.GF32.fromBits(@truncate(g))).bits_()); -} -export fn gf32_is_finite(g: u32) callconv(.c) u8 { - return @intFromBool(gfl.GF32.fromBits(@truncate(g)).isFinite()); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// Tests -// ═════════════════════════════════════════════════════════════════════════════ - -test "C-ABI: gf16_from_f32 and gf16_to_f32" { - const val: f32 = 3.14; - const gf = gf16_from_f32(val); - const back = gf16_to_f32(gf); - const err = @abs(val - back) / (@abs(val) + 0.001); - try std.testing.expect(err < 0.05); -} - -test "C-ABI: gf16_add" { - const a = gf16_from_f32(1.5); - const b = gf16_from_f32(2.5); - const sum = gf16_add(a, b); - const result = gf16_to_f32(sum); - try std.testing.expectApproxEqAbs(@as(f32, 4.0), result, 0.05); -} - -test "C-ABI: gf16_mul" { - const a = gf16_from_f32(2.0); - const b = gf16_from_f32(3.0); - const prod = gf16_mul(a, b); - const result = gf16_to_f32(prod); - try std.testing.expectApproxEqAbs(@as(f32, 6.0), result, 0.05); -} - -test "C-ABI: gf16_neg and gf16_abs" { - const val = gf16_from_f32(-3.14); - const neg = gf16_neg(val); - const abs = gf16_abs(val); - try std.testing.expect(gf16_to_f32(neg) > 0); - try std.testing.expect(gf16_to_f32(abs) > 0); -} - -test "C-ABI: gf16_eq and gf16_lt" { - const a = gf16_from_f32(1.0); - const b = gf16_from_f32(1.0); - const c = gf16_from_f32(2.0); - try std.testing.expect(gf16_eq(a, b)); - try std.testing.expect(gf16_lt(a, c)); - try std.testing.expect(!gf16_lt(c, a)); -} - -test "C-ABI: gf16_is_nan and gf16_is_inf" { - const inf_val = gf16_from_f32(std.math.inf(f32)); - try std.testing.expect(gf16_is_inf(inf_val)); - try std.testing.expect(!gf16_is_nan(inf_val)); - - const zero = gf16_from_f32(0.0); - try std.testing.expect(gf16_is_zero(zero)); - - const nan_val = gf16_from_f32(std.math.nan(f32)); - try std.testing.expect(gf16_is_nan(nan_val)); - try std.testing.expect(!gf16_is_inf(nan_val)); -} - -test "C-ABI: gf16_phi_quantize" { - const original = 2.71828; - const quantized = gf16_phi_quantize(original); - const dequantized = gf16_phi_dequantize(quantized); - - const error_pct = @abs((dequantized - original) / original) * 100.0; - try std.testing.expect(error_pct < 10.0); -} - -test "C-ABI: gf16_fma" { - const a = gf16_from_f32(2.0); - const b = gf16_from_f32(3.0); - const c = gf16_from_f32(4.0); - const result = gf16_fma(a, b, c); - const val = gf16_to_f32(result); - try std.testing.expectApproxEqAbs(@as(f32, 10.0), val, 0.05); -} - -test "C-ABI: gf16_phi_fma" { - const a = gf16_phi_quantize(2.0); - const b = gf16_phi_quantize(3.0); - const c = gf16_phi_quantize(4.0); - const result = gf16_phi_fma(a, b, c); - const deq = gf16_phi_dequantize(result); - try std.testing.expectApproxEqAbs(@as(f32, 10.0), deq, 1.5); -} - -test "C-ABI: gf16_phi_fms" { - const a = gf16_phi_quantize(5.0); - const b = gf16_phi_quantize(3.0); - const c = gf16_phi_quantize(4.0); - const result = gf16_phi_fms(a, b, c); - const deq = gf16_phi_dequantize(result); - try std.testing.expectApproxEqAbs(@as(f32, 11.0), deq, 2.0); -} - -test "C-ABI: library version" { - const version = std.mem.span(goldenfloat_version()); - try std.testing.expectEqualStrings("1.1.0", version); -} - -test "C-ABI: goldenfloat_trinity returns 3.0" { - const trinity = goldenfloat_trinity(); - try std.testing.expectApproxEqAbs(@as(f64, 3.0), trinity, 1e-10); -} - -test "C-ABI: gft16_from_f32 and gft16_to_f32" { - const val: f32 = 3.14159; - const g = gft16_from_f32(val); - const back = gft16_to_f32(g); - try std.testing.expect(@abs(val - back) / (@abs(val) + 1e-9) < 0.005); - // raw is a 17-bit value carried in u32 - try std.testing.expect(g <= 0x1FFFF); -} - -test "C-ABI: gft16 arithmetic matches the codec" { - const a = gft16_from_f32(1.5); - const b = gft16_from_f32(2.5); - try std.testing.expectApproxEqAbs(@as(f32, 4.0), gft16_to_f32(gft16_add(a, b)), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 1.0), gft16_to_f32(gft16_sub(b, a)), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 3.75), gft16_to_f32(gft16_mul(a, b)), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 0.6), gft16_to_f32(gft16_div(a, b)), 0.02); -} - -test "C-ABI: gft16_neg / gft16_abs / gft16_is_finite" { - const x = gft16_from_f32(3.5); - try std.testing.expectApproxEqAbs(@as(f32, -3.5), gft16_to_f32(gft16_neg(x)), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 3.5), gft16_to_f32(gft16_abs(gft16_neg(x))), 0.02); - try std.testing.expectEqual(@as(u8, 1), gft16_is_finite(gft16_from_f32(1.0))); - try std.testing.expectEqual(@as(u8, 0), gft16_is_finite(gft16_from_f32(1e30))); // overflow -> Inf -} - -test "C-ABI: gft16 round-trips through the raw u32 (FFI stability)" { - const g = gft16_from_f32(-6.28); - try std.testing.expectEqual(gft16_to_f32(g), gft16_to_f32(gft16_from_f32(gft16_to_f32(g)))); -} - -test "C-ABI: gft4 / gft8 / gft32 from/to + carrier widths" { - // GF-T4 (u8, 1-bit mantissa -> coarse) - try std.testing.expect(gft4_from_f32(2.0) <= 0x3F); // 6-bit value - try std.testing.expectApproxEqAbs(@as(f32, 2.0), gft4_to_f32(gft4_from_f32(2.0)), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 4.0), gft4_to_f32(gft4_mul(gft4_from_f32(2.0), gft4_from_f32(2.0))), 0.5); - // GF-T8 (u16, 4-bit mantissa) - try std.testing.expect(gft8_from_f32(3.0) <= 0x3FF); // 10-bit value - try std.testing.expectApproxEqAbs(@as(f32, 3.0), gft8_to_f32(gft8_from_f32(3.0)), 0.1); - try std.testing.expectApproxEqAbs(@as(f32, 5.0), gft8_to_f32(gft8_add(gft8_from_f32(2.0), gft8_from_f32(3.0))), 0.2); - // GF-T32 (u64, 25-bit mantissa, huge range) - try std.testing.expect(gft32_from_f32(1.0) <= 0xFFFFFFFFF); // 36-bit value - try std.testing.expectApproxEqAbs(@as(f32, 3.14159), gft32_to_f32(gft32_from_f32(3.14159)), 1e-4); - try std.testing.expect(gft32_is_finite(gft32_from_f32(6.022e23)) == 1); // GF-T32 holds it - try std.testing.expect(gft32_to_f32(gft32_mul(gft32_from_f32(1e10), gft32_from_f32(1e10))) > 5e19); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig deleted file mode 100644 index d52a013..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/formats_root.zig +++ /dev/null @@ -1,687 +0,0 @@ -//! Format Conversion Utilities for Trinity Benchmarks -//! -//! GF16 bit layout (as specified in whitepaper, identical to DLFloat 6:9): -//! [S(1) E(6) M(9)] = [15:15][14:9][8:0] -//! -//! - Sign: bit 15 (0x8000) -//! - Exponent: bits 14-9 (0x7E00), bias = 31 -//! - Mantissa: bits 8-0 (0x01FF) -//! -//! Range: 2^-31 to 2^32 - -const std = @import("std"); - -// ═══════════════════════════════════════════════════════════════════ -// GF16 Constants -// ═══════════════════════════════════════════════════════════════════ - -pub const SignMask: u16 = 0b1_000000_000000000; // 0x8000 -pub const ExpMask: u16 = 0b0_111111_000000000; // 0x7E00 -pub const MantMask: u16 = 0b0_000000_111111111; // 0x01FF - -pub const ExpShift: u5 = 9; -pub const SignShift: u4 = 15; -pub const Bias: i32 = 31; - -pub const ExpMax: u16 = 0b111111; // 63 -pub const ExpMin: u16 = 0; - -// ═══════════════════════════════════════════════════════════════════ -// GF16 → f32 (decode) -// ═══════════════════════════════════════════════════════════════════ - -pub fn gf16ToF32(x: u16) f32 { - const s = @as(i32, (x >> SignShift) & 1); - const e = @as(i32, (x & ExpMask) >> ExpShift); - const m = @as(i32, x & MantMask); - - if (e == 0 and m == 0) { - // Signed zero - return if (s == 0) 0.0 else -0.0; - } else if (e == 0) { - // Denormals: treat as subnormal - const exp = 1 - Bias; - const frac = @as(f32, @floatFromInt(m)) / 512.0; // 2^9 - const val = std.math.exp2(@as(f32, @floatFromInt(exp))) * frac; - return if (s == 0) val else -val; - } else if (e == ExpMax) { - // Special values (Inf/NaN) - if (m == 0) { - return if (s == 0) std.math.inf(f32) else -std.math.inf(f32); - } else { - return std.math.nan(f32); - } - } else { - // Normal: value = (-1)^s * (1 + m/2^9) * 2^(e - Bias) - const exp = e - Bias; - const frac = 1.0 + @as(f32, @floatFromInt(m)) / 512.0; - const val = frac * std.math.exp2(@as(f32, @floatFromInt(exp))); - return if (s == 0) val else -val; - } -} - -// ═══════════════════════════════════════════════════════════════════ -// f32 → GF16 (encode, round-to-nearest) -// ═══════════════════════════════════════════════════════════════════ - -pub fn f32ToGf16(a: f32) u16 { - // Handle signed zero explicitly - if (a == 0.0) { - return if (@as(u32, @bitCast(a)) & 0x80000000 != 0) 0x8000 else 0; - } - - const sign_bit: u16 = if (a < 0) 1 << SignShift else 0; - const abs = if (a < 0) -a else a; - - // Handle special cases - if (std.math.isPositiveInf(abs)) return sign_bit | ExpMask; - if (std.math.isNan(abs)) return sign_bit | ExpMask | 1; - - // Get exponent and mantissa via frexp: abs = m * 2^e, m in [0.5, 1) - // Zig 0.15: frexp returns struct { fract: f32, exp: i32 } - const frexp_result = std.math.frexp(abs); - var m = frexp_result.significand; - var exp_i = frexp_result.exponent; - - // Normalize: want 1.x * 2^(E - Bias), frexp gives m in [0.5, 1) - m *= 2.0; - exp_i -= 1; - - var e = exp_i + Bias; - if (e <= 0) { - // Underflow → zero - return sign_bit; - } else if (e >= ExpMax) { - // Overflow → INF - return sign_bit | ExpMask; - } - - // Mantissa: (m - 1.0) * 2^9, round to nearest - const mant_f = (m - 1.0) * 512.0; - var mant_i = @as(i32, @intFromFloat(std.math.round(mant_f))); - - // Handle mantissa overflow - if (mant_i == 512) { // 2^9 - mant_i = 0; - e += 1; - if (e >= ExpMax) { - return sign_bit | ExpMask; - } - } - - const e_bits: u16 = @as(u16, @intCast(e)) << ExpShift; - const m_bits: u16 = @as(u16, @intCast(mant_i)) & MantMask; - - return sign_bit | e_bits | m_bits; -} - -// ═══════════════════════════════════════════════════════════════════ -// Software fp16 encode/decode (IEEE 754 binary16) -fn f32ToFp16(a: f32) u16 { - if (std.math.isNan(a)) return 0x7E00; - const bits: u32 = @bitCast(a); - const sign: u16 = @intCast((bits >> 16) & 0x8000); - const abs_bits = bits & 0x7FFFFFFF; - - if (abs_bits == 0) return sign; - if (std.math.isInf(a)) return sign | 0x7C00; - - const f32_exp = @as(i32, @intCast((abs_bits >> 23) & 0xFF)) - 127; - const f32_mant = abs_bits & 0x7FFFFF; - - if (f32_exp > 15) return sign | 0x7C00; - - if (f32_exp >= -14) { - const fp16_mant = @as(u16, @intCast(f32_mant >> 13)); - const fp16_exp = @as(u16, @intCast(f32_exp + 15)) << 10; - return sign | fp16_exp | fp16_mant; - } - - const shift = @as(u5, @intCast(@as(i32, 13) - f32_exp - 14 + 1)); - if (shift >= 32) return sign; - const fp16_mant = @as(u16, @intCast(f32_mant >> shift)); - if (fp16_mant == 0) return sign; - return sign | fp16_mant; -} - -fn fp16ToF32(x: u16) f32 { - const sign: u32 = @as(u32, x & 0x8000) << 16; - const e = (x >> 10) & 0x1F; - const m = x & 0x03FF; - - if (e == 0) { - if (m == 0) return @bitCast(sign); - var mant = @as(u32, m) << 13; - var shifts: u32 = 0; - while ((mant & 0x00800000) == 0) : (shifts += 1) { - mant <<= 1; - } - const biased_exp: u32 = 113 - shifts; - const f32_bits = sign | (biased_exp << 23) | (mant & 0x7FFFFF); - return @bitCast(f32_bits); - } - if (e == 0x1F) { - if (m == 0) return @bitCast(sign | 0x7F800000); - return @bitCast(sign | 0x7FC00000); - } - - const f32_bits = sign | ((@as(u32, e) + 112) << 23) | (@as(u32, m) << 13); - return @bitCast(f32_bits); -} - -// Software bf16 encode/decode (Brain Float 16) — IEEE 754 canonical -fn f32ToBf16(a: f32) u16 { - if (std.math.isNan(a)) return 0x7FC0; - const bits: u32 = @bitCast(a); - const rounding: u32 = ((bits >> 16) & 1) + 0x7FFF; - return @intCast((bits +| rounding) >> 16); -} - -fn bf16ToF32(x: u16) f32 { - return @bitCast(@as(u32, x) << 16); -} - -// ═══════════════════════════════════════════════════════════════════ -// Ternary Format: {-1, 0, +1} Symmetric -// ═══════════════════════════════════════════════════════════════════ - -/// Symmetric quantization: w -> {-1, 0, +1} -/// Threshold: |w| > 0.5 -> +/-1, else -> 0 -pub fn f32ToTernary(x: f32) i8 { - if (x > 0.5) return 1; - if (x < -0.5) return -1; - return 0; -} - -pub fn ternaryToF32(t: i8) f32 { - return @as(f32, @floatFromInt(t)); -} - -// ═══════════════════════════════════════════════════════════════════ -// Format Enum and Conversion Interface -// ═══════════════════════════════════════════════════════════════════ - -pub const Format = enum { - fp32, - fp16, - bf16, - gf16, - ternary, -}; - -pub fn formatBytes(fmt: Format) usize { - return switch (fmt) { - .fp32 => 4, - .fp16 => 2, - .bf16 => 2, - .gf16 => 2, - .ternary => 1, - }; -} - -/// Quantize single f32 value to target format (returns f32 for convenience) -pub fn quantizeValue(x: f32, fmt: Format) f32 { - return switch (fmt) { - .fp32 => x, - .fp16 => fp16ToF32(f32ToFp16(x)), - .bf16 => bf16ToF32(f32ToBf16(x)), - .gf16 => gf16ToF32(f32ToGf16(x)), - .ternary => ternaryToF32(f32ToTernary(x)), - }; -} - -// ═════════════════════════════════════════════════════════════════════ -// CNN Operations (2D Convolution + Max Pooling) -// ═════════════════════════════════════════════════════════════════════════════ - -/// 2D convolution: output[y,x,c] = sum over kernel weights -/// -/// Parameters: -/// - input: flattened input [H_in * W_in * C_in] (channel-major layout) -/// - weights: filter weights [C_out * C_in * K_h * K_w] -/// - bias: per-channel bias [C_out] -/// - output: pre-allocated output buffer [H_out * W_out * C_out] -/// - config: layer dimensions and kernel parameters -/// -/// Supports valid padding (padding = kernel_size / 2) -pub fn conv2d( - input: []const f32, - weights: []const f32, - bias: []const f32, - output: []f32, - config: struct { - in_channels: u32, - out_channels: u32, - in_height: u32, - in_width: u32, - kernel_size: u32, - stride: u32, - padding: u32, - }, -) void { - const k = config.kernel_size; - const p = config.padding; - const s = config.stride; - - // Output dimensions with valid padding - const out_h = (config.in_height + 2 * p - k) / s + 1; - const out_w = (config.in_width + 2 * p - k) / s + 1; - - const in_area = config.in_height * config.in_width; - - // For each output channel - for (0..config.out_channels) |oc| { - const bias_val = bias[oc]; - const out_offset = oc * out_h * out_w; - - // For each output position - for (0..out_h) |oy| { - for (0..out_w) |ox| { - var sum: f32 = bias_val; - - // For each input channel - for (0..config.in_channels) |ic| { - // For each kernel position - for (0..k) |ky| { - for (0..k) |kx| { - // Input position - const in_y = oy * s + ky - p; - const in_x = ox * s + kx - p; - - if (in_y >= 0 and in_y < config.in_height and - in_x >= 0 and in_x < config.in_width) - { - const in_idx = ic * in_area + in_y * config.in_width + in_x; - sum += input[in_idx] * weights[oc * config.in_channels * k * k + ic * k * k + ky * k + kx]; - } - } - } - } - - output[out_offset + oy * out_w + ox] = sum; - } - } - } -} - -/// 2D max pooling: output[y,x,c] = max over kernel window -/// -/// Parameters: -/// - input: [H_in * W_in * C_in] -/// - output: pre-allocated output buffer [H_out * W_out * C_in] -/// - config: input dimensions and pooling parameters -pub fn maxPool2d( - input: []const f32, - output: []f32, - config: struct { - height: u32, - width: u32, - channels: u32, - kernel_size: u32, - stride: u32, - }, -) void { - const k = config.kernel_size; - const s = config.stride; - - const out_h = config.height / s; - const out_w = config.width / s; - const in_area = config.height * config.width; - - // For each channel - for (0..config.channels) |c| { - const out_offset = c * out_h * out_w; - - // For each output position - for (0..out_h) |oy| { - for (0..out_w) |ox| { - // Find max in kernel window - var max_val: f32 = -std.math.inf(f32); - for (0..k) |ky| { - const in_y = oy * s + ky; - if (in_y < config.height) { - for (0..k) |kx| { - const in_x = ox * s + kx; - if (in_x < config.width) { - const in_idx = c * in_area + in_y * config.width + in_x; - max_val = @max(max_val, input[in_idx]); - } - } - } - } - - output[out_offset + oy * out_w + ox] = max_val; - } - } - } -} - -// ═══════════════════════════════════════════════════════════════════ -// Trained MLP Weights Loader -// ═══════════════════════════════════════════════════════════════════ - -/// Trained MLP weights loaded from binary file -pub const MlpWeights = struct { - input_dim: u32, - hidden_dim: u32, - output_dim: u32, - - W1: []f32, // hidden_dim * input_dim, row-major - b1: []f32, // hidden_dim - W2: []f32, // output_dim * hidden_dim, row-major - b2: []f32, // output_dim - - allocator: std.mem.Allocator, - - /// Free all allocated arrays - pub fn deinit(self: *const MlpWeights) void { - self.allocator.free(self.W1); - self.allocator.free(self.b1); - self.allocator.free(self.W2); - self.allocator.free(self.b2); - } -}; - -/// Error set for weight loading -pub const LoadWeightsError = error{ - BadMagic, - UnsupportedVersion, - DimensionMismatch, - InvalidFileSize, -}; - -/// Load trained MLP weights from binary file -/// -/// File format (little-endian): -/// - Header (20 bytes): -/// - u32 magic = 0x4D4E4953 ("MNIS") -/// - u32 version = 1 -/// - u32 input_dim -/// - u32 hidden_dim -/// - u32 output_dim -/// - Data (all f32, little-endian): -/// - W1: hidden_dim * input_dim values (row-major) -/// - b1: hidden_dim values -/// - W2: output_dim * hidden_dim values (row-major) -/// - b2: output_dim values -pub fn loadMlpWeights( - allocator: std.mem.Allocator, - path: []const u8, -) !MlpWeights { - const file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); - - const file_size = try file.getEndPos(); - if (file_size < 20) return error.InvalidFileSize; - - // Read header (20 bytes) - var header: [20]u8 = undefined; - _ = try file.readAll(&header); - const magic = std.mem.readInt(u32, header[0..4], .little); - if (magic != 0x4D4E4953) return LoadWeightsError.BadMagic; - - const version = std.mem.readInt(u32, header[4..8], .little); - if (version != 1) return LoadWeightsError.UnsupportedVersion; - - const input_dim = std.mem.readInt(u32, header[8..12], .little); - const hidden_dim = std.mem.readInt(u32, header[12..16], .little); - const output_dim = std.mem.readInt(u32, header[16..20], .little); - - // Calculate sizes - const w1_len = @as(usize, hidden_dim) * @as(usize, input_dim); - const b1_len = @as(usize, hidden_dim); - const w2_len = @as(usize, output_dim) * @as(usize, hidden_dim); - const b2_len = @as(usize, output_dim); - - // Verify file size matches expected - const expected_size = 20 + (w1_len + b1_len + w2_len + b2_len) * 4; - if (file_size != expected_size) return error.InvalidFileSize; - - // Allocate arrays - const W1 = try allocator.alloc(f32, w1_len); - errdefer allocator.free(W1); - const b1 = try allocator.alloc(f32, b1_len); - errdefer allocator.free(b1); - const W2 = try allocator.alloc(f32, w2_len); - errdefer allocator.free(W2); - const b2 = try allocator.alloc(f32, b2_len); - errdefer allocator.free(b2); - - // Read tensor data directly into arrays - var data_offset: usize = 20; - { - const w1_bytes = std.mem.sliceAsBytes(W1); - const n = try file.read(w1_bytes); - if (n != w1_len * 4) return error.InvalidFileSize; - data_offset += n; - } - { - const b1_bytes = std.mem.sliceAsBytes(b1); - const n = try file.read(b1_bytes); - if (n != b1_len * 4) return error.InvalidFileSize; - data_offset += n; - } - { - const w2_bytes = std.mem.sliceAsBytes(W2); - const n = try file.read(w2_bytes); - if (n != w2_len * 4) return error.InvalidFileSize; - data_offset += n; - } - { - const b2_bytes = std.mem.sliceAsBytes(b2); - _ = try file.readAll(b2_bytes); - } - - return MlpWeights{ - .input_dim = input_dim, - .hidden_dim = hidden_dim, - .output_dim = output_dim, - .W1 = W1, - .b1 = b1, - .W2 = W2, - .b2 = b2, - .allocator = allocator, - }; -} - -// ═══════════════════════════════════════════════════════════════════ -// Tests -// ═══════════════════════════════════════════════════════════════════ - -test "GF16: zero" { - try std.testing.expectEqual(@as(u16, 0), f32ToGf16(0.0)); - try std.testing.expectEqual(@as(u16, 0x8000), f32ToGf16(-0.0)); -} - -test "GF16: roundtrip zero" { - try std.testing.expectEqual(@as(f32, 0.0), gf16ToF32(f32ToGf16(0.0))); -} - -test "GF16: infinity" { - try std.testing.expectEqual(@as(u16, 0x7E00), f32ToGf16(std.math.inf(f32))); - try std.testing.expectEqual(@as(u16, 0xFE00), f32ToGf16(-std.math.inf(f32))); -} - -test "GF16: roundtrip small values" { - const values = [_]f32{ 1.0, -1.0, 0.5, -0.5, 2.0, -2.0, 0.1, -0.1, 1.5, -1.5 }; - for (values) |v| { - const gf16 = f32ToGf16(v); - const recovered = gf16ToF32(gf16); - // Allow some error due to quantization - const err = @abs(recovered - v); - try std.testing.expect(err < 0.01); - } -} - -test "GF16: bit masks correct" { - try std.testing.expectEqual(@as(u16, 0x8000), SignMask); - try std.testing.expectEqual(@as(u16, 0x7E00), ExpMask); - try std.testing.expectEqual(@as(u16, 0x01FF), MantMask); -} - -test "GF16: encode preserves sign" { - try std.testing.expect(f32ToGf16(1.0) & 0x8000 == 0); - try std.testing.expect(f32ToGf16(-1.0) & 0x8000 != 0); -} - -test "Ternary: quantization" { - try std.testing.expectEqual(@as(i8, 1), f32ToTernary(1.0)); - try std.testing.expectEqual(@as(i8, -1), f32ToTernary(-1.0)); - try std.testing.expectEqual(@as(i8, 0), f32ToTernary(0.3)); - try std.testing.expectEqual(@as(i8, 0), f32ToTernary(-0.3)); - try std.testing.expectEqual(@as(i8, 1), f32ToTernary(0.6)); -} - -test "formatBytes" { - try std.testing.expectEqual(@as(usize, 4), formatBytes(.fp32)); - try std.testing.expectEqual(@as(usize, 2), formatBytes(.gf16)); - try std.testing.expectEqual(@as(usize, 1), formatBytes(.ternary)); -} - -test "BF16: roundtrip 1.0" { - const bf16 = f32ToBf16(1.0); - try std.testing.expectEqual(@as(u16, 0x3F80), bf16); - const back = bf16ToF32(bf16); - try std.testing.expectEqual(@as(f32, 1.0), back); -} - -test "BF16: roundtrip 100.0" { - const bf16 = f32ToBf16(100.0); - const back = bf16ToF32(bf16); - const err = @abs(back - 100.0); - try std.testing.expect(err < 1.0); -} - -test "BF16: roundtrip 1e10" { - const bf16 = f32ToBf16(1e10); - const back = bf16ToF32(bf16); - const err = @abs(back - 1e10) / 1e10; - try std.testing.expect(err < 0.01); -} - -test "BF16: roundtrip small values" { - const values = [_]f32{ 0.5, -0.5, 2.0, -2.0, 3.14, -3.14, 1e-10, -1e-10 }; - for (values) |v| { - const bf16 = f32ToBf16(v); - const back = bf16ToF32(bf16); - const err = if (@abs(v) > 0.001) @abs(back - v) / @abs(v) else @abs(back - v); - try std.testing.expect(err < 0.01); - } -} - -test "FP16: roundtrip basic values" { - const values = [_]f32{ 1.0, -1.0, 0.5, -0.5, 2.0, -2.0, 0.1, 0.25, 1.5 }; - for (values) |v| { - const fp16 = f32ToFp16(v); - const recovered = fp16ToF32(fp16); - const err = @abs(recovered - v) / @max(@abs(v), 1e-30); - try std.testing.expect(err < 0.005); - } -} - -test "FP16: special values" { - try std.testing.expectEqual(@as(u16, 0x0000), f32ToFp16(0.0)); - try std.testing.expectEqual(@as(u16, 0x8000), f32ToFp16(-0.0)); - try std.testing.expectEqual(@as(u16, 0x7C00), f32ToFp16(std.math.inf(f32))); - try std.testing.expectEqual(@as(u16, 0xFC00), f32ToFp16(-std.math.inf(f32))); - const nan_enc = f32ToFp16(std.math.nan(f32)); - try std.testing.expect(std.math.isNan(fp16ToF32(nan_enc))); -} - -test "FP16: large values (full IEEE exponent)" { - const fp16 = f32ToFp16(100.0); - const back = fp16ToF32(fp16); - try std.testing.expect(@abs(back - 100.0) < 1.0); - - const fp16_big = f32ToFp16(65000.0); - const back_big = fp16ToF32(fp16_big); - try std.testing.expect(back_big > 60000.0); - try std.testing.expect(back_big < 65536.0); -} - -test "FP16: overflow to infinity" { - const fp16 = f32ToFp16(1e10); - try std.testing.expectEqual(@as(u16, 0x7C00), fp16); - try std.testing.expect(std.math.isInf(fp16ToF32(fp16))); -} - -test "FP16: roundtrip 1.0 exact" { - const fp16 = f32ToFp16(1.0); - try std.testing.expectEqual(@as(u16, 0x3C00), fp16); - try std.testing.expectEqual(@as(f32, 1.0), fp16ToF32(fp16)); -} - -test "FP16: denormal roundtrip" { - const small = fp16ToF32(@as(u16, 0x0001)); - try std.testing.expect(small > 0.0); - try std.testing.expect(small < 0.001); -} - -test "BF16: special values" { - try std.testing.expectEqual(@as(u16, 0x3F80), f32ToBf16(1.0)); - try std.testing.expect(bf16ToF32(f32ToBf16(std.math.inf(f32))) > 1e30); - try std.testing.expect(std.math.isNan(bf16ToF32(f32ToBf16(std.math.nan(f32))))); - try std.testing.expectEqual(@as(u16, 0), f32ToBf16(0.0)); - try std.testing.expectEqual(@as(u16, 0x8000), f32ToBf16(-0.0)); - try std.testing.expectEqual(@as(u16, 0x7F80), f32ToBf16(std.math.inf(f32))); - try std.testing.expectEqual(@as(u16, 0xFF80), f32ToBf16(-std.math.inf(f32))); -} - -test "BF16: large values do not flush" { - const bf16_1e10 = f32ToBf16(1e10); - const back_1e10 = bf16ToF32(bf16_1e10); - try std.testing.expect(back_1e10 > 5e9); - try std.testing.expect(back_1e10 < 2e10); - - const bf16_1e_10 = f32ToBf16(1e-10); - const back_1e_10 = bf16ToF32(bf16_1e_10); - try std.testing.expect(back_1e_10 > 5e-11); - try std.testing.expect(back_1e_10 < 2e-9); -} - -test "BF16: quantizeValue roundtrip all formats" { - const test_val: f32 = 42.0; - const gf16_round = quantizeValue(test_val, .gf16); - const bf16_round = quantizeValue(test_val, .bf16); - const fp16_round = quantizeValue(test_val, .fp16); - try std.testing.expect(@abs(gf16_round - test_val) / test_val < 0.05); - try std.testing.expect(@abs(bf16_round - test_val) / test_val < 0.05); - try std.testing.expect(@abs(fp16_round - test_val) / test_val < 0.05); -} - -test "FP16: subnormal decode mantissa=1" { - const bits: u16 = 0x0001; - const val = fp16ToF32(bits); - const expected: f32 = 5.960464e-8; - try std.testing.expectApproxEqAbs(expected, val, 1e-14); -} - -test "FP16: subnormal decode mantissa=2" { - const bits: u16 = 0x0002; - const val = fp16ToF32(bits); - const expected: f32 = 1.192093e-7; - try std.testing.expectApproxEqAbs(expected, val, 1e-14); -} - -test "FP16: subnormal decode mantissa=1023 (max)" { - const bits: u16 = 0x03FF; - const val = fp16ToF32(bits); - try std.testing.expect(val > 0.0); - try std.testing.expect(val < 6.1e-5); -} - -test "FP16: quantizeValue small values preserve sign" { - const pos = quantizeValue(0.003, .fp16); - const neg = quantizeValue(-0.003, .fp16); - try std.testing.expect(pos > 0.0); - try std.testing.expect(neg < 0.0); -} - -test "FP16: subnormal roundtrip accuracy" { - const vals = [_]f32{ 1e-5, 5e-5, 1e-4, 5e-4 }; - for (vals) |v| { - const q = quantizeValue(v, .fp16); - const rel_err = @abs(q - v) / v; - try std.testing.expect(rel_err < 0.1); - } -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig deleted file mode 100644 index 0f12990..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf8.zig +++ /dev/null @@ -1,286 +0,0 @@ -//! GoldenFloat8 — φ-Optimized 8-bit Floating-Point Format -//! -//! Bit Layout: [sign:1][exp:3][mant:4] = 8 bits -//! Exponent bias: 7 -//! φ-optimal distribution: exp/mant ≈ 0.5714 (distance: 0.047) -//! -//! 8-bit = 2^3, so 3-bit exponent (values 0-7) is correct! - -const std = @import("std"); - -// ══════════════════════════════════════════════════════════════════════════════════════════ -// GF8 CONSTANTS -// ════════════════════════════════════════════════════════════════════════════════════════════ - -pub const SignMask: u8 = 0x80; -pub const ExpMask: u8 = 0x70; -pub const MantMask: u8 = 0x0F; - -pub const ExpShift: u3 = 4; -pub const SignShift: u3 = 7; -pub const Bias: i8 = 7; -pub const ExpBits: u8 = 3; -pub const MantBits: u8 = 4; - -// ════════════════════════════════════════════════════════════════════════════════════════════════════════ -// GF8 TYPE DEFINITION -// ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════ - -pub const GF8 = packed struct(u8) { - /// Mantissa (4 bits) - mant: u4, - - /// Exponent (3 bits, bias 7) - values 0-7 (stored in 3 bits) - exp: u3, - - /// Sign bit - sign: u1, -}; - -// ════════════════════════════════════════════════════════════════════════════════════════════════════════ -// GF8 ZERO CONSTANT -// ═══════════════════════════════════════════════════════════════════════════════════════════════════════ - -pub const GF8_ZERO: GF8 = .{ - .mant = 0, - .exp = 0, - .sign = 0, -}; - -pub const GF8_NEG_ZERO: GF8 = .{ - .mant = 0, - .exp = 0, - .sign = 1, -}; - -// ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ -// GF8 CONSTRUCTION -// ═══════════════════════════════════════════════════════════════════════════════════════════════════════ - -pub inline fn fromF32(x: f32) GF8 { - if (x == 0.0) return GF8_ZERO; - if (x < 0.0) return encodeNegative(x); - return encodePositive(x); -} - -pub inline fn toF32(g: GF8) f32 { - if (g == GF8_ZERO or g == GF8_NEG_ZERO) { - return 0.0; - } - - // Exponent bias: 7, range [0, 7] (unbiased: [-7, 0]) - const exp_biased: i32 = @as(i32, g.exp); - - // Denormals: exp = 0 (biased), mant != 0 - if (exp_biased == 0 and g.mant != 0) { - // value = mant/16 * 2^(1 - bias) = mant/16 * 2^(-6) - const denorm = @as(f32, @floatFromInt(g.mant)) / 16.0; - const value = denorm * @as(f32, std.math.pow(f32, 2.0, 1 - Bias)); - return if (g.sign == 0) value else -value; - } - - // Normal: value = (1 + mant/16) * 2^(exp_biased - bias) - const exp_unbiased = exp_biased - Bias; - const mant_scaled: f32 = 1.0 + @as(f32, @floatFromInt(g.mant)) / 16.0; - const value = mant_scaled * std.math.pow(f32, 2.0, @floatFromInt(exp_unbiased)); - return if (g.sign == 0) value else -value; -} - -pub inline fn add(a: GF8, b: GF8) GF8 { - return fromF32(toF32(a) + toF32(b)); -} - -pub inline fn sub(a: GF8, b: GF8) GF8 { - return fromF32(toF32(a) - toF32(b)); -} - -pub inline fn mul(a: GF8, b: GF8) GF8 { - return fromF32(toF32(a) * toF32(b)); -} - -pub inline fn div(a: GF8, b: GF8) GF8 { - return fromF32(toF32(a) / toF32(b)); -} - -pub inline fn fma(a: GF8, b: GF8, c: GF8) GF8 { - // FMA with f32 intermediate, then quantize back to GF8 - const ab = toF32(a) * toF32(b); - const result = ab + toF32(c); - return fromF32(result); -} - -pub inline fn sqrt(a: GF8) GF8 { - if (a.sign == 1) { - return GF8{ .mant = 0, .exp = 7, .sign = 1 }; - } - const abs_v = toF32(a); - if (abs_v <= 0.0) { - return GF8{ .mant = 0, .exp = 0, .sign = 0 }; - } - return fromF32(std.math.sqrt(abs_v)); -} - -pub inline fn abs(a: GF8) GF8 { - return .{ - .mant = a.mant, - .exp = a.exp, - .sign = 0, - }; -} - -pub inline fn neg(a: GF8) GF8 { - return .{ - .mant = a.mant, - .exp = a.exp, - .sign = 1 - a.sign, - }; -} - -pub inline fn eq(a: GF8, b: GF8) bool { - return a.mant == b.mant and a.exp == b.exp and a.sign == b.sign; -} - -pub inline fn ne(a: GF8, b: GF8) bool { - return !eq(a, b); -} - -pub inline fn lt(a: GF8, b: GF8) bool { - if (a.sign != b.sign) return (a.sign < b.sign); - if (a.exp != b.exp) return (a.exp < b.exp); - return a.mant < b.mant; -} - -pub inline fn le(a: GF8, b: GF8) bool { - if (a.sign != b.sign) return (a.sign < b.sign); - if (a.exp != b.exp) return (a.exp < b.exp); - return a.mant <= b.mant; -} - -pub inline fn gt(a: GF8, b: GF8) bool { - return !le(a, b); -} - -pub inline fn ge(a: GF8, b: GF8) bool { - return !lt(a, b); -} - -// ════════════════════════════════════════════════════════════════════════════════════════════════════════════ -// HELPER FUNCTIONS (Internal) -// ═══════════════════════════════════════════════════════════════════════════════════════════════════ - -fn encodePositive(x: f32) GF8 { - if (x == 0.0) return GF8_ZERO; - if (!std.math.isFinite(x)) { - return GF8{ .mant = 0, .exp = 7, .sign = 0 }; - } - - // GF8 max value: (1 + 15/16) * 2^0 = 1.9375 - // Clamp input to valid range - var x_clamped = x; - if (x > 1.9375) x_clamped = 1.9375; - - const frexp = std.math.frexp(x_clamped); - const m = frexp.significand * 2.0; - var e = frexp.exponent - 1; - - // Clamp exponent to valid range for GF8 - // exp_biased = e + Bias, and exp_biased must be in [1, 7] for normals - // So e must be in [-6, 0] - if (e < -6) { - e = -6; // Subnormal range - } else if (e > 0) { - e = 0; // Max normal (exp_biased = 7) - } - - // Round mantissa to 4 bits - const mant_f = (m - 1.0) * 16.0; - var mant_i: u4 = @intFromFloat(std.math.round(mant_f)); - if (mant_i == 16) { - mant_i = 15; // Clamp mantissa to max - } - - // Clamp final exp_biased to [0, 7] - var exp_biased = e + Bias; - if (exp_biased > 7) exp_biased = 7; - if (exp_biased < 0) exp_biased = 0; - - return GF8{ - .mant = mant_i, - .exp = @intCast(exp_biased), - .sign = 0, - }; -} - -fn encodeNegative(x: f32) GF8 { - const abs_x = -x; - const gf8_abs = encodePositive(abs_x); - return GF8{ - .mant = gf8_abs.mant, - .exp = gf8_abs.exp, - .sign = 1, - }; -} - -test "GF8: zero" { - try std.testing.expectEqual(@as(u8, @bitCast(GF8_ZERO)), 0); - try std.testing.expectEqual(toF32(GF8_ZERO), 0.0); -} - -test "GF8: one" { - const one = fromF32(1.0); - // 1.0: sign=0, exp=7 (biased), mant=0 → 0 111 0000 = 0x70 - try std.testing.expectEqual(@as(u8, @bitCast(one)), 0x70); - try std.testing.expectApproxEqRel(toF32(one), 1.0, 0.05); -} - -test "GF8: roundtrip positive" { - // Test values within GF8 representable range: [~0.0078, 1.9375] - const values = [_]f32{ 0.0, 0.01, 0.1, 0.5, 0.75, 1.0, 1.5, 1.9375 }; - for (values) |v| { - const gf8 = fromF32(v); - const back = toF32(gf8); - const err = @abs(back - v) / @max(@abs(v), 1.0); - try std.testing.expect(err < 0.1); // 10% error tolerance for values in range - } -} - -test "GF8: roundtrip negative" { - // Test values within GF8 representable range - const values = [_]f32{ -0.01, -0.1, -0.5, -0.75, -1.0, -1.5, -1.9375 }; - for (values) |v| { - const gf8 = fromF32(v); - const back = toF32(gf8); - const err = @abs(back - v) / @max(@abs(v), 1.0); - try std.testing.expect(err < 0.1); // 10% error tolerance for values in range - } -} - -test "GF8: clamping out of range" { - // Test that values > 1.9375 are clamped - const big = fromF32(10.0); - const back = toF32(big); - // Should be clamped to max value ~1.9375 - try std.testing.expect(back <= 2.0); -} - -test "GF8: sign bit" { - const pos = fromF32(1.0); - const neg_val = fromF32(-1.0); - try std.testing.expect(pos.sign == 0); - try std.testing.expect(neg_val.sign == 1); -} - -test "GF8: mantissa precision" { - // Test that 4-bit mantissa gives ~6% precision - const gf8 = fromF32(1.0 + 1.0/16.0); // 1.0625 - const back = toF32(gf8); - const relative_err = @abs(back - 1.0625) / 1.0625; - try std.testing.expect(relative_err < 0.2); // Allow ~20% error for 4-bit mantissa -} - -test "GF8: exponent range" { - const min_val = toF32(GF8{ .mant = 1, .exp = 0, .sign = 0 }); - const max_val = toF32(GF8{ .mant = MantMask, .exp = 7, .sign = 0 }); - try std.testing.expect(min_val > 0.0); // Smallest normal > 0 - try std.testing.expect(max_val < 15.0); // Max normal with max mantissa -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig deleted file mode 100644 index 4a80b4b..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gf_binary.zig +++ /dev/null @@ -1,299 +0,0 @@ -//! GF binary-exponent ladder — the whole φ-sized rung family from one rule. -//! -//! Every binary GoldenFloat rung is sized by ONE normative rule (FORMAT-SPEC-001 v1.2): -//! -//! e = round((N - 1) / φ²), m = N - 1 - e, bias = 2^(e-1) - 1, exp_max = 2^e - 1 -//! -//! so the exp:mantissa split tracks 1/φ ≈ 0.618 at every width. This factory derives -//! the rungs the README documents: GF4 / GF8 / GF12 / GF16 / GF20 / GF24 / GF32. -//! (GF8 and GF16 also have dedicated, φ-FMA-rich implementations in -//! golden_float16.zig / formats_root.zig; those stay the production entry points — -//! this module completes the *ladder* in code and is the reference for the other rungs.) -//! -//! Value (normal): (-1)^sign * (1 + M / 2^m) * 2^(e - bias) -//! Specials: e = 0 -> zero (mantissa 0) / flush subnormals to 0 -//! e = exp_max -> Inf (mantissa 0) / NaN (mantissa != 0) -//! -//! **Usage:** -//! ```zig -//! const gfb = @import("gf_binary.zig"); -//! const x = gfb.GF12.fromF32(3.14159); -//! std.debug.print("{d}\n", .{x.toF32()}); -//! const Custom = gfb.GF(48); // any width the rule accepts -//! ``` -//! -//! phi^2 + 1/phi^2 = 3 | TRINITY - -const std = @import("std"); - -const PHI: f64 = 1.6180339887498948482; -const PHI_SQ: f64 = PHI * PHI; // ≈ 2.618033988749895 - -/// Build a binary GF rung of `bits` total width using the φ² sizing rule. -pub fn GF(comptime bits: comptime_int) type { - comptime { - if (bits < 4) @compileError("GF requires at least 4 bits (1 sign + >=1 exp + >=1 mantissa)"); - } - const e_bits: comptime_int = @intFromFloat(@round(@as(f64, bits - 1) / PHI_SQ)); - const m_bits: comptime_int = bits - 1 - e_bits; - comptime { - if (e_bits < 1 or m_bits < 1) @compileError("degenerate rung: exp or mantissa < 1 bit"); - } - const bias_c: comptime_int = (1 << (e_bits - 1)) - 1; - const exp_max_c: comptime_int = (1 << e_bits) - 1; - - const Mant = std.meta.Int(.unsigned, m_bits); - const Exp = std.meta.Int(.unsigned, e_bits); - const ReprInt = std.meta.Int(.unsigned, bits); - - return packed struct(ReprInt) { - mant: Mant, - exp: Exp, - sign: u1, - - const Self = @This(); - - pub const BITS: u32 = bits; - pub const EXP_BITS: u32 = e_bits; - pub const MANT_BITS: u32 = m_bits; - pub const BIAS: u32 = bias_c; - pub const EXP_MAX: u32 = exp_max_c; // reserved Inf/NaN exponent - pub const Repr = ReprInt; - - const MANT_SCALE: f32 = @floatFromInt(@as(u64, 1) << m_bits); - const MANT_LIMIT: i64 = @as(i64, 1) << m_bits; - // largest finite unbiased exponent is (exp_max-1) - bias. - const MAX_E: i32 = @as(i32, exp_max_c - 1) - @as(i32, bias_c); - // smallest normal unbiased exponent is 1 - bias (exp field 1). - const MIN_E: i32 = 1 - @as(i32, bias_c); - - /// Encode f32 (round-to-nearest, saturate to Inf, flush subnormals to zero). - pub fn fromF32(v: f32) Self { - if (v == 0.0) return .{ .mant = 0, .exp = 0, .sign = @intFromBool(std.math.signbit(v)) }; - if (std.math.isNan(v)) return .{ .mant = 1, .exp = @intCast(exp_max_c), .sign = 0 }; - if (std.math.isInf(v)) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = @intFromBool(v < 0) }; - - const sign: u1 = @intFromBool(v < 0); - var f: f32 = @abs(v); - var e: i32 = 0; - while (f >= 2.0) : (e += 1) f /= 2.0; - while (f < 1.0) : (e -= 1) f *= 2.0; - - if (e > MAX_E) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = sign }; // -> Inf - if (e < MIN_E) return .{ .mant = 0, .exp = 0, .sign = sign }; // flush to zero (no subnormals) - - var mant: i64 = @intFromFloat(std.math.round((f - 1.0) * MANT_SCALE)); - var exp_field: i32 = e + @as(i32, bias_c); - if (mant >= MANT_LIMIT) { // significand rounded to 2.0 -> carry - mant = 0; - exp_field += 1; - if (exp_field >= @as(i32, exp_max_c)) return .{ .mant = 0, .exp = @intCast(exp_max_c), .sign = sign }; - } - return .{ .mant = @intCast(mant), .exp = @intCast(exp_field), .sign = sign }; - } - - /// Decode back to f32. - pub fn toF32(self: Self) f32 { - if (self.exp == exp_max_c) { - if (self.mant == 0) return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); - return std.math.nan(f32); - } - if (self.exp == 0) return if (self.sign == 1) -0.0 else 0.0; // zero (subnormals flushed) - const e: i32 = @as(i32, self.exp) - @as(i32, bias_c); - const f: f32 = 1.0 + @as(f32, @floatFromInt(self.mant)) / MANT_SCALE; - const val = f * std.math.exp2(@as(f32, @floatFromInt(e))); - return if (self.sign == 1) -val else val; - } - - pub fn isFinite(self: Self) bool { - return self.exp != exp_max_c; - } - pub fn bits_(self: Self) ReprInt { - return @bitCast(self); - } - pub fn fromBits(b: ReprInt) Self { - return @bitCast(b); - } - pub fn zero() Self { - return .{ .mant = 0, .exp = 0, .sign = 0 }; - } - pub fn one() Self { - return fromF32(1.0); - } - pub fn neg(self: Self) Self { - return .{ .mant = self.mant, .exp = self.exp, .sign = self.sign ^ 1 }; - } - pub fn abs(self: Self) Self { - return .{ .mant = self.mant, .exp = self.exp, .sign = 0 }; - } - pub fn add(a: Self, b: Self) Self { - return fromF32(a.toF32() + b.toF32()); - } - pub fn sub(a: Self, b: Self) Self { - return fromF32(a.toF32() - b.toF32()); - } - pub fn mul(a: Self, b: Self) Self { - return fromF32(a.toF32() * b.toF32()); - } - pub fn div(a: Self, b: Self) Self { - return fromF32(a.toF32() / b.toF32()); - } - }; -} - -/// The φ-sized binary rungs the README documents. -pub const GF4 = GF(4); -pub const GF8 = GF(8); -pub const GF12 = GF(12); -pub const GF16 = GF(16); -pub const GF20 = GF(20); -pub const GF24 = GF(24); -pub const GF32 = GF(32); - -// ═══════════════════════════════════════════════════════════════════ -// Tests -// ═══════════════════════════════════════════════════════════════════ - -test "GF ladder rule matches the SSOT catalog (e / m / bias)" { - try std.testing.expectEqual(@as(u32, 1), GF4.EXP_BITS); - try std.testing.expectEqual(@as(u32, 2), GF4.MANT_BITS); - try std.testing.expectEqual(@as(u32, 0), GF4.BIAS); - try std.testing.expectEqual(@as(u32, 3), GF8.EXP_BITS); - try std.testing.expectEqual(@as(u32, 4), GF8.MANT_BITS); - try std.testing.expectEqual(@as(u32, 3), GF8.BIAS); - try std.testing.expectEqual(@as(u32, 4), GF12.EXP_BITS); - try std.testing.expectEqual(@as(u32, 7), GF12.MANT_BITS); - try std.testing.expectEqual(@as(u32, 7), GF12.BIAS); - try std.testing.expectEqual(@as(u32, 6), GF16.EXP_BITS); - try std.testing.expectEqual(@as(u32, 9), GF16.MANT_BITS); - try std.testing.expectEqual(@as(u32, 31), GF16.BIAS); - try std.testing.expectEqual(@as(u32, 7), GF20.EXP_BITS); - try std.testing.expectEqual(@as(u32, 12), GF20.MANT_BITS); - try std.testing.expectEqual(@as(u32, 63), GF20.BIAS); - try std.testing.expectEqual(@as(u32, 9), GF24.EXP_BITS); - try std.testing.expectEqual(@as(u32, 14), GF24.MANT_BITS); - try std.testing.expectEqual(@as(u32, 255), GF24.BIAS); - try std.testing.expectEqual(@as(u32, 12), GF32.EXP_BITS); - try std.testing.expectEqual(@as(u32, 19), GF32.MANT_BITS); - try std.testing.expectEqual(@as(u32, 2047), GF32.BIAS); -} - -test "GF ladder packed struct widths" { - inline for (.{ GF4, GF8, GF12, GF16, GF20, GF24, GF32 }, .{ 4, 8, 12, 16, 20, 24, 32 }) |T, n| { - try std.testing.expectEqual(@as(u32, n), T.BITS); - } -} - -fn checkRoundtrip(comptime T: type, values: []const f32, tol: f32) !void { - for (values) |v| { - const q = T.fromF32(v).toF32(); - const err = @abs(q - v) / (@abs(v) + 1e-9); - try std.testing.expect(err <= tol); - } -} - -test "GF16 roundtrip (9-bit mantissa)" { - const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.14159, -3.14159, 100.0, 0.001, 12345.0 }; - try checkRoundtrip(GF16, &vals, 0.005); -} - -test "GF32 roundtrip (19-bit mantissa, wide range)" { - const vals = [_]f32{ 1.0, 3.14159, 1e30, -1e30, 1e-30, 6.022e23, 1e-9 }; - try checkRoundtrip(GF32, &vals, 1e-4); -} - -test "GF8 roundtrip (small, 4-bit mantissa)" { - const vals = [_]f32{ 1.0, -1.0, 1.5, 2.0, 0.5, 0.25 }; - try checkRoundtrip(GF8, &vals, 0.05); -} - -test "GF specials: Inf / NaN / zero across the ladder" { - inline for (.{ GF4, GF8, GF12, GF16, GF20, GF24, GF32 }) |T| { - try std.testing.expect(std.math.isInf(T.fromF32(std.math.inf(f32)).toF32())); - try std.testing.expect(T.fromF32(-std.math.inf(f32)).toF32() < 0); - try std.testing.expect(std.math.isNan(T.fromF32(std.math.nan(f32)).toF32())); - try std.testing.expectEqual(@as(f32, 0.0), T.zero().toF32()); - try std.testing.expect(!T.fromF32(std.math.inf(f32)).isFinite()); - } -} - -test "GF wider rungs strictly extend range (GF16 overflows where GF32 holds)" { - try std.testing.expect(std.math.isInf(GF16.fromF32(1e30).toF32())); // GF16 max ~2^31 - try std.testing.expect(GF32.fromF32(1e30).isFinite()); // GF32 max ~2^2047 -} - -test "GF arithmetic (GF16)" { - const a = GF16.fromF32(1.5); - const b = GF16.fromF32(2.5); - try std.testing.expectApproxEqAbs(@as(f32, 4.0), a.add(b).toF32(), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 3.75), a.mul(b).toF32(), 0.02); -} - -test "GF bits roundtrip" { - const x = GF20.fromF32(-6.28); - try std.testing.expectEqual(x.bits_(), GF20.fromBits(x.bits_()).bits_()); -} - -// Exact-bit golden vectors — pin the encoding, not just an approximate round-trip. -// Tolerance tests are blind to a systematic layout shift (e.g. a wrong exponent bias -// still round-trips symmetrically); these catch it. Values are exact in a 4-bit -// mantissa and inside GF8's tight range. Hand-check: GF8(-2.5) = sign 1, |2.5| = -// 1.25·2^1 -> exp field bias+1 = 4 = 0b100, mant 0.25·16 = 4 = 0b0100 -> 0b1_100_0100 -// = 0xC4. A deliberate codec change must update these on purpose. -test "GF ladder: exact-bit golden vectors (encoding regression guard)" { - const E = std.testing.expectEqual; - // gf8 [1:3:4] b3 - try E(@as(GF8.Repr, 0x30), GF8.fromF32(1.0).bits_()); - try E(@as(GF8.Repr, 0x38), GF8.fromF32(1.5).bits_()); - try E(@as(GF8.Repr, 0x40), GF8.fromF32(2.0).bits_()); - try E(@as(GF8.Repr, 0xC4), GF8.fromF32(-2.5).bits_()); - // gf12 [1:4:7] b7 - try E(@as(GF12.Repr, 0x380), GF12.fromF32(1.0).bits_()); - try E(@as(GF12.Repr, 0x3C0), GF12.fromF32(1.5).bits_()); - try E(@as(GF12.Repr, 0x400), GF12.fromF32(2.0).bits_()); - try E(@as(GF12.Repr, 0xC20), GF12.fromF32(-2.5).bits_()); - // gf16 [1:6:9] b31 — the primary production rung (shared with golden_float16.GF16) - try E(@as(GF16.Repr, 0x3E00), GF16.fromF32(1.0).bits_()); - try E(@as(GF16.Repr, 0x3F00), GF16.fromF32(1.5).bits_()); - try E(@as(GF16.Repr, 0x4000), GF16.fromF32(2.0).bits_()); - try E(@as(GF16.Repr, 0xC080), GF16.fromF32(-2.5).bits_()); - // gf20 [1:7:12] b63 - try E(@as(GF20.Repr, 0x3F000), GF20.fromF32(1.0).bits_()); - try E(@as(GF20.Repr, 0x3F800), GF20.fromF32(1.5).bits_()); - try E(@as(GF20.Repr, 0x40000), GF20.fromF32(2.0).bits_()); - try E(@as(GF20.Repr, 0xC0400), GF20.fromF32(-2.5).bits_()); - // gf24 [1:9:14] b255 - try E(@as(GF24.Repr, 0x3FC000), GF24.fromF32(1.0).bits_()); - try E(@as(GF24.Repr, 0x3FE000), GF24.fromF32(1.5).bits_()); - try E(@as(GF24.Repr, 0x400000), GF24.fromF32(2.0).bits_()); - try E(@as(GF24.Repr, 0xC01000), GF24.fromF32(-2.5).bits_()); - // gf32 [1:12:19] b2047 - try E(@as(GF32.Repr, 0x3FF80000), GF32.fromF32(1.0).bits_()); - try E(@as(GF32.Repr, 0x3FFC0000), GF32.fromF32(1.5).bits_()); - try E(@as(GF32.Repr, 0x40000000), GF32.fromF32(2.0).bits_()); - try E(@as(GF32.Repr, 0xC0020000), GF32.fromF32(-2.5).bits_()); -} - -// Normative-rule conformance — machine-check that every rung's factory constants satisfy -// the ONE sizing rule the spec encodes (FORMAT-SPEC-001): -// e = round((N-1)/φ²), m = N-1-e, bias = 2^(e-1)-1, exp_max = 2^e-1 -// Re-derived here independently of the factory, so a future edit to GF() that drifts from -// the rule fails loudly. This is the class of guard that was missing when GF8 carried a -// wrong bias (spec bias=7 vs canonical=3, #84). (A full spec<->.tri parse-time check is a -// separate effort — tri_reader lives outside this module's import path.) -test "GF ladder: factory constants obey the normative φ² rule" { - const rungs = [_]u32{ 8, 12, 16, 20, 24, 32 }; - inline for (rungs) |N| { - const T = GF(N); - const e: u32 = @intFromFloat(@round(@as(f64, N - 1) / PHI_SQ)); - const m: u32 = N - 1 - e; - const bias: u32 = (@as(u32, 1) << @intCast(e - 1)) - 1; - const exp_max: u32 = (@as(u32, 1) << @intCast(e)) - 1; - try std.testing.expectEqual(e, T.EXP_BITS); - try std.testing.expectEqual(m, T.MANT_BITS); - try std.testing.expectEqual(bias, T.BIAS); - try std.testing.expectEqual(exp_max, T.EXP_MAX); - try std.testing.expectEqual(@as(u32, N), T.BITS); - try std.testing.expectEqual(N, 1 + T.EXP_BITS + T.MANT_BITS); // fields tile the width - } -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig deleted file mode 100644 index 8921acb..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/gft.zig +++ /dev/null @@ -1,298 +0,0 @@ -//! GF-T — balanced-ternary-EXPONENT GoldenFloat ladder (GF-T4 / GF-T8 / GF-T16 / GF-T32). -//! -//! GF-T is the ternary-exponent sibling of the binary GF ladder. The exponent is a -//! balanced-ternary number (digits -1/0/+1) stored as an unsigned OFFSET in -//! `[0, 3^E - 1]`; the balanced exponent is `e = offset - EXP_OFFSET`. There is no -//! regime decode (unlike posit/tekum) and the mantissa keeps GF's uniform binary -//! precision. -//! -//! value = (-1)^sign * (1 + M / 2^m) * 2^e, e = offset - EXP_OFFSET -//! EXP_OFFSET = (3^E - 1) / 2 (balanced zero point; offset EXP_OFFSET => e = 0) -//! OFFSET_MAX = 3^E - 1 (reserved top row: Inf / NaN) -//! finite iff offset < OFFSET_MAX -//! -//! Rungs (authoritative — t27/specs/numeric/gft{4,8,16,32}.t27, see specs/gft.tri): -//! GF-T4 : E=2 trits, M=1 bit , EXP_OFFSET=4 , e in [-4 ,+3 ], ~2.4 decades -//! GF-T8 : E=3 trits, M=4 bits, EXP_OFFSET=13 , e in [-13 ,+12 ], ~8 decades -//! GF-T16 : E=4 trits, M=9 bits, EXP_OFFSET=40 , e in [-40 ,+39 ], ~24 decades -//! GF-T32 : E=6 trits, M=25 bits, EXP_OFFSET=364, e in [-364,+363], ~219 decades -//! -//! **Usage:** -//! ```zig -//! const gft = @import("gft.zig"); -//! const x = gft.GFT16.fromF32(3.14159); -//! const y = gft.GFT16.fromF32(2.71828); -//! const z = x.mul(y); -//! std.debug.print("{d}\n", .{z.toF32()}); // ~8.539 -//! ``` -//! -//! phi^2 + 1/phi^2 = 3 | TRINITY - -const std = @import("std"); - -/// Build a GF-T rung from its exponent-trit count and mantissa-bit count. -/// `exp_trits` and `mant_bits` fully determine the format; everything else -/// (offset range, bias, storage width) is derived at comptime. -pub fn GFT(comptime exp_trits: comptime_int, comptime mant_bits: comptime_int) type { - // 3^exp_trits - const pow3: comptime_int = blk: { - var p: comptime_int = 1; - var i: comptime_int = 0; - while (i < exp_trits) : (i += 1) p *= 3; - break :blk p; - }; - const offset_max: comptime_int = pow3 - 1; // reserved special row - const exp_offset: comptime_int = (pow3 - 1) / 2; // balanced zero point - - // Bits needed to hold an offset in [0, offset_max]: smallest b with 2^b >= pow3. - const exp_field_bits: comptime_int = blk: { - var b: comptime_int = 0; - while ((1 << b) < pow3) : (b += 1) {} - break :blk b; - }; - const total_bits: comptime_int = 1 + exp_field_bits + mant_bits; - - const Mant = std.meta.Int(.unsigned, mant_bits); - const Off = std.meta.Int(.unsigned, exp_field_bits); - const ReprInt = std.meta.Int(.unsigned, total_bits); - - return packed struct(ReprInt) { - // Field order is low-to-high: mantissa in the low bits, sign in the top bit - // (mirrors GF16's `[mant][exp][sign]` packed layout). - mant: Mant, - offset: Off, - sign: u1, - - const Self = @This(); - - pub const EXP_TRITS: u32 = exp_trits; - pub const MANT_BITS: u32 = mant_bits; - pub const EXP_OFFSET: u32 = exp_offset; - pub const OFFSET_MAX: u32 = offset_max; // reserved Inf/NaN row - pub const BITS: u32 = total_bits; - /// Underlying unsigned storage integer (use with `bits()` / `fromBits()`). - pub const Repr = ReprInt; - - const MAX_E: i32 = @as(i32, exp_offset) - 1; // max finite exponent (offset_max-1 - exp_offset) - const MIN_E: i32 = -@as(i32, exp_offset); // min finite exponent (offset 0) - const MANT_SCALE: f32 = @floatFromInt(@as(u64, 1) << mant_bits); - const MANT_LIMIT: i64 = @as(i64, 1) << mant_bits; - - /// Encode an f32 into this GF-T rung (round-to-nearest, saturate to Inf, - /// flush-to-zero on underflow). - pub fn fromF32(v: f32) Self { - if (v == 0.0) return .{ .mant = 0, .offset = 0, .sign = @intFromBool(std.math.signbit(v)) }; - if (std.math.isNan(v)) return .{ .mant = 1, .offset = @intCast(offset_max), .sign = 0 }; - if (std.math.isInf(v)) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = @intFromBool(v < 0) }; - - const sign: u1 = @intFromBool(v < 0); - var f: f32 = @abs(v); - var e: i32 = 0; - // Normalize the significand into [1, 2). - while (f >= 2.0) : (e += 1) f /= 2.0; - while (f < 1.0) : (e -= 1) f *= 2.0; - - if (e > MAX_E) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = sign }; // -> Inf - if (e < MIN_E) return .{ .mant = 0, .offset = 0, .sign = sign }; // underflow -> 0 - - var mant: i64 = @intFromFloat(std.math.round((f - 1.0) * MANT_SCALE)); - var off: i32 = e + @as(i32, exp_offset); - if (mant >= MANT_LIMIT) { // significand rounded up to 2.0 -> carry into exponent - mant = 0; - off += 1; - if (off >= @as(i32, offset_max)) return .{ .mant = 0, .offset = @intCast(offset_max), .sign = sign }; - } - return .{ .mant = @intCast(mant), .offset = @intCast(off), .sign = sign }; - } - - /// Decode this GF-T rung back to f32 (exact for the represented value). - pub fn toF32(self: Self) f32 { - if (self.offset == offset_max) { - if (self.mant == 0) return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); - return std.math.nan(f32); - } - if (self.offset == 0 and self.mant == 0) return if (self.sign == 1) -0.0 else 0.0; - const e: i32 = @as(i32, self.offset) - @as(i32, exp_offset); - const f: f32 = 1.0 + @as(f32, @floatFromInt(self.mant)) / MANT_SCALE; - const val = f * std.math.exp2(@as(f32, @floatFromInt(e))); - return if (self.sign == 1) -val else val; - } - - /// True unless this is the reserved Inf/NaN row. - pub fn isFinite(self: Self) bool { - return self.offset != offset_max; - } - - /// Raw storage bits (for serialization / FFI). - pub fn bits(self: Self) ReprInt { - return @bitCast(self); - } - /// Rebuild from raw storage bits. - pub fn fromBits(b: ReprInt) Self { - return @bitCast(b); - } - - pub fn zero() Self { - return .{ .mant = 0, .offset = 0, .sign = 0 }; - } - pub fn one() Self { - return fromF32(1.0); - } - pub fn neg(self: Self) Self { - return .{ .mant = self.mant, .offset = self.offset, .sign = self.sign ^ 1 }; - } - pub fn abs(self: Self) Self { - return .{ .mant = self.mant, .offset = self.offset, .sign = 0 }; - } - - // Arithmetic via f32 (exact-enough; the format is the storage, f32 is the ALU). - pub fn add(a: Self, b: Self) Self { - return fromF32(a.toF32() + b.toF32()); - } - pub fn sub(a: Self, b: Self) Self { - return fromF32(a.toF32() - b.toF32()); - } - pub fn mul(a: Self, b: Self) Self { - return fromF32(a.toF32() * b.toF32()); - } - pub fn div(a: Self, b: Self) Self { - return fromF32(a.toF32() / b.toF32()); - } - }; -} - -/// The four practical rungs of the GF-T ladder. -pub const GFT4 = GFT(2, 1); -pub const GFT8 = GFT(3, 4); -pub const GFT16 = GFT(4, 9); -pub const GFT32 = GFT(6, 25); - -// ═══════════════════════════════════════════════════════════════════ -// Tests -// ═══════════════════════════════════════════════════════════════════ - -test "GF-T constants match the authoritative ladder" { - try std.testing.expectEqual(@as(u32, 4), GFT4.EXP_OFFSET); - try std.testing.expectEqual(@as(u32, 8), GFT4.OFFSET_MAX); - try std.testing.expectEqual(@as(u32, 13), GFT8.EXP_OFFSET); - try std.testing.expectEqual(@as(u32, 26), GFT8.OFFSET_MAX); - try std.testing.expectEqual(@as(u32, 40), GFT16.EXP_OFFSET); - try std.testing.expectEqual(@as(u32, 80), GFT16.OFFSET_MAX); - try std.testing.expectEqual(@as(u32, 364), GFT32.EXP_OFFSET); - try std.testing.expectEqual(@as(u32, 728), GFT32.OFFSET_MAX); -} - -test "GF-T unity encodes to the balanced zero offset" { - const one16 = GFT16.fromF32(1.0); - try std.testing.expectEqual(@as(u32, 40), @as(u32, one16.offset)); // e = 0 - try std.testing.expectEqual(@as(u9, 0), one16.mant); - try std.testing.expectApproxEqAbs(@as(f32, 1.0), one16.toF32(), 1e-6); -} - -test "GF-T zero and negative zero" { - try std.testing.expectEqual(@as(f32, 0.0), GFT16.zero().toF32()); - try std.testing.expectEqual(@as(f32, 0.0), GFT16.fromF32(0.0).toF32()); - try std.testing.expect(std.math.signbit(GFT16.fromF32(-0.0).toF32())); -} - -fn checkRoundtrip(comptime T: type, values: []const f32, tol: f32) !void { - for (values) |v| { - const q = T.fromF32(v).toF32(); - const err = @abs(q - v) / (@abs(v) + 1e-9); - try std.testing.expect(err <= tol); - } -} - -test "GF-T16 roundtrip (9-bit mantissa, tight)" { - const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.14159, -3.14159, 100.0, 0.001, -0.001, 12345.0, 1e-9 }; - try checkRoundtrip(GFT16, &vals, 0.005); // < 0.5% for a 9-bit mantissa -} - -test "GF-T8 roundtrip (4-bit mantissa, looser)" { - const vals = [_]f32{ 1.0, -1.0, 0.5, 2.0, 3.0, -3.0, 50.0, 0.01 }; - try checkRoundtrip(GFT8, &vals, 0.05); // < 5% for a 4-bit mantissa -} - -test "GF-T4 roundtrip (1-bit mantissa, coarse)" { - const vals = [_]f32{ 1.0, -1.0, 1.5, 2.0, 4.0, 0.5, 0.25 }; - try checkRoundtrip(GFT4, &vals, 0.30); // 1-bit mantissa -> ~25% steps -} - -test "GF-T32 huge dynamic range" { - const vals = [_]f32{ 1e30, -1e30, 1e-30, 1e18, 1e-18, 6.022e23 }; - try checkRoundtrip(GFT32, &vals, 0.001); // 25-bit mantissa is very precise -} - -test "GF-T Inf and NaN roundtrip" { - inline for (.{ GFT4, GFT8, GFT16, GFT32 }) |T| { - try std.testing.expect(std.math.isInf(T.fromF32(std.math.inf(f32)).toF32())); - try std.testing.expect(T.fromF32(-std.math.inf(f32)).toF32() < 0); - try std.testing.expect(std.math.isNan(T.fromF32(std.math.nan(f32)).toF32())); - try std.testing.expect(!T.fromF32(std.math.inf(f32)).isFinite()); - try std.testing.expect(T.fromF32(1.0).isFinite()); - } -} - -test "GF-T overflow saturates to Inf, underflow flushes to zero" { - // GF-T16 max finite exponent is +39 (~5.5e11); 1e30 overflows. - try std.testing.expect(std.math.isInf(GFT16.fromF32(1e30).toF32())); - // ...and 1e-30 underflows below 2^-40. - try std.testing.expectEqual(@as(f32, 0.0), GFT16.fromF32(1e-30).toF32()); - // GF-T32 covers both. - try std.testing.expect(GFT32.fromF32(1e30).isFinite()); -} - -test "GF-T neg / abs" { - const x = GFT16.fromF32(3.5); - try std.testing.expectApproxEqAbs(@as(f32, -3.5), x.neg().toF32(), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 3.5), x.neg().abs().toF32(), 0.02); -} - -test "GF-T arithmetic (GF-T16)" { - const a = GFT16.fromF32(1.5); - const b = GFT16.fromF32(2.5); - try std.testing.expectApproxEqAbs(@as(f32, 4.0), a.add(b).toF32(), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 1.0), b.sub(a).toF32(), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 3.75), a.mul(b).toF32(), 0.02); - try std.testing.expectApproxEqAbs(@as(f32, 0.6), a.div(b).toF32(), 0.02); -} - -test "GF-T bits roundtrip" { - const x = GFT16.fromF32(-6.28); - const y = GFT16.fromBits(x.bits()); - try std.testing.expectEqual(x.bits(), y.bits()); - try std.testing.expectApproxEqAbs(x.toF32(), y.toF32(), 1e-9); -} - -test "GF-T storage widths (nominal name vs real bits)" { - // The nominal name (4/8/16/32) tags the GF lineage; ternary exponent + full - // mantissa need a wider container than the binary rung. - try std.testing.expectEqual(@as(u32, 6), GFT4.BITS); // 1 + 4 + 1 - try std.testing.expectEqual(@as(u32, 10), GFT8.BITS); // 1 + 5 + 4 - try std.testing.expectEqual(@as(u32, 17), GFT16.BITS); // 1 + 7 + 9 - try std.testing.expectEqual(@as(u32, 36), GFT32.BITS); // 1 + 10 + 25 -} - -// Exact-bit golden vectors — pin the ternary-exponent encoding. A tolerance-based -// round-trip is blind to an offset/bias shift; these pin it. Hand-check: -// GFT16(-2.5) = sign 1, |2.5| = 1.25·2^1 -> offset EXP_OFFSET+1 = 41, mant 0.25·512 = -// 128 = 0x80 -> (41<<9) | 0x80 | (1<<16) = 0x15280. Update deliberately if the codec -// layout changes. -test "GF-T: exact-bit golden vectors (encoding regression guard)" { - const E = std.testing.expectEqual; - // gft8 (E3 M4, offset 13) - try E(@as(GFT8.Repr, 0x0D0), GFT8.fromF32(1.0).bits()); - try E(@as(GFT8.Repr, 0x0E0), GFT8.fromF32(2.0).bits()); - try E(@as(GFT8.Repr, 0x2D0), GFT8.fromF32(-1.0).bits()); - try E(@as(GFT8.Repr, 0x2E4), GFT8.fromF32(-2.5).bits()); - // gft16 (E4 M9, offset 40) - try E(@as(GFT16.Repr, 0x05000), GFT16.fromF32(1.0).bits()); - try E(@as(GFT16.Repr, 0x05200), GFT16.fromF32(2.0).bits()); - try E(@as(GFT16.Repr, 0x15000), GFT16.fromF32(-1.0).bits()); - try E(@as(GFT16.Repr, 0x15280), GFT16.fromF32(-2.5).bits()); - // gft32 (E6 M25, offset 364) - try E(@as(GFT32.Repr, 0x2D8000000), GFT32.fromF32(1.0).bits()); - try E(@as(GFT32.Repr, 0x2DA000000), GFT32.fromF32(2.0).bits()); - try E(@as(GFT32.Repr, 0xAD8000000), GFT32.fromF32(-1.0).bits()); - try E(@as(GFT32.Repr, 0xADA800000), GFT32.fromF32(-2.5).bits()); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig deleted file mode 100644 index e793775..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/formats/golden_float16.zig +++ /dev/null @@ -1,463 +0,0 @@ -//! Trinity ML Formats — GF16 and TF3-9 (Consolidated) -//! -//! This module provides φ-optimized number formats for Trinity's HSLM (Hybrid Symbolic Language Model). -//! -//! **Formats:** -//! - GF16: Golden Float16 — φ-optimized 16-bit format [sign:1][exp:6][mant:9] -//! - TF3: Ternary Float3 — packed ternary [sign:1][exp:6][mant:11] (18 bits) -//! -//! **Mathematical Foundation:** -//! φ² + 1/φ² = 3 | TRINITY -//! where φ = (1 + √5) / 2 ≈ 1.6180339887498949 -//! -//! **Reference:** -//! - IBM DLFloat: https://research.ibm.com/publications/dlfloat-a-16-floating-point-format-designed-for-deep-learning-training-and-inference -//! -//! **Usage:** -//! ```zig -//! const std = @import("std"); -//! const golden = @import("golden_float16.zig"); -//! -//! const gf = golden.GF16.fromF32(3.14159); -//! const tf3 = golden.TF3.fromF32(2.71828); -//! ``` -//! - -const std = @import("std"); -const gf_binary = @import("gf_binary.zig"); - -// ═════════════════════════════════════════════════════════════════════════ -// TRINITY CONSTANTS -// ═════════════════════════════════════════════════════════════════════ - -/// Golden ratio φ = (1 + √5) / 2 -pub const PHI = 1.6180339887498948482; - -/// φ² = φ × φ -pub const PHI_SQ = PHI * PHI; - -/// 1/φ² -pub const PHI_INV_SQ = 1.0 / PHI_SQ; - -/// Trinity Identity: φ² + 1/φ² = 3 -pub const TRINITY = PHI_SQ + PHI_INV_SQ; - -// ═════════════════════════════════════════════════════════════════════ -// GF16: GOLDEN FLOAT16 -// ═════════════════════════════════════════════════════════════════════════ - -/// GF16: Golden Float16 — φ-optimized packed format -/// -/// **Bit Layout:** -/// ``` -/// ┌──────┬─────────┬─────────┐ -/// │ sign │ exp │ mant │ -/// │ 1bit │ 6bit │ 9bit │ -/// └──────┴─────────┴─────────┘ -/// ``` -/// -/// **Phi-optimal distribution** — Unlike IEEE 754 f16 [sign:1][exp:5][mant:10], -/// GF16 has phi-optimal bit distribution: [sign:1][exp:6][mant:9]. -/// -/// **Parameters:** -/// - Exponent bias: 31 (0x1F) -/// - Min positive: 2^(-31) ≈ 4.66e-10 -/// - Max value: ~2^31 × 1.999 ≈ 4.29e9 -/// - phi-distance: |exp/mant - 1/φ| ≈ 0.049 (close to φ-optimal) -/// -/// **Example:** -/// ```zig -/// const gf = GF16.fromF32(3.14159); -/// try std.testing.expectApproxEqAbs(3.14, gf.toF32(), 0.01); -/// ``` -pub const GF16 = packed struct(u16) { - /// Mantissa (9 bits) — φ-optimized precision - mant: u9, - - /// Exponent (6 bits, bias 31) - exp: u6, - - /// Sign bit (1 = negative) - sign: u1, - - /// phi-distance: measures how close bit distribution is to φ-optimal - /// Lower is better — GF16 achieves 0.049 (vs 0.082 for IEEE f16) - /// comptime calculation (0.049 for GF16) - // pub const phi_distance: comptime_float = @import("std").math.fabs(6.0 / 9.0 - 1.0 / PHI); - - /// Create GF16 from f32. - /// - /// Delegates to the single normative codec `gf_binary.GF16` (the φ²-sized - /// binary rung factory) so GF16 has exactly ONE encoding across the repo: - /// the standard `(1 + M/512)·2^(E−31)` significand with the full 9-bit - /// mantissa (FORMAT-SPEC-001, specs/gf16.tri). e.g. 1.0 → 0x3E00. - pub fn fromF32(v: f32) GF16 { - return @bitCast(gf_binary.GF16.fromF32(v).bits_()); - } - - /// Convert GF16 to f32 (via the same normative `gf_binary.GF16` codec). - pub fn toF32(self: GF16) f32 { - return gf_binary.GF16.fromBits(@bitCast(self)).toF32(); - } - - /// GF16 addition (via f32 for precision) - pub fn add(a: GF16, b: GF16) GF16 { - return fromF32(a.toF32() + b.toF32()); - } - - /// GF16 subtraction - pub fn sub(a: GF16, b: GF16) GF16 { - return fromF32(a.toF32() - b.toF32()); - } - - /// GF16 multiplication - pub fn mul(a: GF16, b: GF16) GF16 { - return fromF32(a.toF32() * b.toF32()); - } - - /// GF16 division - pub fn div(a: GF16, b: GF16) GF16 { - return fromF32(a.toF32() / b.toF32()); - } - - /// Zero GF16 - pub inline fn zero() GF16 { - return .{ .mant = 0, .exp = 0, .sign = 0 }; - } - - /// One GF16 - pub inline fn one() GF16 { - return fromF32(1.0); - } - - /// Negate GF16 - pub inline fn neg(self: GF16) GF16 { - return .{ - .mant = self.mant, - .exp = self.exp, - .sign = if (self.sign == 1) 0 else 1, - }; - } - - /// Absolute value - pub inline fn abs(self: GF16) GF16 { - return .{ - .mant = self.mant, - .exp = self.exp, - .sign = 0, - }; - } - - /// φ-weighted quantization for better distribution - pub fn phiQuantize(v: f32) GF16 { - return fromF32(v * PHI_INV_SQ); - } - - /// φ-weighted dequantization - pub fn phiDequantize(gf: GF16) f32 { - return gf.toF32() * PHI_SQ; - } - - /// φ-optimized fused multiply-add: dequantize(a)*dequantize(b) + dequantize(c), then φ-quantize - pub fn phiFma(a: GF16, b: GF16, c: GF16) GF16 { - const fa = phiDequantize(a); - const fb = phiDequantize(b); - const fc = phiDequantize(c); - return phiQuantize(fa * fb + fc); - } - - /// φ-optimized fused multiply-subtract: dequantize(a)*dequantize(b) - dequantize(c), then φ-quantize - pub fn phiFms(a: GF16, b: GF16, c: GF16) GF16 { - const fa = phiDequantize(a); - const fb = phiDequantize(b); - const fc = phiDequantize(c); - return phiQuantize(fa * fb - fc); - } - - /// Standard fused multiply-add (no φ scaling): a*b + c in f32, rounded to GF16 - pub fn fma(a: GF16, b: GF16, c: GF16) GF16 { - return fromF32(a.toF32() * b.toF32() + c.toF32()); - } -}; - -// ═════════════════════════════════════════════════════════════════════════════ -// TF3: TERNARY FLOAT3 -// ═══════════════════════════════════════════════════════════════════════ - -/// TF3: Ternary Float3 — packed ternary format -/// -/// **Bit Layout:** -/// ``` -/// ┌──────┬─────────┬────────────┐ -/// │ sign │ exp │ mant │ -/// │ 1bit │ 6bit │ 11 bit │ -/// └──────┴─────────┴────────────┘ -/// ``` -/// (18 bits total) -/// -/// **Structure:** -/// - sign: 1 sign bit -/// - exp: 6 exponent bits (values -31..+32, base 3) -/// - mant: 11 mantissa bits (ternary digits: {-1, 0, +1}) -/// -/// **Encoding:** -/// ``` -/// trit value | TF3 encoding -/// ----------|------------- -/// -1 | NEG = 2 (binary: 10) -/// 0 | ZERO = 0 -/// +1 | POS = 1 -/// ``` -/// -/// **Example:** -/// ```zig -/// const tf3 = TF3.fromF32(2.71828); -/// try std.testing.expect(tf3.toF32() > 2.5 and tf3.toF32() < 3.0); -/// ``` -pub const TF3 = packed struct(u18) { - /// Mantissa (11 bits) — ternary digits packed as unsigned - mant: u11, - - /// Exponent (6 bits, bias 31 for ternary base 3) - exp: u6, - - /// Sign bit (1 = negative) - sign: u1, - - /// Exponent bias for TF3 (ternary base 3) - const EXP_BIAS: u6 = 31; - - /// Ternary value encodings for packing - const NEG: u2 = 2; - const ZERO: u2 = 0; - const POS: u2 = 1; - - /// phi-distance for ternary format - /// comptime calculation (0.194 for TF3) - // pub const phi_distance: comptime_float = @import("std").math.fabs(3.0 / 11.0 - 1.0 / PHI); - - /// Create TF3 from f32 (ternary base 3) - pub fn fromF32(v: f32) TF3 { - if (v == 0.0) return .{ .mant = 0, .exp = 0, .sign = 0 }; - - if (!std.math.isFinite(v)) { - return .{ .mant = 0, .exp = 0x3F, .sign = @intFromBool(v < 0) }; - } - - const sign_bit: u1 = @intFromBool(v < 0); - const abs_v = @abs(v); - - // Find exponent (ternary base 3) - // Use i16 to avoid overflow during calculations - var exp: i16 = 0; - var mant_f = abs_v; - - // Normalize: mant_f in [1/3, 1] - const MAX_EXP: i16 = 31; - const MIN_EXP: i16 = -31; - - while (mant_f >= 1.0 and exp < MAX_EXP) : (exp += 1) mant_f /= 3.0; - while (mant_f < 1.0 / 3.0 and exp > MIN_EXP) : (exp -= 1) mant_f *= 3.0; - - // Clamp and convert to u6 (biased exponent) - const exp_biased = @min(@max(exp + 31, 0), 63); - const exp_u6: u6 = @intCast(exp_biased); - const mant_u11: u11 = @intFromFloat(@min(mant_f * 2047.0, 2047.0)); - - return .{ - .mant = mant_u11, - .exp = exp_u6, - .sign = sign_bit, - }; - } - - /// Convert TF3 to f32 - pub fn toF32(self: TF3) f32 { - if (self.exp == 0 and self.mant == 0) { - return if (self.sign == 1) -0.0 else 0.0; - } - if (self.exp == 0x3F) { - return if (self.sign == 1) -std.math.inf(f32) else std.math.inf(f32); - } - - const exp_unbiased = @as(i16, self.exp) - 31; - const mant_f = @as(f32, @floatFromInt(self.mant)) / 2047.0; - const value = mant_f * std.math.pow(f32, 3.0, @floatFromInt(exp_unbiased)); - return if (self.sign == 1) -value else value; - } - - /// Get ternary sign {-1, 0, +1} - pub inline fn getSign(self: TF3) i8 { - return if (self.sign == 1) -1 else if (self.mant == 0) 0 else 1; - } - - /// Zero TF3 - pub inline fn zero() TF3 { - return .{ .mant = 0, .exp = 0, .sign = 0 }; - } - - /// One TF3 - pub inline fn one() TF3 { - return fromF32(1.0); - } -}; - -// ═════════════════════════════════════════════════════════════════════════════ -// COMPILE-TIME GUARDS -// ═════════════════════════════════════════════════════════════════════════════ - -comptime { - // Check packed struct sizes - std.debug.assert(@sizeOf(GF16) == 2); - std.debug.assert(@sizeOf(TF3) == @sizeOf(u18)); -} - -// ═════════════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═════════════════════════════════════════════════════════════════════════════════ - -test "GF16 zero and one" { - const zero = GF16.zero(); - try std.testing.expectEqual(@as(f32, 0), zero.toF32()); - - const one = GF16.one(); - try std.testing.expectApproxEqAbs(@as(f32, 1.0), one.toF32(), 0.01); -} - -test "GF16 roundtrip positive" { - const values = [_]f32{ 0.0, 0.5, 1.0, 2.0, 3.14, 100.0, 1000.0 }; - for (values) |v| { - const gf = GF16.fromF32(v); - const result = gf.toF32(); - const err = @abs(v - result) / (@abs(v) + 0.001); - try std.testing.expect(err < 0.05); // 5% error tolerance - } -} - -test "GF16 roundtrip negative" { - const values = [_]f32{ -0.5, -1.0, -2.0, -3.14, -100.0, -1000.0 }; - for (values) |v| { - const gf = GF16.fromF32(v); - const result = gf.toF32(); - const err = @abs(v - result) / (@abs(v) + 0.001); - try std.testing.expect(err < 0.05); - } -} - -test "GF16 exact-bit encoding is the standard (1 + M/512) form" { - // Pin the wire format: 1.0 -> 0x3E00 (E=31, mantissa 0), NOT the old - // waste-a-bit 0x4000. Full 9-bit mantissa is now reachable. See specs/gf16.tri - // and testdata/gf_conformance.csv (gf16 rows). - const E = std.testing.expectEqual; - try E(@as(u16, 0x3E00), @as(u16, @bitCast(GF16.fromF32(1.0)))); - try E(@as(u16, 0x3F00), @as(u16, @bitCast(GF16.fromF32(1.5)))); - try E(@as(u16, 0x4000), @as(u16, @bitCast(GF16.fromF32(2.0)))); - try E(@as(u16, 0x4100), @as(u16, @bitCast(GF16.fromF32(3.0)))); - try E(@as(u16, 0x3C00), @as(u16, @bitCast(GF16.fromF32(0.5)))); - try E(@as(u16, 0xBE00), @as(u16, @bitCast(GF16.fromF32(-1.0)))); - try E(@as(u16, 0xC080), @as(u16, @bitCast(GF16.fromF32(-2.5)))); -} - -test "GF16 is bit-identical to the normative gf_binary.GF16 codec" { - // One implementation: golden_float16.GF16 delegates to gf_binary.GF16, so - // every value must encode to the exact same raw u16. Guards against re-drift. - const vals = [_]f32{ 0.0, 1.0, -1.0, 0.5, 1.5, 2.0, 3.0, -2.5, 3.14159, 100.0, 0.001, 12345.0, 1e30, -1e30 }; - for (vals) |v| { - const a: u16 = @bitCast(GF16.fromF32(v)); - const b: u16 = gf_binary.GF16.fromF32(v).bits_(); - try std.testing.expectEqual(b, a); - } - // NaN encodes consistently too (exp all-ones, mantissa != 0). - const na: u16 = @bitCast(GF16.fromF32(std.math.nan(f32))); - const nb: u16 = gf_binary.GF16.fromF32(std.math.nan(f32)).bits_(); - try std.testing.expectEqual(nb, na); -} - -test "GF16 arithmetic" { - const a = GF16.fromF32(1.5); - const b = GF16.fromF32(2.5); - const sum = GF16.add(a, b); - const diff = GF16.sub(b, a); - const prod = GF16.mul(a, b); - const quot = GF16.div(a, b); - - try std.testing.expectApproxEqAbs(@as(f32, 4.0), sum.toF32(), 0.05); - try std.testing.expectApproxEqAbs(@as(f32, 1.0), diff.toF32(), 0.05); - try std.testing.expectApproxEqAbs(@as(f32, 3.75), prod.toF32(), 0.05); - try std.testing.expectApproxEqAbs(@as(f32, 0.6), quot.toF32(), 0.05); -} - -test "GF16 phi quantization roundtrip" { - const original = 2.71828; - const quantized = GF16.phiQuantize(original); - const dequantized = GF16.phiDequantize(quantized); - - const error_pct = @abs((dequantized - original) / original) * 100.0; - try std.testing.expect(error_pct < 10.0); -} - -test "TF3 zero and one" { - const zero = TF3.zero(); - try std.testing.expectEqual(@as(i8, 0), zero.getSign()); - try std.testing.expectEqual(@as(f32, 0), zero.toF32()); - - const one = TF3.one(); - try std.testing.expectEqual(@as(i8, 1), one.getSign()); - try std.testing.expect(one.toF32() > 0.5 and one.toF32() < 1.5); -} - -test "TF3 roundtrip" { - const values = [_]f32{ 0.0, 0.1, 0.5, 1.0, -0.5, -1.0 }; - for (values) |v| { - const tf3 = TF3.fromF32(v); - const result = tf3.toF32(); - const err = @abs(v - result) / (@abs(v) + 0.001); - try std.testing.expect(err < 0.5); // Ternary format less precise - } -} - -// TODO: Implement pack8/unpack8 with proper type handling -test "TF3 pack unpack 8 (pending)" { - try std.testing.expect(true); -} - -test "TRINITY constant" { - try std.testing.expectApproxEqAbs(@as(f32, 3.0), TRINITY, 1e-10); -} - -test "PHI constant" { - try std.testing.expectApproxEqAbs(@as(f32, 1.6180339887498948482), PHI, 1e-15); -} - -test "PHI_SQ + 1/PHI_SQ equals 3" { - const computed = PHI_SQ + 1.0 / PHI_SQ; - try std.testing.expectApproxEqAbs(@as(f32, 3.0), computed, 1e-10); -} - -test "GF16 phi-fused multiply-add" { - const a = GF16.phiQuantize(2.0); - const b = GF16.phiQuantize(3.0); - const c = GF16.phiQuantize(4.0); - const result = GF16.phiFma(a, b, c); - const deq = GF16.phiDequantize(result); - try std.testing.expectApproxEqAbs(@as(f32, 10.0), deq, 1.5); -} - -test "GF16 phi-fused multiply-subtract" { - const a = GF16.phiQuantize(5.0); - const b = GF16.phiQuantize(3.0); - const c = GF16.phiQuantize(4.0); - const result = GF16.phiFms(a, b, c); - const deq = GF16.phiDequantize(result); - try std.testing.expectApproxEqAbs(@as(f32, 11.0), deq, 2.0); -} - -test "GF16 standard fused multiply-add" { - const a = GF16.fromF32(2.0); - const b = GF16.fromF32(3.0); - const c = GF16.fromF32(4.0); - const result = GF16.fma(a, b, c); - try std.testing.expectApproxEqAbs(@as(f32, 10.0), result.toF32(), 0.5); -} - -// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig deleted file mode 100644 index 545bfe6..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/jepa_t.zig +++ /dev/null @@ -1,70 +0,0 @@ -const std = @import("std"); -const tc = @import("trinity_constants.zig"); - -pub const EncoderLayers: u32 = 6; -pub const PredictorLayers: u32 = 3; -pub const PhiSplit: f64 = @as(f64, @floatFromInt(EncoderLayers)) / @as(f64, @floatFromInt(EncoderLayers + PredictorLayers)); - -pub fn encoderParams() u64 { - const embed_params = @as(u64, tc.VOCAB) * tc.D_MODEL; - const per_layer = 4 * @as(u64, tc.D_MODEL) * tc.D_MODEL + 2 * @as(u64, tc.D_MODEL) * tc.D_FFN + 4 * tc.D_MODEL; - return embed_params + EncoderLayers * per_layer; -} - -pub fn predictorParams() u64 { - const per_layer = 4 * @as(u64, tc.D_MODEL) * tc.D_MODEL + 2 * @as(u64, tc.D_MODEL) * tc.D_FFN + 4 * tc.D_MODEL; - return PredictorLayers * per_layer; -} - -pub fn totalParams() u64 { - return encoderParams() + predictorParams(); -} - -pub fn totalBytesGF16() u64 { - return totalParams() * 2; -} - -pub fn totalMB() f64 { - return @as(f64, @floatFromInt(totalBytesGF16())) / (1024.0 * 1024.0); -} - -pub fn jepaLoss( - pred: []const f64, - target: []const f64, -) f64 { - std.debug.assert(pred.len == target.len); - var sum: f64 = 0; - for (pred, target) |p, t| { - const d = p - t; - sum += d * d; - } - return sum / @as(f64, @floatFromInt(pred.len)); -} - -test "JEPA-T: phi split ratio" { - try std.testing.expectApproxEqAbs(@as(f64, 0.667), PhiSplit, 0.01); -} - -test "JEPA-T: total params fit in 17MB GF16" { - const mb = totalMB(); - try std.testing.expect(mb <= 17.0); - try std.testing.expect(mb > 10.0); -} - -test "JEPA-T: jepaLoss correct" { - const pred = [_]f64{ 1.0, 2.0, 3.0 }; - const tgt = [_]f64{ 1.0, 2.0, 3.0 }; - const loss = jepaLoss(&pred, &tgt); - try std.testing.expectApproxEqAbs(@as(f64, 0.0), loss, 1e-10); -} - -test "JEPA-T: jepaLoss nonzero for mismatch" { - const pred = [_]f64{ 1.0, 0.0 }; - const tgt = [_]f64{ 0.0, 1.0 }; - const loss = jepaLoss(&pred, &tgt); - try std.testing.expect(loss > 0); -} - -test "JEPA-T: encoder > predictor" { - try std.testing.expect(encoderParams() > predictorParams()); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs deleted file mode 100644 index 3853db0..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -// GoldenFloat Rust Wrapper -// -// Provides FFI bindings to Zig-compiled golden-float binary -// Downloads the appropriate binary from GitHub releases - -pub const VERSION: &str = "1.0.0"; -pub const GITHUB_RELEASES: &str = "https://github.com/gHashTag/zig-golden-float/releases/download"; - -#[cfg(target_os = "windows")] -use std::os::windows::process::Command; - -/// Get binary path for current platform -pub fn get_binary_path() -> std::path.PathBuf { - let bin_name = "golden-float"; - let mut path = std::env::var("HOME").unwrap(); - path.push(".golden-float"); - path.push(bin_name); - - #[cfg(windows)] - { - path.set_extension("exe"); - } - - path -} - -/// Launch golden-float binary -pub fn run_golden_float(args: &[&str]) -> std::process::Child { - let binary = get_binary_path(); - - let cmd = Command::new(&binary); - cmd.args(args); - - cmd.spawn().expect("Failed to spawn golden-float binary") -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_get_binary_path() { - let path = get_binary_path(); - assert!(path.to_str().unwrap().contains("golden-float")); - } -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig deleted file mode 100644 index e4183c9..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/constants.zig +++ /dev/null @@ -1,148 +0,0 @@ -// @origin(spec:constants.tri) @regen(manual-impl) -//! Mathematical Constants v8.21 -//! -//! Foundation of AGENT MU intelligence calculations -//! Features: -//! - Golden Ratio φ (Phi) from canonical source -//! - Trinity Identity: φ² + 1/φ² = 3 -//! - MU = 1/φ²/10 = 0.0382 (intelligence gain per fix) -//! - Lucas numbers and Berry phase -// @origin(manual) @regen(pending) - -const std = @import("std"); - -// Import from canonical source (ANTI-PATTERN: no inline constants!) -// sacred/constants.zig does not exist in this repository and never has, so -// this file could not compile and neither could anything importing it -- -// including src/root.zig, which is the module root every consumer gets. -// The two values it supplied are PHI and PHI squared, and this repository -// already carries them at 1.6180339887498948482 in trinity_constants.zig, -// gf_binary.zig and golden_float16.zig, all three identical. Pointing at -// the repository's own constants keeps the value rather than inventing one. -const sacred_constants = @import("../trinity_constants.zig"); - -/// Golden Ratio φ = (1 + √5) / 2 ≈ 1.618033988749895 -pub const PHI = sacred_constants.PHI; - -/// φ² = φ + 1 ≈ 2.618033988749895 -pub const PHI_SQUARED = sacred_constants.PHI_SQ; - -/// 1/φ² ≈ 0.381966011250105 -pub const INVERSE_PHI_SQUARED: f64 = 1.0 / PHI_SQUARED; - -/// Trinity Identity: φ² + 1/φ² = 3 (exactly) -pub const TRINITY_SUM: f64 = PHI_SQUARED + INVERSE_PHI_SQUARED; - -/// MU = 1/φ²/10 = 0.0382 (intelligence gain per successful fix) -pub const MU: f64 = INVERSE_PHI_SQUARED / 10.0; - -/// Lucas number L(10) = 123 (used in checksum validation) -pub const LAMBDA_10: f64 = 123.0; - -/// Lambda scaling factor for predictive intelligence -pub const LAMBDA_SCALE: f64 = 1.105572809; - -/// Berry phase for quantum-inspired computation -pub const BERRY_PHASE: f64 = std.math.pi * (1.0 - 1.0 / PHI); - -/// SU3 energy harvesting constant -pub const SU3_CONSTANT: f64 = 3.0 / (2.0 * PHI); - -// Verify Trinity identity at compile time -comptime { - if (!(TRINITY_SUM >= 2.999 and TRINITY_SUM <= 3.001)) { - @compileError("Trinity identity violation: φ² + 1/φ² must equal 3"); - } -} - -/// Sacred math utilities -pub const SacredMath = struct { - /// Calculate intelligence multiplier after n successful fixes - /// Formula: I(t) = I₀ × e^(μ×fixes) - pub fn intelligenceMultiplier(fixes: usize) f64 { - return @exp(MU * @as(f64, @floatFromInt(fixes))); - } - - /// Calculate φ-weighted consensus score - pub fn phiWeightedConsensus(scores: []const f64) f64 { - var weighted_sum: f64 = 0; - var total_weight: f64 = 0; - - for (scores, 0..) |score, i| { - // Use powers of φ as weights - const weight = std.math.pow(f64, PHI, @as(f64, @floatFromInt(i))); - weighted_sum += score * weight; - total_weight += weight; - } - - return if (total_weight > 0) weighted_sum / total_weight else 0; - } - - /// Calculate Berry phase rotation - pub fn berryPhaseRotation(angle: f64) f64 { - return angle + BERRY_PHASE; - } - - /// Generate sacred checksum for validation - pub fn sacredChecksum(data: []const u8) u64 { - // The multiplier is 2^64/φ, which is what φ IS in integer hashing -- - // Knuth's multiplicative constant. The previous line multiplied a u64 - // by the f64 PHI and then called @intFromFloat on the u64 result, so - // this function has never compiled and no stored checksum can depend - // on it. Taking the standard constant keeps the golden ratio rather - // than inventing a number. - const PHI_U64: u64 = 0x9E3779B97F4A7C15; - var hash: u64 = @intFromFloat(LAMBDA_10); - for (data) |byte| { - hash = hash *% PHI_U64 +% byte; - } - return hash; - } - - /// Verify Trinity alignment - pub fn isTrinityAligned(value: f64) bool { - return value >= (3.0 - 0.01) and value <= (3.0 + 0.01); - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// Tests -// ═══════════════════════════════════════════════════════════════════════════════ - -test "Sacred Constants: Trinity Identity" { - try std.testing.expectApproxEqAbs(3.0, TRINITY_SUM, 0.001); -} - -test "Sacred Constants: MU calculation" { - try std.testing.expectApproxEqAbs(0.0382, MU, 0.0001); -} - -test "Sacred Constants: PHI squared" { - try std.testing.expectApproxEqAbs(2.6180, PHI_SQUARED, 0.001); -} - -test "Sacred Math: Intelligence multiplier" { - const mult_0 = SacredMath.intelligenceMultiplier(0); - try std.testing.expectApproxEqAbs(1.0, mult_0, 0.01); - - const mult_10 = SacredMath.intelligenceMultiplier(10); - try std.testing.expect(mult_10 > 1.4 and mult_10 < 1.6); -} - -test "Sacred Math: Phi-weighted consensus" { - const scores = [_]f64{ 0.9, 0.95, 0.85 }; - const consensus = SacredMath.phiWeightedConsensus(&scores); - try std.testing.expect(consensus > 0.85 and consensus < 0.95); -} - -test "Sacred Math: Trinity alignment" { - try std.testing.expect(SacredMath.isTrinityAligned(3.0)); - try std.testing.expect(SacredMath.isTrinityAligned(2.995)); - try std.testing.expect(!SacredMath.isTrinityAligned(2.9)); -} - -test "Sacred Math: Checksum" { - const data = "trinity"; - const checksum = SacredMath.sacredChecksum(data); - try std.testing.expect(checksum > 0); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig deleted file mode 100644 index 8703675..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_bench.zig +++ /dev/null @@ -1,566 +0,0 @@ -//! Math Benchmark — Generated from specs/tri/math/math_bench.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from math_bench.tri spec -//! Performance benchmarks vs Python/Rust with nexus logging - -const std = @import("std"); - -// Re-export sacred constants -const PHI = @import("gen_constants.zig").PHI; -const PHI_SQUARED = @import("gen_constants.zig").PHI_SQUARED; -const PHI_INV_SQUARED = @import("gen_constants.zig").PHI_INV_SQUARED; -const TRINITY_SUM = @import("gen_constants.zig").TRINITY_SUM; - -// ============================================================================ -// TYPES -// ============================================================================ - -/// Benchmark category -pub const BenchmarkCategory = enum(u8) { - core, - simd, - sequence, - floating_point, - geometry, - verification, -}; - -/// Single benchmark result -pub const BenchmarkResult = struct { - name: []const u8, - category: BenchmarkCategory, - iterations: usize, - total_time_ns: u64, - ops_per_second: f64, - avg_time_ns: f64, - baseline_ratio: ?f64, - python_ratio: ?f64, - rust_ratio: ?f64, -}; - -/// Complete benchmark suite -pub const BenchmarkSuite = struct { - results: []BenchmarkResult, - total_time_ns: u64, - timestamp: i64, -}; - -/// Configuration for benchmark run -pub const BenchmarkConfig = struct { - iterations_override: ?usize = null, - warmup_iterations: usize = 1000, - log_to_nexus: bool = true, - nexus_path: []const u8 = "trinity-nexus/benchmarks/", -}; - -/// Output format for results -pub const OutputFormat = enum(u8) { - table, - json, - csv, -}; - -// ============================================================================ -// BENCHMARK FUNCTIONS -// ============================================================================ - -/// Benchmark golden wrap operation -pub fn runGoldenWrapBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { - _ = allocator; - const n = if (iterations > 0) iterations else 10_000_000; - - const start = try std.time.Instant.now(); - - var sum: f64 = 0.0; - var i: usize = 0; - while (i < n) : (i += 1) { - // Golden wrap: wrap sum into [0, 1) using PHI - const wrapped = sum - @floor(sum); - sum = wrapped + PHI; - if (sum >= 1000.0) sum = sum - @floor(sum / 1000.0) * 1000.0; - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "golden_wrap_10m", - .category = .core, - .iterations = n, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Benchmark Fibonacci hash -pub fn runPhiHashBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { - _ = allocator; - const n = if (iterations > 0) iterations else 10_000_000; - - const start = try std.time.Instant.now(); - - var hash_sum: u64 = 0; - var i: usize = 0; - while (i < n) : (i += 1) { - // Phi hash: mix key with golden ratio - const key = @as(u64, @intCast(i)); - const hash = phiHashMod(key, 16); - hash_sum +%= hash; - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "phi_hash_10m", - .category = .core, - .iterations = n, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Fibonacci hash with modulo -fn phiHashMod(key: u64, shift: u64) u64 { - const phi_bits: u64 = 11400714819323198549; // 2^64 / phi - const hashed = key +% phi_bits; - const clamped_shift = @min(shift, @as(u64, 63)); - const mask = (@as(u64, 1) << clamped_shift) - 1; - return (hashed >> clamped_shift) ^ (hashed & mask); -} - -/// Benchmark SIMD golden wrap (placeholder for future SIMD implementation) -pub fn runSIMDBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { - _ = allocator; - const n = if (iterations > 0) iterations else 10_000_000; - - const start = try std.time.Instant.now(); - - // Placeholder: scalar implementation for now - var sum: f64 = 0.0; - var i: usize = 0; - while (i < n) : (i += 1) { - const wrapped = sum - @floor(sum); - sum = wrapped + PHI; - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "simd_golden_wrap_10m", - .category = .simd, - .iterations = n, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Benchmark Fibonacci sequence -pub fn runFibonacciBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { - _ = allocator; - const iters = if (iterations > 0) iterations else 100; - - const start = try std.time.Instant.now(); - - var result_sum: u64 = 0; - var iter: usize = 0; - while (iter < iters) : (iter += 1) { - _ = fibonacci(n); - result_sum +%= @truncate(iter); - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "fibonacci_10000", - .category = .sequence, - .iterations = iters, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Fast Fibonacci using fast doubling (clamped to prevent overflow) -fn fibonacci(n: usize) u64 { - if (n == 0) return 0; - if (n == 1) return 1; - if (n > 90) return 2_880_067_194_370_816_120; // F(90), clamped for safety - - var a: u64 = 0; - var b: u64 = 1; - var i: usize = 2; - while (i <= n and i < 100) : (i += 1) { - const next = a + b; - if (next < a) return b; // Overflow detected - a = b; - b = next; - } - - return b; -} - -/// Benchmark Lucas sequence -pub fn runLucasBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { - _ = allocator; - const iters = if (iterations > 0) iterations else 100; - - const start = try std.time.Instant.now(); - - var iter: usize = 0; - while (iter < iters) : (iter += 1) { - _ = lucas(n); - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "lucas_10000", - .category = .sequence, - .iterations = iters, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Lucas number calculation (clamped to prevent overflow) -fn lucas(n: usize) u64 { - if (n == 0) return 2; - if (n == 1) return 1; - if (n > 90) return 3_788_906_237_314_390_60; // L(90), clamped for safety - - var a: u64 = 2; - var b: u64 = 1; - var i: usize = 2; - while (i <= n and i < 100) : (i += 1) { - const next = a + b; - if (next < a) return b; // Overflow detected - a = b; - b = next; - } - - return b; -} - -/// Benchmark φ^n computation -pub fn runPhiPowerBench(allocator: std.mem.Allocator, n: usize, iterations: usize) !BenchmarkResult { - _ = allocator; - const power = if (n > 0) n else 1000; - const iters = if (iterations > 0) iterations else 10000; - - const start = try std.time.Instant.now(); - - var result: f64 = 0.0; - var i: usize = 0; - while (i < iters) : (i += 1) { - result += std.math.pow(f64, PHI, @as(f64, @floatFromInt(power))); - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "phi_power_1000", - .category = .floating_point, - .iterations = iters, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(iters)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Benchmark φ-spiral computation -pub fn runSpiralBench(allocator: std.mem.Allocator, count: usize, iterations: usize) !BenchmarkResult { - _ = allocator; - const n = if (count > 0) count else 1000; - const iters = if (iterations > 0) iterations else 1000; - - const start = try std.time.Instant.now(); - - var result_sum: f64 = 0.0; - var iter: usize = 0; - while (iter < iters) : (iter += 1) { - var i: usize = 0; - while (i < n) : (i += 1) { - const angle = @as(f64, @floatFromInt(i)) * PHI; - const radius = std.math.sqrt(@as(f64, @floatFromInt(i))); - const x = radius * @cos(angle); - const y = radius * @sin(angle); - result_sum += x + y; - } - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "spiral_1000", - .category = .geometry, - .iterations = iters * n, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(iters * n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(iters * n)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Benchmark Trinity identity verification -pub fn runVerifyBench(allocator: std.mem.Allocator, iterations: usize) !BenchmarkResult { - _ = allocator; - const n = if (iterations > 0) iterations else 1_000_000; - - const start = try std.time.Instant.now(); - - var verified_count: usize = 0; - var i: usize = 0; - while (i < n) : (i += 1) { - const trinity_check = PHI_SQUARED + PHI_INV_SQUARED; - if (@abs(trinity_check - 3.0) < 1e-10) { - verified_count += 1; - } - } - - const end = try std.time.Instant.now(); - const elapsed_ns = end.since(start); - - return BenchmarkResult{ - .name = "trinity_verify", - .category = .verification, - .iterations = n, - .total_time_ns = @intCast(elapsed_ns), - .ops_per_second = @as(f64, @floatFromInt(n)) / @as(f64, @floatFromInt(elapsed_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(elapsed_ns)) / @as(f64, @floatFromInt(n)), - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; -} - -/// Run complete benchmark suite -pub fn runAllBenchmarks(allocator: std.mem.Allocator, config: BenchmarkConfig) !BenchmarkSuite { - const results = try allocator.alloc(BenchmarkResult, 9); - - const iter = config.iterations_override orelse 10_000_000; - - results[0] = try runGoldenWrapBench(allocator, iter); - results[1] = try runPhiHashBench(allocator, iter); - results[2] = try runSIMDBench(allocator, iter); - results[3] = try runFibonacciBench(allocator, 10000, 100); - results[4] = try runLucasBench(allocator, 10000, 100); - results[5] = try runPhiPowerBench(allocator, 1000, 10000); - results[6] = try runSpiralBench(allocator, 1000, 1000); - results[7] = try runVerifyBench(allocator, 1_000_000); - - // Verify all identities - const verify_start = try std.time.Instant.now(); - var verify_count: usize = 0; - var i: usize = 0; - while (i < 10000) : (i += 1) { - if (verifyTrinityIdentity()) verify_count += 1; - if (verifyPhiIdentity()) verify_count += 1; - } - const verify_end = try std.time.Instant.now(); - const verify_ns = verify_end.since(verify_start); - - results[8] = BenchmarkResult{ - .name = "verify_all_identities", - .category = .verification, - .iterations = 20000, - .total_time_ns = @intCast(verify_ns), - .ops_per_second = 20000.0 / @as(f64, @floatFromInt(verify_ns)) * 1_000_000_000.0, - .avg_time_ns = @as(f64, @floatFromInt(verify_ns)) / 20000.0, - .baseline_ratio = null, - .python_ratio = null, - .rust_ratio = null, - }; - - var total_ns: u64 = 0; - for (results) |r| { - total_ns += r.total_time_ns; - } - - const timestamp128 = std.time.nanoTimestamp(); - const timestamp = @as(i64, @truncate(timestamp128)); - - return BenchmarkSuite{ - .results = results, - .total_time_ns = total_ns, - .timestamp = timestamp, - }; -} - -/// Verify Trinity identity -fn verifyTrinityIdentity() bool { - const diff = @abs((PHI_SQUARED + PHI_INV_SQUARED) - 3.0); - return diff < 1e-10; -} - -/// Verify Phi identity -fn verifyPhiIdentity() bool { - const diff = @abs(PHI_SQUARED - (PHI + 1.0)); - return diff < 1e-10; -} - -/// Print benchmark results as formatted table -pub fn printBenchmarkResults(suite: BenchmarkSuite, format: OutputFormat) !void { - switch (format) { - .table => { - std.debug.print("╔══════════════════════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ SACRED MATHEMATICS — BENCHMARK RESULTS ║\n", .{}); - std.debug.print("╠══════════════════════════════════════════════════════════════════════════════╣\n", .{}); - std.debug.print("║ {:30} {:>15} {:>12} ║\n", .{ "Benchmark", "Ops/sec", "Time (ns)" }); - std.debug.print("║ ────────────────────────────────────────────────────────────────────────── ║\n", .{}); - - for (suite.results) |r| { - const ops_str = formatOpsPerSec(r.ops_per_second); - const time_str = formatTime(r.avg_time_ns); - std.debug.print("║ {:30} {:>15} {:>12} ║\n", .{ r.name, ops_str, time_str }); - } - - std.debug.print("║ ║\n", .{}); - std.debug.print("╚══════════════════════════════════════════════════════════════════════════════╝\n", .{}); - }, - .json => { - std.debug.print("{{\n", .{}); - std.debug.print(" \"timestamp\": {},\n", .{suite.timestamp}); - std.debug.print(" \"total_time_ns\": {},\n", .{suite.total_time_ns}); - std.debug.print(" \"results\": [\n", .{}); - for (suite.results, 0..) |r, i| { - const comma = if (i < suite.results.len - 1) "," else ""; - std.debug.print(" {{\"name\": \"{s}\", \"ops_per_second\": {d:.2}, \"avg_time_ns\": {d:.2}}}{}\n", .{ r.name, r.ops_per_second, r.avg_time_ns, comma }); - } - std.debug.print(" ]\n", .{}); - std.debug.print("}}\n", .{}); - }, - .csv => { - std.debug.print("Benchmark,Category,Iterations,Ops/sec,AvgTime_ns\n", .{}); - for (suite.results) |r| { - std.debug.print("{s},{s},{},{d:.2},{d:.2}\n", .{ r.name, @tagName(r.category), r.iterations, r.ops_per_second, r.avg_time_ns }); - } - }, - } -} - -/// Format operations per second with appropriate units -fn formatOpsPerSec(ops: f64) []const u8 { - var buf: [64]u8 = undefined; - if (ops >= 1_000_000_000) { - std.fmt.bufPrint(&buf, "{d:.2} G", .{ops / 1_000_000_000.0}) catch return "N/A"; - } else if (ops >= 1_000_000) { - std.fmt.bufPrint(&buf, "{d:.2} M", .{ops / 1_000_000.0}) catch return "N/A"; - } else if (ops >= 1_000) { - std.fmt.bufPrint(&buf, "{d:.2} K", .{ops / 1_000.0}) catch return "N/A"; - } else { - std.fmt.bufPrint(&buf, "{d:.2}", .{ops}) catch return "N/A"; - } - return &buf; -} - -/// Format time with appropriate units -fn formatTime(ns: f64) []const u8 { - var buf: [64]u8 = undefined; - if (ns >= 1_000_000) { - std.fmt.bufPrint(&buf, "{d:.2} ms", .{ns / 1_000_000.0}) catch return "N/A"; - } else if (ns >= 1_000) { - std.fmt.bufPrint(&buf, "{d:.2} us", .{ns / 1_000.0}) catch return "N/A"; - } else { - std.fmt.bufPrint(&buf, "{d:.2} ns", .{ns}) catch return "N/A"; - } - return &buf; -} - -/// Compare with baseline -pub fn compareWithBaseline(current: BenchmarkResult, baseline: BenchmarkResult) f64 { - if (baseline.avg_time_ns == 0) return 1.0; - return baseline.avg_time_ns / current.avg_time_ns; -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Math Bench: runGoldenWrapBench" { - const allocator = std.testing.allocator; - const result = try runGoldenWrapBench(allocator, 1000); - try std.testing.expectEqual(@as(usize, 1000), result.iterations); - try std.testing.expect(result.ops_per_second > 0); -} - -test "Math Bench: runPhiHashBench" { - const allocator = std.testing.allocator; - const result = try runPhiHashBench(allocator, 1000); - try std.testing.expectEqual(@as(usize, 1000), result.iterations); - try std.testing.expect(result.ops_per_second > 0); -} - -test "Math Bench: runVerifyBench" { - const allocator = std.testing.allocator; - const result = try runVerifyBench(allocator, 10000); - try std.testing.expectEqual(@as(usize, 10000), result.iterations); - try std.testing.expect(result.ops_per_second > 0); -} - -test "Math Bench: runAllBenchmarks" { - const allocator = std.testing.allocator; - const config = BenchmarkConfig{ .iterations_override = 100, .log_to_nexus = false }; - const suite = try runAllBenchmarks(allocator, config); - defer allocator.free(suite.results); - try std.testing.expectEqual(@as(usize, 9), suite.results.len); -} - -test "Math Bench: phiHashMod" { - const hash1 = phiHashMod(12345, 16); - const hash2 = phiHashMod(12345, 16); - try std.testing.expectEqual(hash1, hash2); -} - -test "Math Bench: fibonacci" { - try std.testing.expectEqual(@as(u64, 0), fibonacci(0)); - try std.testing.expectEqual(@as(u64, 1), fibonacci(1)); - try std.testing.expectEqual(@as(u64, 1), fibonacci(2)); - try std.testing.expectEqual(@as(u64, 2), fibonacci(3)); - try std.testing.expectEqual(@as(u64, 3), fibonacci(4)); -} - -test "Math Bench: lucas" { - try std.testing.expectEqual(@as(u64, 2), lucas(0)); - try std.testing.expectEqual(@as(u64, 1), lucas(1)); - try std.testing.expectEqual(@as(u64, 3), lucas(2)); - try std.testing.expectEqual(@as(u64, 4), lucas(3)); -} - -test "Math Bench: verifyTrinityIdentity" { - try std.testing.expect(verifyTrinityIdentity()); -} - -test "Math Bench: verifyPhiIdentity" { - try std.testing.expect(verifyPhiIdentity()); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig deleted file mode 100644 index f1b961a..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_commands.zig +++ /dev/null @@ -1,470 +0,0 @@ -//! Math CLI Commands — Generated from specs/tri/math/math_cli.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from math_cli.tri spec -//! Command hierarchy, aliases, help text, argument parsing - -const std = @import("std"); - -// Re-export from other math modules -const gen_constants = @import("gen_constants.zig"); -const gen_eval = @import("gen_eval.zig"); -const gen_identities = @import("gen_identities.zig"); - -pub const PHI = gen_constants.PHI; -pub const PI = gen_constants.PI; -pub const E = gen_constants.E; - -// ============================================================================ -// TYPES -// ============================================================================ - -/// Output format for commands -pub const OutputFormat = enum(u8) { - pretty, - json, - csv, -}; - -// ============================================================================ -// HELP TEXT -// ============================================================================ - -pub const MATH_HELP_TEXT = - \\╔══════════════════════════════════════════════════════════════════════════════╗ - \\║ SACRED MATHEMATICS FRAMEWORK v2.0 ║ - \\║ φ² + 1/φ² = 3 = TRINITY ║ - \\╠══════════════════════════════════════════════════════════════════════════════╣ - \\║ ║ - \\║ HIERARCHICAL COMMANDS ║ - \\║ ───────────────────────────────────────────────────────────────────────── ║ - \\║ tri math Show all math commands ║ - \\║ tri math constants Show all sacred constants ║ - \\║ tri math eval phi Compute φ^n ║ - \\║ tri math eval fib Fibonacci F(n) (BigInt) ║ - \\║ tri math eval lucas Lucas L(n) ║ - \\║ tri math compute spiral φ-spiral + ASCII plot ║ - \\║ tri math compute verify Verify all sacred identities ║ - \\║ tri math compute compare Compare φ^n vs F(n) vs L(n) ║ - \\║ tri math bench Run benchmarks ║ - \\║ tri math identities Show all φ-identities with proofs ║ - \\║ ║ - \\║ ALIASES (Quick Access) ║ - \\║ ───────────────────────────────────────────────────────────────────────── ║ - \\║ tri constants Same as 'tri math constants' ║ - \\║ tri phi Same as 'tri math eval phi ' ║ - \\║ tri fib Same as 'tri math eval fib ' ║ - \\║ tri lucas Same as 'tri math eval lucas ' ║ - \\║ tri spiral Same as 'tri math compute spiral ' ║ - \\║ tri verify Same as 'tri math compute verify' ║ - \\║ ║ - \\║ FLAGS ║ - \\║ ───────────────────────────────────────────────────────────────────────── ║ - \\║ --format=pretty|json|csv Output format ║ - \\║ --precision=N Decimal precision (default: 16) ║ - \\║ --plot Show ASCII spiral plot ║ - \\║ --max-n=N Comparison range (default: 20) ║ - \\║ ║ - \\║ EXAMPLES ║ - \\║ ───────────────────────────────────────────────────────────────────────── ║ - \\║ tri phi 42 Compute φ⁴² ║ - \\║ tri fib 1000 F(1000) = 4346655... (209 digits) ║ - \\║ tri lucas 2 L(2) = 3 = TRINITY ║ - \\║ tri spiral 12 --plot φ-spiral with ASCII plot ║ - \\║ tri verify Check all sacred identities ║ - \\║ tri math constants --json Export constants as JSON ║ - \\║ ║ - \\╚══════════════════════════════════════════════════════════════════════════════╝ -; - -// ============================================================================ -// PARSING FUNCTIONS -// ============================================================================ - -/// Parse a specific flag from arguments -pub fn parseFlag(args: [][]const u8, flag_name: []const u8) ?[]const u8 { - const flag_with_dash = "--"; - const full_flag = std.fmt.allocPrint(std.heap.page_allocator, "--{s}", .{flag_name}) catch return null; - defer std.heap.page_allocator.free(full_flag); - - for (args) |arg| { - if (std.mem.eql(u8, arg, full_flag)) { - return ""; - } - if (std.mem.startsWith(u8, arg, flag_with_dash)) { - const eq_idx = std.mem.indexOfScalar(u8, arg, '='); - if (eq_idx) |idx| { - if (std.mem.eql(u8, arg[2..idx], flag_name)) { - return arg[idx + 1 ..]; - } - } - } - } - return null; -} - -/// Parse output format from arguments -pub fn parseFormatFlag(args: [][]const u8) OutputFormat { - if (parseFlag(args, "format")) |fmt| { - if (std.mem.eql(u8, fmt, "json")) return .json; - if (std.mem.eql(u8, fmt, "csv")) return .csv; - } - return .pretty; -} - -// ============================================================================ -// COMMAND DISPATCHERS -// ============================================================================ - -/// Main math command dispatcher -pub fn runMathCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - if (args.len == 0) { - showMathHelp(); - return; - } - - const subcommand = args[0]; - const remaining = args[1..]; - - if (std.mem.eql(u8, subcommand, "constants")) { - runConstantsCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "eval")) { - runEvalCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "compute")) { - runComputeCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "bench")) { - runBenchCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "identities")) { - runIdentitiesCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "help")) { - showMathHelp(); - } else { - std.debug.print("Unknown math subcommand: {s}\n\n", .{subcommand}); - showMathHelp(); - } -} - -/// Show all sacred constants -pub fn runConstantsCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - - const format = parseFormatFlag(args); - - if (format == .json) { - std.debug.print("{{\n", .{}); - std.debug.print(" \"PHI\": {d:.16},\n", .{PHI}); - std.debug.print(" \"PI\": {d:.16},\n", .{PI}); - std.debug.print(" \"E\": {d:.16},\n", .{E}); - std.debug.print(" \"TRINITY_SUM\": {d:.1}\n", .{gen_constants.TRINITY_SUM}); - std.debug.print("}}\n", .{}); - } else { - std.debug.print("╔══════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ SACRED CONSTANTS ║\n", .{}); - std.debug.print("╠══════════════════════════════════════════════════════════════╣\n", .{}); - std.debug.print("║ PHI (φ) = {d:>20.16} ║\n", .{PHI}); - std.debug.print("║ PI (π) = {d:>20.16} ║\n", .{PI}); - std.debug.print("║ E = {d:>20.16} ║\n", .{E}); - std.debug.print("║ TRINITY = {d:>20.1} (= φ² + 1/φ²) ║\n", .{gen_constants.TRINITY_SUM}); - std.debug.print("╚══════════════════════════════════════════════════════════════╝\n", .{}); - } -} - -/// Eval dispatcher (phi/fib/lucas) -pub fn runEvalCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - if (args.len == 0) { - std.debug.print("Usage: tri math eval [phi|fib|lucas] \n", .{}); - return; - } - - const subcommand = args[0]; - const remaining = args[1..]; - - if (std.mem.eql(u8, subcommand, "phi")) { - runPhiCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "fib")) { - runFibCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "lucas")) { - runLucasCommand(allocator, remaining); - } else { - std.debug.print("Unknown eval subcommand: {s}\n", .{subcommand}); - } -} - -/// Compute φ^n -pub fn runPhiCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - if (args.len == 0) { - std.debug.print("Usage: tri math eval phi \n", .{}); - return; - } - - const n_str = args[0]; - const n = std.fmt.parseInt(usize, n_str, 10) catch { - std.debug.print("Invalid number: {s}\n", .{n_str}); - return; - }; - - const result = gen_eval.phiPower(n); - std.debug.print("φ^{d} = {d:.16}\n", .{ n, result }); -} - -/// Compute Fibonacci F(n) -pub fn runFibCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - if (args.len == 0) { - std.debug.print("Usage: tri math eval fib \n", .{}); - return; - } - - const n_str = args[0]; - const n = std.fmt.parseInt(usize, n_str, 10) catch { - std.debug.print("Invalid number: {s}\n", .{n_str}); - return; - }; - - const result = gen_eval.fibonacciBigInt(allocator, n) catch |err| { - std.debug.print("Error computing F({d}): {}\n", .{ n, err }); - return; - }; - defer allocator.free(result.value_str); - - gen_eval.printEvalResult(result, .{}); -} - -/// Compute Lucas L(n) -pub fn runLucasCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - if (args.len == 0) { - std.debug.print("Usage: tri math eval lucas \n", .{}); - return; - } - - const n_str = args[0]; - const n = std.fmt.parseInt(usize, n_str, 10) catch { - std.debug.print("Invalid number: {s}\n", .{n_str}); - return; - }; - - const result = gen_eval.lucasBigInt(allocator, n) catch |err| { - std.debug.print("Error computing L({d}): {}\n", .{ n, err }); - return; - }; - defer allocator.free(result.value_str); - - gen_eval.printEvalResult(result, .{}); -} - -/// Compute dispatcher (spiral/verify/compare) -pub fn runComputeCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - if (args.len == 0) { - std.debug.print("Usage: tri math compute [spiral|verify|compare] [args...]\n", .{}); - return; - } - - const subcommand = args[0]; - const remaining = args[1..]; - - if (std.mem.eql(u8, subcommand, "spiral")) { - runSpiralCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "verify")) { - runVerifyCommand(allocator, remaining); - } else if (std.mem.eql(u8, subcommand, "compare")) { - runCompareCommand(allocator, remaining); - } else { - std.debug.print("Unknown compute subcommand: {s}\n", .{subcommand}); - showMathHelp(); - } -} - -/// Show φ-spiral coordinates -pub fn runSpiralCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - if (args.len == 0) { - std.debug.print("Usage: tri math compute spiral \n", .{}); - return; - } - - const n_str = args[0]; - const n = std.fmt.parseInt(usize, n_str, 10) catch { - std.debug.print("Invalid number: {s}\n", .{n_str}); - return; - }; - - const plot = parseFlag(args, "plot") != null; - - std.debug.print("φ-Spiral (n={d}):\n", .{n}); - std.debug.print("{s:>10} {s:>10} {s:>10}\n", .{ "x", "y", "r" }); - std.debug.print("────────────────────────────────\n", .{}); - - const angle = @as(f64, @floatFromInt(n)) * PHI; - const radius = std.math.sqrt(@as(f64, @floatFromInt(n))); - const x = radius * @cos(angle); - const y = radius * @sin(angle); - - std.debug.print("{d:>10.4} {d:>10.4} {d:>10.4}\n", .{ x, y, radius }); - - if (plot) { - std.debug.print("\nASCII Plot:\n", .{}); - printSpiralPlot(n); - } -} - -/// Simple ASCII spiral plot -fn printSpiralPlot(n: usize) void { - const size = @min(20, @as(usize, @intFromFloat(@sqrt(@as(f64, @floatFromInt(n))) * 2)) + 1); - var i: usize = 0; - while (i < size) : (i += 1) { - var j: usize = 0; - while (j < size) : (j += 1) { - const cx = @as(i64, @intCast(i)) - @as(i64, @intCast(size / 2)); - const cy = @as(i64, @intCast(j)) - @as(i64, @intCast(size / 2)); - const dist = std.math.sqrt(@as(f64, @floatFromInt(cx * cx + cy * cy))); - if (dist < 2) { - std.debug.print("●", .{}); - } else if (dist < 4) { - std.debug.print("○", .{}); - } else if (dist < 6) { - std.debug.print("◌", .{}); - } else { - std.debug.print("·", .{}); - } - } - std.debug.print("\n", .{}); - } -} - -/// Verify all sacred identities -pub fn runVerifyCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - _ = args; - - std.debug.print("Verifying Sacred Identities:\n", .{}); - std.debug.print("══════════════════════════\n", .{}); - - // Trinity Identity - const trinity_ok = gen_identities.TRINITY_IDENTITY.actual == 3.0; - std.debug.print("φ² + 1/φ² = 3: {s}\n", .{if (trinity_ok) "✓ PASS" else "✗ FAIL"}); - - // Phi Squared - const phi_sq = PHI * PHI; - const phi_sq_ok = @abs(phi_sq - (PHI + 1.0)) < 1e-10; - std.debug.print("φ² = φ + 1: {s}\n", .{if (phi_sq_ok) "✓ PASS" else "✗ FAIL"}); - - // Phi Inverse - const phi_inv = 1.0 / PHI; - const phi_inv_ok = @abs(phi_inv - (PHI - 1.0)) < 1e-10; - std.debug.print("1/φ = φ - 1: {s}\n", .{if (phi_inv_ok) "✓ PASS" else "✗ FAIL"}); - - std.debug.print("\nAll identities verified!\n", .{}); -} - -/// Compare φ^n vs F(n) vs L(n) -pub fn runCompareCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - - const max_n = if (parseFlag(args, "max-n")) |n| - std.fmt.parseInt(usize, n, 10) catch 20 - else - 20; - - std.debug.print("Comparing φ^n, F(n), L(n) for n=0..{d}:\n", .{max_n}); - std.debug.print("{s:>5} {s:>15} {s:>15} {s:>15}\n", .{ "n", "φ^n", "F(n)", "L(n)" }); - std.debug.print("{s:>5} {s:>15} {s:>15} {s:>15}\n", .{ "─────", "───────────────", "───────────────", "───────────────" }); - - var i: usize = 0; - while (i < @min(max_n, 20)) : (i += 1) { - const phi_val = gen_eval.phiPower(i); - const fib_val = if (i < gen_eval.fibonacci_cache.len) gen_eval.fibonacci_cache[i] else 0; - const lucas_val = if (i < gen_eval.lucas_cache.len) gen_eval.lucas_cache[i] else 0; - - std.debug.print("{d:>5} {d:>15.6} {d:>15} {d:>15}\n", .{ i, phi_val, fib_val, lucas_val }); - } -} - -/// Run performance benchmarks -pub fn runBenchCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = args; - - const gen_bench = @import("gen_bench.zig"); - - std.debug.print("Running Sacred Mathematics Benchmarks...\n", .{}); - - const config = gen_bench.BenchmarkConfig{ - .iterations_override = 10000, - .warmup_iterations = 100, - .log_to_nexus = false, - }; - - const suite = gen_bench.runAllBenchmarks(allocator, config) catch { - std.debug.print("Benchmark failed\n", .{}); - return; - }; - defer allocator.free(suite.results); - - std.debug.print("\n{s:>30} {s:>15}\n", .{ "Benchmark", "Ops/sec" }); - std.debug.print("{s:>30} {s:>15}\n", .{ "─────────────────────────────", "───────────────" }); - - for (suite.results) |r| { - std.debug.print("{s:>30} {d:>15.0}\n", .{ r.name, r.ops_per_second }); - } -} - -/// Show all φ-identities with proofs -pub fn runIdentitiesCommand(allocator: std.mem.Allocator, args: [][]const u8) void { - _ = allocator; - _ = args; - - const identities = gen_identities.ALL_IDENTITIES; - - std.debug.print("╔══════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ SACRED IDENTITIES ║\n", .{}); - std.debug.print("╠══════════════════════════════════════════════════════════════╣\n", .{}); - - for (identities) |id| { - std.debug.print("║ {s}: {s}\n", .{ id.name, id.formula }); - if (id.verified) { - std.debug.print("║ ✓ {s}\n", .{id.proof}); - } - if (id.special_note) |note| { - std.debug.print("║ Note: {s}\n", .{note}); - } - std.debug.print("║\n", .{}); - } - - std.debug.print("╚══════════════════════════════════════════════════════════════╝\n", .{}); -} - -/// Display math command help -pub fn showMathHelp() void { - std.debug.print("{s}\n", .{MATH_HELP_TEXT}); -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Math CLI: MATH_HELP_TEXT not empty" { - try std.testing.expect(@as(usize, 1000) < MATH_HELP_TEXT.len); -} - -test "Math CLI: parseFormatFlag default" { - const args3_arr = [_][]const u8{}; - try std.testing.expectEqual(.pretty, parseFormatFlag(&args3_arr)); -} - -test "Math CLI: parseFormatFlag json" { - var args1 = try std.ArrayList([]const u8).initCapacity(std.testing.allocator, 1); - defer args1.deinit(std.testing.allocator); - try args1.append(std.testing.allocator, "--format=json"); - - try std.testing.expectEqual(.json, parseFormatFlag(args1.items)); -} - -test "Math CLI: parseFlag basic" { - var args = try std.ArrayList([]const u8).initCapacity(std.testing.allocator, 2); - defer args.deinit(std.testing.allocator); - try args.append(std.testing.allocator, "--format=json"); - try args.append(std.testing.allocator, "--verbose"); - - try std.testing.expect(parseFlag(args.items, "format") != null); - try std.testing.expect(parseFlag(args.items, "verbose") != null); - try std.testing.expect(parseFlag(args.items, "missing") == null); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig deleted file mode 100644 index 7035198..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_constants.zig +++ /dev/null @@ -1,374 +0,0 @@ -//! Math Constants — Generated from specs/tri/math_constants.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from constants.tri spec -//! Modify spec and regenerate: vibee gen constants - -const std = @import("std"); - -// ============================================================================ -// GOLDEN RATIO CONSTANTS -// ============================================================================ - -/// Golden Ratio — divine proportion -/// φ = (1 + √5) / 2 -pub const PHI: f64 = 1.6180339887498948482; - -/// Phi squared -/// φ² = φ + 1 -pub const PHI_SQUARED: f64 = 2.6180339887498948482; - -/// Inverse phi squared -/// 1/φ² = φ - 1 -pub const PHI_INV_SQUARED: f64 = 0.3819660112501051518; - -/// TRINITY IDENTITY — exact equality -/// φ² + 1/φ² = 3 -pub const TRINITY_SUM: f64 = 3.0; - -// ============================================================================ -// TRANSCENDENTAL CONSTANTS -// ============================================================================ - -/// Pi — circle constant -/// π = circle circumference / diameter -pub const PI: f64 = 3.14159265358979323846; - -/// Euler's number — natural log base -/// e = lim(n→∞) (1 + 1/n)ⁿ -pub const E: f64 = 2.71828182845904523536; - -/// Transcendental product — ≈ TRYTE_MAX (13) -/// π × φ × e ≈ 13.82 -pub const TRANSCENDENTAL_PRODUCT: f64 = 13.816890703380645; - -// ============================================================================ -// GENETIC ALGORITHM CONSTANTS -// ============================================================================ - -/// Mutation rate -/// μ = 1/φ²/10 -pub const MU: f64 = 0.0382; - -/// Crossover rate -/// χ = 1/φ/10 -pub const CHI: f64 = 0.0618; - -/// Selection pressure -/// σ = φ -pub const SIGMA: f64 = 1.618; - -/// Elitism rate -/// ε = 1/3 -pub const EPSILON: f64 = 0.333; - -// ============================================================================ -// QUANTUM CONSTANTS -// ============================================================================ - -/// Bell inequality violation — quantum advantage -/// CHSH = 2√2 -pub const CHSH: f64 = 2.8284271247461903; - -/// Fine structure constant inverse -/// α⁻¹ = 4π³ + π² + π -pub const FINE_STRUCTURE: f64 = 137.036; - -/// Berry phase for quantum-inspired computation -/// β = π(1 - 1/φ) -pub const BERRY_PHASE: f64 = 2.112; - -/// SU3 energy harvesting constant -/// SU3 = 3/(2φ) -pub const SU3_CONSTANT: f64 = 0.927; - -// ============================================================================ -// DATA STRUCTURES -// ============================================================================ - -/// Single constant entry for display -pub const ConstantEntry = struct { - name: []const u8, - symbol: []const u8, - value: f64, - formula: []const u8, - description: []const u8, - color: []const u8, -}; - -/// Group of related constants -pub const ConstantGroup = struct { - name: []const u8, - constants: []const ConstantEntry, -}; - -// ============================================================================ -// BEHAVIORS / FUNCTIONS -// ============================================================================ - -/// Verify TRINITY IDENTITY at runtime -/// φ² + 1/φ² = 3 -pub fn verifyTrinityIdentity() bool { - const left = PHI_SQUARED + PHI_INV_SQUARED; - return std.math.approxEqAbs(f64, left, TRINITY_SUM, 1e-10); -} - -/// Get all sacred constants grouped by category -pub const ALL_CONSTANT_GROUPS = blk: { - // GOLDEN RATIO constants - const gold_constants = [_]ConstantEntry{ - ConstantEntry{ - .name = "phi", - .symbol = "φ", - .value = PHI, - .formula = "(1 + √5) / 2", - .description = "Golden Ratio — divine proportion", - .color = "gold", - }, - ConstantEntry{ - .name = "phi_squared", - .symbol = "φ²", - .value = PHI_SQUARED, - .formula = "φ² = φ + 1", - .description = "Phi squared", - .color = "gold", - }, - ConstantEntry{ - .name = "phi_inv_squared", - .symbol = "1/φ²", - .value = PHI_INV_SQUARED, - .formula = "1/φ² = φ - 1", - .description = "Inverse phi squared", - .color = "gold", - }, - ConstantEntry{ - .name = "trinity_sum", - .symbol = "φ² + 1/φ²", - .value = TRINITY_SUM, - .formula = "φ² + 1/φ² = 3", - .description = "TRINITY IDENTITY — exact equality", - .color = "gold", - }, - }; - - // TRANSCENDENTAL constants - const transcend_constants = [_]ConstantEntry{ - ConstantEntry{ - .name = "pi", - .symbol = "π", - .value = PI, - .formula = "Circle circumference / diameter", - .description = "Pi — circle constant", - .color = "cyan", - }, - ConstantEntry{ - .name = "e", - .symbol = "e", - .value = E, - .formula = "lim(n→∞) (1 + 1/n)ⁿ", - .description = "Euler's number — natural log base", - .color = "cyan", - }, - ConstantEntry{ - .name = "transcendental_product", - .symbol = "π × φ × e", - .value = TRANSCENDENTAL_PRODUCT, - .formula = "π × φ × e", - .description = "Transcendental product — ≈ TRYTE_MAX (13)", - .color = "purple", - }, - }; - - // GENETIC ALGORITHM constants - const genetic_constants = [_]ConstantEntry{ - ConstantEntry{ - .name = "mu", - .symbol = "μ", - .value = MU, - .formula = "1/φ²/10", - .description = "Mutation rate", - .color = "yellow", - }, - ConstantEntry{ - .name = "chi", - .symbol = "χ", - .value = CHI, - .formula = "1/φ/10", - .description = "Crossover rate", - .color = "yellow", - }, - ConstantEntry{ - .name = "sigma", - .symbol = "σ", - .value = SIGMA, - .formula = "φ", - .description = "Selection pressure", - .color = "yellow", - }, - ConstantEntry{ - .name = "epsilon", - .symbol = "ε", - .value = EPSILON, - .formula = "1/3", - .description = "Elitism rate", - .color = "yellow", - }, - }; - - // QUANTUM constants - const quantum_constants = [_]ConstantEntry{ - ConstantEntry{ - .name = "chsh", - .symbol = "CHSH", - .value = CHSH, - .formula = "2√2", - .description = "Bell inequality violation — quantum advantage", - .color = "purple", - }, - ConstantEntry{ - .name = "fine_structure", - .symbol = "α⁻¹", - .value = FINE_STRUCTURE, - .formula = "4π³ + π² + π", - .description = "Fine structure constant inverse", - .color = "purple", - }, - ConstantEntry{ - .name = "berry_phase", - .symbol = "β", - .value = BERRY_PHASE, - .formula = "π(1 - 1/φ)", - .description = "Berry phase for quantum-inspired computation", - .color = "purple", - }, - ConstantEntry{ - .name = "su3_constant", - .symbol = "SU3", - .value = SU3_CONSTANT, - .formula = "3/(2φ)", - .description = "SU3 energy harvesting constant", - .color = "purple", - }, - }; - - break :blk [_]ConstantGroup{ - ConstantGroup{ - .name = "GOLDEN RATIO", - .constants = &gold_constants, - }, - ConstantGroup{ - .name = "TRANSCENDENTAL", - .constants = &transcend_constants, - }, - ConstantGroup{ - .name = "GENETIC ALGORITHM", - .constants = &genetic_constants, - }, - ConstantGroup{ - .name = "QUANTUM", - .constants = &quantum_constants, - }, - }; -}; - -/// Lookup constant by name (returns null if not found) -pub fn getConstantByName(name: []const u8) ?ConstantEntry { - const groups = &ALL_CONSTANT_GROUPS; - for (groups) |group| { - for (group.constants) |entry| { - if (std.mem.eql(u8, entry.name, name)) { - return entry; - } - } - } - return null; -} - -// ============================================================================ -// COMPILE-TIME VERIFICATION -// ============================================================================ - -// Verify the TRINITY IDENTITY at compile time -comptime { - const trinity_identity = PHI_SQUARED + PHI_INV_SQUARED; - const diff = @abs(trinity_identity - TRINITY_SUM); - if (diff > 1e-10) { - @compileError("TRINITY IDENTITY VIOLATED: φ² + 1/φ² ≠ 3"); - } -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Math Constants - TRINITY identity" { - try std.testing.expect(verifyTrinityIdentity()); - const left = PHI_SQUARED + PHI_INV_SQUARED; - try std.testing.expectApproxEqAbs(TRINITY_SUM, left, 1e-10); -} - -test "Math Constants - PHI relationships" { - // φ² = φ + 1 - try std.testing.expectApproxEqAbs(PHI_SQUARED, PHI + 1.0, 1e-10); - // 1/φ² = 2 - φ (since φ² = φ + 1, so 1/φ² = 1/(φ+1) = φ - 1... wait) - // Actually: 1/φ = φ - 1 ≈ 0.618 - // And 1/φ² = (1/φ)² ≈ 0.382 - // So φ² + 1/φ² = 2.618 + 0.382 = 3.0 ✓ - try std.testing.expectApproxEqAbs(PHI_INV_SQUARED, 2.0 - PHI, 1e-10); -} - -test "Math Constants - transcendental product" { - // π × φ × e ≈ 13.82 - const product = PI * PHI * E; - try std.testing.expectApproxEqAbs(TRANSCENDENTAL_PRODUCT, product, 0.001); -} - -test "Math Constants - genetic algorithm constants" { - try std.testing.expectApproxEqAbs(MU, 1.0 / (PHI * PHI) / 10.0, 1e-5); - try std.testing.expectApproxEqAbs(CHI, 1.0 / PHI / 10.0, 1e-5); - try std.testing.expectApproxEqAbs(SIGMA, PHI, 1e-3); - try std.testing.expectApproxEqAbs(EPSILON, 1.0 / 3.0, 0.001); -} - -test "Math Constants - quantum constants" { - // CHSH = 2√2 - try std.testing.expectApproxEqAbs(CHSH, 2.0 * std.math.sqrt(2.0), 1e-10); - // SU3 = 3/(2φ) ≈ 0.927 - try std.testing.expectApproxEqAbs(SU3_CONSTANT, 3.0 / (2.0 * PHI), 0.001); - // Berry phase — verify it's in expected range (2.0 - 2.2) - try std.testing.expect(BERRY_PHASE > 2.0 and BERRY_PHASE < 2.2); - // Berry phase formula: π(1 - 1/φ) ≈ 1.2, but spec uses 2.112 - // Test that our constant is non-zero and positive - try std.testing.expect(BERRY_PHASE > 0); -} - -test "Math Constants - ALL_CONSTANT_GROUPS" { - const groups = &ALL_CONSTANT_GROUPS; - try std.testing.expectEqual(@as(usize, 4), groups.len); - - // Check GOLDEN RATIO group - try std.testing.expectEqualSlices(u8, "GOLDEN RATIO", groups[0].name); - try std.testing.expectEqual(@as(usize, 4), groups[0].constants.len); - - // Check TRANSCENDENTAL group - try std.testing.expectEqualSlices(u8, "TRANSCENDENTAL", groups[1].name); - try std.testing.expectEqual(@as(usize, 3), groups[1].constants.len); - - // Check GENETIC ALGORITHM group - try std.testing.expectEqualSlices(u8, "GENETIC ALGORITHM", groups[2].name); - try std.testing.expectEqual(@as(usize, 4), groups[2].constants.len); - - // Check QUANTUM group - try std.testing.expectEqualSlices(u8, "QUANTUM", groups[3].name); - try std.testing.expectEqual(@as(usize, 4), groups[3].constants.len); -} - -test "Math Constants - getConstantByName" { - const phi_entry = getConstantByName("phi"); - try std.testing.expect(phi_entry != null); - try std.testing.expectEqualSlices(u8, "phi", phi_entry.?.name); - try std.testing.expectApproxEqAbs(PHI, phi_entry.?.value, 1e-10); - - const unknown_entry = getConstantByName("unknown"); - try std.testing.expect(unknown_entry == null); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig deleted file mode 100644 index 86bf7d5..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_eval.zig +++ /dev/null @@ -1,497 +0,0 @@ -//! Math Eval — Generated from specs/tri/math/math_eval.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from math_eval.tri spec -//! phi^n, fib(n), lucas(n) evaluation - -const std = @import("std"); - -// Re-export sacred constants -const PHI = @import("gen_constants.zig").PHI; -const TRINITY_SUM = @import("gen_constants.zig").TRINITY_SUM; - -// ============================================================================ -// TYPES -// ============================================================================ - -/// Type of mathematical sequence -pub const SequenceType = enum(u8) { - phi_power, - fibonacci, - lucas, -}; - -/// Result of sequence evaluation -pub const EvalResult = struct { - sequence: SequenceType, - n: usize, - value_str: []const u8, - digit_count: usize, - is_trinity: bool, - is_tryte_max: bool, - special_note: ?[]const u8, -}; - -/// Configuration for evaluation -pub const EvalConfig = struct { - precision: usize = 16, - use_cache: bool = true, - format: OutputFormat = .decimal, -}; - -/// Output format for results -pub const OutputFormat = enum(u8) { - decimal, - scientific, - mixed, -}; - -// ============================================================================ -// CACHE TABLES -// ============================================================================ - -/// Pre-computed φⁿ for n = 0..99 -pub const phi_powers_cache = [100]f64{ - 1.0, // φ⁰ - 1.618033988749895, // φ¹ - 2.618033988749895, // φ² - 4.23606797749979, // φ³ - 6.854101966249685, // φ⁴ - 11.090169943749474, // φ⁵ - 17.94427190999916, // φ⁶ - 29.034441853748636, // φ⁷ - 46.978713763747806, // φ⁸ - 76.01315561749616, // φ⁹ - 122.99186938124422, // φ¹⁰ - 199.0050249987404, // φ¹¹ - 321.9968943800, // φ¹² - 521.0019193787403, // φ¹³ - 842.9988137674033, // φ¹⁴ - 1364.0007331458488, // φ¹⁵ - 2206.999546913252, // φ¹⁶ - 3571.000280059101, // φ¹⁷ - 5777.999826972353, // φ¹⁸ - 9349.000107031454, // φ¹⁹ - 15126.999934011399, // φ²⁰ - 24476.000041077506, // φ²¹ - 39602.9999750889, // φ²² - 64079.0000161664, // φ²³ - 103682.00001233732, // φ²⁴ - 167761.00002850372, // φ²⁵ - 271443.00004084104, // φ²⁶ - 439204.00006934477, // φ²⁷ - 710647.0001101858, // φ²⁸ - 1149851.0001795305, // φ²⁹ - 1860498.0002897163, // φ³⁰ - 3010349.0004692469, // φ³¹ - 4870847.0007589633, // φ³² - 7881196.00122821, // φ³³ - 12752043.001987173, // φ³⁴ - 20633239.003215383, // φ³⁵ - 33385282.005202556, // φ³⁶ - 54018521.008417938, // φ³⁷ - 87403803.013620496, // φ³⁸ - 141422324.02203843, // φ³⁹ - 228826127.03565893, // φ⁴⁰ - 370248451.05769736, // φ⁴¹ - 599074578.0933563, // φ⁴² - 969323029.1510537, // φ⁴³ - 1568397607.24441, // φ⁴⁴ - 2537720636.3954635, // φ⁴⁵ - 4106116243.639874, // φ⁴⁶ - 6643836880.035337, // φ⁴⁷ - 10749953123.675211, // φ⁴⁸ - 17393790003.71055, // φ⁴⁹ - 28143743127.38576, // φ⁵⁰ - 45537533131.09631, // φ⁵¹ - 73681276258.48207, // φ⁵² - 119218809389.57838, // φ⁵³ - 192900085648.06046, // φ⁵⁴ - 312118895037.63882, // φ⁵⁵ - 505018980685.6993, // φ⁵⁶ - 817137875723.3381, // φ⁵⁷ - 1322156759409.0374, // φ⁵⁸ - 2139294635132.3755, // φ⁵⁹ - 3461451394541.413, // φ⁶⁰ - 5600746029673.788, // φ⁶¹ - 9062197424215.201, // φ⁶² - 14662943553889.0, // φ⁶³ - 23725140981206.102, // φ⁶⁴ - 38388084533273.3, // φ⁶⁵ - 62113225514479.4, // φ⁶⁶ - 100501310047752.7, // φ⁶⁷ - 162614535562232.12, // φ⁶⁸ - 263115845609984.84, // φ⁶⁹ - 425730381172216.94, // φ⁷⁰ - 688846226782201.8, // φ⁷¹ - 1114576607954418.8, // φ⁷² - 1803422834736620.5, // φ⁷³ - 2917999442691039.5, // φ⁷⁴ - 4721422277427660.0, // φ⁷⁵ - 7639421720118699.0, // φ⁷⁶ - 12360843997546359.0, // φ⁷⁷ - 20000265717665056.0, // φ⁷⁸ - 32361109715211412.0, // φ⁷⁹ - 52361375432876472.0, // φ⁸⁰ - 84722485148087888.0, // φ⁸¹ - 137083860580964368.0, // φ⁸² - 221806345729052256.0, // φ⁸³ - 358890206310016640.0, // φ⁸⁴ - 580696552039068928.0, // φ⁸⁵ - 939586758349085632.0, // φ⁸⁶ - 1520283310388154624.0, // φ⁸⁷ - 2459870068737240064.0, // φ⁸⁸ - 3980153379125393920.0, // φ⁸⁹ - 6440023447862633984.0, // φ⁹⁰ - 10420176826988028032.0, // φ⁹¹ - 16860200274850662016.0, // φ⁹² - 27280377101838690304.0, // φ⁹³ - 44140577376689353216.0, // φ⁹⁴ - 71420954478528043520.0, // φ⁹⁵ - 115561531855217393664.0, // φ⁹⁶ - 186982486333745437696.0, // φ⁹⁷ - 302544018188962839552.0, // φ⁹⁸ - 489526504522708323840.0, // φ⁹⁹ -}; - -/// F(n) for n < 94 (fits in u64) -pub const fibonacci_cache = [94]u64{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738 }; - -/// L(n) for n < 94 (fits in u64) -pub const lucas_cache = [94]u64{ 2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636, 4106116243, 6643836879, 10749953122, 17393790001, 28143743123, 45537533124, 73681276247, 119218809371, 192900165618, 312119054989, 505019220607, 817138275596, 1322157506203, 2139295781799, 3461453288002, 5600749069801, 9062202357803, 14662951427584, 23725153785387, 38388105212971, 62113258998358, 100501364211329, 162614623209687, 263115987421016, 425730610630703, 6888465093728719, 111457761359422, 180342412896671, 291800174256093, 472142587152764, 763942761408857, 1236085348561621, 2000028109970478, 3236113458532099, 5236141568502577, 8472255027034676, 13708396595537253, 22180651622567229, 35889048218139782, 58069699840707011, 93958748058846793, 152028447999553804, 245987228054385597, 398015713049924401, 644002941104309998, 1042018654154234399, 1686021595258544397, 2728040249412778796 }; - -// ============================================================================ -// SEQUENCE FUNCTIONS -// ============================================================================ - -/// Compute φ^n using cache for small n -pub fn phiPower(n: usize) f64 { - if (n < phi_powers_cache.len) { - return phi_powers_cache[n]; - } - return std.math.pow(f64, PHI, @as(f64, @floatFromInt(n))); -} - -/// Compute F(n) - Fibonacci number -pub fn fibonacciBigInt(allocator: std.mem.Allocator, n: usize) !EvalResult { - var value: u64 = 0; - - if (n < fibonacci_cache.len) { - value = fibonacci_cache[n]; - } else { - // Fast doubling algorithm (clamped for safety) - value = fibonacciFastDoubing(n); - } - - var buf: [64]u8 = undefined; - const value_str = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "N/A"; - const digit_count = countDigits(value); - - return EvalResult{ - .sequence = .fibonacci, - .n = n, - .value_str = try allocator.dupe(u8, value_str), - .digit_count = digit_count, - .is_trinity = (n == 4), // F(4) = 3 = TRINITY - .is_tryte_max = (n == 7), // F(7) = 13 = TRYTE_MAX - .special_note = null, - }; -} - -/// Fast doubling algorithm for Fibonacci (clamped) -fn fibonacciFastDoubing(n: usize) u64 { - if (n == 0) return 0; - if (n == 1) return 1; - if (n > 90) return 2_880_067_194_370_816_120; // F(90), clamped - - var a: u64 = 0; - var b: u64 = 1; - - var i: usize = 2; - while (i <= n) : (i += 1) { - const next = a + b; - if (next < a) return b; // Overflow - a = b; - b = next; - } - - return b; -} - -/// Compute L(n) - Lucas number -pub fn lucasBigInt(allocator: std.mem.Allocator, n: usize) !EvalResult { - var value: u64 = 0; - - if (n < lucas_cache.len) { - value = lucas_cache[n]; - } else { - value = lucasFastDoubing(n); - } - - var buf: [64]u8 = undefined; - const value_str = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "N/A"; - const digit_count = countDigits(value); - - return EvalResult{ - .sequence = .lucas, - .n = n, - .value_str = try allocator.dupe(u8, value_str), - .digit_count = digit_count, - .is_trinity = (n == 2), // L(2) = 3 = TRINITY - .is_tryte_max = false, - .special_note = if (n <= 10) "L(n) = φⁿ + 1/φⁿ" else null, - }; -} - -/// Fast doubling for Lucas (clamped) -fn lucasFastDoubing(n: usize) u64 { - if (n == 0) return 2; - if (n == 1) return 1; - if (n > 90) return 3_788_906_237_314_390_60; // L(90), clamped - - var a: u64 = 2; - var b: u64 = 1; - - var i: usize = 2; - while (i <= n) : (i += 1) { - const next = a + b; - if (next < a) return b; // Overflow - a = b; - b = next; - } - - return b; -} - -/// Print evaluation result with formatting -pub fn printEvalResult(result: EvalResult, config: EvalConfig) void { - _ = config; - const seq_name = switch (result.sequence) { - .phi_power => "φ", - .fibonacci => "F", - .lucas => "L", - }; - - std.debug.print("{s}({d}) = {s}", .{ seq_name, result.n, result.value_str }); - - if (result.digit_count > 0) { - std.debug.print(" [{d} digits]", .{result.digit_count}); - } - - if (result.is_trinity) { - std.debug.print(" = TRINITY (3)", .{}); - } - - if (result.is_tryte_max) { - std.debug.print(" = TRYTE_MAX (13)", .{}); - } - - if (result.special_note) |note| { - std.debug.print(" [{s}]", .{note}); - } - - std.debug.print("\n", .{}); -} - -/// Format number with digit grouping (commas every 3 digits) -pub fn formatBigInt(allocator: std.mem.Allocator, value: anytype, use_cache: bool) ![]const u8 { - _ = value; - _ = use_cache; - _ = allocator; - return error.NotImplemented; -} - -/// Count digits in a number -pub fn countDigits(value: u64) usize { - if (value == 0) return 1; - var count: usize = 0; - var n = value; - while (n > 0) { - n /= 10; - count += 1; - } - return count; -} - -/// Format number with commas -fn formatNumber(allocator: std.mem.Allocator, value: u64, use_cache: bool) ![]const u8 { - _ = use_cache; - var buf: [64]u8 = undefined; - - const int_part = std.fmt.bufPrint(&buf, "{d}", .{value}) catch "0"; - - // Add commas every 3 digits - const len = int_part.len; - var result: [128]u8 = undefined; - var result_idx: usize = 0; - var digits_seen: usize = 0; - - var i: usize = len; - while (i > 0) : (i -= 1) { - if (digits_seen > 0 and digits_seen % 3 == 0 and i > 0) { - result[result_idx] = ','; - result_idx += 1; - } - result[result_idx] = int_part[i - 1]; - result_idx += 1; - digits_seen += 1; - } - - const formatted = result[0..result_idx]; - return allocator.dupe(u8, formatted); -} - -/// Check if value equals 3 (TRINITY) -pub fn verifyTrinityValue(value: anytype) bool { - if (@typeInfo(@TypeOf(value)) == .int) { - return @as(u64, value) == 3; - } - if (@typeInfo(@TypeOf(value)) == .float) { - return @abs(@as(f64, value) - 3.0) < 1e-10; - } - return false; -} - -/// Check if value equals 13 (TRYTE_MAX) -pub fn verifyTryteMax(value: anytype) bool { - if (@typeInfo(@TypeOf(value)) == .int) { - return @as(u64, value) == 13; - } - if (@typeInfo(@TypeOf(value)) == .float) { - return @abs(@as(f64, value) - 13.0) < 1e-10; - } - return false; -} - -/// Get metadata about sequence value -pub fn getSequenceInfo(allocator: std.mem.Allocator, seq_type: SequenceType, n: usize) !EvalResult { - return switch (seq_type) { - .phi_power => { - const val = phiPower(n); - var buf: [64]u8 = undefined; - const str = std.fmt.bufPrint(&buf, "{d:.16}", .{val}) catch "N/A"; - return EvalResult{ - .sequence = .phi_power, - .n = n, - .value_str = try allocator.dupe(u8, str), - .digit_count = 0, - .is_trinity = false, - .is_tryte_max = false, - .special_note = null, - }; - }, - .fibonacci => try fibonacciBigInt(allocator, n), - .lucas => try lucasBigInt(allocator, n), - }; -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Math Eval: phiPower basic" { - try std.testing.expectApproxEqAbs(@as(f64, 1.0), phiPower(0), 1e-10); - try std.testing.expectApproxEqAbs(PHI, phiPower(1), 1e-10); - try std.testing.expectApproxEqAbs(2.618033988749895, phiPower(2), 1e-10); -} - -test "Math Eval: phiPower cache" { - for (0..20) |i| { - const cached = phi_powers_cache[i]; - const computed = std.math.pow(f64, PHI, @as(f64, @floatFromInt(i))); - try std.testing.expectApproxEqAbs(cached, computed, 1e-7); - } -} - -test "Math Eval: fibonacci small" { - try std.testing.expectEqual(@as(u64, 0), fibonacci_cache[0]); - try std.testing.expectEqual(@as(u64, 1), fibonacci_cache[1]); - try std.testing.expectEqual(@as(u64, 1), fibonacci_cache[2]); - try std.testing.expectEqual(@as(u64, 2), fibonacci_cache[3]); - try std.testing.expectEqual(@as(u64, 3), fibonacci_cache[4]); -} - -test "Math Eval: lucas small" { - try std.testing.expectEqual(@as(u64, 2), lucas_cache[0]); - try std.testing.expectEqual(@as(u64, 1), lucas_cache[1]); - try std.testing.expectEqual(@as(u64, 3), lucas_cache[2]); - try std.testing.expectEqual(@as(u64, 4), lucas_cache[3]); -} - -test "Math Eval: fibonacciBigInt F(4) = TRINITY" { - const allocator = std.testing.allocator; - const result = try fibonacciBigInt(allocator, 4); - defer allocator.free(result.value_str); - try std.testing.expect(result.is_trinity); -} - -test "Math Eval: lucasBigInt L(2) = TRINITY" { - const allocator = std.testing.allocator; - const result = try lucasBigInt(allocator, 2); - defer allocator.free(result.value_str); - try std.testing.expect(result.is_trinity); -} - -test "Math Eval: fibonacciBigInt F(7) = TRYTE_MAX" { - const allocator = std.testing.allocator; - const result = try fibonacciBigInt(allocator, 7); - defer allocator.free(result.value_str); - try std.testing.expect(result.is_tryte_max); -} - -test "Math Eval: verifyTrinityValue" { - try std.testing.expect(verifyTrinityValue(@as(u64, 3))); - try std.testing.expect(verifyTrinityValue(@as(f64, 3.0))); - try std.testing.expect(!verifyTrinityValue(4)); -} - -test "Math Eval: verifyTryteMax" { - try std.testing.expect(verifyTryteMax(@as(u64, 13))); - try std.testing.expect(verifyTryteMax(@as(f64, 13.0))); - try std.testing.expect(!verifyTryteMax(14)); -} - -test "Math Eval: countDigits" { - try std.testing.expectEqual(@as(usize, 1), countDigits(0)); - try std.testing.expectEqual(@as(usize, 1), countDigits(5)); - try std.testing.expectEqual(@as(usize, 2), countDigits(42)); - try std.testing.expectEqual(@as(usize, 3), countDigits(100)); - try std.testing.expectEqual(@as(usize, 4), countDigits(9999)); -} - -test "Math Eval: phi_powers_cache size" { - try std.testing.expectEqual(@as(usize, 100), phi_powers_cache.len); -} - -test "Math Eval: fibonacci_cache size" { - try std.testing.expectEqual(@as(usize, 94), fibonacci_cache.len); -} - -test "Math Eval: lucas_cache size" { - try std.testing.expectEqual(@as(usize, 94), lucas_cache.len); -} - -test "Math Eval: getSequenceInfo phi_power" { - const allocator = std.testing.allocator; - const result = try getSequenceInfo(allocator, .phi_power, 10); - defer allocator.free(result.value_str); - try std.testing.expectEqual(.phi_power, result.sequence); - try std.testing.expectEqual(@as(usize, 10), result.n); -} - -test "Math Eval: getSequenceInfo fibonacci" { - const allocator = std.testing.allocator; - const result = try getSequenceInfo(allocator, .fibonacci, 10); - defer allocator.free(result.value_str); - try std.testing.expectEqual(.fibonacci, result.sequence); - try std.testing.expectEqual(@as(usize, 10), result.n); - try std.testing.expect(result.is_tryte_max == false); -} - -test "Math Eval: getSequenceInfo lucas" { - const allocator = std.testing.allocator; - const result = try getSequenceInfo(allocator, .lucas, 10); - defer allocator.free(result.value_str); - try std.testing.expectEqual(.lucas, result.sequence); - try std.testing.expectEqual(@as(usize, 10), result.n); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig deleted file mode 100644 index 6a2d5c1..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_format.zig +++ /dev/null @@ -1,394 +0,0 @@ -//! Math Format — Generated from specs/tri/math_format.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from format.tri spec -//! Modify spec and regenerate: vibee gen format - -const std = @import("std"); - -// ============================================================================ -// COLOR STYLES -// ============================================================================ - -/// ANSI color codes for terminal output -pub const ColorStyle = struct { - /// Reset all styles - pub const RESET: []const u8 = "\x1b[0m"; - - /// Gold color — for Golden ratio values, TRINITY - pub const GOLD: []const u8 = "\x1b[38;5;220m"; - - /// Cyan color — for Transcendental numbers (π, e) - pub const CYAN: []const u8 = "\x1b[36m"; - - /// Purple color — for Quantum constants, sacred identities - pub const PURPLE: []const u8 = "\x1b[38;5;141m"; - - /// Green color — for Success, verification passed - pub const GREEN: []const u8 = "\x1b[32m"; - - /// Red color — for Errors, verification failed - pub const RED: []const u8 = "\x1b[31m"; - - /// Yellow color — for Warnings, benchmarks - pub const YELLOW: []const u8 = "\x1b[33m"; -}; - -// ============================================================================ -// OUTPUT FORMAT -// ============================================================================ - -/// Output format options -pub const OutputFormat = enum(u8) { - pretty = 0, - json = 1, - csv = 2, -}; - -/// Text alignment -pub const Alignment = enum(u8) { - left = 0, - center = 1, - right = 2, -}; - -// ============================================================================ -// DATA STRUCTURES -// ============================================================================ - -/// Configuration for output formatting -pub const FormatConfig = struct { - format: OutputFormat = .pretty, - precision: usize = 16, - use_colors: bool = true, - show_plot: bool = false, -}; - -/// Table column definition -pub const TableColumn = struct { - header: []const u8, - width: usize, - alignment: Alignment, -}; - -/// Table formatting configuration -pub const TableFormat = struct { - columns: []const TableColumn, - padding: usize = 2, - show_borders: bool = true, -}; - -// ============================================================================ -// BEHAVIORS / FUNCTIONS -// ============================================================================ - -/// Print text with specified color -pub fn printColored(color: []const u8, text: []const u8) void { - std.debug.print("{s}{s}{s}", .{ color, text, ColorStyle.RESET }); -} - -/// Format float with precision (simplified - uses default Zig float formatting) -pub fn formatFloat(allocator: std.mem.Allocator, value: f64, precision: usize) ![]u8 { - _ = precision; - - // For Zig 0.15, use bufPrint for float formatting - var buf: [64]u8 = undefined; - const formatted = std.fmt.bufPrint(&buf, "{d}", .{value}) catch return error.FormatFailed; - - // Copy to allocated buffer - const result = try allocator.alloc(u8, formatted.len); - @memcpy(result, formatted); - - return result; -} - -/// Format integer with digit grouping (commas every 3 digits) -pub fn formatIntGrouped(allocator: std.mem.Allocator, value: i64) ![]u8 { - // Handle zero case - if (value == 0) { - return allocator.dupe(u8, "0"); - } - - // Handle negative numbers - const is_negative = value < 0; - const abs_value: u64 = if (is_negative) @intCast(-value) else @intCast(value); - - // Count digits - var temp: u64 = abs_value; - var num_digits: usize = 0; - while (temp > 0) { - temp /= 10; - num_digits += 1; - } - - // Calculate commas needed - const num_commas = if (num_digits > 3) (num_digits - 1) / 3 else 0; - - // Total length including optional minus sign - const total_len = num_digits + num_commas + @as(usize, @intFromBool(is_negative)); - - var buffer = try allocator.alloc(u8, total_len); - var write_pos: usize = total_len; - - // Build string from right to left - temp = abs_value; - var digit_idx: usize = 0; - - while (temp > 0) { - // Insert comma every 3 digits (but not at the start) - if (digit_idx > 0 and digit_idx % 3 == 0) { - write_pos -= 1; - buffer[write_pos] = ','; - } - - const digit = @as(u8, @intCast(temp % 10)) + '0'; - write_pos -= 1; - buffer[write_pos] = digit; - temp /= 10; - digit_idx += 1; - } - - // Add minus sign if needed - if (is_negative) { - buffer[0] = '-'; - } - - return buffer; -} - -/// Print table header -pub fn printTableHeader(columns: []const TableColumn, padding: usize) void { - // Print top border - printTableBorder(columns, padding, "╔", "╦", "╗"); - - // Print header row - for (columns, 0..) |col, i| { - const pad = " " ** padding; - const sep = if (i < columns.len - 1) "║" else "║"; - std.debug.print("{s}{s}{s}{s}", .{ pad, col.header, pad, sep }); - } - std.debug.print("\n", .{}); - - // Print header separator - printTableBorder(columns, padding, "╠", "╬", "╣"); -} - -/// Print table row -pub fn printTableRow(columns: []const TableColumn, values: []const []const u8, padding: usize) void { - for (columns, values, 0..) |col, val, i| { - _ = col; - const pad = " " ** padding; - const sep = if (i < columns.len - 1) "║" else "║"; - std.debug.print("{s}{s}{s}{s}", .{ pad, val, pad, sep }); - } - std.debug.print("\n", .{}); -} - -/// Print table footer -pub fn printTableFooter(columns: []const TableColumn, padding: usize) void { - printTableBorder(columns, padding, "╚", "╩", "╝"); -} - -/// Print table border -fn printTableBorder(columns: []const TableColumn, padding: usize, left: []const u8, mid: []const u8, right: []const u8) void { - std.debug.print("{s}", .{left}); - for (columns, 0..) |col, i| { - const width = col.width + (padding * 2); - const sep = if (i < columns.len - 1) mid else right; - const line = "═" ** width; - std.debug.print("{s}{s}", .{ line, sep }); - } - std.debug.print("\n", .{}); -} - -/// Export data as CSV string -pub fn exportCsv( - allocator: std.mem.Allocator, - headers: []const []const u8, - rows: []const []const []const u8, -) ![]u8 { - // Calculate needed length (approximate) - var total_len: usize = 0; - for (headers) |h| total_len += h.len + 3; // quotes + comma - total_len += 1; // newline - for (rows) |row| { - for (row) |cell| total_len += cell.len + 3; - total_len += 1; - } - - var buffer = try allocator.alloc(u8, total_len); - var pos: usize = 0; - - // Write header row - for (headers, 0..) |h, i| { - if (i > 0) { - buffer[pos] = ','; - pos += 1; - } - buffer[pos] = '"'; - pos += 1; - @memcpy(buffer[pos..][0..h.len], h); - pos += h.len; - buffer[pos] = '"'; - pos += 1; - } - buffer[pos] = '\n'; - pos += 1; - - // Write data rows - for (rows) |row| { - for (row, 0..) |cell, i| { - if (i > 0) { - buffer[pos] = ','; - pos += 1; - } - buffer[pos] = '"'; - pos += 1; - @memcpy(buffer[pos..][0..cell.len], cell); - pos += cell.len; - buffer[pos] = '"'; - pos += 1; - } - buffer[pos] = '\n'; - pos += 1; - } - - return buffer[0..pos]; -} - -/// Pad string to specified width with alignment -pub fn padString(allocator: std.mem.Allocator, s: []const u8, width: usize, alignment: Alignment) ![]u8 { - const len = s.len; - if (len >= width) { - return allocator.dupe(u8, s[0..width]); - } - - const padding = width - len; - const result = try allocator.alloc(u8, width); - - switch (alignment) { - .left => { - @memcpy(result[0..len], s); - @memset(result[len..], ' '); - }, - .right => { - @memset(result[0..padding], ' '); - @memcpy(result[padding..], s); - }, - .center => { - const left_pad = padding / 2; - @memset(result[0..left_pad], ' '); - @memcpy(result[left_pad..][0..len], s); - @memset(result[left_pad + len ..], ' '); - }, - } - - return result; -} - -// ============================================================================ -// TABLE TEMPLATES -// ============================================================================ - -/// Constants table template -pub const CONSTANTS_TABLE_COLUMNS = [_]TableColumn{ - TableColumn{ .header = "Constant", .width = 20, .alignment = .left }, - TableColumn{ .header = "Symbol", .width = 12, .alignment = .center }, - TableColumn{ .header = "Value", .width = 24, .alignment = .right }, - TableColumn{ .header = "Description", .width = 35, .alignment = .left }, -}; - -/// Compare table template -pub const COMPARE_TABLE_COLUMNS = [_]TableColumn{ - TableColumn{ .header = "n", .width = 6, .alignment = .right }, - TableColumn{ .header = "φⁿ", .width = 20, .alignment = .right }, - TableColumn{ .header = "F(n)", .width = 25, .alignment = .right }, - TableColumn{ .header = "L(n)", .width = 25, .alignment = .right }, -}; - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Format: printColored" { - // Just verify it compiles and doesn't crash - printColored(ColorStyle.GOLD, "test"); - printColored(ColorStyle.CYAN, "test"); - printColored(ColorStyle.PURPLE, "test"); - printColored(ColorStyle.GREEN, "test"); - printColored(ColorStyle.RED, "test"); - printColored(ColorStyle.YELLOW, "test"); -} - -test "Format: formatFloat" { - const allocator = std.testing.allocator; - - // formatFloat returns default Zig float formatting - const result1 = try formatFloat(allocator, 3.14159, 2); - defer allocator.free(result1); - // Check that it contains "3.14" somewhere (formatting may vary) - try std.testing.expect(std.mem.indexOf(u8, result1, "3.14") != null); - - const result2 = try formatFloat(allocator, 1.618, 6); - defer allocator.free(result2); - try std.testing.expect(std.mem.indexOf(u8, result2, "1.618") != null); -} - -test "Format: formatIntGrouped" { - const allocator = std.testing.allocator; - - const result1 = try formatIntGrouped(allocator, 1000); - defer allocator.free(result1); - try std.testing.expectEqualStrings("1,000", result1); - - const result2 = try formatIntGrouped(allocator, 1234567); - defer allocator.free(result2); - try std.testing.expectEqualStrings("1,234,567", result2); - - const result3 = try formatIntGrouped(allocator, -999); - defer allocator.free(result3); - try std.testing.expectEqualStrings("-999", result3); -} - -test "Format: exportCsv" { - const allocator = std.testing.allocator; - - const headers = [_][]const u8{ "Name", "Value" }; - const rows = [_][]const []const u8{ - &[_][]const u8{ "Phi", "1.618" }, - &[_][]const u8{ "Pi", "3.141" }, - }; - - const result = try exportCsv(allocator, &headers, &rows); - defer allocator.free(result); - - try std.testing.expectEqualStrings("\"Name\",\"Value\"\n\"Phi\",\"1.618\"\n\"Pi\",\"3.141\"\n", result); -} - -test "Format: padString" { - const allocator = std.testing.allocator; - - const result1 = try padString(allocator, "test", 10, .left); - defer allocator.free(result1); - try std.testing.expectEqualStrings("test ", result1); - - const result2 = try padString(allocator, "test", 10, .right); - defer allocator.free(result2); - try std.testing.expectEqualStrings(" test", result2); - - const result3 = try padString(allocator, "test", 10, .center); - defer allocator.free(result3); - try std.testing.expectEqualStrings(" test ", result3); -} - -test "Format: CONSTANTS_TABLE_COLUMNS" { - try std.testing.expectEqual(@as(usize, 4), CONSTANTS_TABLE_COLUMNS.len); - try std.testing.expectEqualStrings("Constant", CONSTANTS_TABLE_COLUMNS[0].header); - try std.testing.expectEqual(@as(usize, 20), CONSTANTS_TABLE_COLUMNS[0].width); -} - -test "Format: COMPARE_TABLE_COLUMNS" { - try std.testing.expectEqual(@as(usize, 4), COMPARE_TABLE_COLUMNS.len); - try std.testing.expectEqualStrings("n", COMPARE_TABLE_COLUMNS[0].header); - try std.testing.expectEqual(.right, COMPARE_TABLE_COLUMNS[0].alignment); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig deleted file mode 100644 index f635468..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_identities.zig +++ /dev/null @@ -1,235 +0,0 @@ -//! Math Identities — Generated from specs/tri/math_identities.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from identities.tri spec -//! Core sacred identities with proofs - -const std = @import("std"); - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -/// Golden Ratio — φ = (1 + √5) / 2 -pub const PHI: f64 = 1.618033988749895; - -/// Pi — circle constant -pub const PI: f64 = 3.141592653589793; - -/// Euler's number -pub const E: f64 = 2.718281828459045; - -/// Square root of 5 -pub const SQRT5: f64 = 2.2360679774979; - -// ============================================================================ -// TYPES -// ============================================================================ - -/// Category of mathematical identity -pub const IdentityCategory = enum(u8) { - golden_ratio, - sequences, - transcendental, - quantum, - trinity, - ternary, -}; - -/// Mathematical identity with proof -pub const Identity = struct { - name: []const u8, - formula: []const u8, - latex: []const u8, - category: IdentityCategory, - proof: []const u8, - verified: bool, - tolerance: ?f64, - special_note: ?[]const u8, - actual: f64 = 0.0, -}; - -/// Result of identity verification -pub const VerificationResult = struct { - identity: Identity, - expected: f64, - actual: f64, - diff: f64, - passed: bool, -}; - -// ============================================================================ -// ALL IDENTITIES (6 sacred identities) -// ============================================================================ - -/// Trinity Identity -pub const TRINITY_IDENTITY = Identity{ - .name = "Trinity Identity", - .formula = "φ² + 1/φ² = 3", - .latex = "\\phi^2 + \\phi^{-2} = 3", - .category = .trinity, - .proof = "Given φ² = φ + 1: 1/φ² = 3\nDivide by φ²: φ/φ = 1 → φ\nTherefore: φ² + 1/φ² = 3", - .verified = true, - .tolerance = 0.0, - .special_note = null, - .actual = 3.0, -}; - -/// Phi Squared -pub const PHI_SQUARED_IDENTITY = Identity{ - .name = "Phi Squared", - .formula = "φ² = φ + 1", - .latex = "\\phi^2 = \\phi + 1", - .category = .golden_ratio, - .proof = "From φ² = φ + 1, we have φ² = φ + 1\nTherefore: φ² = φ + 1", - .verified = true, - .tolerance = 0.0, - .special_note = null, - .actual = PHI * PHI, -}; - -/// Phi Inverse -pub const PHI_INVERSE_IDENTITY = Identity{ - .name = "Phi Inverse", - .formula = "1/φ = φ - 1", - .latex = "\\phi^{-1} = \\phi - 1", - .category = .golden_ratio, - .proof = "From 1/φ = φ - 1, multiply both sides by φ:\n1/φ = φ - 1 → φ² - φ = φ + 1 - φ² - 1 = φ² - φ - 1 = φ\nSimplify: φ² - 1 - φ = φ - 1 = (φ - 1)(φ - 1) = 1/φ² - 1\nSubtract φ² from both: φ² - 1 - (φ² - 1) - (φ - 1) = φ² - 1\nDivide by (φ² - 1): φ² - 1 / (φ² - 1) = 1 / (φ² - 1) = 1\nTherefore: φ² - 1 / φ² - 1 = 1 / φ² - 1 = 0.382", - .verified = true, - .tolerance = 0.001, - .special_note = "Using binet's formula for derivation", - .actual = 1.0 / PHI, -}; - -/// Phi Reciprocal -pub const PHI_RECIPROCAL_IDENTITY = Identity{ - .name = "Phi Reciprocal", - .formula = "1/φ = φ - 1", - .latex = "\\phi^{-1} = \\phi - 1", - .category = .golden_ratio, - .proof = "From 1/φ = φ - 1, multiply both sides by φ:\n1/φ = φ - 1 → φ\nTherefore: φ² - 1 = φ × (1/φ) / (1/φ)² = 1\nThis equals φ² + 1/φ² / φ² = 1 + 2(1/φ) / (1/φ)² = 1 = φ² + 1 / φ² - 1", - .verified = true, - .tolerance = 0.001, - .special_note = "Using series formula, binet derivation with ψ = 1 - 1/φ", - .actual = 1.0 / PHI, -}; - -/// Lucas Phi Powers -pub const LUCAS_PHI_POWERS_IDENTITY = Identity{ - .name = "Lucas Phi Powers", - .formula = "L(n) = φⁿ + 1/φⁿ", - .latex = "L(n) = \\phi^n + \\phi^{-n}", - .category = .sequences, - .proof = "Binet's formula for Lucas numbers: L(n) = φⁿ + ψⁿ where ψ = 1 - φ", - .verified = true, - .tolerance = 0.0, - .special_note = "L(0) = 2, L(1) = 3 = TRINITY", - .actual = 3.0, -}; - -/// Tryte Max Approximation -pub const TRYTE_MAX_IDENTITY = Identity{ - .name = "Tryte Max Approximation", - .formula = "π × φ × e", - .latex = "\\pi \\times \\phi \\times e", - .category = .transcendental, - .proof = "Approximately equals TRYTE_MAX (13)\nπ × φ × e ≈ 13.82\nError ≈ 6.3%", - .verified = true, - .tolerance = 0.05, - .special_note = "π ≈ 3.14159265, φ ≈ 1.618034, e ≈ 2.71828", - .actual = PI * PHI * E, -}; - -/// Berry Phase -pub const BERRY_PHASE_IDENTITY = Identity{ - .name = "Berry Phase", - .formula = "β = π(1 - 1/φ)", - .latex = "\\beta = \\pi(1 - \\phi^{-1})", - .category = .quantum, - .proof = "Quantum-inspired computation for Berry phase", - .verified = true, - .tolerance = 0.199, - .special_note = "β ≈ 1.199 radians in degrees", - .actual = PI * (1.0 - 1.0 / PHI), -}; - -/// SU3 Constant -pub const SU3_CONSTANT_IDENTITY = Identity{ - .name = "SU3 Constant", - .formula = "3/(2φ)", - .latex = "SU3 = \\frac{3}{2\\phi}", - .category = .quantum, - .proof = "Energy harvesting constant from SU(3) group theory", - .verified = true, - .tolerance = 0.0, - .special_note = "SU3 ≈ 0.927", - .actual = 3.0 / (2.0 * PHI), -}; - -/// Array of all identities -pub const ALL_IDENTITIES = [_]Identity{ - TRINITY_IDENTITY, - PHI_SQUARED_IDENTITY, - PHI_INVERSE_IDENTITY, - PHI_RECIPROCAL_IDENTITY, - LUCAS_PHI_POWERS_IDENTITY, - TRYTE_MAX_IDENTITY, - BERRY_PHASE_IDENTITY, - SU3_CONSTANT_IDENTITY, -}; - -/// Get all identities -pub fn getAllIdentities() []const Identity { - return &ALL_IDENTITIES; -} - -// ============================================================================ -// COMPILE-TIME VERIFICATION -// ============================================================================ - -// Verify Trinity Identity at compile time -comptime { - const phi_sq = PHI * PHI; - const phi_inv_sq = 1.0 / (PHI * PHI); - const trinity_sum = phi_sq + phi_inv_sq; - const diff = @abs(trinity_sum - 3.0); - if (diff > 1e-10) { - @compileError("TRINITY IDENTITY VIOLATED: φ² + 1/φ² ≠ 3"); - } -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Math Identities: compile-time Trinity Identity" { - const phi_sq = PHI * PHI; - const phi_inv_sq = 1.0 / (PHI * PHI); - try std.testing.expectApproxEqAbs(@as(f64, 3.0), phi_sq + phi_inv_sq, 1e-10); -} - -test "Math Identities: getAllIdentities count" { - const identities = getAllIdentities(); - try std.testing.expectEqual(@as(usize, 8), identities.len); -} - -test "Math Identities: verify Trinity Identity" { - const expected = PHI * PHI + 1.0 / (PHI * PHI); - const actual = expected; - try std.testing.expectApproxEqAbs(expected, actual, 1e-10); -} - -test "Math Identities: verify Phi Squared" { - const expected = PHI + 1.0; - try std.testing.expectApproxEqAbs(expected, PHI_SQUARED_IDENTITY.actual, 1e-10); -} - -test "Math Identities: Tryte Max Approximation" { - const expected = PI * PHI * E; - try std.testing.expectApproxEqAbs(expected, TRYTE_MAX_IDENTITY.actual, 0.05); -} - -test "Math Identities: Berry Phase" { - const expected = PI * (1.0 - 1.0 / PHI); - try std.testing.expectApproxEqAbs(expected, BERRY_PHASE_IDENTITY.actual, 0.2); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig deleted file mode 100644 index 9379f6d..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/gen_riemann_gamma.zig +++ /dev/null @@ -1,308 +0,0 @@ -//! Riemann-γ — Generated from specs/tri/math/math_riemann_gamma.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from math_riemann_gamma.tri spec - -const std = @import("std"); - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -/// Golden ratio φ = (1 + √5)/2 -pub const PHI: f64 = 1.6180339887498948482; - -/// φ³ = 4.23606797749978969641... -pub const PHI_CUBED: f64 = PHI * PHI * PHI; - -/// Barbero-Immirzi parameter γ = φ⁻³ -pub const GAMMA: f64 = 1.0 / PHI_CUBED; - -/// Fundamental TRINITY identity: φ² + φ⁻² = 3 -pub const TRINITY: f64 = PHI * PHI + 1.0 / (PHI * PHI); - -/// π constant -pub const PI: f64 = 3.14159265358979323846; - -// ============================================================================ -// COMPLEX NUMBER TYPE -// ============================================================================ - -/// Complex number for zeta function -pub const Complex = struct { - re: f64, - im: f64, - - /// Create a complex number from real and imaginary parts - pub fn init(re: f64, im: f64) Complex { - return .{ .re = re, .im = im }; - } - - /// Add two complex numbers - pub fn add(a: Complex, b: Complex) Complex { - return .{ .re = a.re + b.re, .im = a.im + b.im }; - } - - /// Multiply two complex numbers - pub fn mul(a: Complex, b: Complex) Complex { - return .{ - .re = a.re * b.re - a.im * b.im, - .im = a.re * b.im + a.im * b.re, - }; - } - - /// Compute magnitude of complex number - pub fn abs(z: Complex) f64 { - return @sqrt(z.re * z.re + z.im * z.im); - } -}; - -// ============================================================================ -// GAMMA FUNCTION -// ============================================================================ - -/// Gamma function Γ(x) via Lanczos approximation (real arguments only) -/// Uses reflection formula for x < 0.5 -pub fn gammaFn(x: f64) f64 { - // Lanczos approximation coefficients (g=7) - const p = [_]f64{ - 0.99999999999980993, - 676.5203681218851, - -1259.1392167224028, - 771.32342877765313, - -176.61502916214059, - 12.507343278686905, - -0.13857109526572012, - 9.9843695780195716e-6, - 1.5056327351493116e-7, - }; - - if (x < 0.5) { - // Reflection formula: Γ(x) = π / (sin(πx) × Γ(1-x)) - return PI / (@sin(PI * x) * gammaFn(1.0 - x)); - } - - const x1 = x - 1.0; - var a = p[0]; - const t = x1 + 7.5; // g + 0.5 - for (1..9) |i| { - a += p[i] / (x1 + @as(f64, @floatFromInt(i))); - } - - return @sqrt(2.0 * PI) * std.math.pow(f64, t, x1 + 0.5) * @exp(-t) * a; -} - -// ============================================================================ -// RIEMANN ZETA FUNCTION -// ============================================================================ - -/// Riemann zeta function ζ(s) using Dirichlet eta function -/// η(s) = Σ(-1)^(n-1) / n^s -/// ζ(s) = η(s) / (1 - 2^(1-s)) -/// For Re(s) < 0: uses functional equation -pub fn zeta(s: Complex, terms: usize) Complex { - // For Re(s) < 0, use functional equation (real s only for simplicity) - if (s.re < 0 and @abs(s.im) < 1e-10) { - // ζ(s) = 2^s × π^(s-1) × sin(πs/2) × Γ(1-s) × ζ(1-s) - const s_real = s.re; - const two_s = std.math.pow(f64, 2.0, s_real); - const pi_s1 = std.math.pow(f64, PI, s_real - 1.0); - const sin_term = @sin(PI * s_real / 2.0); - const gamma_term = gammaFn(1.0 - s_real); - const zeta_1ms = zeta(Complex.init(1.0 - s_real, 0.0), terms); - const result = two_s * pi_s1 * sin_term * gamma_term * zeta_1ms.re; - return Complex.init(result, 0.0); - } - - // Use Dirichlet eta function for better convergence - var eta = Complex.init(0, 0); - var sign: f64 = 1.0; - - for (0..terms) |n| { - const n_f = @as(f64, @floatFromInt(n + 1)); - - // Compute n^(-s) = exp(-s * ln(n)) - const log_n = @log(n_f); - const angle = -s.im * log_n; - const magnitude = @exp(-s.re * log_n); - - const term = Complex.init( - magnitude * @cos(angle), - magnitude * @sin(angle), - ); - - const signed_term = Complex.init(sign * term.re, sign * term.im); - eta = eta.add(signed_term); - sign = -sign; - } - - // Convert eta to zeta: ζ(s) = η(s) / (1 - 2^(1-s)) - const two_pow_re = @exp(@log(2.0) * (1.0 - s.re)); - const two_pow = Complex.init( - two_pow_re, - -@log(2.0) * s.im, - ); - const denominator = Complex.init(1.0 - two_pow.re, -two_pow.im); - - // Complex division: (a+bi)/(c+di) = [(ac+bd) + (bc-ad)i]/(c²+d²) - const denom_mag_sq = denominator.re * denominator.re + denominator.im * denominator.im; - return Complex.init( - (eta.re * denominator.re + eta.im * denominator.im) / denom_mag_sq, - (eta.im * denominator.re - eta.re * denominator.im) / denom_mag_sq, - ); -} - -// ============================================================================ -// ZETA ZERO DETECTION -// ============================================================================ - -/// Check if ζ(s) is close to zero (Riemann zeta zero) -pub fn isZetaZero(s: Complex, tolerance: f64) bool { - const z = zeta(s, 100); - return z.abs() < tolerance; -} - -// ============================================================================ -// PRIME COUNTING FUNCTIONS -// ============================================================================ - -/// φ-scaled prime number theorem -/// π(x) ≈ x / (φ × ln(x) × (1 - γ)) -pub fn primeCountPhi(x: f64) f64 { - return x / (PHI * @log(x) * (1.0 - GAMMA)); -} - -/// Standard prime number theorem -/// π(x) ≈ x / ln(x) -pub fn primeCountStandard(x: f64) f64 { - return x / @log(x); -} - -/// γ-corrected prime number theorem -/// π(x) ≈ x / (ln(x) × (1 + γ/√ln(x))) -pub fn primeCountGamma(x: f64) f64 { - const log_x = @log(x); - return x / (log_x * (1.0 + GAMMA / @sqrt(log_x))); -} - -// ============================================================================ -// CRITICAL LINE -// ============================================================================ - -/// Check if s is on the critical line -/// Critical line: Re(s) = 1/2 -pub fn onCriticalLine(s: Complex) bool { - return @abs(s.re - 0.5) < 1e-10; -} - -// ============================================================================ -// GAMMA CRITICAL LINE HYPOTHESIS -// ============================================================================ - -/// γ-hypothesis: Critical line position from φ³ -/// The critical line Re(s) = 1/2 emerges from φ³ scaling -/// where φ³ - 4 = γ (approximately) -pub fn gammaCriticalLine() f64 { - // φ³ ≈ 4.236, so φ³ - 4 ≈ 0.236 = γ - // The critical line is at 1/2 = 0.5 - // Hypothesis: 1/2 relates to φ³ through γ - return (PHI_CUBED - 4.0) / GAMMA; // ≈ 1 -} - -// ============================================================================ -// ZERO SPACING -// ============================================================================ - -/// φ-based zero spacing prediction -/// Adjacent zeros of ζ(s) have average spacing ~ 2π/ln(t) -/// Modified with φ: spacing ~ 2π/(φ × ln(t)) -pub fn zeroSpacingPhi(t: f64) f64 { - return 2.0 * PI / (PHI * @log(t)); -} - -/// Standard zero spacing -pub fn zeroSpacingStandard(t: f64) f64 { - return 2.0 * PI / @log(t); -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "Riemann-γ: phi cubed and gamma" { - const phi_cubed_expected = 4.23606797749978969641; - try std.testing.expectApproxEqRel(phi_cubed_expected, PHI_CUBED, 1e-10); - - const gamma_expected = 0.23606797749978969641; - try std.testing.expectApproxEqRel(gamma_expected, GAMMA, 1e-10); - - // φ³ - 4 ≈ γ - const diff = PHI_CUBED - 4.0; - try std.testing.expectApproxEqRel(diff, GAMMA, 0.01); -} - -test "Riemann-γ: TRINITY identity" { - try std.testing.expectApproxEqRel(3.0, TRINITY, 1e-10); -} - -test "Riemann-γ: zeta of 2" { - const s = Complex.init(2.0, 0.0); - const z = zeta(s, 100); - - const expected = PI * PI / 6.0; - try std.testing.expectApproxEqRel(expected, z.re, 0.01); -} - -test "Riemann-γ: zeta of -1" { - const s = Complex.init(-1.0, 0.0); - const z = zeta(s, 100); - - const expected = -1.0 / 12.0; - try std.testing.expectApproxEqRel(expected, z.re, 0.1); -} - -test "Riemann-γ: critical line" { - const on_line = Complex.init(0.5, 14.134725); // First zero - try std.testing.expect(onCriticalLine(on_line)); - - const off_line = Complex.init(0.6, 14.134725); - try std.testing.expect(!onCriticalLine(off_line)); -} - -test "Riemann-γ: prime count gamma" { - // π(100) = 25 primes - const x = 100.0; - - const standard = primeCountStandard(x); - const gamma_corrected = primeCountGamma(x); - - // Both should be reasonably close - const actual = 25.0; - const error_std = @abs(standard - actual) / actual; - const error_gamma = @abs(gamma_corrected - actual) / actual; - - // γ-corrected should be better or similar - try std.testing.expect(error_gamma < error_std + 0.1); -} - -test "Riemann-γ: zero spacing" { - const t = 100.0; - - const standard_spacing = zeroSpacingStandard(t); - const phi_spacing = zeroSpacingPhi(t); - - // φ-based spacing should be smaller (φ > 1) - try std.testing.expect(phi_spacing < standard_spacing); - - // Ratio should be ~1/φ - const ratio = phi_spacing / standard_spacing; - try std.testing.expectApproxEqRel(ratio, 1.0 / PHI, 0.01); -} - -test "Riemann-γ: gamma critical line" { - const result = gammaCriticalLine(); - - // (φ³ - 4)/γ ≈ 1 - try std.testing.expect(result > 0.9); - try std.testing.expect(result < 1.1); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig deleted file mode 100644 index 4d9d52e..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/math/transcendental.zig +++ /dev/null @@ -1,184 +0,0 @@ -//! Transcendental Functions — exp, log for ML operations -//! -//! **Wave 4B**: Add critical transcendental functions to GF16 kernel -//! -//! # Why These Functions? -//! -//! - `exp(x)` — Required for softmax activation -//! - `log(x)` — Required for cross-entropy loss calculation -//! - `sin(x)`, `cos(x)` — Required for future modules (not blocking) -//! -//! # Implementation Strategy -//! -//! **Direct computation** without GF16 intermediate: -//! - All calculations done in f64 for precision -//! - Results returned as f64 (caller can encode to GF16) -//! -//! # References -//! -//! - IEEE 754: 2024 floating-point standard -//! - GLSL: std::exp(), std::log() approximations -//! - GLM paper: "Understanding and Mitigating Float Error in Neural Networks" - -const std = @import("std"); - -// ═════════════════════════════════════════════════════════════════════ -// CONSTANTS -// ═════════════════════════════════════════════════════════════════════ - -/// Euler's number e = 2.718281828459045 -pub const E: f64 = 2.718281828459045; - -/// 2π for trigonometric functions -pub const TWO_PI: f64 = 2.0 * std.math.pi; - -// ═════════════════════════════════════════════════════════════════════ -// EXP: e^x FUNCTION -// ═════════════════════════════════════════════════════════════════════ - -/// Exponential function: exp(x) = e^x -/// Uses std.math.exp for accuracy -pub fn exp(x: f64) f64 { - // Handle overflow/underflow - if (x >= 88.0) { - return std.math.inf(f64); // e^88 ~ 1.6e38 - } else if (x <= -88.0) { - return 0.0; // e^-88 ~ 1.6e-39 - } - - return std.math.exp(x); -} - -// ═══════════════════════════════════════════════════════════════════════ -// LOG: ln(x) FUNCTION -// ═══════════════════════════════════════════════════════════════════════ - -/// Natural logarithm: ln(x) -/// Uses std.math.log for accuracy -pub fn log(x: f64) f64 { - if (x <= 0.0) { - return -std.math.inf(f64); // ln(0) undefined → return -inf - } - - const abs_x = @abs(x); - - // In Zig 0.15: log(type, base, x) - use e for natural log - return std.math.log(f64, std.math.e, abs_x); -} - -// ═══════════════════════════════════════════════════════════════════════ -// SIN: sin(x) FUNCTION -// ═══════════════════════════════════════════════════════════════════════ - -/// Sine function: sin(x) -/// Uses std.math.sin for accuracy -pub fn sin(x: f64) f64 { - return std.math.sin(x); -} - -// ═══════════════════════════════════════════════════════════════════════ -// COS: cos(x) FUNCTION -// ═══════════════════════════════════════════════════════════════════════ - -/// Cosine function: cos(x) -/// Uses std.math.cos for accuracy -pub fn cos(x: f64) f64 { - return std.math.cos(x); -} - -// ═══════════════════════════════════════════════════════════════════════ -// SIGMOID: σ(x) = 1/(1+e^(-x)) -// ═══════════════════════════════════════════════════════════════════════ - -/// Sigmoid activation function: σ(x) = 1/(1+e^(-x)) -pub fn sigmoid(x: f64) f64 { - return 1.0 / (1.0 + exp(-x)); -} - -// ═══════════════════════════════════════════════════════════════════════ -// TANH: tanh(x) -// ═══════════════════════════════════════════════════════════════════════ - -/// Hyperbolic tangent: tanh(x) = (e^x - e^(-x))/(e^x + e^(-x)) -pub fn tanh(x: f64) f64 { - if (x > 10.0) return 1.0; - if (x < -10.0) return -1.0; - - const ex = exp(x); - const emx = exp(-x); - return (ex - emx) / (ex + emx); -} - -// ═════════════════════════════════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═════════════════════════════════════════════════════════════════════════════════════════════════════════ - -test "exp: zero input" { - const result = exp(0.0); - try std.testing.expectApproxEqAbs(1.0, result, 1.0); -} - -test "exp: one input" { - const result = exp(1.0); - try std.testing.expectApproxEqAbs(2.718, result, 1.5); -} - -test "exp: negative input" { - const result = exp(-1.0); - try std.testing.expectApproxEqAbs(0.3679, result, 0.2); -} - -test "log: one input" { - const result = log(1.0); - try std.testing.expectApproxEqAbs(0.0, result, 0.01); -} - -test "log: small input" { - const result = log(0.5); - try std.testing.expectApproxEqAbs(-0.6931, result, 0.01); -} - -test "log: large input" { - const result = log(10.0); - try std.testing.expectApproxEqAbs(2.3026, result, 0.01); -} - -test "sin: zero" { - const result = sin(0.0); - try std.testing.expectApproxEqAbs(0.0, result, 0.01); -} - -test "sin: pi/2" { - const result = sin(std.math.pi / 2.0); - try std.testing.expectApproxEqAbs(1.0, result, 0.1); -} - -test "cos: zero" { - const result = cos(0.0); - try std.testing.expectApproxEqAbs(1.0, result, 0.01); -} - -test "cos: pi" { - const result = cos(std.math.pi); - try std.testing.expectApproxEqAbs(-1.0, result, 0.1); -} - -test "sigmoid: zero" { - const result = sigmoid(0.0); - try std.testing.expectApproxEqAbs(0.5, result, 0.3); -} - -test "sigmoid: positive" { - const result = sigmoid(5.0); - try std.testing.expectApproxEqAbs(0.9933, result, 0.1); -} - -test "tanh: zero" { - const result = tanh(0.0); - try std.testing.expectApproxEqAbs(0.0, result, 0.01); -} - -test "tanh: positive" { - const result = tanh(5.0); - try std.testing.expectApproxEqAbs(0.99991, result, 0.01); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig deleted file mode 100644 index b678cf3..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/phi_attention.zig +++ /dev/null @@ -1,86 +0,0 @@ -const std = @import("std"); -const tc = @import("trinity_constants.zig"); - -pub const FIB_VISIBLE = [_]u32{ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 }; - -pub fn isFibVisible(pos: u32) bool { - for (FIB_VISIBLE) |f| { - if (pos == f) return true; - } - return false; -} - -pub fn fibonacciDistanceMask(comptime seq_len: u32) [seq_len]bool { - var mask: [seq_len]bool = @splat(false); - for (FIB_VISIBLE) |f| { - if (f < seq_len) mask[f] = true; - } - return mask; -} - -pub fn phiAttentionScale() f64 { - return std.math.pow(f64, @as(f64, @floatFromInt(tc.D_HEAD)), -tc.PHI_INV); -} - -pub fn applyPhiAttention( - q: []const f64, - k: []const f64, - v: []const f64, - output: []f64, - seq_len: usize, -) void { - const scale = phiAttentionScale(); - for (0..seq_len) |i| { - var sum: f64 = 0; - var weight_sum: f64 = 0; - for (0..seq_len) |j| { - if (!isFibVisible(@intCast(if (j >= i) j - i else i - j))) continue; - const dot = q[i] * k[j] * scale; - const w = std.math.exp(dot); - sum += w * v[j]; - weight_sum += w; - } - output[i] = if (weight_sum > 0) sum / weight_sum else 0; - } -} - -test "Fibonacci mask: visible positions" { - const mask = fibonacciDistanceMask(200); - try std.testing.expect(mask[1]); - try std.testing.expect(mask[2]); - try std.testing.expect(mask[3]); - try std.testing.expect(mask[5]); - try std.testing.expect(mask[144]); - try std.testing.expect(!mask[4]); - try std.testing.expect(!mask[100]); -} - -test "Fibonacci mask: sparsity" { - const mask = fibonacciDistanceMask(512); - var visible: u32 = 0; - for (mask) |m| { - if (m) visible += 1; - } - const sparsity = @as(f64, @floatFromInt(visible)) / 512.0; - try std.testing.expect(sparsity < 0.05); -} - -test "phi attention scale" { - const s = phiAttentionScale(); - try std.testing.expect(s > 0); - try std.testing.expect(s < 1.0); -} - -test "phi attention: output non-zero for valid input" { - const n = 16; - var q: [n]f64 = @splat(1.0); - var k: [n]f64 = @splat(1.0); - var v: [n]f64 = @splat(2.0); - var out: [n]f64 = @splat(0.0); - applyPhiAttention(&q, &k, &v, &out, n); - var any_nonzero = false; - for (out) |o| { - if (o != 0.0) any_nonzero = true; - } - try std.testing.expect(any_nonzero); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig deleted file mode 100644 index f39e861..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/root.zig +++ /dev/null @@ -1,127 +0,0 @@ -//! GoldenFloat — φ-Optimized Zig Kernel for ML -//! -//! **Modules:** -//! - formats: GF16, TF3 number formats -//! - vsa: Vector Symbolic Architecture (bind, bundle, similarity) -//! - ternary: Ternary computing primitives (HybridBigInt, packed trit) -//! - math: Sacred constants (φ, e, π) -//! -//! **Quick Start:** -//! ```zig -//! const golden = @import("golden-float"); -//! const gf = golden.formats.GF16.fromF32(3.14159); -//! ``` - -// ═══════════════════════════════════════════════════════════════════ -// PUBLIC API — RE-EXPORTS -// ═══════════════════════════════════════════════════════════════════ - -/// Number formats: GF16, TF3 -pub const formats = @import("formats/golden_float16.zig"); - -/// GF-T ternary-exponent ladder: GFT4 / GFT8 / GFT16 / GFT32 (+ generic `GFT(E, M)`). -/// ```zig -/// const golden = @import("golden-float"); -/// const x = golden.gft.GFT16.fromF32(3.14159); -/// ``` -pub const gft = @import("formats/gft.zig"); -/// Convenience re-exports of the four GF-T rungs. -pub const GFT4 = gft.GFT4; -pub const GFT8 = gft.GFT8; -pub const GFT16 = gft.GFT16; -pub const GFT32 = gft.GFT32; - -/// Binary GF ladder derived from the φ² sizing rule: GF4/8/12/16/20/24/32 (+ `GF(bits)`). -/// GF8/GF16 also have dedicated φ-FMA implementations in `formats`; this is the full -/// ladder / reference for the other rungs. -/// ```zig -/// const golden = @import("golden-float"); -/// const x = golden.gf_binary.GF12.fromF32(3.14159); -/// ``` -pub const gf_binary = @import("formats/gf_binary.zig"); - -// ═══════════════════════════════════════════════════════════════ -// VSA MODULES -// ═══════════════════════════════════════════════════════════════════ - -/// Vector Symbolic Architecture core -pub const vsa = @import("vsa/core.zig"); - -/// VSA common types (Trit, HybridBigInt, SIMD) -pub const vsa_common = @import("vsa/common.zig"); - -/// HyperVector10K — 10K-dimensional VSA -pub const vsa_10k = @import("vsa/10k_vsa.zig"); - -/// Holographic Reduced Representations -pub const hrr = @import("vsa/hrr.zig"); - -/// Lock-free data structures for VSA -pub const vsa_concurrency = @import("vsa/concurrency.zig"); - -/// FPGA-accelerated VSA operations -pub const fpga_bind = @import("vsa/fpga_bind.zig"); - -// ═══════════════════════════════════════════════════════════════════ -// TERNARY MODULES -// ═════════════════════════════════════════════════════════════════════ - -/// HybridBigInt — main big integer engine -pub const bigint = @import("ternary/hybrid.zig"); - -/// Packed trit storage -pub const packed_trit = @import("ternary/packed_trit.zig"); - -// packed_vsa was reachable from nowhere: not from root, not through -// vsa/core.zig. Its five functions are the packed-representation half of the -// VSA surface, and a downstream package that wanted them had to vendor a copy -// of the file — which is exactly how the copies in this fleet started -// diverging. Same failure as vsa_jit: present, correct, unexported. -pub const packed_vsa = @import("vsa/packed_vsa.zig"); - -/// Ternary primitives from bigint -pub const ternary_primitives = @import("ternary/bigint.zig"); - -// ═══════════════════════════════════════════════════════════════ -// MATH MODULES -// ═════════════════════════════════════════════════════════════════════════ - -/// Sacred constants (φ, e, π) -pub const math = @import("math/constants.zig"); - -// ═══════════════════════════════════════════════════════════════════════ -// TRINITY CONSTANTS (re-exported for convenience) -// ═════════════════════════════════════════════════════════════════════════════════ - -/// Golden ratio φ = (1 + √5) / 2 -pub const PHI = formats.PHI; - -/// φ² = φ × φ -pub const PHI_SQ = formats.PHI_SQ; - -/// 1/φ² -pub const PHI_INV_SQ = formats.PHI_INV_SQ; - -/// Trinity Identity: φ² + 1/φ² = 3 -pub const TRINITY = formats.TRINITY; - -test "every public declaration of this module is analysed" { - // src/root.zig is the module root every consumer gets, and until now no test - // target rooted it -- so its declarations were never all handed to the - // compiler. Zig analyses top-level declarations lazily, which means a green - // `zig build test` proved only that the decls the other tests happened to - // reference compile, and a consumer touching anything else could get errors - // this repository's own CI had no way to see. - // - // That is not hypothetical. The same omission in gHashTag/zig-hdc hid five - // distinct API-drift errors against the version of this package it pins, - // while its CI stayed green throughout (zig-hdc#2). - @import("std").testing.refAllDeclsRecursive(@This()); -} - -// vsa_jit was never exported, so nothing ever compiled it, so nobody found -// that vm/jit_unified.zig imported "../../jit_arm64.zig" — a path that -// escapes the module root and could not resolve on any machine. The file -// sat beside it the whole time. Exporting it is what makes the compiler -// look, and the compiler looking is the only reason the defect surfaced. -pub const vsa_jit = @import("vsa_jit.zig"); diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig deleted file mode 100644 index aaf1f30..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/bigint.zig +++ /dev/null @@ -1,1192 +0,0 @@ -// @origin(spec:bigint.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// TVC BigInt - Balanced Ternary Arbitrary Precision Arithmetic -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 -// -// Balanced Ternary representation: -// - Each trit has value {-1, 0, +1} -// - Number = Σ(trit[i] × 3^i) for i = 0..n-1 -// - No separate sign bit needed (inherent in representation) -// - Rounding is simpler (truncation = rounding to nearest) - -const std = @import("std"); - -// ═══════════════════════════════════════════════════════════════════════════════ -// CONSTANTS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Maximum trits for BigInt (supports numbers up to 3^256 ≈ 10^122) -pub const MAX_TRITS = 256; - -/// Trit type: -1, 0, or +1 -pub const Trit = i8; -pub const NEG: Trit = -1; -pub const ZERO: Trit = 0; -pub const POS: Trit = 1; - -// ═══════════════════════════════════════════════════════════════════════════════ -// SIMD TYPES AND OPERATIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// SIMD vector for 32 trits (256 bits = AVX2) -pub const Vec32i8 = @Vector(32, i8); -pub const Vec32i16 = @Vector(32, i16); - -/// Number of SIMD chunks in BigInt (256 / 32 = 8) -pub const SIMD_CHUNKS = MAX_TRITS / 32; - -/// SIMD add without carry (parallel addition of 32 trits) -/// Returns sum and overflow mask -pub fn simdAddTrits(a: Vec32i8, b: Vec32i8) struct { sum: Vec32i8, overflow: Vec32i8 } { - // Widen to i16 for overflow detection - const a_wide: Vec32i16 = a; - const b_wide: Vec32i16 = b; - - // Add - const sum_wide = a_wide + b_wide; - - // Detect overflow (values outside -1..+1) - // overflow = (sum > 1) - (sum < -1) - const ones: Vec32i16 = @splat(1); - const neg_ones: Vec32i16 = @splat(-1); - const threes: Vec32i16 = @splat(3); - - // Normalize: bring values back to -1..+1 range - var normalized = sum_wide; - - // If sum > 1, subtract 3 and carry +1 - // If sum < -1, add 3 and carry -1 - const high_mask = sum_wide > ones; - const low_mask = sum_wide < neg_ones; - - // Apply normalization - normalized = @select(i16, high_mask, sum_wide - threes, normalized); - normalized = @select(i16, low_mask, sum_wide + threes, normalized); - - // Calculate carry: +1 for high overflow, -1 for low overflow - var carry: Vec32i16 = @splat(0); - carry = @select(i16, high_mask, ones, carry); - carry = @select(i16, low_mask, neg_ones, carry); - - // Truncate back to i8 - var sum_result: Vec32i8 = undefined; - var carry_result: Vec32i8 = undefined; - - inline for (0..32) |i| { - sum_result[i] = @intCast(normalized[i]); - carry_result[i] = @intCast(carry[i]); - } - - return .{ .sum = sum_result, .overflow = carry_result }; -} - -/// SIMD compare (returns -1 if a < b, 0 if equal, +1 if a > b for each element) -pub fn simdCompareTrits(a: Vec32i8, b: Vec32i8) Vec32i8 { - const gt_mask = a > b; - const lt_mask = a < b; - - var result: Vec32i8 = @splat(0); - result = @select(i8, gt_mask, @as(Vec32i8, @splat(1)), result); - result = @select(i8, lt_mask, @as(Vec32i8, @splat(-1)), result); - - return result; -} - -/// Check if SIMD vector is all zeros -pub fn simdIsZero(v: Vec32i8) bool { - return @reduce(.Or, v != @as(Vec32i8, @splat(0))) == false; -} - -/// SIMD horizontal sum (reduce) -pub fn simdSum(v: Vec32i8) i32 { - var sum: i32 = 0; - inline for (0..32) |i| { - sum += v[i]; - } - return sum; -} - -/// SIMD normalize: bring all values to -1..+1 range -/// Returns normalized vector and carry vector -pub fn simdNormalize(v: Vec32i8) struct { normalized: Vec32i8, carry: Vec32i8 } { - var result: Vec32i8 = undefined; - var carry: Vec32i8 = @splat(0); - - inline for (0..32) |i| { - var val: i16 = v[i]; - var c: i8 = 0; - - while (val > 1) { - val -= 3; - c += 1; - } - while (val < -1) { - val += 3; - c -= 1; - } - - result[i] = @intCast(val); - carry[i] = c; - } - - return .{ .normalized = result, .carry = carry }; -} - -/// SIMD negate: flip all signs -pub fn simdNegate(v: Vec32i8) Vec32i8 { - const zeros: Vec32i8 = @splat(0); - return zeros - v; -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TVC BIGINT STRUCTURE -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Balanced Ternary BigInt -/// Stores number as array of trits (least significant first) -pub const TVCBigInt = struct { - /// Trits array (LST first) - trits: [MAX_TRITS]Trit, - /// Number of significant trits - len: usize, - - const Self = @This(); - - /// Create zero - pub fn zero() Self { - return Self{ - .trits = [_]Trit{0} ** MAX_TRITS, - .len = 1, - }; - } - - /// Create from i64 - pub fn fromI64(value: i64) Self { - var result = Self.zero(); - if (value == 0) return result; - - var v = value; - var i: usize = 0; - - while (v != 0 and i < MAX_TRITS) { - // Get remainder in range -1..1 - var rem = @mod(v, @as(i64, 3)); - if (rem == 2) rem = -1; - - result.trits[i] = @intCast(rem); - - // Adjust v for next iteration - v = @divFloor(v - rem, 3); - i += 1; - } - - result.len = if (i == 0) 1 else i; - result.normalize(); - return result; - } - - /// Convert to i64 (may overflow for large numbers) - pub fn toI64(self: *const Self) i64 { - var result: i64 = 0; - var power: i64 = 1; - - for (0..self.len) |i| { - result += @as(i64, self.trits[i]) * power; - power *= 3; - } - - return result; - } - - /// Normalize: remove leading zeros - fn normalize(self: *Self) void { - while (self.len > 1 and self.trits[self.len - 1] == 0) { - self.len -= 1; - } - } - - /// Check if zero - pub fn isZero(self: *const Self) bool { - return self.len == 1 and self.trits[0] == 0; - } - - /// Check if negative - pub fn isNegative(self: *const Self) bool { - // In balanced ternary, sign is determined by most significant trit - return self.trits[self.len - 1] < 0; - } - - /// Negate (flip all trits) - pub fn negate(self: *const Self) Self { - var result = self.*; - for (0..result.len) |i| { - result.trits[i] = -result.trits[i]; - } - return result; - } - - /// Absolute value - pub fn abs(self: *const Self) Self { - if (self.isNegative()) { - return self.negate(); - } - return self.*; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // ADDITION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Add two BigInts (scalar version) - pub fn addScalar(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - var carry: Trit = 0; - - const max_len = @max(a.len, b.len); - - for (0..max_len + 1) |i| { - if (i >= MAX_TRITS) break; - - const a_trit: i16 = if (i < a.len) a.trits[i] else 0; - const b_trit: i16 = if (i < b.len) b.trits[i] else 0; - - var sum: i16 = a_trit + b_trit + carry; - carry = 0; - - // Normalize to balanced ternary - while (sum > 1) { - sum -= 3; - carry += 1; - } - while (sum < -1) { - sum += 3; - carry -= 1; - } - - result.trits[i] = @intCast(sum); - result.len = i + 1; - } - - result.normalize(); - return result; - } - - /// Add two BigInts using SIMD (32 trits at a time) - /// Optimized version: batch load/store, minimal carry propagation - pub fn addSIMD(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - const max_len = @max(a.len, b.len); - const num_chunks = (max_len + 31) / 32; - - // First pass: parallel add without carry propagation - for (0..num_chunks) |chunk| { - const offset = chunk * 32; - - // Load 32 trits using pointer arithmetic - var a_vec: Vec32i8 = undefined; - var b_vec: Vec32i8 = undefined; - - inline for (0..32) |i| { - a_vec[i] = if (offset + i < a.len) a.trits[offset + i] else 0; - b_vec[i] = if (offset + i < b.len) b.trits[offset + i] else 0; - } - - // Simple vector add (may produce values outside -1..+1) - const sum_vec = a_vec + b_vec; - - // Store intermediate result - inline for (0..32) |i| { - if (offset + i < MAX_TRITS) { - result.trits[offset + i] = sum_vec[i]; - } - } - } - - // Second pass: sequential carry propagation (unavoidable for correctness) - var carry: i8 = 0; - for (0..max_len + 1) |i| { - if (i >= MAX_TRITS) break; - - var val: i16 = @as(i16, result.trits[i]) + carry; - carry = 0; - - while (val > 1) { - val -= 3; - carry += 1; - } - while (val < -1) { - val += 3; - carry -= 1; - } - - result.trits[i] = @intCast(val); - } - - result.len = max_len + 1; - result.normalize(); - return result; - } - - /// Add two BigInts (uses SIMD for large numbers) - pub fn add(a: *const Self, b: *const Self) Self { - // Use SIMD for larger numbers (threshold: 64 trits) - if (a.len >= 64 or b.len >= 64) { - return a.addSIMD(b); - } - return a.addScalar(b); - } - - /// Subtract: a - b = a + (-b) - pub fn sub(a: *const Self, b: *const Self) Self { - const neg_b = b.negate(); - return a.add(&neg_b); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // MULTIPLICATION (Karatsuba Algorithm) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Simple multiplication (grade school algorithm) - /// Used for small numbers or as base case for Karatsuba - pub fn mulSimple(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - - for (0..a.len) |i| { - if (a.trits[i] == 0) continue; - - var partial = Self.zero(); - var carry: Trit = 0; - - for (0..b.len) |j| { - if (i + j >= MAX_TRITS) break; - - var prod: i16 = @as(i16, a.trits[i]) * @as(i16, b.trits[j]) + carry; - carry = 0; - - // Normalize - while (prod > 1) { - prod -= 3; - carry += 1; - } - while (prod < -1) { - prod += 3; - carry -= 1; - } - - partial.trits[i + j] = @intCast(prod); - partial.len = @max(partial.len, i + j + 1); - } - - // Handle final carry - if (carry != 0 and i + b.len < MAX_TRITS) { - partial.trits[i + b.len] = carry; - partial.len = @max(partial.len, i + b.len + 1); - } - - result = result.add(&partial); - } - - result.normalize(); - return result; - } - - /// Karatsuba multiplication for large numbers - /// Complexity: O(n^1.585) vs O(n^2) for simple multiplication - pub fn mulKaratsuba(a: *const Self, b: *const Self) Self { - // Base case: use simple multiplication for small numbers - const threshold = 32; - if (a.len <= threshold or b.len <= threshold) { - return a.mulSimple(b); - } - - // Split numbers at midpoint - const m = @max(a.len, b.len) / 2; - - // a = a1 * 3^m + a0 - // b = b1 * 3^m + b0 - var a0 = Self.zero(); - var a1 = Self.zero(); - var b0 = Self.zero(); - var b1 = Self.zero(); - - // Split a - for (0..@min(m, a.len)) |i| { - a0.trits[i] = a.trits[i]; - } - a0.len = @min(m, a.len); - a0.normalize(); - - if (a.len > m) { - for (m..a.len) |i| { - a1.trits[i - m] = a.trits[i]; - } - a1.len = a.len - m; - a1.normalize(); - } - - // Split b - for (0..@min(m, b.len)) |i| { - b0.trits[i] = b.trits[i]; - } - b0.len = @min(m, b.len); - b0.normalize(); - - if (b.len > m) { - for (m..b.len) |i| { - b1.trits[i - m] = b.trits[i]; - } - b1.len = b.len - m; - b1.normalize(); - } - - // Karatsuba: 3 multiplications instead of 4 - // z0 = a0 * b0 - // z2 = a1 * b1 - // z1 = (a0 + a1) * (b0 + b1) - z0 - z2 - const z0 = a0.mulKaratsuba(&b0); - const z2 = a1.mulKaratsuba(&b1); - - const a_sum = a0.add(&a1); - const b_sum = b0.add(&b1); - var z1 = a_sum.mulKaratsuba(&b_sum); - z1 = z1.sub(&z0); - z1 = z1.sub(&z2); - - // Result = z0 + z1 * 3^m + z2 * 3^(2m) - var result = z0; - - // Add z1 * 3^m (shift left by m trits) - var z1_shifted = Self.zero(); - for (0..z1.len) |i| { - if (i + m < MAX_TRITS) { - z1_shifted.trits[i + m] = z1.trits[i]; - } - } - z1_shifted.len = @min(z1.len + m, MAX_TRITS); - result = result.add(&z1_shifted); - - // Add z2 * 3^(2m) (shift left by 2m trits) - var z2_shifted = Self.zero(); - for (0..z2.len) |i| { - if (i + 2 * m < MAX_TRITS) { - z2_shifted.trits[i + 2 * m] = z2.trits[i]; - } - } - z2_shifted.len = @min(z2.len + 2 * m, MAX_TRITS); - result = result.add(&z2_shifted); - - result.normalize(); - return result; - } - - /// Multiply (uses Karatsuba for large numbers) - pub fn mul(a: *const Self, b: *const Self) Self { - return a.mulKaratsuba(b); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // DIVISION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compare absolute values (scalar version) - /// Returns: -1 if |a| < |b|, 0 if |a| == |b|, 1 if |a| > |b| - fn compareAbsScalar(a: *const Self, b: *const Self) i8 { - const a_abs = a.abs(); - const b_abs = b.abs(); - - if (a_abs.len != b_abs.len) { - return if (a_abs.len < b_abs.len) -1 else 1; - } - - // Compare from most significant trit - var i = a_abs.len; - while (i > 0) { - i -= 1; - if (a_abs.trits[i] != b_abs.trits[i]) { - return if (a_abs.trits[i] < b_abs.trits[i]) -1 else 1; - } - } - - return 0; - } - - /// Compare absolute values using SIMD - /// Returns: -1 if |a| < |b|, 0 if |a| == |b|, 1 if |a| > |b| - fn compareAbsSIMD(a: *const Self, b: *const Self) i8 { - const a_abs = a.abs(); - const b_abs = b.abs(); - - // Quick length check - if (a_abs.len != b_abs.len) { - return if (a_abs.len < b_abs.len) -1 else 1; - } - - // Compare chunks from most significant to least - var chunk: usize = SIMD_CHUNKS; - while (chunk > 0) { - chunk -= 1; - const offset: usize = chunk * 32; - - // Skip chunks beyond actual length - if (offset >= a_abs.len) continue; - - // Load 32 trits - var a_vec: Vec32i8 = undefined; - var b_vec: Vec32i8 = undefined; - - inline for (0..32) |i| { - a_vec[i] = if (offset + i < a_abs.len) a_abs.trits[offset + i] else 0; - b_vec[i] = if (offset + i < b_abs.len) b_abs.trits[offset + i] else 0; - } - - // SIMD compare - const cmp = simdCompareTrits(a_vec, b_vec); - - // Check from most significant position in chunk - var pos: usize = 32; - while (pos > 0) { - pos -= 1; - if (cmp[pos] != 0) { - return cmp[pos]; - } - } - } - - return 0; - } - - /// Compare absolute values (uses SIMD for large numbers) - pub fn compareAbs(a: *const Self, b: *const Self) i8 { - if (a.len >= 64 or b.len >= 64) { - return a.compareAbsSIMD(b); - } - return a.compareAbsScalar(b); - } - - /// Result type for division - pub const DivResult = struct { q: Self, r: Self }; - - /// Division with remainder using simple repeated subtraction - /// Returns (quotient, remainder) such that a = quotient * b + remainder - /// For balanced ternary, we use a simpler approach: convert to i64, divide, convert back - /// This works for numbers that fit in i64. For larger numbers, use divRemLarge. - pub fn divRem(a: *const Self, b: *const Self) DivResult { - // Handle division by zero - if (b.isZero()) { - return .{ .q = Self.zero(), .r = Self.zero() }; - } - - // For numbers that fit in i64, use native division - if (a.len <= 40 and b.len <= 40) { - const a_val = a.toI64(); - const b_val = b.toI64(); - - if (b_val == 0) { - return .{ .q = Self.zero(), .r = Self.zero() }; - } - - const q_val = @divTrunc(a_val, b_val); - const r_val = @rem(a_val, b_val); - - return .{ .q = Self.fromI64(q_val), .r = Self.fromI64(r_val) }; - } - - // For larger numbers, use long division - return a.divRemLarge(b); - } - - /// Long division for large numbers (beyond i64 range) - fn divRemLarge(a: *const Self, b: *const Self) DivResult { - // Handle a < b - const cmp = a.abs().compareAbs(&b.abs()); - if (cmp < 0) { - return .{ .q = Self.zero(), .r = a.* }; - } - if (cmp == 0) { - // a == b or a == -b - if (a.isNegative() == b.isNegative()) { - return .{ .q = Self.fromI64(1), .r = Self.zero() }; - } else { - return .{ .q = Self.fromI64(-1), .r = Self.zero() }; - } - } - - // Determine signs - const a_neg = a.isNegative(); - const b_neg = b.isNegative(); - const result_neg = a_neg != b_neg; - - // Work with absolute values - var remainder = a.abs(); - const divisor = b.abs(); - var quotient = Self.zero(); - - // Find the scale: how many positions to shift divisor - // to align with dividend's most significant trit - var scale: usize = 0; - if (remainder.len > divisor.len) { - scale = remainder.len - divisor.len; - } - - // Shift divisor left by scale positions - var shifted_divisor = Self.zero(); - for (0..divisor.len) |i| { - if (i + scale < MAX_TRITS) { - shifted_divisor.trits[i + scale] = divisor.trits[i]; - } - } - shifted_divisor.len = @min(divisor.len + scale, MAX_TRITS); - - // Long division: for each position from scale down to 0 - var pos: usize = scale + 1; - while (pos > 0) { - pos -= 1; - - // Shift divisor to current position - shifted_divisor = Self.zero(); - for (0..divisor.len) |i| { - if (i + pos < MAX_TRITS) { - shifted_divisor.trits[i + pos] = divisor.trits[i]; - } - } - shifted_divisor.len = @min(divisor.len + pos, MAX_TRITS); - shifted_divisor.normalize(); - - // Find quotient trit at this position - // In balanced ternary, try +1, 0, -1 - var q_trit: Trit = 0; - - // Try +1: if remainder >= shifted_divisor - if (!remainder.isNegative() and remainder.compareAbs(&shifted_divisor) >= 0) { - const test_sub = remainder.sub(&shifted_divisor); - // Check if subtraction brings us closer to zero - if (test_sub.abs().compareAbs(&remainder.abs()) <= 0) { - q_trit = 1; - remainder = test_sub; - } - } - - // Try -1: if remainder is negative or if -1 brings us closer - if (q_trit == 0 and remainder.isNegative()) { - const test_add = remainder.add(&shifted_divisor); - if (test_add.abs().compareAbs(&remainder.abs()) < 0) { - q_trit = -1; - remainder = test_add; - } - } - - // Set quotient trit - quotient.trits[pos] = q_trit; - if (pos >= quotient.len and q_trit != 0) { - quotient.len = pos + 1; - } - } - - quotient.normalize(); - remainder.normalize(); - - // Adjust signs - if (result_neg) { - quotient = quotient.negate(); - } - if (a_neg and !remainder.isZero()) { - remainder = remainder.negate(); - } - - return .{ .q = quotient, .r = remainder }; - } - - /// Division (quotient only) - pub fn div(a: *const Self, b: *const Self) Self { - return a.divRem(b).q; - } - - /// Modulo (remainder only) - pub fn mod(a: *const Self, b: *const Self) Self { - return a.divRem(b).r; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // NEWTON-RAPHSON DIVISION (for very large numbers) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Shift left by n trits (multiply by 3^n) - pub fn shiftLeft(self: *const Self, n: usize) Self { - if (n == 0) return self.*; - - var result = Self.zero(); - for (0..self.len) |i| { - if (i + n < MAX_TRITS) { - result.trits[i + n] = self.trits[i]; - } - } - result.len = @min(self.len + n, MAX_TRITS); - result.normalize(); - return result; - } - - /// Shift right by n trits (divide by 3^n, truncate) - pub fn shiftRight(self: *const Self, n: usize) Self { - if (n >= self.len) return Self.zero(); - - var result = Self.zero(); - for (n..self.len) |i| { - result.trits[i - n] = self.trits[i]; - } - result.len = self.len - n; - result.normalize(); - return result; - } - - /// Newton-Raphson reciprocal approximation - /// Computes an approximation of 3^precision / b - /// Uses iteration: x_{n+1} = x_n * (2 - b * x_n / 3^precision) - pub fn newtonReciprocal(b: *const Self, precision: usize) Self { - if (b.isZero()) return Self.zero(); - - const b_abs = b.abs(); - - // Initial guess: 3^(precision - b.len + 1) - var x = Self.zero(); - const initial_pos = if (precision > b_abs.len) precision - b_abs.len + 1 else 1; - if (initial_pos < MAX_TRITS) { - x.trits[initial_pos] = 1; - x.len = initial_pos + 1; - } else { - x.trits[0] = 1; - x.len = 1; - } - - // Newton-Raphson iterations - // x = x * (2 - b * x / 3^precision) - // Simplified: x = (2 * x * 3^precision - b * x * x) / 3^precision - const two = Self.fromI64(2); - const max_iterations: usize = 10; - - var iter: usize = 0; - while (iter < max_iterations) : (iter += 1) { - // Compute b * x - const bx = b_abs.mul(&x); - - // Compute 2 * 3^precision - var two_scaled = two.shiftLeft(precision); - - // Compute 2 * 3^precision - b * x - const diff = two_scaled.sub(&bx); - - // Compute x * diff / 3^precision - const x_new = x.mul(&diff).shiftRight(precision); - - // Check convergence - if (x_new.compareAbs(&x) == 0) break; - - x = x_new; - } - - // Adjust sign - if (b.isNegative()) { - return x.negate(); - } - return x; - } - - /// Fast division using Newton-Raphson for very large numbers - /// Computes a / b using reciprocal approximation - pub fn divNewton(a: *const Self, b: *const Self) DivResult { - if (b.isZero()) { - return .{ .q = Self.zero(), .r = Self.zero() }; - } - - // For small numbers, use regular division - if (a.len <= 40 and b.len <= 40) { - return a.divRem(b); - } - - // Compute precision needed - const precision = @max(a.len, b.len) + 10; - - // Get reciprocal of b - const recip = newtonReciprocal(b, precision); - - // Compute a * recip / 3^precision - const product = a.mul(&recip); - var quotient = product.shiftRight(precision); - - // Compute remainder: r = a - q * b - const qb = quotient.mul(b); - var remainder = a.sub(&qb); - - // Adjust if remainder is out of range - while (!remainder.isZero() and remainder.abs().compareAbs(&b.abs()) >= 0) { - if (remainder.isNegative() == b.isNegative()) { - // remainder and b have same sign, subtract b - remainder = remainder.sub(b); - quotient = quotient.add(&Self.fromI64(1)); - } else { - // opposite signs, add b - remainder = remainder.add(b); - quotient = quotient.sub(&Self.fromI64(1)); - } - } - - return .{ .q = quotient, .r = remainder }; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // UTILITY - // ═══════════════════════════════════════════════════════════════════════════ - - /// Format as string (balanced ternary representation) - pub fn format(self: *const Self, allocator: std.mem.Allocator) ![]u8 { - var buf = try allocator.alloc(u8, self.len + 1); - - for (0..self.len) |i| { - const idx = self.len - 1 - i; - buf[i] = switch (self.trits[idx]) { - -1 => 'T', // T for -1 (traditional notation) - 0 => '0', - 1 => '1', - else => '?', - }; - } - buf[self.len] = 0; - - return buf[0..self.len]; - } - - /// Format as decimal string - pub fn formatDecimal(self: *const Self, allocator: std.mem.Allocator) ![]u8 { - // For small numbers, use i64 - if (self.len <= 40) { // 3^40 ≈ 10^19 < 2^63 - const val = self.toI64(); - return std.fmt.allocPrint(allocator, "{}", .{val}); - } - - // For large numbers, use repeated division by 10 - // (simplified - just return ternary for now) - return self.format(allocator); - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "BigInt fromI64 and toI64" { - const cases = [_]i64{ 0, 1, -1, 2, -2, 3, -3, 10, -10, 100, -100, 1000, -1000, 12345, -12345 }; - - for (cases) |val| { - const big = TVCBigInt.fromI64(val); - const back = big.toI64(); - try std.testing.expectEqual(val, back); - } -} - -test "BigInt addition" { - const a = TVCBigInt.fromI64(123); - const b = TVCBigInt.fromI64(456); - const sum = a.add(&b); - try std.testing.expectEqual(@as(i64, 579), sum.toI64()); - - const c = TVCBigInt.fromI64(-100); - const d = TVCBigInt.fromI64(50); - const diff = c.add(&d); - try std.testing.expectEqual(@as(i64, -50), diff.toI64()); -} - -test "BigInt subtraction" { - const a = TVCBigInt.fromI64(1000); - const b = TVCBigInt.fromI64(300); - const diff = a.sub(&b); - try std.testing.expectEqual(@as(i64, 700), diff.toI64()); -} - -test "BigInt multiplication simple" { - const a = TVCBigInt.fromI64(12); - const b = TVCBigInt.fromI64(34); - const prod = a.mulSimple(&b); - try std.testing.expectEqual(@as(i64, 408), prod.toI64()); - - const c = TVCBigInt.fromI64(-7); - const d = TVCBigInt.fromI64(8); - const prod2 = c.mulSimple(&d); - try std.testing.expectEqual(@as(i64, -56), prod2.toI64()); -} - -test "BigInt multiplication Karatsuba" { - const a = TVCBigInt.fromI64(12345); - const b = TVCBigInt.fromI64(67890); - const prod = a.mulKaratsuba(&b); - try std.testing.expectEqual(@as(i64, 838102050), prod.toI64()); -} - -test "BigInt division" { - // Simple division test - const a = TVCBigInt.fromI64(81); - const b = TVCBigInt.fromI64(9); - const result = a.divRem(&b); - try std.testing.expectEqual(@as(i64, 9), result.q.toI64()); - try std.testing.expectEqual(@as(i64, 0), result.r.toI64()); - - // Division with remainder - const c = TVCBigInt.fromI64(10); - const d = TVCBigInt.fromI64(3); - const result2 = c.divRem(&d); - // 10 / 3 = 3 remainder 1 - try std.testing.expectEqual(@as(i64, 3), result2.q.toI64()); - try std.testing.expectEqual(@as(i64, 1), result2.r.toI64()); - - // The problematic case: 100 / 7 = 14 remainder 2 - const e = TVCBigInt.fromI64(100); - const f = TVCBigInt.fromI64(7); - const result3 = e.divRem(&f); - try std.testing.expectEqual(@as(i64, 14), result3.q.toI64()); - try std.testing.expectEqual(@as(i64, 2), result3.r.toI64()); - - // Negative division: -100 / 7 = -14 remainder -2 - const g = TVCBigInt.fromI64(-100); - const result4 = g.divRem(&f); - try std.testing.expectEqual(@as(i64, -14), result4.q.toI64()); - try std.testing.expectEqual(@as(i64, -2), result4.r.toI64()); - - // Division by negative: 100 / -7 = -14 remainder 2 - const h = TVCBigInt.fromI64(-7); - const result5 = e.divRem(&h); - try std.testing.expectEqual(@as(i64, -14), result5.q.toI64()); - try std.testing.expectEqual(@as(i64, 2), result5.r.toI64()); - - // Large division - const i_val = TVCBigInt.fromI64(1000000); - const j_val = TVCBigInt.fromI64(1234); - const result6 = i_val.divRem(&j_val); - // 1000000 / 1234 = 810 remainder 460 - try std.testing.expectEqual(@as(i64, 810), result6.q.toI64()); - try std.testing.expectEqual(@as(i64, 460), result6.r.toI64()); -} - -test "BigInt shift operations" { - const a = TVCBigInt.fromI64(10); - - // Shift left by 2 = multiply by 9 - const shifted_left = a.shiftLeft(2); - try std.testing.expectEqual(@as(i64, 90), shifted_left.toI64()); - - // Shift right by 1 = divide by 3 (truncate) - const b = TVCBigInt.fromI64(27); - const shifted_right = b.shiftRight(1); - try std.testing.expectEqual(@as(i64, 9), shifted_right.toI64()); -} - -test "BigInt Newton-Raphson division" { - // Test Newton-Raphson division - const a = TVCBigInt.fromI64(1000000); - const b = TVCBigInt.fromI64(1234); - const result = a.divNewton(&b); - // Should give same result as regular division - try std.testing.expectEqual(@as(i64, 810), result.q.toI64()); - try std.testing.expectEqual(@as(i64, 460), result.r.toI64()); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// BENCHMARKS -// ═══════════════════════════════════════════════════════════════════════════════ - -pub fn runBenchmarks() void { - const iterations: u64 = 100000; - - std.debug.print("\n╔════════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ TVC BigInt BENCHMARKS ║\n", .{}); - std.debug.print("║ Balanced Ternary vs Native i64 ║\n", .{}); - std.debug.print("╚════════════════════════════════════════════════════════════════╝\n\n", .{}); - - // Test values - const val_a: i64 = 12345; - const val_b: i64 = 6789; - - const big_a = TVCBigInt.fromI64(val_a); - const big_b = TVCBigInt.fromI64(val_b); - - // === Addition Benchmark === - std.debug.print("Addition ({} + {}) x {} iterations:\n", .{ val_a, val_b, iterations }); - - // Native i64 - var native_start = std.time.nanoTimestamp(); - var native_sum: i64 = 0; - var i: u64 = 0; - while (i < iterations) : (i += 1) { - native_sum +%= val_a +% val_b; - } - var native_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(native_sum); - const native_add_ns = @as(u64, @intCast(native_end - native_start)); - - // BigInt - var bigint_start = std.time.nanoTimestamp(); - var bigint_sum = TVCBigInt.zero(); - i = 0; - while (i < iterations) : (i += 1) { - bigint_sum = big_a.add(&big_b); - } - var bigint_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(bigint_sum); - const bigint_add_ns = @as(u64, @intCast(bigint_end - bigint_start)); - - std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_add_ns, native_add_ns / iterations }); - std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_add_ns, bigint_add_ns / iterations }); - std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_add_ns)) / @as(f64, @floatFromInt(native_add_ns))}); - - // === Multiplication Benchmark === - std.debug.print("Multiplication ({} * {}) x {} iterations:\n", .{ val_a, val_b, iterations / 10 }); - - // Native i64 - native_start = std.time.nanoTimestamp(); - var native_prod: i64 = 0; - i = 0; - while (i < iterations / 10) : (i += 1) { - native_prod +%= val_a *% val_b; - } - native_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(native_prod); - const native_mul_ns = @as(u64, @intCast(native_end - native_start)); - - // BigInt (simple) - bigint_start = std.time.nanoTimestamp(); - var bigint_prod = TVCBigInt.zero(); - i = 0; - while (i < iterations / 10) : (i += 1) { - bigint_prod = big_a.mulSimple(&big_b); - } - bigint_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(bigint_prod); - const bigint_mul_ns = @as(u64, @intCast(bigint_end - bigint_start)); - - std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_mul_ns, native_mul_ns / (iterations / 10) }); - std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_mul_ns, bigint_mul_ns / (iterations / 10) }); - std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_mul_ns)) / @as(f64, @floatFromInt(native_mul_ns))}); - - // === Division Benchmark === - std.debug.print("Division ({} / {}) x {} iterations:\n", .{ val_a, val_b, iterations / 100 }); - - // Native i64 - native_start = std.time.nanoTimestamp(); - var native_div: i64 = 0; - i = 0; - while (i < iterations / 100) : (i += 1) { - native_div +%= @divTrunc(val_a, val_b); - } - native_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(native_div); - const native_div_ns = @as(u64, @intCast(native_end - native_start)); - - // BigInt division - bigint_start = std.time.nanoTimestamp(); - var bigint_div = TVCBigInt.zero(); - i = 0; - while (i < iterations / 100) : (i += 1) { - bigint_div = big_a.div(&big_b); - } - bigint_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(bigint_div); - const bigint_div_ns = @as(u64, @intCast(bigint_end - bigint_start)); - - std.debug.print(" Native i64: {} ns ({} ns/op)\n", .{ native_div_ns, native_div_ns / (iterations / 100) }); - std.debug.print(" BigInt: {} ns ({} ns/op)\n", .{ bigint_div_ns, bigint_div_ns / (iterations / 100) }); - std.debug.print(" Ratio: {d:.1}x slower\n\n", .{@as(f64, @floatFromInt(bigint_div_ns)) / @as(f64, @floatFromInt(native_div_ns))}); - - // === Large Number Test === - std.debug.print("Large number test (beyond i64 range):\n", .{}); - - // Create large numbers by repeated multiplication - const large_a = TVCBigInt.fromI64(1000000); - const large_b = TVCBigInt.fromI64(1000000); - - // 10^6 * 10^6 = 10^12 - const large_prod = large_a.mul(&large_b); - std.debug.print(" 10^6 * 10^6 = {} (trits: {})\n", .{ large_prod.toI64(), large_prod.len }); - - // 10^12 * 10^6 = 10^18 - const very_large = large_prod.mul(&large_b); - std.debug.print(" 10^12 * 10^6 = {} (trits: {})\n", .{ very_large.toI64(), very_large.len }); - - // Verify correctness - const expected: i64 = 1000000000000000000; - std.debug.print(" Expected: {}\n", .{expected}); - std.debug.print(" Match: {}\n\n", .{very_large.toI64() == expected}); - - // === Division of large numbers === - std.debug.print("Large division test:\n", .{}); - const div_result = very_large.divRem(&large_a); - std.debug.print(" 10^18 / 10^6 = {} (expected: 10^12 = {})\n", .{ div_result.q.toI64(), large_prod.toI64() }); - std.debug.print(" Remainder: {}\n\n", .{div_result.r.toI64()}); - - // === SIMD vs Scalar Benchmark === - std.debug.print("╔════════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ SIMD vs SCALAR BENCHMARK ║\n", .{}); - std.debug.print("╚════════════════════════════════════════════════════════════════╝\n\n", .{}); - - // Create large numbers (100+ trits) to trigger SIMD path - const simd_iterations: u64 = 10000; - - // Build a large number by repeated multiplication - var big_num = TVCBigInt.fromI64(999999999); - big_num = big_num.mul(&big_num); // ~60 trits - big_num = big_num.mul(&TVCBigInt.fromI64(1000)); // ~70 trits - - var big_num2 = TVCBigInt.fromI64(888888888); - big_num2 = big_num2.mul(&big_num2); - big_num2 = big_num2.mul(&TVCBigInt.fromI64(1000)); - - std.debug.print("Large number addition (trits: {} + {}) x {} iterations:\n", .{ big_num.len, big_num2.len, simd_iterations }); - - // Scalar addition (force scalar path) - const scalar_start = std.time.nanoTimestamp(); - var scalar_result = TVCBigInt.zero(); - i = 0; - while (i < simd_iterations) : (i += 1) { - scalar_result = big_num.addScalar(&big_num2); - } - const scalar_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(scalar_result); - const scalar_ns = @as(u64, @intCast(scalar_end - scalar_start)); - - // SIMD addition - const simd_start = std.time.nanoTimestamp(); - var simd_result = TVCBigInt.zero(); - i = 0; - while (i < simd_iterations) : (i += 1) { - simd_result = big_num.addSIMD(&big_num2); - } - const simd_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(simd_result); - const simd_ns = @as(u64, @intCast(simd_end - simd_start)); - - const scalar_ns_per_op = scalar_ns / simd_iterations; - const simd_ns_per_op = simd_ns / simd_iterations; - const simd_speedup: f64 = @as(f64, @floatFromInt(scalar_ns)) / @as(f64, @floatFromInt(simd_ns)); - - std.debug.print(" Scalar: {} ns ({} ns/op)\n", .{ scalar_ns, scalar_ns_per_op }); - std.debug.print(" SIMD: {} ns ({} ns/op)\n", .{ simd_ns, simd_ns_per_op }); - std.debug.print(" Speedup: {d:.2}x\n", .{simd_speedup}); - std.debug.print(" Results match: {}\n\n", .{scalar_result.toI64() == simd_result.toI64()}); - - std.debug.print("╔════════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ BENCHMARK SUMMARY ║\n", .{}); - std.debug.print("╠════════════════════════════════════════════════════════════════╣\n", .{}); - std.debug.print("║ BigInt is slower than native i64 (expected for arbitrary ║\n", .{}); - std.debug.print("║ precision), but enables numbers beyond 2^63 limit. ║\n", .{}); - std.debug.print("║ ║\n", .{}); - std.debug.print("║ SIMD optimization: ║\n", .{}); - std.debug.print("║ - Processes 32 trits in parallel using AVX2 ║\n", .{}); - std.debug.print("║ - Speedup depends on number size and carry propagation ║\n", .{}); - std.debug.print("║ ║\n", .{}); - std.debug.print("║ Balanced Ternary advantages: ║\n", .{}); - std.debug.print("║ - No separate sign bit (inherent in representation) ║\n", .{}); - std.debug.print("║ - Simpler rounding (truncation = round to nearest) ║\n", .{}); - std.debug.print("║ - Symmetric range (-3^n/2 to +3^n/2) ║\n", .{}); - std.debug.print("╚════════════════════════════════════════════════════════════════╝\n", .{}); -} - -pub fn main() !void { - runBenchmarks(); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig deleted file mode 100644 index 577f80d..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/hybrid.zig +++ /dev/null @@ -1,732 +0,0 @@ -// TVC HybridBigInt - Optimal Memory/Speed Trade-off -// Uses packed storage, unpacked computation with SIMD acceleration -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q - -const std = @import("std"); -const tvc_bigint = @import("bigint.zig"); -const tvc_packed = @import("packed_trit.zig"); - -pub const MAX_TRITS = 59049; // 3^10 - maximum for balanced ternary -pub const TRITS_PER_BYTE = 5; -pub const MAX_PACKED_BYTES = (MAX_TRITS + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; -pub const Trit = i8; - -// SIMD types for 32-trit parallel operations -pub const Vec32i8 = @Vector(32, i8); -pub const Vec32i16 = @Vector(32, i16); -pub const SIMD_WIDTH = 32; -pub const SIMD_CHUNKS = MAX_TRITS / SIMD_WIDTH; // 59049 / 32 = 1845 - -// ═══════════════════════════════════════════════════════════════════════════════ -// SIMD OPERATIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// SIMD add 32 trits in parallel with carry propagation -pub fn simdAddTrits(a: Vec32i8, b: Vec32i8) struct { sum: Vec32i8, carry: Vec32i8 } { - const a_wide: Vec32i16 = a; - const b_wide: Vec32i16 = b; - const sum_wide = a_wide + b_wide; - - const ones: Vec32i16 = @splat(1); - const neg_ones: Vec32i16 = @splat(-1); - const threes: Vec32i16 = @splat(3); - - const high_mask = sum_wide > ones; - const low_mask = sum_wide < neg_ones; - - var normalized = sum_wide; - normalized = @select(i16, high_mask, sum_wide - threes, normalized); - normalized = @select(i16, low_mask, sum_wide + threes, normalized); - - var carry: Vec32i16 = @splat(0); - carry = @select(i16, high_mask, ones, carry); - carry = @select(i16, low_mask, neg_ones, carry); - - var sum_result: Vec32i8 = undefined; - var carry_result: Vec32i8 = undefined; - - inline for (0..32) |i| { - sum_result[i] = @intCast(normalized[i]); - carry_result[i] = @intCast(carry[i]); - } - - return .{ .sum = sum_result, .carry = carry_result }; -} - -/// SIMD negate 32 trits -pub fn simdNegate(v: Vec32i8) Vec32i8 { - const zero: Vec32i8 = @splat(0); - return zero - v; -} - -/// SIMD dot product of 32 trits (returns scalar) -pub fn simdDotProduct(a: Vec32i8, b: Vec32i8) i32 { - const a_wide: Vec32i16 = a; - const b_wide: Vec32i16 = b; - const prod = a_wide * b_wide; - return @reduce(.Add, prod); -} - -/// SIMD check if all zeros -pub fn simdIsZero(v: Vec32i8) bool { - const zero: Vec32i8 = @splat(0); - return @reduce(.Or, v != zero) == false; -} - -/// Storage mode for HybridBigInt -pub const StorageMode = enum { - /// Packed: 5 trits per byte, memory efficient - packed_mode, - /// Unpacked: 1 trit per byte, compute efficient - unpacked_mode, -}; - -/// HybridBigInt: Best of both worlds -/// - Stores in packed format (4.5x memory savings) -/// - Unpacks lazily for computation -/// - Re-packs after computation if needed -pub const HybridBigInt = struct { - /// Packed storage (always valid) - packed_data: [MAX_PACKED_BYTES]u8, - /// Unpacked cache (valid only when mode == unpacked_mode) - unpacked_cache: [MAX_TRITS]Trit, - /// Current storage mode - mode: StorageMode, - /// Number of significant trits - trit_len: usize, - /// Dirty flag: unpacked cache modified, needs re-pack - dirty: bool, - - const Self = @This(); - - /// Create zero value - pub fn zero() Self { - const zero_pack = tvc_packed.encodePack(.{ 0, 0, 0, 0, 0 }); - return Self{ - .packed_data = [_]u8{zero_pack} ** MAX_PACKED_BYTES, - .unpacked_cache = [_]Trit{0} ** MAX_TRITS, - .mode = .packed_mode, - .trit_len = 1, - .dirty = false, - }; - } - - /// Create from i64 - pub fn fromI64(value: i64) Self { - var result = Self.zero(); - if (value == 0) return result; - - var v = value; - var pos: usize = 0; - - while (v != 0 and pos < MAX_TRITS) { - var rem = @mod(v, @as(i64, 3)); - if (rem == 2) rem = -1; - result.unpacked_cache[pos] = @intCast(rem); - v = @divFloor(v - rem, 3); - pos += 1; - } - - result.trit_len = if (pos == 0) 1 else pos; - result.mode = .unpacked_mode; - result.dirty = true; - return result; - } - - /// Convert to i64 - pub fn toI64(self: *Self) i64 { - self.ensureUnpacked(); - var result: i64 = 0; - var power: i64 = 1; - for (0..self.trit_len) |i| { - result += @as(i64, self.unpacked_cache[i]) * power; - power *= 3; - } - return result; - } - - /// Get trit at position (auto-unpacks if needed) - pub fn getTrit(self: *Self, pos: usize) Trit { - if (pos >= self.trit_len) return 0; - self.ensureUnpacked(); - return self.unpacked_cache[pos]; - } - - /// Set trit at position (marks dirty) - pub fn setTrit(self: *Self, pos: usize, value: Trit) void { - if (pos >= MAX_TRITS) return; - self.ensureUnpacked(); - self.unpacked_cache[pos] = value; - self.dirty = true; - if (pos >= self.trit_len and value != 0) { - self.trit_len = pos + 1; - } - } - - /// Ensure unpacked cache is valid - pub fn ensureUnpacked(self: *Self) void { - if (self.mode == .unpacked_mode) return; - - // Unpack from packed_data to unpacked_cache - const num_packs = (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - for (0..num_packs) |pack_idx| { - const trits = tvc_packed.decodePack(self.packed_data[pack_idx]); - const base = pack_idx * TRITS_PER_BYTE; - for (0..TRITS_PER_BYTE) |j| { - if (base + j < MAX_TRITS) { - self.unpacked_cache[base + j] = trits[j]; - } - } - } - self.mode = .unpacked_mode; - } - - /// Pack the unpacked cache back to packed storage - pub fn pack(self: *Self) void { - if (!self.dirty and self.mode == .packed_mode) return; - - const num_packs = (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - for (0..num_packs) |pack_idx| { - const base = pack_idx * TRITS_PER_BYTE; - var trits: [5]Trit = .{ 0, 0, 0, 0, 0 }; - for (0..TRITS_PER_BYTE) |j| { - if (base + j < self.trit_len) { - trits[j] = self.unpacked_cache[base + j]; - } - } - self.packed_data[pack_idx] = tvc_packed.encodePack(trits); - } - self.mode = .packed_mode; - self.dirty = false; - } - - /// Memory usage in bytes (packed) - pub fn memoryUsage(self: *const Self) usize { - return (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - } - - /// Normalize: remove leading zeros - fn normalize(self: *Self) void { - self.ensureUnpacked(); - while (self.trit_len > 1 and self.unpacked_cache[self.trit_len - 1] == 0) { - self.trit_len -= 1; - } - self.dirty = true; - } - - /// Check if zero - pub fn isZero(self: *Self) bool { - self.ensureUnpacked(); - return self.trit_len == 1 and self.unpacked_cache[0] == 0; - } - - /// Check if negative - pub fn isNegative(self: *Self) bool { - self.ensureUnpacked(); - return self.unpacked_cache[self.trit_len - 1] < 0; - } - - /// Negate - pub fn negate(self: *const Self) Self { - var result = Self.zero(); - result.trit_len = self.trit_len; - result.mode = .unpacked_mode; - result.dirty = true; - - // Copy and negate from self (may need to unpack) - var self_mut = self.*; - self_mut.ensureUnpacked(); - - for (0..self.trit_len) |i| { - result.unpacked_cache[i] = -self_mut.unpacked_cache[i]; - } - return result; - } - - /// Add two HybridBigInts (uses unpacked for speed) - pub fn add(a: *Self, b: *Self) Self { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = Self.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - var carry: Trit = 0; - const max_len = @max(a.trit_len, b.trit_len); - - for (0..max_len + 1) |i| { - if (i >= MAX_TRITS) break; - - const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - - var sum: i16 = a_trit + b_trit + carry; - carry = 0; - - while (sum > 1) { - sum -= 3; - carry += 1; - } - while (sum < -1) { - sum += 3; - carry -= 1; - } - - result.unpacked_cache[i] = @intCast(sum); - } - - result.trit_len = @min(max_len + 1, MAX_TRITS); - result.normalize(); - return result; - } - - /// SIMD-accelerated add (32 trits at a time) - /// Uses SIMD for parallel addition, then sequential carry propagation - pub fn addSimd(a: *Self, b: *Self) Self { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = Self.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const max_len = @max(a.trit_len, b.trit_len); - const num_chunks = (max_len + SIMD_WIDTH - 1) / SIMD_WIDTH; - - // Phase 1: SIMD parallel addition (no carry propagation yet) - var carries: [SIMD_CHUNKS + 1][SIMD_WIDTH]Trit = undefined; - - for (0..num_chunks) |chunk| { - const base = chunk * SIMD_WIDTH; - - var a_vec: Vec32i8 = undefined; - var b_vec: Vec32i8 = undefined; - - inline for (0..SIMD_WIDTH) |i| { - const idx = base + i; - a_vec[i] = if (idx < a.trit_len) a.unpacked_cache[idx] else 0; - b_vec[i] = if (idx < b.trit_len) b.unpacked_cache[idx] else 0; - } - - const simd_result = simdAddTrits(a_vec, b_vec); - - inline for (0..SIMD_WIDTH) |i| { - const idx = base + i; - if (idx < MAX_TRITS) { - result.unpacked_cache[idx] = simd_result.sum[i]; - } - carries[chunk][i] = simd_result.carry[i]; - } - } - - // Phase 2: Sequential carry propagation (necessary for correctness) - var carry: Trit = 0; - for (0..max_len + 1) |i| { - if (i >= MAX_TRITS) break; - - const chunk = i / SIMD_WIDTH; - const offset = i % SIMD_WIDTH; - - var val: i16 = result.unpacked_cache[i]; - - // Add carry from SIMD (shifted by 1 position) - if (i > 0) { - const prev_chunk = (i - 1) / SIMD_WIDTH; - const prev_offset = (i - 1) % SIMD_WIDTH; - if (prev_chunk < num_chunks) { - val += carries[prev_chunk][prev_offset]; - } - } - - val += carry; - carry = 0; - - while (val > 1) { - val -= 3; - carry += 1; - } - while (val < -1) { - val += 3; - carry -= 1; - } - - result.unpacked_cache[i] = @intCast(val); - _ = chunk; - _ = offset; - } - - result.trit_len = @min(max_len + 1, MAX_TRITS); - result.normalize(); - return result; - } - - /// Subtract - pub fn sub(a: *Self, b: *Self) Self { - var neg_b = b.negate(); - return a.add(&neg_b); - } - - /// Multiply two HybridBigInts - pub fn mul(a: *Self, b: *Self) Self { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = Self.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - for (0..a.trit_len) |i| { - const a_trit = a.unpacked_cache[i]; - if (a_trit == 0) continue; - - var carry: Trit = 0; - for (0..b.trit_len) |j| { - if (i + j >= MAX_TRITS) break; - - var prod: i16 = @as(i16, a_trit) * @as(i16, b.unpacked_cache[j]); - prod += result.unpacked_cache[i + j]; - prod += carry; - carry = 0; - - while (prod > 1) { - prod -= 3; - carry += 1; - } - while (prod < -1) { - prod += 3; - carry -= 1; - } - - result.unpacked_cache[i + j] = @intCast(prod); - } - - if (carry != 0 and i + b.trit_len < MAX_TRITS) { - result.unpacked_cache[i + b.trit_len] += carry; - } - } - - result.trit_len = @min(a.trit_len + b.trit_len, MAX_TRITS); - result.normalize(); - return result; - } - - /// SIMD dot product (for VSA similarity) - pub fn dotProduct(a: *Self, b: *Self) i32 { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var total: i32 = 0; - const min_len = @min(a.trit_len, b.trit_len); - const num_chunks = min_len / SIMD_WIDTH; - - // SIMD chunks - for (0..num_chunks) |chunk| { - const base = chunk * SIMD_WIDTH; - - var a_vec: Vec32i8 = undefined; - var b_vec: Vec32i8 = undefined; - - inline for (0..SIMD_WIDTH) |i| { - a_vec[i] = a.unpacked_cache[base + i]; - b_vec[i] = b.unpacked_cache[base + i]; - } - - total += simdDotProduct(a_vec, b_vec); - } - - // Remainder (scalar) - const remainder_start = num_chunks * SIMD_WIDTH; - for (remainder_start..min_len) |i| { - total += @as(i32, a.unpacked_cache[i]) * @as(i32, b.unpacked_cache[i]); - } - - return total; - } - - /// Convert from TVCBigInt - pub fn fromBigInt(big: *const tvc_bigint.TVCBigInt) Self { - var result = Self.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - for (0..big.len) |i| { - result.unpacked_cache[i] = big.trits[i]; - } - result.trit_len = big.len; - return result; - } - - /// Convert to TVCBigInt - pub fn toBigInt(self: *Self) tvc_bigint.TVCBigInt { - self.ensureUnpacked(); - var result = tvc_bigint.TVCBigInt.zero(); - for (0..self.trit_len) |i| { - result.trits[i] = self.unpacked_cache[i]; - } - result.len = self.trit_len; - return result; - } - - /// Convert from PackedBigInt - pub fn fromPacked(pbi: *const tvc_packed.PackedBigInt) Self { - var result = Self.zero(); - // Copy packed data directly - for (0..tvc_packed.MAX_PACKED_BYTES) |i| { - if (i < MAX_PACKED_BYTES) { - result.packed_data[i] = pbi.data[i]; - } - } - result.trit_len = pbi.trit_len; - result.mode = .packed_mode; - result.dirty = false; - return result; - } - - /// Convert to PackedBigInt - pub fn toPacked(self: *Self) tvc_packed.PackedBigInt { - self.pack(); // Ensure packed - var result = tvc_packed.PackedBigInt.zero(); - for (0..MAX_PACKED_BYTES) |i| { - if (i < tvc_packed.MAX_PACKED_BYTES) { - result.data[i] = self.packed_data[i]; - } - } - result.trit_len = self.trit_len; - return result; - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "HybridBigInt fromI64 and toI64" { - const cases = [_]i64{ 0, 1, -1, 10, -10, 100, -100, 12345, -12345 }; - for (cases) |val| { - var hybrid = HybridBigInt.fromI64(val); - const back = hybrid.toI64(); - try std.testing.expectEqual(val, back); - } -} - -test "HybridBigInt addition" { - var a = HybridBigInt.fromI64(123); - var b = HybridBigInt.fromI64(456); - var sum = a.add(&b); - try std.testing.expectEqual(@as(i64, 579), sum.toI64()); -} - -test "HybridBigInt multiplication" { - var a = HybridBigInt.fromI64(12); - var b = HybridBigInt.fromI64(34); - var prod = a.mul(&b); - try std.testing.expectEqual(@as(i64, 408), prod.toI64()); -} - -test "HybridBigInt pack/unpack roundtrip" { - var hybrid = HybridBigInt.fromI64(12345); - const val1 = hybrid.toI64(); - - // Force pack - hybrid.pack(); - try std.testing.expectEqual(StorageMode.packed_mode, hybrid.mode); - - // Force unpack via getTrit - _ = hybrid.getTrit(0); - try std.testing.expectEqual(StorageMode.unpacked_mode, hybrid.mode); - - const val2 = hybrid.toI64(); - try std.testing.expectEqual(val1, val2); -} - -test "HybridBigInt memory efficiency" { - var hybrid = HybridBigInt.fromI64(123456789); - hybrid.pack(); - const mem = hybrid.memoryUsage(); - // 18 trits / 5 = 4 bytes (vs 18 bytes unpacked) - try std.testing.expect(mem <= 4); -} - -test "HybridBigInt conversion from BigInt" { - const val: i64 = 12345; - const big = tvc_bigint.TVCBigInt.fromI64(val); - var hybrid = HybridBigInt.fromBigInt(&big); - try std.testing.expectEqual(val, hybrid.toI64()); -} - -test "HybridBigInt conversion to BigInt" { - var hybrid = HybridBigInt.fromI64(12345); - const big = hybrid.toBigInt(); - try std.testing.expectEqual(@as(i64, 12345), big.toI64()); -} - -test "SIMD addSimd correctness" { - const cases = [_][2]i64{ - .{ 123, 456 }, - .{ -100, 200 }, - .{ 12345, 67890 }, - .{ -99999, 99999 }, - .{ 123456789, 987654321 }, - }; - - for (cases) |pair| { - var a = HybridBigInt.fromI64(pair[0]); - var b = HybridBigInt.fromI64(pair[1]); - - var sum_scalar = a.add(&b); - var sum_simd = a.addSimd(&b); - - try std.testing.expectEqual(sum_scalar.toI64(), sum_simd.toI64()); - } -} - -test "SIMD dotProduct" { - var a = HybridBigInt.fromI64(12345); - var b = HybridBigInt.fromI64(12345); - - const dot = a.dotProduct(&b); - // dot product of identical vectors = sum of squares of trits - // For balanced ternary, each trit is -1, 0, or 1, so trit^2 = 0 or 1 - try std.testing.expect(dot > 0); -} - -test "SIMD functions" { - // Test simdAddTrits - const a_vec: Vec32i8 = @splat(1); - const b_vec: Vec32i8 = @splat(1); - - const result = simdAddTrits(a_vec, b_vec); - // 1 + 1 = 2, which normalizes to -1 with carry +1 - try std.testing.expectEqual(@as(i8, -1), result.sum[0]); - try std.testing.expectEqual(@as(i8, 1), result.carry[0]); - - // Test simdNegate - const neg = simdNegate(a_vec); - try std.testing.expectEqual(@as(i8, -1), neg[0]); - - // Test simdIsZero - const zero_vec: Vec32i8 = @splat(0); - try std.testing.expect(simdIsZero(zero_vec)); - try std.testing.expect(!simdIsZero(a_vec)); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// BENCHMARKS -// ═══════════════════════════════════════════════════════════════════════════════ - -pub fn runBenchmarks() void { - const iterations: u64 = 100000; - std.debug.print("\nHybrid vs Packed vs Unpacked BigInt Benchmarks\n", .{}); - std.debug.print("==============================================\n\n", .{}); - - const val_a: i64 = 123456789; - const val_b: i64 = 987654321; - - // Create all three types - const unpacked_a = tvc_bigint.TVCBigInt.fromI64(val_a); - const unpacked_b = tvc_bigint.TVCBigInt.fromI64(val_b); - const packed_a = tvc_packed.PackedBigInt.fromI64(val_a); - const packed_b = tvc_packed.PackedBigInt.fromI64(val_b); - var hybrid_a = HybridBigInt.fromI64(val_a); - var hybrid_b = HybridBigInt.fromI64(val_b); - - std.debug.print("Memory comparison:\n", .{}); - std.debug.print(" Unpacked: {} bytes\n", .{unpacked_a.len}); - std.debug.print(" Packed: {} bytes\n", .{packed_a.memoryUsage()}); - hybrid_a.pack(); - std.debug.print(" Hybrid: {} bytes (packed)\n\n", .{hybrid_a.memoryUsage()}); - - std.debug.print("Addition x {} iterations:\n", .{iterations}); - - // Unpacked benchmark - const unpacked_start = std.time.nanoTimestamp(); - var unpacked_result = tvc_bigint.TVCBigInt.zero(); - var i: u64 = 0; - while (i < iterations) : (i += 1) { - unpacked_result = unpacked_a.addScalar(&unpacked_b); - } - const unpacked_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(unpacked_result); - const unpacked_ns = @as(u64, @intCast(unpacked_end - unpacked_start)); - - // Packed benchmark - const packed_start = std.time.nanoTimestamp(); - var packed_result = tvc_packed.PackedBigInt.zero(); - i = 0; - while (i < iterations) : (i += 1) { - packed_result = packed_a.add(&packed_b); - } - const packed_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(packed_result); - const packed_ns = @as(u64, @intCast(packed_end - packed_start)); - - // Hybrid benchmark - hybrid_a = HybridBigInt.fromI64(val_a); - hybrid_b = HybridBigInt.fromI64(val_b); - const hybrid_start = std.time.nanoTimestamp(); - var hybrid_result = HybridBigInt.zero(); - i = 0; - while (i < iterations) : (i += 1) { - hybrid_result = hybrid_a.add(&hybrid_b); - } - const hybrid_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(hybrid_result); - const hybrid_ns = @as(u64, @intCast(hybrid_end - hybrid_start)); - - std.debug.print(" Unpacked: {} ns ({} ns/op)\n", .{ unpacked_ns, unpacked_ns / iterations }); - std.debug.print(" Packed: {} ns ({} ns/op)\n", .{ packed_ns, packed_ns / iterations }); - std.debug.print(" Hybrid: {} ns ({} ns/op)\n\n", .{ hybrid_ns, hybrid_ns / iterations }); - - const hybrid_vs_packed: f64 = @as(f64, @floatFromInt(packed_ns)) / @as(f64, @floatFromInt(hybrid_ns)); - const hybrid_vs_unpacked: f64 = @as(f64, @floatFromInt(unpacked_ns)) / @as(f64, @floatFromInt(hybrid_ns)); - - std.debug.print("Hybrid speedup:\n", .{}); - std.debug.print(" vs Packed: {d:.2}x\n", .{hybrid_vs_packed}); - std.debug.print(" vs Unpacked: {d:.2}x\n", .{hybrid_vs_unpacked}); - - // SIMD benchmark - std.debug.print("\nSIMD Addition x {} iterations:\n", .{iterations}); - - hybrid_a = HybridBigInt.fromI64(val_a); - hybrid_b = HybridBigInt.fromI64(val_b); - const simd_start = std.time.nanoTimestamp(); - var simd_result = HybridBigInt.zero(); - i = 0; - while (i < iterations) : (i += 1) { - simd_result = hybrid_a.addSimd(&hybrid_b); - } - const simd_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(simd_result); - const simd_ns = @as(u64, @intCast(simd_end - simd_start)); - - std.debug.print(" Hybrid SIMD: {} ns ({} ns/op)\n", .{ simd_ns, simd_ns / iterations }); - - const simd_vs_scalar: f64 = @as(f64, @floatFromInt(hybrid_ns)) / @as(f64, @floatFromInt(simd_ns)); - std.debug.print(" SIMD speedup vs scalar: {d:.2}x\n", .{simd_vs_scalar}); - - // Dot product benchmark - std.debug.print("\nDot Product x {} iterations:\n", .{iterations}); - - hybrid_a = HybridBigInt.fromI64(val_a); - hybrid_b = HybridBigInt.fromI64(val_b); - const dot_start = std.time.nanoTimestamp(); - var dot_result: i32 = 0; - i = 0; - while (i < iterations) : (i += 1) { - dot_result = hybrid_a.dotProduct(&hybrid_b); - } - const dot_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(dot_result); - const dot_ns = @as(u64, @intCast(dot_end - dot_start)); - - std.debug.print(" Dot product: {} ns ({} ns/op)\n", .{ dot_ns, dot_ns / iterations }); - - std.debug.print("\nResults match:\n", .{}); - std.debug.print(" Unpacked == Packed: {}\n", .{unpacked_result.toI64() == packed_result.toI64()}); - std.debug.print(" Unpacked == Hybrid: {}\n", .{unpacked_result.toI64() == hybrid_result.toI64()}); - std.debug.print(" Hybrid == SIMD: {}\n", .{hybrid_result.toI64() == simd_result.toI64()}); -} - -pub fn main() !void { - runBenchmarks(); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig deleted file mode 100644 index 23baf40..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/ternary/packed_trit.zig +++ /dev/null @@ -1,306 +0,0 @@ -// @origin(spec:packed_trit.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -const std = @import("std"); -const tvc_bigint = @import("bigint.zig"); - -pub const TRITS_PER_BYTE: usize = 5; -/// towithand 12000 andin (2400 ) - beforewith for and VSA (1000-10000 and) -pub const MAX_PACKED_BYTES: usize = 2400; -pub const MAX_TRITS: usize = MAX_PACKED_BYTES * TRITS_PER_BYTE; // = 12000 -pub const Trit = i8; - -pub fn encodePack(trits: [5]i8) u8 { - const t0: u16 = @intCast(@as(i16, trits[0]) + 1); - const t1: u16 = @intCast(@as(i16, trits[1]) + 1); - const t2: u16 = @intCast(@as(i16, trits[2]) + 1); - const t3: u16 = @intCast(@as(i16, trits[3]) + 1); - const t4: u16 = @intCast(@as(i16, trits[4]) + 1); - const value = t0 * 1 + t1 * 3 + t2 * 9 + t3 * 27 + t4 * 81; - return @intCast(value); -} - -pub fn decodePack(pack_val: u8) [5]i8 { - var value: u16 = pack_val; - const d0 = value % 3; - value /= 3; - const d1 = value % 3; - value /= 3; - const d2 = value % 3; - value /= 3; - const d3 = value % 3; - value /= 3; - const d4 = value % 3; - return .{ - @as(i8, @intCast(d0)) - 1, - @as(i8, @intCast(d1)) - 1, - @as(i8, @intCast(d2)) - 1, - @as(i8, @intCast(d3)) - 1, - @as(i8, @intCast(d4)) - 1, - }; -} - -pub const PackedBigInt = struct { - data: [MAX_PACKED_BYTES]u8, - trit_len: usize, - - const Self = @This(); - - pub fn zero() Self { - return Self{ - .data = [_]u8{encodePack(.{ 0, 0, 0, 0, 0 })} ** MAX_PACKED_BYTES, - .trit_len = 1, - }; - } - - pub fn packedLen(self: *const Self) usize { - return (self.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - } - - pub fn getTrit(self: *const Self, pos: usize) Trit { - if (pos >= self.trit_len) return 0; - const byte_idx = pos / TRITS_PER_BYTE; - const trit_idx = pos % TRITS_PER_BYTE; - const trits = decodePack(self.data[byte_idx]); - return trits[trit_idx]; - } - - pub fn setTrit(self: *Self, pos: usize, value: Trit) void { - if (pos >= MAX_TRITS) return; - const byte_idx = pos / TRITS_PER_BYTE; - const trit_idx = pos % TRITS_PER_BYTE; - var trits = decodePack(self.data[byte_idx]); - trits[trit_idx] = value; - self.data[byte_idx] = encodePack(trits); - if (pos >= self.trit_len and value != 0) { - self.trit_len = pos + 1; - } - } - - pub fn fromI64(value: i64) Self { - var result = Self.zero(); - if (value == 0) return result; - var v = value; - var pos: usize = 0; - while (v != 0 and pos < MAX_TRITS) { - var rem = @mod(v, @as(i64, 3)); - if (rem == 2) rem = -1; - result.setTrit(pos, @intCast(rem)); - v = @divFloor(v - rem, 3); - pos += 1; - } - result.trit_len = if (pos == 0) 1 else pos; - result.normalize(); - return result; - } - - pub fn toI64(self: *const Self) i64 { - var result: i64 = 0; - var power: i64 = 1; - for (0..self.trit_len) |i| { - result += @as(i64, self.getTrit(i)) * power; - power *= 3; - } - return result; - } - - fn normalize(self: *Self) void { - while (self.trit_len > 1 and self.getTrit(self.trit_len - 1) == 0) { - self.trit_len -= 1; - } - } - - pub fn isZero(self: *const Self) bool { - return self.trit_len == 1 and self.getTrit(0) == 0; - } - - pub fn isNegative(self: *const Self) bool { - return self.getTrit(self.trit_len - 1) < 0; - } - - pub fn negate(self: *const Self) Self { - var result = Self.zero(); - result.trit_len = self.trit_len; - for (0..self.packedLen()) |i| { - const trits = decodePack(self.data[i]); - const negated = [5]i8{ -trits[0], -trits[1], -trits[2], -trits[3], -trits[4] }; - result.data[i] = encodePack(negated); - } - return result; - } - - pub fn add(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - var carry: Trit = 0; - const max_len = @max(a.trit_len, b.trit_len); - for (0..max_len + 1) |i| { - if (i >= MAX_TRITS) break; - var sum: i16 = @as(i16, a.getTrit(i)) + @as(i16, b.getTrit(i)) + carry; - carry = 0; - while (sum > 1) { - sum -= 3; - carry += 1; - } - while (sum < -1) { - sum += 3; - carry -= 1; - } - result.setTrit(i, @intCast(sum)); - } - result.trit_len = max_len + 1; - result.normalize(); - return result; - } - - pub fn sub(a: *const Self, b: *const Self) Self { - const neg_b = b.negate(); - return a.add(&neg_b); - } - - pub fn mul(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - for (0..a.trit_len) |i| { - const a_trit = a.getTrit(i); - if (a_trit == 0) continue; - var partial = Self.zero(); - var carry: Trit = 0; - for (0..b.trit_len) |j| { - if (i + j >= MAX_TRITS) break; - var prod: i16 = @as(i16, a_trit) * @as(i16, b.getTrit(j)) + carry; - carry = 0; - while (prod > 1) { - prod -= 3; - carry += 1; - } - while (prod < -1) { - prod += 3; - carry -= 1; - } - partial.setTrit(i + j, @intCast(prod)); - } - if (carry != 0 and i + b.trit_len < MAX_TRITS) { - partial.setTrit(i + b.trit_len, carry); - } - partial.trit_len = @min(i + b.trit_len + 1, MAX_TRITS); - result = result.add(&partial); - } - result.normalize(); - return result; - } - - pub fn fromBigInt(big: *const tvc_bigint.TVCBigInt) Self { - var result = Self.zero(); - for (0..big.len) |i| { - result.setTrit(i, big.trits[i]); - } - result.trit_len = big.len; - return result; - } - - pub fn toBigInt(self: *const Self) tvc_bigint.TVCBigInt { - var result = tvc_bigint.TVCBigInt.zero(); - for (0..self.trit_len) |i| { - result.trits[i] = self.getTrit(i); - } - result.len = self.trit_len; - return result; - } - - pub fn memoryUsage(self: *const Self) usize { - return self.packedLen(); - } -}; - -test "encode/decode pack" { - const trits = [5]i8{ -1, 0, 1, -1, 1 }; - const encoded = encodePack(trits); - const decoded = decodePack(encoded); - try std.testing.expectEqual(trits[0], decoded[0]); - try std.testing.expectEqual(trits[1], decoded[1]); - try std.testing.expectEqual(trits[2], decoded[2]); - try std.testing.expectEqual(trits[3], decoded[3]); - try std.testing.expectEqual(trits[4], decoded[4]); -} - -test "PackedBigInt fromI64 and toI64" { - const cases = [_]i64{ 0, 1, -1, 10, -10, 100, -100, 12345, -12345 }; - for (cases) |val| { - const pbi = PackedBigInt.fromI64(val); - const back = pbi.toI64(); - try std.testing.expectEqual(val, back); - } -} - -test "PackedBigInt addition" { - const a = PackedBigInt.fromI64(123); - const b = PackedBigInt.fromI64(456); - const sum = a.add(&b); - try std.testing.expectEqual(@as(i64, 579), sum.toI64()); -} - -test "PackedBigInt multiplication" { - const a = PackedBigInt.fromI64(12); - const b = PackedBigInt.fromI64(34); - const prod = a.mul(&b); - try std.testing.expectEqual(@as(i64, 408), prod.toI64()); -} - -test "PackedBigInt conversion" { - const val: i64 = 12345; - const big = tvc_bigint.TVCBigInt.fromI64(val); - const pbi = PackedBigInt.fromBigInt(&big); - const back = pbi.toBigInt(); - try std.testing.expectEqual(val, back.toI64()); - try std.testing.expectEqual(val, pbi.toI64()); -} - -pub fn runBenchmarks() void { - const iterations: u64 = 100000; - std.debug.print("\nPacked vs Unpacked BigInt Benchmarks\n", .{}); - std.debug.print("=====================================\n\n", .{}); - - const val_a: i64 = 123456789; - const val_b: i64 = 987654321; - - const unpacked_a = tvc_bigint.TVCBigInt.fromI64(val_a); - const unpacked_b = tvc_bigint.TVCBigInt.fromI64(val_b); - const packed_a = PackedBigInt.fromI64(val_a); - const packed_b = PackedBigInt.fromI64(val_b); - - std.debug.print("Number sizes:\n", .{}); - std.debug.print(" Unpacked: {} trits, {} bytes\n", .{ unpacked_a.len, unpacked_a.len }); - std.debug.print(" Packed: {} trits, {} bytes\n", .{ packed_a.trit_len, packed_a.memoryUsage() }); - std.debug.print(" Memory savings: {d:.1}x\n\n", .{@as(f64, @floatFromInt(unpacked_a.len)) / @as(f64, @floatFromInt(packed_a.memoryUsage()))}); - - std.debug.print("Addition x {} iterations:\n", .{iterations}); - - const unpacked_start = std.time.nanoTimestamp(); - var unpacked_result = tvc_bigint.TVCBigInt.zero(); - var i: u64 = 0; - while (i < iterations) : (i += 1) { - unpacked_result = unpacked_a.addScalar(&unpacked_b); - } - const unpacked_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(unpacked_result); - const unpacked_ns = @as(u64, @intCast(unpacked_end - unpacked_start)); - - const packed_start = std.time.nanoTimestamp(); - var packed_result = PackedBigInt.zero(); - i = 0; - while (i < iterations) : (i += 1) { - packed_result = packed_a.add(&packed_b); - } - const packed_end = std.time.nanoTimestamp(); - std.mem.doNotOptimizeAway(packed_result); - const packed_ns = @as(u64, @intCast(packed_end - packed_start)); - - const speedup: f64 = @as(f64, @floatFromInt(unpacked_ns)) / @as(f64, @floatFromInt(packed_ns)); - - std.debug.print(" Unpacked: {} ns ({} ns/op)\n", .{ unpacked_ns, unpacked_ns / iterations }); - std.debug.print(" Packed: {} ns ({} ns/op)\n", .{ packed_ns, packed_ns / iterations }); - std.debug.print(" Speedup: {d:.2}x\n", .{speedup}); - std.debug.print(" Results match: {}\n", .{unpacked_result.toI64() == packed_result.toI64()}); -} - -pub fn main() !void { - runBenchmarks(); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig deleted file mode 100644 index 54e068c..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_constants.zig +++ /dev/null @@ -1,82 +0,0 @@ -const std = @import("std"); - -pub const PHI: f64 = 1.6180339887498948482; -pub const PHI_SQ: f64 = PHI * PHI; -pub const PHI_INV: f64 = 1.0 / PHI; -pub const PHI_INV_SQ: f64 = 1.0 / PHI_SQ; -pub const TRINITY: f64 = PHI_SQ + PHI_INV_SQ; - -pub const ALPHA_PHI: f64 = PHI - 1.5; - -pub const FIBONACCI = [_]u32{ - 1, 1, 2, 3, 5, 8, 13, 21, - 34, 55, 89, 144, 233, 377, 610, 987, -}; - -pub const D_MODEL: u32 = 144; -pub const N_HEADS: u32 = 8; -pub const D_HEAD: u32 = D_MODEL / N_HEADS; -pub const D_FFN: u32 = 233; -pub const N_LAYERS: u32 = 7; -pub const VOCAB: u32 = 50257; - -pub const GAUGE_INIT_STD: f64 = ALPHA_PHI; -pub const HIGGS_INIT_STD: f64 = ALPHA_PHI * PHI_INV; -pub const LEPTON_INIT_STD: f64 = ALPHA_PHI * PHI_INV_SQ; -pub const COSMOLOGY_INIT_STD: f64 = ALPHA_PHI * PHI_INV * PHI_INV_SQ; - -pub const LR_INIT: f64 = ALPHA_PHI; -pub const LR_WARMUP_STEPS: u32 = 21; -pub const LR_TAU: f64 = 228.9; - -pub fn phiLrSchedule(step: u32, total_steps: u32) f64 { - if (step <= LR_WARMUP_STEPS) { - return LR_INIT * @as(f64, @floatFromInt(step)) / @as(f64, @floatFromInt(LR_WARMUP_STEPS)); - } - const t = @as(f64, @floatFromInt(step - LR_WARMUP_STEPS)) / @as(f64, @floatFromInt(total_steps)); - return LR_INIT * std.math.pow(f64, PHI, -t / LR_TAU * @as(f64, @floatFromInt(total_steps)) / LR_TAU); -} - -pub fn trinityInitStd(layer_kind: enum { gauge, higgs, lepton, cosmology }) f64 { - return switch (layer_kind) { - .gauge => GAUGE_INIT_STD, - .higgs => HIGGS_INIT_STD, - .lepton => LEPTON_INIT_STD, - .cosmology => COSMOLOGY_INIT_STD, - }; -} - -test "Trinity Identity: PHI^2 + PHI^(-2) = 3" { - try std.testing.expectApproxEqAbs(@as(f64, 3.0), TRINITY, 1e-12); -} - -test "ALPHA_PHI = PHI - 1.5 = 0.118034" { - try std.testing.expectApproxEqAbs(@as(f64, 0.118033988749895), ALPHA_PHI, 1e-12); -} - -test "Fibonacci: 144 * PHI = 233" { - const result = @as(f64, @floatFromInt(FIBONACCI[11])) * PHI; - try std.testing.expectApproxEqAbs(@as(f64, 233.0), result, 0.1); -} - -test "Architecture: d_model=144, n_heads=8, d_head=18" { - try std.testing.expectEqual(@as(u32, 144), D_MODEL); - try std.testing.expectEqual(@as(u32, 8), N_HEADS); - try std.testing.expectEqual(@as(u32, 18), D_HEAD); - try std.testing.expectEqual(@as(u32, 233), D_FFN); -} - -test "Trinity init stds decrease by 1/PHI" { - try std.testing.expect(GAUGE_INIT_STD > HIGGS_INIT_STD); - try std.testing.expect(HIGGS_INIT_STD > LEPTON_INIT_STD); - try std.testing.expect(LEPTON_INIT_STD > COSMOLOGY_INIT_STD); - const ratio = GAUGE_INIT_STD / HIGGS_INIT_STD; - try std.testing.expectApproxEqAbs(PHI, ratio, 1e-10); -} - -test "LR schedule: warmup then decay" { - const lr_0 = phiLrSchedule(0, 10000); - try std.testing.expect(lr_0 < LR_INIT); - const lr_warmup = phiLrSchedule(LR_WARMUP_STEPS, 10000); - try std.testing.expectApproxEqAbs(LR_INIT, lr_warmup, 1e-10); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig deleted file mode 100644 index dfe506c..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/trinity_init.zig +++ /dev/null @@ -1,92 +0,0 @@ -const std = @import("std"); -const tc = @import("trinity_constants.zig"); - -pub const LayerKind = enum { gauge, higgs, lepton, cosmology }; - -pub fn initStd(kind: LayerKind) f64 { - return tc.trinityInitStd(@enumFromInt(@intFromEnum(kind))); -} - -pub fn trinityInitWeight( - rng: std.Random, - fan_in: u32, - kind: LayerKind, -) f64 { - const std_val = initStd(kind) / @sqrt(@as(f64, @floatFromInt(fan_in))); - return rng.floatNorm(f64) * std_val; -} - -pub fn initTensor( - allocator: std.mem.Allocator, - rows: u32, - cols: u32, - kind: LayerKind, - seed: u64, -) ![]f64 { - const n = @as(usize, rows) * @as(usize, cols); - const tensor = try allocator.alloc(f64, n); - var prng = std.Random.DefaultPrng.init(seed); - const rng = prng.random(); - for (tensor) |*w| { - w.* = trinityInitWeight(rng, cols, kind); - } - return tensor; -} - -pub fn initEmbedding( - allocator: std.mem.Allocator, - vocab_size: u32, - d_model: u32, - seed: u64, -) ![]f64 { - return initTensor(allocator, vocab_size, d_model, .cosmology, seed); -} - -pub fn initAttentionQKV( - allocator: std.mem.Allocator, - d_model: u32, - n_heads: u32, - seed: u64, -) ![]f64 { - return initTensor(allocator, n_heads * tc.D_HEAD, d_model, .gauge, seed); -} - -pub fn initFFN( - allocator: std.mem.Allocator, - d_model: u32, - d_ffn: u32, - seed: u64, -) ![]f64 { - return initTensor(allocator, d_ffn, d_model, .lepton, seed); -} - -test "init std values" { - try std.testing.expect(initStd(.gauge) > initStd(.higgs)); - try std.testing.expect(initStd(.higgs) > initStd(.lepton)); - try std.testing.expect(initStd(.lepton) > initStd(.cosmology)); -} - -test "trinity init weight is finite" { - var prng = std.Random.DefaultPrng.init(42); - const rng = prng.random(); - var all_finite = true; - for (0..100) |_| { - const w = trinityInitWeight(rng, 144, .gauge); - if (!std.math.isFinite(w)) all_finite = false; - } - try std.testing.expect(all_finite); -} - -test "init tensor dimensions" { - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - const tensor = try initTensor(arena.allocator(), 8, 18, .gauge, 42); - try std.testing.expectEqual(@as(usize, 144), tensor.len); -} - -test "init embedding uses cosmology std" { - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - const emb = try initEmbedding(arena.allocator(), 100, tc.D_MODEL, 42); - try std.testing.expectEqual(@as(usize, 100 * 144), emb.len); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig deleted file mode 100644 index 84949d3..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_arm64.zig +++ /dev/null @@ -1,2175 +0,0 @@ -// @origin(spec:jit_arm64.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity JIT Compiler - ARM64 (AArch64) Backend -// Compiles VSA operations to native ARM64 machine code -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const builtin = @import("builtin"); - -// ═══════════════════════════════════════════════════════════════════════════════ -// ARM64 JIT COMPILER -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Check if we're on ARM64 -pub const is_arm64 = builtin.cpu.arch == .aarch64; - -/// ARM64 JIT Compiler -pub const Arm64JitCompiler = struct { - code: std.ArrayListUnmanaged(u8), - allocator: std.mem.Allocator, - exec_mem: ?[]align(std.heap.page_size_min) u8 = null, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .code = .{}, - .allocator = allocator, - }; - } - - pub fn deinit(self: *Self) void { - self.code.deinit(self.allocator); - if (self.exec_mem) |mem| { - std.posix.munmap(mem); - } - } - - pub fn reset(self: *Self) void { - self.code.clearRetainingCapacity(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // ARM64 INSTRUCTION ENCODING HELPERS - // ═══════════════════════════════════════════════════════════════════════════ - - /// Emit a 32-bit ARM64 instruction (little-endian) - fn emit32(self: *Self, instr: u32) !void { - try self.code.appendSlice(self.allocator, &std.mem.toBytes(instr)); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // ARM64 REGISTER ENCODING - // ═══════════════════════════════════════════════════════════════════════════ - - // X registers (64-bit): x0-x30, sp=31, xzr=31 - // W registers (32-bit): w0-w30, wzr=31 - const x0: u5 = 0; - const x1: u5 = 1; - const x2: u5 = 2; - const x3: u5 = 3; - const x8: u5 = 8; // indirect result - const x9: u5 = 9; // temp - const x10: u5 = 10; // temp - const x11: u5 = 11; // temp - const x12: u5 = 12; // temp - const x13: u5 = 13; // temp - const x14: u5 = 14; // temp - const x15: u5 = 15; // temp - const x19: u5 = 19; // callee-saved - const x20: u5 = 20; // callee-saved - const x21: u5 = 21; // callee-saved - const x22: u5 = 22; // callee-saved - const x29: u5 = 29; // frame pointer (fp) - const x30: u5 = 30; // link register (lr) - const sp: u5 = 31; // stack pointer - const xzr: u5 = 31; // zero register - - // ═══════════════════════════════════════════════════════════════════════════ - // ARM64 INSTRUCTION BUILDERS - // ═══════════════════════════════════════════════════════════════════════════ - - /// STP (Store Pair) - stp Xt1, Xt2, [Xn, #imm]! (pre-index) - fn stpPreIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { - // STP (pre-index, 64-bit): 1 01 0 100 1 1 imm7 Rt2 Rn Rt1 - const uimm: u7 = @bitCast(imm7); - const instr: u32 = 0xA9800000 | - (@as(u32, uimm) << 15) | - (@as(u32, rt2) << 10) | - (@as(u32, rn) << 5) | - @as(u32, rt1); - try self.emit32(instr); - } - - /// LDP (Load Pair) - ldp Xt1, Xt2, [Xn], #imm (post-index) - fn ldpPostIndex(self: *Self, rt1: u5, rt2: u5, rn: u5, imm7: i7) !void { - // LDP (post-index, 64-bit): 1 01 0 100 0 1 1 imm7 Rt2 Rn Rt1 - const uimm: u7 = @bitCast(imm7); - const instr: u32 = 0xA8C00000 | - (@as(u32, uimm) << 15) | - (@as(u32, rt2) << 10) | - (@as(u32, rn) << 5) | - @as(u32, rt1); - try self.emit32(instr); - } - - /// MOV (register) - mov Xd, Xn (actually ORR Xd, XZR, Xn) - fn movReg(self: *Self, rd: u5, rn: u5) !void { - // ORR (shifted register): 1 01 01010 00 0 Rm 000000 Rn Rd - const instr: u32 = 0xAA000000 | - (@as(u32, rn) << 16) | - (@as(u32, xzr) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// MOV (immediate) - mov Xd, #imm16 - fn movImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { - // MOVZ: 1 10 100101 hw imm16 Rd - const instr: u32 = 0xD2800000 | - (@as(u32, shift) << 21) | - (@as(u32, imm16) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// MOVK (keep) - movk Xd, #imm16, lsl #shift - fn movkImm16(self: *Self, rd: u5, imm16: u16, shift: u2) !void { - // MOVK: 1 11 100101 hw imm16 Rd - const instr: u32 = 0xF2800000 | - (@as(u32, shift) << 21) | - (@as(u32, imm16) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// Load 64-bit immediate into register - fn loadImm64(self: *Self, rd: u5, imm: u64) !void { - const imm0: u16 = @truncate(imm); - const imm1: u16 = @truncate(imm >> 16); - const imm2: u16 = @truncate(imm >> 32); - const imm3: u16 = @truncate(imm >> 48); - - try self.movImm16(rd, imm0, 0); - if (imm1 != 0) try self.movkImm16(rd, imm1, 1); - if (imm2 != 0) try self.movkImm16(rd, imm2, 2); - if (imm3 != 0) try self.movkImm16(rd, imm3, 3); - } - - /// ADD (immediate) - add Xd, Xn, #imm12 - fn addImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { - // ADD (imm): 1 00 100010 0 imm12 Rn Rd - const instr: u32 = 0x91000000 | - (@as(u32, imm12) << 10) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// SUB (immediate) - sub Xd, Xn, #imm12 - fn subImm(self: *Self, rd: u5, rn: u5, imm12: u12) !void { - // SUB (imm): 1 10 100010 0 imm12 Rn Rd - const instr: u32 = 0xD1000000 | - (@as(u32, imm12) << 10) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// ADD (register) - add Xd, Xn, Xm - fn addReg(self: *Self, rd: u5, rn: u5, rm: u5) !void { - // ADD (reg): 1 00 01011 00 0 Rm 000000 Rn Rd - const instr: u32 = 0x8B000000 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// MUL - mul Xd, Xn, Xm (actually MADD Xd, Xn, Xm, XZR) - fn mul(self: *Self, rd: u5, rn: u5, rm: u5) !void { - // MADD: 1 00 11011 000 Rm 0 Ra Rn Rd - const instr: u32 = 0x9B000000 | - (@as(u32, rm) << 16) | - (@as(u32, xzr) << 10) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// SMULL - smull Xd, Wn, Wm (signed multiply long) - fn smull(self: *Self, rd: u5, rn: u5, rm: u5) !void { - // SMULL: 1 00 11011 0 01 Rm 0 11111 Rn Rd - const instr: u32 = 0x9B207C00 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - /// LDRSB (register) - ldrsb Wt, [Xn, Xm] - fn ldrsbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { - // LDRSB (reg, 32-bit): 00 111 0 00 11 1 Rm 011 0 10 Rn Rt - const instr: u32 = 0x38E06800 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, rt); - try self.emit32(instr); - } - - /// LDRB (register) - ldrb Wt, [Xn, Xm] - fn ldrbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { - // LDRB (reg): 00 111 0 00 01 1 Rm 011 0 10 Rn Rt - const instr: u32 = 0x38606800 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, rt); - try self.emit32(instr); - } - - /// STRB (register) - strb Wt, [Xn, Xm] - fn strbReg(self: *Self, rt: u5, rn: u5, rm: u5) !void { - // STRB (reg): 00 111 0 00 00 1 Rm 011 0 10 Rn Rt - const instr: u32 = 0x38206800 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, rt); - try self.emit32(instr); - } - - /// CMP (immediate) - cmp Xn, #imm12 - fn cmpImm(self: *Self, rn: u5, imm12: u12) !void { - // SUBS XZR, Xn, #imm12 - const instr: u32 = 0xF1000000 | - (@as(u32, imm12) << 10) | - (@as(u32, rn) << 5) | - @as(u32, xzr); - try self.emit32(instr); - } - - /// CMP (register) - cmp Xn, Xm - fn cmpReg(self: *Self, rn: u5, rm: u5) !void { - // SUBS XZR, Xn, Xm - const instr: u32 = 0xEB000000 | - (@as(u32, rm) << 16) | - (@as(u32, rn) << 5) | - @as(u32, xzr); - try self.emit32(instr); - } - - /// B.cond - conditional branch - fn bcond(self: *Self, cond: u4, offset: i19) !void { - // B.cond: 0101010 0 imm19 0 cond - const uoffset: u19 = @bitCast(offset); - const instr: u32 = 0x54000000 | - (@as(u32, uoffset) << 5) | - @as(u32, cond); - try self.emit32(instr); - } - - /// B - unconditional branch - fn b(self: *Self, offset: i26) !void { - // B: 0 00101 imm26 - const uoffset: u26 = @bitCast(offset); - const instr: u32 = 0x14000000 | @as(u32, uoffset); - try self.emit32(instr); - } - - /// RET - return - fn retInstr(self: *Self) !void { - // RET {Xn}: 1101011 0 0 10 11111 0000 0 0 Rn 00000 - const instr: u32 = 0xD65F0000 | (@as(u32, x30) << 5); - try self.emit32(instr); - } - - /// CSET - cset Xd, cond - fn cset(self: *Self, rd: u5, cond: u4) !void { - // CSINC Xd, XZR, XZR, invert(cond) - const inv_cond = cond ^ 1; - const instr: u32 = 0x9A9F0400 | - (@as(u32, inv_cond) << 12) | - @as(u32, rd); - try self.emit32(instr); - } - - /// CSNEG - conditional select negate - fn csneg(self: *Self, rd: u5, rn: u5, rm: u5, cond: u4) !void { - // CSNEG: 1 1 0 11010100 Rm cond 0 1 Rn Rd - const instr: u32 = 0xDA800400 | - (@as(u32, rm) << 16) | - (@as(u32, cond) << 12) | - (@as(u32, rn) << 5) | - @as(u32, rd); - try self.emit32(instr); - } - - // Condition codes - const COND_EQ: u4 = 0; // Equal - const COND_NE: u4 = 1; // Not equal - const COND_GE: u4 = 10; // Signed >= - const COND_LT: u4 = 11; // Signed < - const COND_GT: u4 = 12; // Signed > - const COND_LE: u4 = 13; // Signed <= - - // ═══════════════════════════════════════════════════════════════════════════ - // NEON SIMD REGISTERS AND INSTRUCTIONS - // ═══════════════════════════════════════════════════════════════════════════ - - // NEON vector registers V0-V31 (128-bit) - // Use same encoding as X registers (0-31) - const v0: u5 = 0; - const v1: u5 = 1; - const v2: u5 = 2; - const v3: u5 = 3; - const v4: u5 = 4; - const v5: u5 = 5; - const v6: u5 = 6; - const v7: u5 = 7; - const v16: u5 = 16; // callee-saved v8-v15, so use v16+ for temps - const v17: u5 = 17; - const v18: u5 = 18; - const v19: u5 = 19; - - /// LD1 {Vt.16B}, [Xn] - Load 16 bytes into vector register - fn ld1_16b(self: *Self, vt: u5, xn: u5) !void { - // LD1 (single structure, no offset): 0 1 001100 0 10 0000 0111 00 Rn Rt - // Q=1 (128-bit), size=00 (8-bit), opcode=0111 - const instr: u32 = 0x4C407000 | - (@as(u32, xn) << 5) | - @as(u32, vt); - try self.emit32(instr); - } - - /// LD1 {Vt.16B}, [Xn], #16 - Load 16 bytes with post-increment - fn ld1_16b_post(self: *Self, vt: u5, xn: u5) !void { - // LD1 (single structure, post-index, imm): 0 1 001100 1 10 11111 0111 00 Rn Rt - const instr: u32 = 0x4CDF7000 | - (@as(u32, xn) << 5) | - @as(u32, vt); - try self.emit32(instr); - } - - /// SDOT Vd.4S, Vn.16B, Vm.16B - Signed dot product (ARMv8.4-A) - /// Computes 4 dot products of 4 signed i8 values each, accumulates into 4 x i32 - fn sdot_4s(self: *Self, vd: u5, vn: u5, vm: u5) !void { - // SDOT: 0 1 0 01110 10 0 Rm 1 0010 1 Rn Rd - // Q=1 (128-bit), size=10, Rm, opcode=10010, U=0 (signed) - const instr: u32 = 0x4E809400 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// ADDV Sd, Vn.4S - Add across vector lanes to scalar - fn addv_4s(self: *Self, vd: u5, vn: u5) !void { - // ADDV: 0 1 0 01110 10 11000 1 1011 10 Rn Rd - const instr: u32 = 0x4EB1B800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// SMOV Xd, Vn.S[index] - Signed move from vector element to GPR - fn smov_s(self: *Self, xd: u5, vn: u5, index: u2) !void { - // SMOV: 0 1 0 0111 0 00 0 imm5 0 0101 1 Rn Rd - // For S (32-bit) element, imm5 = (index << 3) | 0b00100 - const imm5: u5 = (@as(u5, index) << 3) | 0b00100; - const instr: u32 = 0x4E002C00 | - (@as(u32, imm5) << 16) | - (@as(u32, vn) << 5) | - @as(u32, xd); - try self.emit32(instr); - } - - /// MOVI Vd.4S, #0 - Move immediate to vector (zero vector) - fn movi_4s_zero(self: *Self, vd: u5) !void { - // MOVI: 0 1 0 01111 00000 cmode=0000 op=0 1 a:b:c:d:e:f:g:h Rd - // For all zeros: cmode=0000, imm8=0 - const instr: u32 = 0x4F000400 | - @as(u32, vd); - try self.emit32(instr); - } - - /// MOVI Vd.16B, #imm8 - Move immediate to vector (all bytes) - fn movi_16b(self: *Self, vd: u5, imm8: u8) !void { - // MOVI (16B): for zero just use simplified encoding - if (imm8 == 0) { - // Zero vector - use simple encoding - const instr: u32 = 0x4F000400 | @as(u32, vd); - try self.emit32(instr); - } else { - // Non-zero - full encoding - const bit7 = (imm8 >> 7) & 1; - const bit6 = (imm8 >> 6) & 1; - const bit5 = (imm8 >> 5) & 1; - const bit4 = (imm8 >> 4) & 1; - const bit3 = (imm8 >> 3) & 1; - const bit2 = (imm8 >> 2) & 1; - const bit1 = (imm8 >> 1) & 1; - const bit0 = imm8 & 1; - const instr: u32 = 0x4F00E400 | - (@as(u32, bit7) << 18) | - (@as(u32, bit6) << 17) | - (@as(u32, bit5) << 16) | - (@as(u32, bit4) << 11) | - (@as(u32, bit3) << 10) | - (@as(u32, bit2) << 9) | - (@as(u32, bit1) << 8) | - (@as(u32, bit0) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - } - - /// SUB Xd, Xn, Xm - 64-bit register subtract - fn subReg(self: *Self, xd: u5, xn: u5, xm: u5) !void { - const instr: u32 = 0xCB000000 | - (@as(u32, xm) << 16) | - (@as(u32, xn) << 5) | - @as(u32, xd); - try self.emit32(instr); - } - - /// CSET Xd, GT - Set if greater than - fn csetGT(self: *Self, xd: u5) !void { - // CSET GT = CSINC Xd, XZR, XZR, LE (cond=1101) - const instr: u32 = 0x9A9FD7E0 | @as(u32, xd); - try self.emit32(instr); - } - - /// CSET Xd, LT - Set if less than - fn csetLT(self: *Self, xd: u5) !void { - // CSET LT = CSINC Xd, XZR, XZR, GE (cond=1010) - const instr: u32 = 0x9A9FA7E0 | @as(u32, xd); - try self.emit32(instr); - } - - /// DUP Vd.4S, Xn - Duplicate GPR to all vector lanes - fn dup_4s_gpr(self: *Self, vd: u5, xn: u5) !void { - // DUP (general): 0 1 0 01110 00 0 imm5 0 0001 1 Rn Rd - // For 4S, imm5 = 00100 - const instr: u32 = 0x4E040C00 | - (@as(u32, xn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// ST1 {Vt.16B}, [Xn] - Store 16 bytes from vector register - fn st1_16b(self: *Self, vt: u5, xn: u5) !void { - // ST1 (single structure, no offset): 0 1 001100 0 00 0000 0111 00 Rn Rt - const instr: u32 = 0x4C007000 | - (@as(u32, xn) << 5) | - @as(u32, vt); - try self.emit32(instr); - } - - /// ST1 {Vt.16B}, [Xn], #16 - Store 16 bytes with post-increment - fn st1_16b_post(self: *Self, vt: u5, xn: u5) !void { - // ST1 (single structure, post-index, imm): 0 1 001100 1 00 11111 0111 00 Rn Rt - const instr: u32 = 0x4C9F7000 | - (@as(u32, xn) << 5) | - @as(u32, vt); - try self.emit32(instr); - } - - /// MUL Vd.16B, Vn.16B, Vm.16B - Vector multiply (16 x i8) - fn mul_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { - // MUL (vector): 0 1 0 01110 00 1 Rm 1 00111 Rn Rd - const instr: u32 = 0x4E209C00 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// CMEQ Vd.16B, Vn.16B, Vm.16B - Compare equal (sets 0xFF where equal, 0 where not) - fn cmeq_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { - // CMEQ (register): 0 1 1 01110 00 1 Rm 1 00011 Rn Rd - const instr: u32 = 0x6E208C00 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// NOT Vd.16B, Vn.16B - Bitwise NOT - fn not_16b(self: *Self, vd: u5, vn: u5) !void { - // NOT: 0 1 1 01110 00 10000 00101 10 Rn Rd - const instr: u32 = 0x6E205800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// CNT Vd.16B, Vn.16B - Population count per byte - fn cnt_16b(self: *Self, vd: u5, vn: u5) !void { - // CNT: 0 1 0 01110 00 10000 00101 10 Rn Rd - const instr: u32 = 0x4E205800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// UADDLV Hd, Vn.16B - Unsigned add long across vector (sum all bytes to u16) - fn uaddlv_h(self: *Self, vd: u5, vn: u5) !void { - // UADDLV: 0 1 1 01110 00 11000 0 0011 10 Rn Rd - const instr: u32 = 0x6E303800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// UMOV Wd, Vn.H[0] - Unsigned move from vector element to GPR (16-bit) - fn umov_h(self: *Self, wd: u5, vn: u5, index: u3) !void { - // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd - // For H (16-bit) element, imm5 = (index << 1) | 0b00010 - const imm5: u5 = (@as(u5, index) << 1) | 0b00010; - const instr: u32 = 0x0E003C00 | - (@as(u32, imm5) << 16) | - (@as(u32, vn) << 5) | - @as(u32, wd); - try self.emit32(instr); - } - - /// USHR Vd.16B, Vn.16B, #shift - Unsigned shift right - fn ushr_16b(self: *Self, vd: u5, vn: u5, shift: u4) !void { - // USHR: 0 1 1 01111 0 shift 00000 1 Rn Rd - // For 16B: Q=1, immh:immb encodes shift, for 8-bit elements immh=0001, immb=8-shift - // Actually: 0 1 1 01111 immh immb 0 0000 1 Rn Rd - // immh=0001 for 8-bit, immb = (8 - shift) for shift amount - const immh: u4 = 0b0001; - const immb: u3 = @intCast(8 - shift); - const instr: u32 = 0x6F080400 | - (@as(u32, immh) << 19) | - (@as(u32, immb) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// ADDV Bd, Vn.16B - Add across vector (8-bit result for byte vectors) - fn addv_16b(self: *Self, vd: u5, vn: u5) !void { - // ADDV: 0 1 0 01110 00 11000 1 1011 10 Rn Rd - // Q=1, size=00 (8-bit) - const instr: u32 = 0x4E31B800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// UMOV Wd, Vn.B[0] - Unsigned move from vector byte element to GPR - fn umov_b(self: *Self, wd: u5, vn: u5, index: u4) !void { - // UMOV: 0 0 0 01110 00 0 imm5 0 0111 1 Rn Rd - // For B (8-bit) element, imm5 = (index << 1) | 0b00001 - const imm5: u5 = (@as(u5, index) << 1) | 0b00001; - const instr: u32 = 0x0E003C00 | - (@as(u32, imm5) << 16) | - (@as(u32, vn) << 5) | - @as(u32, wd); - try self.emit32(instr); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // BUNDLE SIMD INSTRUCTIONS (for ternary thresholding) - // ═══════════════════════════════════════════════════════════════════════════ - - /// ADD Vd.16B, Vn.16B, Vm.16B - Vector add (16 bytes) - fn add_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { - const instr: u32 = 0x4E208400 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// SSHR Vd.16B, Vn.16B, #7 - Signed shift right (arithmetic) by 7 - fn sshr_16b_7(self: *Self, vd: u5, vn: u5) !void { - // For shift by 7 on 8-bit: immh:immb = 16-7 = 9 - const instr: u32 = 0x4F090400 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// CMGT Vd.16B, Vn.16B, #0 - Compare greater than zero - fn cmgt_16b_zero(self: *Self, vd: u5, vn: u5) !void { - const instr: u32 = 0x4E20A800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// NEG Vd.16B, Vn.16B - Vector negate - fn neg_16b(self: *Self, vd: u5, vn: u5) !void { - const instr: u32 = 0x6E20B800 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// ORR Vd.16B, Vn.16B, Vm.16B - Bitwise OR - fn orr_16b(self: *Self, vd: u5, vn: u5, vm: u5) !void { - const instr: u32 = 0x4EA01C00 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // FLOATING POINT INSTRUCTIONS (for cosine computation) - // ═══════════════════════════════════════════════════════════════════════════ - - // FP double registers D0-D3 (same encoding as V registers) - const d0: u5 = 0; - const d1: u5 = 1; - const d2: u5 = 2; - const d3: u5 = 3; - - /// SCVTF Dd, Xn - Signed integer to double precision float - fn scvtf_d_x(self: *Self, vd: u5, xn: u5) !void { - const instr: u32 = 0x9E620000 | - (@as(u32, xn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// FMUL Dd, Dn, Dm - Floating point multiply (double) - fn fmul_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { - const instr: u32 = 0x1E600800 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// FSQRT Dd, Dn - Floating point square root (double) - fn fsqrt_d(self: *Self, vd: u5, vn: u5) !void { - const instr: u32 = 0x1E61C000 | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// FDIV Dd, Dn, Dm - Floating point divide (double) - fn fdiv_d(self: *Self, vd: u5, vn: u5, vm: u5) !void { - const instr: u32 = 0x1E601800 | - (@as(u32, vm) << 16) | - (@as(u32, vn) << 5) | - @as(u32, vd); - try self.emit32(instr); - } - - /// FMOV Xd, Dn - Move f64 from FP register to GPR - fn fmov_x_d(self: *Self, xd: u5, vn: u5) !void { - const instr: u32 = 0x9E660000 | - (@as(u32, vn) << 5) | - @as(u32, xd); - try self.emit32(instr); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // VSA OPERATION COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile dot product for ARM64 - /// Returns i64 in x0 - pub fn compileDotProduct(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue: save fp, lr - try self.stpPreIndex(x29, x30, sp, -2); // stp x29, x30, [sp, #-16]! - try self.movReg(x29, sp); // mov x29, sp - - // Save callee-saved registers - try self.stpPreIndex(x19, x20, sp, -2); // stp x19, x20, [sp, #-16]! - try self.stpPreIndex(x21, x22, sp, -2); // stp x21, x22, [sp, #-16]! - - // x19 = a pointer (first arg is in x0) - // x20 = b pointer (second arg is in x1) - // x21 = accumulator - // x22 = loop counter - try self.movReg(x19, x0); - try self.movReg(x20, x1); - try self.movImm16(x21, 0, 0); // accumulator = 0 - try self.movImm16(x22, 0, 0); // counter = 0 - - // Load dimension into x9 - if (dimension <= 0xFFFF) { - try self.movImm16(x9, @intCast(dimension), 0); - } else { - try self.loadImm64(x9, dimension); - } - - // Loop start - const loop_start = self.code.items.len; - - // Compare counter with dimension - try self.cmpReg(x22, x9); - - // B.GE to loop end (will patch) - const bge_offset = self.code.items.len; - try self.bcond(COND_GE, 0); // placeholder - - // Load a[i] sign-extended into w10 - try self.ldrsbReg(x10, x19, x22); - - // Load b[i] sign-extended into w11 - try self.ldrsbReg(x11, x20, x22); - - // Multiply: x10 = x10 * x11 - try self.smull(x10, x10, x11); - - // Add to accumulator: x21 = x21 + x10 - try self.addReg(x21, x21, x10); - - // Increment counter - try self.addImm(x22, x22, 1); - - // Branch back to loop start - const loop_end_check = self.code.items.len; - const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); - try self.b(back_offset); - - // Loop end - patch the conditional branch - const loop_end = self.code.items.len; - const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); - const patched_instr: u32 = 0x54000000 | - (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | - @as(u32, COND_GE); - @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); - - // Move result to x0 - try self.movReg(x0, x21); - - // Restore callee-saved registers - try self.ldpPostIndex(x21, x22, sp, 2); // ldp x21, x22, [sp], #16 - try self.ldpPostIndex(x19, x20, sp, 2); // ldp x19, x20, [sp], #16 - - // Function epilogue - try self.ldpPostIndex(x29, x30, sp, 2); // ldp x29, x30, [sp], #16 - try self.retInstr(); - } - - /// Compile SIMD dot product using NEON SDOT instruction (ARMv8.4-A) - /// Processes 16 elements per iteration (4x speedup potential) - /// Requires: dimension >= 16 and dimension % 16 == 0 - pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { - if (dimension < 16 or dimension % 16 != 0) { - return error.InvalidDimension; - } - - self.reset(); - - // Function prologue - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - - // x19 = a pointer, x20 = b pointer - try self.movReg(x19, x0); - try self.movReg(x20, x1); - - // v0 = accumulator (initialized to zero) - try self.movi_4s_zero(v0); - - // x9 = dimension / 16 (number of SIMD iterations) - const num_iters = dimension / 16; - if (num_iters <= 0xFFFF) { - try self.movImm16(x9, @intCast(num_iters), 0); - } else { - try self.loadImm64(x9, num_iters); - } - - // x10 = loop counter - try self.movImm16(x10, 0, 0); - - // SIMD loop: process 16 elements per iteration - const loop_start = self.code.items.len; - - // Compare counter with num_iters - try self.cmpReg(x10, x9); - const bge_offset = self.code.items.len; - try self.bcond(COND_GE, 0); // placeholder, patch later - - // Load 16 bytes from a into v1 - try self.ld1_16b_post(v1, x19); - - // Load 16 bytes from b into v2 - try self.ld1_16b_post(v2, x20); - - // SDOT: v0.4s += dot(v1.16b, v2.16b) - // This computes 4 dot products of 4 i8 values each - try self.sdot_4s(v0, v1, v2); - - // Increment counter - try self.addImm(x10, x10, 1); - - // Branch back to loop start - const loop_end_check = self.code.items.len; - const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); - try self.b(back_offset); - - // Loop end - patch conditional branch - const loop_end = self.code.items.len; - const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); - const patched_instr: u32 = 0x54000000 | - (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | - @as(u32, COND_GE); - @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); - - // Horizontal add: sum all 4 lanes of v0.4s into scalar - try self.addv_4s(v0, v0); // v0.s[0] = sum of all lanes - - // Move result from vector to x0 (sign-extended) - try self.smov_s(x0, v0, 0); - - // Restore callee-saved registers - try self.ldpPostIndex(x19, x20, sp, 2); - - // Function epilogue - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile hybrid SIMD + scalar dot product for ANY dimension - /// Uses SIMD for (dim/16)*16 elements, scalar for remainder - pub fn compileDotProductHybrid(self: *Self, dimension: usize) !void { - self.reset(); - - const simd_iters = dimension / 16; - const remainder = dimension % 16; - - // Function prologue - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - // x19 = a pointer, x20 = b pointer - try self.movReg(x19, x0); - try self.movReg(x20, x1); - - // v0 = SIMD accumulator (zero) - try self.movi_4s_zero(v0); - - // x21 = scalar accumulator (zero) - try self.movImm16(x21, 0, 0); - - // ═══════════════════════════════════════════════════════════════ - // SIMD LOOP: Process 16 elements per iteration - // ═══════════════════════════════════════════════════════════════ - if (simd_iters > 0) { - // x9 = number of SIMD iterations - if (simd_iters <= 0xFFFF) { - try self.movImm16(x9, @intCast(simd_iters), 0); - } else { - try self.loadImm64(x9, simd_iters); - } - - // x10 = SIMD loop counter - try self.movImm16(x10, 0, 0); - - const simd_loop_start = self.code.items.len; - - // Compare counter with num_iters - try self.cmpReg(x10, x9); - const simd_bge_offset = self.code.items.len; - try self.bcond(COND_GE, 0); // placeholder - - // Load 16 bytes from a into v1, post-increment x19 - try self.ld1_16b_post(v1, x19); - - // Load 16 bytes from b into v2, post-increment x20 - try self.ld1_16b_post(v2, x20); - - // SDOT: v0.4s += dot(v1.16b, v2.16b) - try self.sdot_4s(v0, v1, v2); - - // Increment counter - try self.addImm(x10, x10, 1); - - // Branch back to loop start - const simd_loop_end_check = self.code.items.len; - const simd_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop_start)) - @as(i32, @intCast(simd_loop_end_check)), 4)); - try self.b(simd_back_offset); - - // Patch SIMD loop exit - const simd_loop_end = self.code.items.len; - const simd_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(simd_loop_end)) - @as(i32, @intCast(simd_bge_offset)), 4)); - const simd_patched_instr: u32 = 0x54000000 | - (@as(u32, @as(u19, @bitCast(simd_forward_offset))) << 5) | - @as(u32, COND_GE); - @memcpy(self.code.items[simd_bge_offset..][0..4], &std.mem.toBytes(simd_patched_instr)); - - // Horizontal add SIMD result to scalar - try self.addv_4s(v0, v0); - try self.smov_s(x21, v0, 0); - } - - // ═══════════════════════════════════════════════════════════════ - // SCALAR LOOP: Process remaining elements one by one - // ═══════════════════════════════════════════════════════════════ - if (remainder > 0) { - // x9 = remainder count - try self.movImm16(x9, @intCast(remainder), 0); - - // x10 = scalar loop counter - try self.movImm16(x10, 0, 0); - - const scalar_loop_start = self.code.items.len; - - // Compare counter with remainder - try self.cmpReg(x10, x9); - const scalar_bge_offset = self.code.items.len; - try self.bcond(COND_GE, 0); // placeholder - - // Load a[i] sign-extended - try self.ldrsbReg(x11, x19, x10); - - // Load b[i] sign-extended - try self.ldrsbReg(x22, x20, x10); - - // Multiply - try self.smull(x11, x11, x22); - - // Add to accumulator - try self.addReg(x21, x21, x11); - - // Increment counter - try self.addImm(x10, x10, 1); - - // Branch back - const scalar_loop_end_check = self.code.items.len; - const scalar_back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_start)) - @as(i32, @intCast(scalar_loop_end_check)), 4)); - try self.b(scalar_back_offset); - - // Patch scalar loop exit - const scalar_loop_end = self.code.items.len; - const scalar_forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_loop_end)) - @as(i32, @intCast(scalar_bge_offset)), 4)); - const scalar_patched_instr: u32 = 0x54000000 | - (@as(u32, @as(u19, @bitCast(scalar_forward_offset))) << 5) | - @as(u32, COND_GE); - @memcpy(self.code.items[scalar_bge_offset..][0..4], &std.mem.toBytes(scalar_patched_instr)); - } - - // Move result to x0 - try self.movReg(x0, x21); - - // Restore callee-saved registers - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - - // Function epilogue - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile bind operation for ARM64 - pub fn compileBindDirect(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - // x19 = a pointer, x20 = b pointer, x21 = dimension, x22 = counter - try self.movReg(x19, x0); - try self.movReg(x20, x1); - try self.movImm16(x22, 0, 0); - - if (dimension <= 0xFFFF) { - try self.movImm16(x21, @intCast(dimension), 0); - } else { - try self.loadImm64(x21, dimension); - } - - const loop_start = self.code.items.len; - try self.cmpReg(x22, x21); - - const bge_offset = self.code.items.len; - try self.bcond(COND_GE, 0); - - // Load a[i] and b[i] - try self.ldrsbReg(x10, x19, x22); - try self.ldrsbReg(x11, x20, x22); - - // Multiply (for ternary: -1*-1=1, -1*1=-1, 1*-1=-1, 1*1=1, 0*x=0) - try self.smull(x10, x10, x11); - - // Store result - try self.strbReg(x10, x19, x22); - - try self.addImm(x22, x22, 1); - - const loop_end_check = self.code.items.len; - const back_offset: i26 = @intCast(@divExact(@as(i32, @intCast(loop_start)) - @as(i32, @intCast(loop_end_check)), 4)); - try self.b(back_offset); - - const loop_end = self.code.items.len; - const forward_offset: i19 = @intCast(@divExact(@as(i32, @intCast(loop_end)) - @as(i32, @intCast(bge_offset)), 4)); - const patched_instr: u32 = 0x54000000 | - (@as(u32, @as(u19, @bitCast(forward_offset))) << 5) | - @as(u32, COND_GE); - @memcpy(self.code.items[bge_offset..][0..4], &std.mem.toBytes(patched_instr)); - - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile SIMD bind operation using NEON vector multiply - /// Processes 16 elements per iteration - pub fn compileBindSIMD(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - // x19 = a pointer (modified in place), x20 = b pointer - try self.movReg(x19, x0); - try self.movReg(x20, x1); - - // SIMD loop for dimension / 16 iterations - const simd_iters = dimension / 16; - if (simd_iters > 0) { - if (simd_iters <= 0xFFFF) { - try self.movImm16(x21, @intCast(simd_iters), 0); - } else { - try self.loadImm64(x21, simd_iters); - } - try self.movImm16(x22, 0, 0); // counter - - const simd_loop = self.code.items.len; - try self.cmpReg(x22, x21); - const bge_simd = self.code.items.len; - try self.bcond(COND_GE, 0); - - // Load 16 bytes from a and b - try self.ld1_16b(v0, x19); - try self.ld1_16b(v1, x20); - - // Multiply: v0 = v0 * v1 (element-wise i8 multiply) - try self.mul_16b(v0, v0, v1); - - // Store result back to a with post-increment - try self.st1_16b_post(v0, x19); - - // Advance b pointer - try self.addImm(x20, x20, 16); - - // Increment counter - try self.addImm(x22, x22, 1); - - const simd_end_check = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); - try self.b(back); - - // Patch branch - const simd_end = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); - } - - // Scalar loop for remainder (dimension % 16) - const remainder = dimension % 16; - if (remainder > 0) { - try self.movImm16(x21, @intCast(remainder), 0); - try self.movImm16(x22, 0, 0); - - const scalar_loop = self.code.items.len; - try self.cmpReg(x22, x21); - const bge_scalar = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ldrsbReg(x10, x19, x22); - try self.ldrsbReg(x11, x20, x22); - try self.smull(x10, x10, x11); - try self.strbReg(x10, x19, x22); - try self.addImm(x22, x22, 1); - - const scalar_end_check = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); - try self.b(back); - - const scalar_end = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); - } - - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile SIMD hamming distance using NEON compare - /// Counts positions where a[i] != b[i] - pub fn compileHammingSIMD(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - // x19 = a pointer, x20 = b pointer, x21 = accumulator - try self.movReg(x19, x0); - try self.movReg(x20, x1); - try self.movImm16(x21, 0, 0); // hamming distance = 0 - - // SIMD loop for dimension / 16 iterations - const simd_iters = dimension / 16; - if (simd_iters > 0) { - if (simd_iters <= 0xFFFF) { - try self.movImm16(x9, @intCast(simd_iters), 0); - } else { - try self.loadImm64(x9, simd_iters); - } - try self.movImm16(x22, 0, 0); // counter - - const simd_loop = self.code.items.len; - try self.cmpReg(x22, x9); - const bge_simd = self.code.items.len; - try self.bcond(COND_GE, 0); - - // Load 16 bytes from a and b with post-increment - try self.ld1_16b_post(v0, x19); - try self.ld1_16b_post(v1, x20); - - // Compare equal: v2 = (v0 == v1) ? 0xFF : 0x00 - try self.cmeq_16b(v2, v0, v1); - - // NOT: v2 = (v0 != v1) ? 0xFF : 0x00 - try self.not_16b(v2, v2); - - // Shift right by 7: 0xFF >> 7 = 1, 0x00 >> 7 = 0 - // Now each byte is 1 if positions differ, 0 if same - try self.ushr_16b(v2, v2, 7); - - // Sum all 16 bytes into a single value - try self.addv_16b(v3, v2); // v3.b[0] = sum of all bytes - - // Move byte to GPR - try self.umov_b(x10, v3, 0); - - // Add to accumulator - try self.addReg(x21, x21, x10); - - // Increment counter - try self.addImm(x22, x22, 1); - - const simd_end_check = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end_check)), 4)); - try self.b(back); - - const simd_end = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_end)) - @as(i32, @intCast(bge_simd)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); - } - - // Scalar loop for remainder - const remainder = dimension % 16; - if (remainder > 0) { - try self.movImm16(x9, @intCast(remainder), 0); - try self.movImm16(x22, 0, 0); - - const scalar_loop = self.code.items.len; - try self.cmpReg(x22, x9); - const bge_scalar = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ldrsbReg(x10, x19, x22); - try self.ldrsbReg(x11, x20, x22); - - // Compare and increment if not equal - try self.cmpReg(x10, x11); - // CSINC x10, xzr, xzr, EQ -> x10 = (EQ) ? 0 : 1 - const csinc: u32 = 0x9A9F07E0 | // CSINC Xd, XZR, XZR, cond - (@as(u32, COND_EQ) << 12) | - @as(u32, x10); - try self.emit32(csinc); - try self.addReg(x21, x21, x10); - - try self.addImm(x22, x22, 1); - - const scalar_end_check = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end_check)), 4)); - try self.b(back); - - const scalar_end = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_end)) - @as(i32, @intCast(bge_scalar)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); - } - - // Return result - try self.movReg(x0, x21); - - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile fused cosine: dot_ab, dot_aa, dot_bb in single pass - /// Returns f64 bit pattern: cos = dot_ab / sqrt(dot_aa * dot_bb) - pub fn compileFusedCosine(self: *Self, dimension: usize) !void { - self.reset(); - - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - try self.movReg(x19, x0); - try self.movReg(x20, x1); - - // Three accumulators - try self.movi_16b(v2, 0); - try self.movi_16b(v3, 0); - try self.movi_16b(v4, 0); - - const simd_iters = dimension / 16; - if (simd_iters > 0) { - if (simd_iters <= 0xFFFF) { - try self.movImm16(x9, @intCast(simd_iters), 0); - } else { - try self.loadImm64(x9, simd_iters); - } - try self.movImm16(x21, 0, 0); - - const simd_loop = self.code.items.len; - try self.cmpReg(x21, x9); - const bge_simd = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ld1_16b_post(v0, x19); - try self.ld1_16b_post(v1, x20); - try self.sdot_4s(v2, v0, v1); - try self.sdot_4s(v3, v0, v0); - try self.sdot_4s(v4, v1, v1); - try self.addImm(x21, x21, 1); - - const simd_end = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); - try self.b(back); - - const simd_exit = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); - } - - try self.addv_4s(v2, v2); - try self.addv_4s(v3, v3); - try self.addv_4s(v4, v4); - try self.smov_s(x10, v2, 0); - try self.smov_s(x11, v3, 0); - try self.smov_s(x12, v4, 0); - - const remainder = dimension % 16; - if (remainder > 0) { - try self.movReg(x19, x0); - try self.movReg(x20, x1); - const offset = simd_iters * 16; - if (offset > 0) { - try self.loadImm64(x9, offset); - try self.addReg(x19, x19, x9); - try self.addReg(x20, x20, x9); - } - - try self.movImm16(x9, @intCast(remainder), 0); - try self.movImm16(x21, 0, 0); - - const scalar_loop = self.code.items.len; - try self.cmpReg(x21, x9); - const bge_scalar = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ldrsbReg(x13, x19, x21); - try self.ldrsbReg(x14, x20, x21); - try self.mul(x15, x13, x14); - try self.addReg(x10, x10, x15); - try self.mul(x15, x13, x13); - try self.addReg(x11, x11, x15); - try self.mul(x15, x14, x14); - try self.addReg(x12, x12, x15); - try self.addImm(x21, x21, 1); - - const scalar_end = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); - try self.b(back); - - const scalar_exit = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); - } - - try self.scvtf_d_x(d0, x10); - try self.scvtf_d_x(d1, x11); - try self.scvtf_d_x(d2, x12); - try self.fmul_d(d1, d1, d2); - try self.fsqrt_d(d1, d1); - try self.fdiv_d(d0, d0, d1); - try self.fmov_x_d(x0, d0); - - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - /// Compile bundle SIMD: result[i] = threshold(a[i] + b[i]) - pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { - self.reset(); - - try self.stpPreIndex(x29, x30, sp, -2); - try self.movReg(x29, sp); - try self.stpPreIndex(x19, x20, sp, -2); - try self.stpPreIndex(x21, x22, sp, -2); - - try self.movReg(x19, x0); - try self.movReg(x20, x1); - try self.movReg(x21, x0); - - const simd_iters = dimension / 16; - if (simd_iters > 0) { - if (simd_iters <= 0xFFFF) { - try self.movImm16(x9, @intCast(simd_iters), 0); - } else { - try self.loadImm64(x9, simd_iters); - } - try self.movImm16(x22, 0, 0); - - const simd_loop = self.code.items.len; - try self.cmpReg(x22, x9); - const bge_simd = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ld1_16b_post(v0, x21); - try self.ld1_16b_post(v1, x20); - try self.add_16b(v2, v0, v1); - try self.sshr_16b_7(v3, v2); - try self.cmgt_16b_zero(v4, v2); - try self.neg_16b(v4, v4); - try self.orr_16b(v2, v3, v4); - try self.st1_16b_post(v2, x19); - try self.addImm(x22, x22, 1); - - const simd_end = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(simd_loop)) - @as(i32, @intCast(simd_end)), 4)); - try self.b(back); - - const simd_exit = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(simd_exit)) - @as(i32, @intCast(bge_simd)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_simd..][0..4], &std.mem.toBytes(patched)); - } - - const remainder = dimension % 16; - if (remainder > 0) { - try self.movImm16(x9, @intCast(remainder), 0); - try self.movImm16(x22, 0, 0); - - const scalar_loop = self.code.items.len; - try self.cmpReg(x22, x9); - const bge_scalar = self.code.items.len; - try self.bcond(COND_GE, 0); - - try self.ldrsbReg(x10, x21, x22); - try self.ldrsbReg(x11, x20, x22); - try self.addReg(x10, x10, x11); - try self.cmpImm(x10, 0); - try self.movImm16(x11, 0, 0); - try self.csetGT(x11); - try self.movImm16(x12, 0, 0); - try self.csetLT(x12); - try self.subReg(x10, x11, x12); - try self.strbReg(x10, x19, x22); - try self.addImm(x22, x22, 1); - - const scalar_end = self.code.items.len; - const back: i26 = @intCast(@divExact(@as(i32, @intCast(scalar_loop)) - @as(i32, @intCast(scalar_end)), 4)); - try self.b(back); - - const scalar_exit = self.code.items.len; - const fwd: i19 = @intCast(@divExact(@as(i32, @intCast(scalar_exit)) - @as(i32, @intCast(bge_scalar)), 4)); - const patched: u32 = 0x54000000 | (@as(u32, @as(u19, @bitCast(fwd))) << 5) | @as(u32, COND_GE); - @memcpy(self.code.items[bge_scalar..][0..4], &std.mem.toBytes(patched)); - } - - try self.ldpPostIndex(x21, x22, sp, 2); - try self.ldpPostIndex(x19, x20, sp, 2); - try self.ldpPostIndex(x29, x30, sp, 2); - try self.retInstr(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // EXECUTION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Make code executable and return function pointer - pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { - const code_size = self.code.items.len; - if (code_size == 0) return error.EmptyCode; - - // ARM64 can have 16KB pages on Apple Silicon - const page_size: usize = 16384; - const alloc_size = std.mem.alignForward(usize, code_size, page_size); - - const mem = try std.posix.mmap( - null, - alloc_size, - std.posix.PROT.READ | std.posix.PROT.WRITE, - .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, - -1, - 0, - ); - - @memcpy(mem[0..code_size], self.code.items); - - try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); - - self.exec_mem = mem; - - return @ptrCast(mem.ptr); - } - - pub fn codeSize(self: *const Self) usize { - return self.code.items.len; - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "ARM64 JIT compiler init and deinit" { - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - try std.testing.expectEqual(@as(usize, 0), compiler.codeSize()); -} - -test "ARM64 JIT dot product compilation" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileDotProduct(dim); - - // Code should be generated - try std.testing.expect(compiler.codeSize() > 0); - // ARM64 instructions are 4 bytes each - try std.testing.expect(compiler.codeSize() % 4 == 0); -} - -test "ARM64 JIT dot product execution" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileDotProduct(dim); - - const func = try compiler.finalize(); - - // Create test data - const a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; - const b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; - - // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) - // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 - const expected: i64 = -2; - - var a_mut = a; - var b_mut = b; - const result = func(@ptrCast(&a_mut), @ptrCast(&b_mut)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 JIT bind compilation" { - if (!is_arm64) { - return; - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileBindDirect(dim); - - try std.testing.expect(compiler.codeSize() > 0); - try std.testing.expect(compiler.codeSize() % 4 == 0); -} - -test "ARM64 NEON SIMD dot product compilation" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 64; // Must be multiple of 16 - try compiler.compileDotProductSIMD(dim); - - try std.testing.expect(compiler.codeSize() > 0); - try std.testing.expect(compiler.codeSize() % 4 == 0); -} - -test "ARM64 NEON SIMD dot product execution" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 16; // Minimum SIMD dimension - try compiler.compileDotProductSIMD(dim); - - const func = try compiler.finalize(); - - // Create test data: all 1s dot all 1s = 16 - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - } - - const expected: i64 = 16; - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 NEON SIMD dot product with mixed values" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 32; - try compiler.compileDotProductSIMD(dim); - - const func = try compiler.finalize(); - - // Create test data: alternating 1, -1 - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = if (i % 2 == 0) 1 else -1; - b[i] = 1; - } - // Expected: 16 * 1 + 16 * (-1) = 0 - const expected: i64 = 0; - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 NEON SIMD dot product large dimension" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 256; // 16 SIMD iterations - try compiler.compileDotProductSIMD(dim); - - const func = try compiler.finalize(); - - // Create test data - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - // Ternary values: -1, 0, 1 - const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a[i] = val_a; - b[i] = val_b; - expected += @as(i64, val_a) * @as(i64, val_b); - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 NEON SIMD benchmark vs scalar" { - if (!is_arm64) { - return; // Skip on non-ARM64 - } - - const dim = 1024; // Large dimension for meaningful benchmark - const iterations = 10000; - - // Prepare test data - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - } - - // Compile scalar version - var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer scalar_compiler.deinit(); - try scalar_compiler.compileDotProduct(dim); - const scalar_func = try scalar_compiler.finalize(); - - // Compile SIMD version - var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer simd_compiler.deinit(); - try simd_compiler.compileDotProductSIMD(dim); - const simd_func = try simd_compiler.finalize(); - - // Benchmark scalar - var timer = try std.time.Timer.start(); - var scalar_result: i64 = 0; - for (0..iterations) |_| { - scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); - } - const scalar_ns = timer.read(); - - // Benchmark SIMD - timer.reset(); - var simd_result: i64 = 0; - for (0..iterations) |_| { - simd_result = simd_func(@ptrCast(&a), @ptrCast(&b)); - } - const simd_ns = timer.read(); - - // Verify results match - try std.testing.expectEqual(scalar_result, simd_result); - - // Print benchmark results - const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; - const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; - const speedup = scalar_ms / simd_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" ARM64 NEON SIMD BENCHMARK RESULTS\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Dimension: {d} elements\n", .{dim}); - std.debug.print(" Iterations: {d}\n", .{iterations}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); - std.debug.print(" SIMD: {d:.3} ms ({d:.0} ns/iter)\n", .{ simd_ms, @as(f64, @floatFromInt(simd_ns)) / @as(f64, iterations) }); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - // Assert SIMD is faster (relaxed for CI/heavy-load environments) - try std.testing.expect(speedup > 0.8); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// HYBRID SIMD + SCALAR TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "ARM64 hybrid dot product - aligned dimension (32)" { - if (!is_arm64) return; - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 32; // Aligned: 2 SIMD iters, 0 scalar - try compiler.compileDotProductHybrid(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - expected += 1; - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 hybrid dot product - non-aligned dimension (17)" { - if (!is_arm64) return; - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 17; // Non-aligned: 1 SIMD iter + 1 scalar - try compiler.compileDotProductHybrid(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - expected += 1; - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 hybrid dot product - small dimension (7)" { - if (!is_arm64) return; - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 7; // Pure scalar: 0 SIMD iters, 7 scalar - try compiler.compileDotProductHybrid(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - const val_a: i8 = if (i % 2 == 0) 1 else -1; - a[i] = val_a; - b[i] = 1; - expected += val_a; - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 hybrid dot product - dimension 100" { - if (!is_arm64) return; - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 100; // 6 SIMD iters + 4 scalar - try compiler.compileDotProductHybrid(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a[i] = val_a; - b[i] = val_b; - expected += @as(i64, val_a) * @as(i64, val_b); - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 hybrid dot product - dimension 1000" { - if (!is_arm64) return; - - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 1000; // 62 SIMD iters + 8 scalar - try compiler.compileDotProductHybrid(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - for (0..dim) |i| { - const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a[i] = val_a; - b[i] = val_b; - expected += @as(i64, val_a) * @as(i64, val_b); - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "ARM64 hybrid benchmark vs pure scalar" { - if (!is_arm64) return; - - const dim = 1000; // Non-aligned dimension - const iterations = 10000; - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - } - - // Compile pure scalar - var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer scalar_compiler.deinit(); - try scalar_compiler.compileDotProduct(dim); - const scalar_func = try scalar_compiler.finalize(); - - // Compile hybrid - var hybrid_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer hybrid_compiler.deinit(); - try hybrid_compiler.compileDotProductHybrid(dim); - const hybrid_func = try hybrid_compiler.finalize(); - - // Benchmark scalar - var timer = try std.time.Timer.start(); - var scalar_result: i64 = 0; - for (0..iterations) |_| { - scalar_result = scalar_func(@ptrCast(&a), @ptrCast(&b)); - } - const scalar_ns = timer.read(); - - // Benchmark hybrid - timer.reset(); - var hybrid_result: i64 = 0; - for (0..iterations) |_| { - hybrid_result = hybrid_func(@ptrCast(&a), @ptrCast(&b)); - } - const hybrid_ns = timer.read(); - - // Verify results match - try std.testing.expectEqual(scalar_result, hybrid_result); - - const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; - const hybrid_ms = @as(f64, @floatFromInt(hybrid_ns)) / 1_000_000.0; - const speedup = scalar_ms / hybrid_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" ARM64 HYBRID SIMD+SCALAR BENCHMARK (dim=1000)\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" SIMD iters: {d}, Scalar remainder: {d}\n", .{ dim / 16, dim % 16 }); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" Pure Scalar: {d:.3} ms ({d:.0} ns/iter)\n", .{ scalar_ms, @as(f64, @floatFromInt(scalar_ns)) / @as(f64, iterations) }); - std.debug.print(" Hybrid: {d:.3} ms ({d:.0} ns/iter)\n", .{ hybrid_ms, @as(f64, @floatFromInt(hybrid_ns)) / @as(f64, iterations) }); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - // Hybrid should be faster (lenient threshold for flaky benchmarks on loaded systems) - // Minimum 1.0x means it's not slower - any speedup is acceptable - try std.testing.expect(speedup > 1.0); -} - -test "ARM64 SIMD bind correctness" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 64; - try compiler.compileBindSIMD(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: [dim]i8 = undefined; - - // Initialize: a = [1, -1, 0, 1, ...], b = [1, 1, -1, -1, ...] - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = if (i % 4 < 2) @as(i8, 1) else @as(i8, -1); - expected[i] = a[i] * b[i]; - } - - // Run SIMD bind (modifies a in place) - _ = func(@ptrCast(&a), @ptrCast(&b)); - - // Verify - for (0..dim) |i| { - try std.testing.expectEqual(expected[i], a[i]); - } -} - -test "ARM64 SIMD bind non-aligned dimension" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 100; // Not divisible by 16 - try compiler.compileBindSIMD(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: [dim]i8 = undefined; - - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - expected[i] = a[i] * b[i]; - } - - _ = func(@ptrCast(&a), @ptrCast(&b)); - - for (0..dim) |i| { - try std.testing.expectEqual(expected[i], a[i]); - } -} - -test "ARM64 SIMD hamming correctness" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 64; - try compiler.compileHammingSIMD(dim); - const func = try compiler.finalize(); - - // Test 1: identical vectors -> hamming = 0 - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - } - const hamming_identical = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(@as(i64, 0), hamming_identical); - - // Test 2: all different -> hamming = dim - for (0..dim) |i| { - a[i] = 1; - b[i] = -1; - } - const hamming_all_diff = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(@as(i64, dim), hamming_all_diff); - - // Test 3: half different - for (0..dim) |i| { - a[i] = 1; - b[i] = if (i < dim / 2) @as(i8, 1) else @as(i8, -1); - } - const hamming_half = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); -} - -test "ARM64 SIMD hamming non-aligned dimension" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 100; // Not divisible by 16 - try compiler.compileHammingSIMD(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - - // Count expected differences manually - var expected_hamming: i64 = 0; - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - if (a[i] != b[i]) expected_hamming += 1; - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected_hamming, result); -} - -test "ARM64 SIMD bind benchmark vs scalar" { - if (!is_arm64) return; - const dim = 1024; - const iterations = 10000; - - var a_simd: [dim]i8 = undefined; - var a_scalar: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - - for (0..dim) |i| { - const val = @as(i8, @intCast(@as(i32, @intCast(i % 3)) - 1)); - a_simd[i] = val; - a_scalar[i] = val; - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - } - - // Compile SIMD - var simd_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer simd_compiler.deinit(); - try simd_compiler.compileBindSIMD(dim); - const simd_func = try simd_compiler.finalize(); - - // Compile scalar - var scalar_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer scalar_compiler.deinit(); - try scalar_compiler.compileBindDirect(dim); - const scalar_func = try scalar_compiler.finalize(); - - // Benchmark SIMD - var timer = try std.time.Timer.start(); - for (0..iterations) |_| { - // Reset a for fair comparison - for (0..dim) |i| { - a_simd[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - } - _ = simd_func(@ptrCast(&a_simd), @ptrCast(&b)); - } - const simd_ns = timer.read(); - - // Benchmark scalar - timer.reset(); - for (0..iterations) |_| { - for (0..dim) |i| { - a_scalar[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - } - _ = scalar_func(@ptrCast(&a_scalar), @ptrCast(&b)); - } - const scalar_ns = timer.read(); - - const simd_ms = @as(f64, @floatFromInt(simd_ns)) / 1_000_000.0; - const scalar_ms = @as(f64, @floatFromInt(scalar_ns)) / 1_000_000.0; - const speedup = scalar_ms / simd_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" ARM64 SIMD BIND BENCHMARK (dim={d})\n", .{dim}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Scalar: {d:.3} ms\n", .{scalar_ms}); - std.debug.print(" SIMD: {d:.3} ms\n", .{simd_ms}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - // Note: Bind speedup modest due to array reset overhead -} - -test "ARM64 fused cosine correctness" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 64; - try compiler.compileFusedCosine(dim); - const func = try compiler.finalize(); - - // Test identical vectors: cos = 1.0 - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - } - - const result_bits = func(@ptrCast(&a), @ptrCast(&b)); - const result: f64 = @bitCast(result_bits); - try std.testing.expectApproxEqRel(@as(f64, 1.0), result, 0.001); - - // Test opposite vectors: cos = -1.0 - for (0..dim) |i| { - a[i] = 1; - b[i] = -1; - } - const neg_bits = func(@ptrCast(&a), @ptrCast(&b)); - const neg_result: f64 = @bitCast(neg_bits); - try std.testing.expectApproxEqRel(@as(f64, -1.0), neg_result, 0.001); -} - -test "ARM64 fused cosine benchmark vs 3x dot" { - if (!is_arm64) return; - var fused_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer fused_compiler.deinit(); - var dot_compiler = Arm64JitCompiler.init(std.testing.allocator); - defer dot_compiler.deinit(); - - const dim = 1024; - const iterations = 10000; - - try fused_compiler.compileFusedCosine(dim); - const fused_func = try fused_compiler.finalize(); - - try dot_compiler.compileDotProductHybrid(dim); - const dot_func = try dot_compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - } - - // Benchmark fused - var timer = try std.time.Timer.start(); - var fused_result: f64 = 0; - for (0..iterations) |_| { - const bits = fused_func(@ptrCast(&a), @ptrCast(&b)); - fused_result = @bitCast(bits); - } - const fused_ns = timer.read(); - - // Benchmark 3x dot - timer.reset(); - var dot_result: f64 = 0; - for (0..iterations) |_| { - const dot_ab = dot_func(@ptrCast(&a), @ptrCast(&b)); - const dot_aa = dot_func(@ptrCast(&a), @ptrCast(&a)); - const dot_bb = dot_func(@ptrCast(&b), @ptrCast(&b)); - const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); - dot_result = @as(f64, @floatFromInt(dot_ab)) / norm; - } - const dot_ns = timer.read(); - - const fused_ms = @as(f64, @floatFromInt(fused_ns)) / 1_000_000.0; - const dot_ms = @as(f64, @floatFromInt(dot_ns)) / 1_000_000.0; - const speedup = dot_ms / fused_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" ARM64 FUSED COSINE BENCHMARK (dim={d})\n", .{dim}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" 3x Dot: {d:.3} ms\n", .{dot_ms}); - std.debug.print(" Fused: {d:.3} ms\n", .{fused_ms}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print(" Results: fused={d:.6}, 3xdot={d:.6}\n", .{ fused_result, dot_result }); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); -} - -test "ARM64 bundle SIMD compilation" { - if (!is_arm64) return; - // Just verify compilation works, bundle correctness tested via vsa_jit - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 32; - try compiler.compileBundleSIMD(dim); - const func = try compiler.finalize(); - _ = func; -} - -test "ARM64 bundle SIMD non-aligned" { - if (!is_arm64) return; - var compiler = Arm64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 23; // Non-aligned - try compiler.compileBundleSIMD(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - } - - _ = func(@ptrCast(&a), @ptrCast(&b)); - // Bundle SIMD correctness to be verified via integration tests -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig deleted file mode 100644 index 45ac5c6..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_unified.zig +++ /dev/null @@ -1,434 +0,0 @@ -// @origin(spec:jit_unified.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity Unified JIT Compiler -// Architecture-agnostic interface with compile-time backend selection -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const builtin = @import("builtin"); - -// Import architecture-specific backends -const arm64 = @import("jit_arm64.zig"); -const x86_64 = @import("jit_x86_64.zig"); - -// ═══════════════════════════════════════════════════════════════════════════════ -// ARCHITECTURE DETECTION -// ═══════════════════════════════════════════════════════════════════════════════ - -pub const Architecture = enum { - arm64, - x86_64, - unsupported, -}; - -pub const current_arch: Architecture = switch (builtin.cpu.arch) { - .aarch64 => .arm64, - .x86_64 => .x86_64, - else => .unsupported, -}; - -pub const is_arm64 = current_arch == .arm64; -pub const is_x86_64 = current_arch == .x86_64; -pub const is_jit_supported = current_arch != .unsupported; - -// ═══════════════════════════════════════════════════════════════════════════════ -// UNIFIED JIT FUNCTION TYPES -// ═══════════════════════════════════════════════════════════════════════════════ - -/// JIT-compiled dot product function -/// Takes two i8 array pointers and returns i64 dot product -pub const JitDotFn = *const fn (*anyopaque, *anyopaque) callconv(.c) i64; - -/// JIT-compiled bind function -/// Takes two i8 array pointers, stores result in first -pub const JitBindFn = *const fn (*anyopaque, *anyopaque) callconv(.c) void; - -// ═══════════════════════════════════════════════════════════════════════════════ -// UNIFIED JIT COMPILER -// ═══════════════════════════════════════════════════════════════════════════════ - -pub const UnifiedJitCompiler = struct { - allocator: std.mem.Allocator, - - // Architecture-specific backend - backend: Backend, - - const Backend = union(Architecture) { - arm64: arm64.Arm64JitCompiler, - x86_64: x86_64.X86_64JitCompiler, - unsupported: void, - }; - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .allocator = allocator, - .backend = switch (current_arch) { - .arm64 => .{ .arm64 = arm64.Arm64JitCompiler.init(allocator) }, - .x86_64 => .{ .x86_64 = x86_64.X86_64JitCompiler.init(allocator) }, - .unsupported => .{ .unsupported = {} }, - }, - }; - } - - pub fn deinit(self: *Self) void { - switch (self.backend) { - .arm64 => |*b| b.deinit(), - .x86_64 => |*b| b.deinit(), - .unsupported => {}, - } - } - - /// Get current architecture name - pub fn archName() []const u8 { - return switch (current_arch) { - .arm64 => "ARM64 (AArch64)", - .x86_64 => "x86-64", - .unsupported => "Unsupported", - }; - } - - /// Check if SIMD is available - pub fn hasSIMD() bool { - return switch (current_arch) { - .arm64 => true, // NEON is always available on AArch64 - .x86_64 => false, // DEFERRED: Add CPUID-based AVX/SSE detection for x86_64 - .unsupported => false, - }; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // DOT PRODUCT COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile dot product - automatically selects best implementation - /// For ARM64: uses hybrid SIMD+scalar for any dimension - /// For x86_64: uses scalar loop - pub fn compileDotProduct(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| { - // Use hybrid for best performance on any dimension - try b.compileDotProductHybrid(dimension); - }, - .x86_64 => |*b| { - // x86_64 scalar implementation - try b.compileDotProduct(dimension); - }, - .unsupported => return error.UnsupportedArchitecture, - } - } - - /// Compile pure SIMD dot product (requires dimension % 16 == 0 on ARM64) - pub fn compileDotProductSIMD(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileDotProductSIMD(dimension), - .x86_64 => |*b| { - // x86_64 falls back to scalar (DEFERRED: add AVX2 SIMD implementation) - try b.compileDotProduct(dimension); - }, - .unsupported => return error.UnsupportedArchitecture, - } - } - - /// Compile pure scalar dot product - pub fn compileDotProductScalar(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileDotProduct(dimension), - .x86_64 => |*b| try b.compileDotProduct(dimension), - .unsupported => return error.UnsupportedArchitecture, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // BIND COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile bind operation - uses SIMD on ARM64 - pub fn compileBind(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileBindSIMD(dimension), - .x86_64 => |*b| try b.compileBindDirect(dimension), - .unsupported => return error.UnsupportedArchitecture, - } - } - - /// Compile bind operation (scalar version) - pub fn compileBindScalar(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileBindDirect(dimension), - .x86_64 => |*b| try b.compileBindDirect(dimension), - .unsupported => return error.UnsupportedArchitecture, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // HAMMING DISTANCE COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile hamming distance - uses SIMD on ARM64 - pub fn compileHamming(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileHammingSIMD(dimension), - .x86_64 => return error.UnsupportedOperation, - .unsupported => return error.UnsupportedArchitecture, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // FUSED COSINE COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile fused cosine similarity - computes dot(a,b), dot(a,a), dot(b,b) in single pass - /// Returns f64 bit pattern (2.5x faster than 3 separate dot products) - pub fn compileFusedCosine(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileFusedCosine(dimension), - .x86_64 => return error.UnsupportedOperation, - .unsupported => return error.UnsupportedArchitecture, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // BUNDLE COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile bundle operation - threshold(a + b) to {-1, 0, 1} - pub fn compileBundleSIMD(self: *Self, dimension: usize) !void { - switch (self.backend) { - .arm64 => |*b| try b.compileBundleSIMD(dimension), - .x86_64 => return error.UnsupportedOperation, - .unsupported => return error.UnsupportedArchitecture, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // FINALIZATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Make compiled code executable and return function pointer - pub fn finalize(self: *Self) !JitDotFn { - switch (self.backend) { - .arm64 => |*b| return try b.finalize(), - .x86_64 => |*b| return try b.finalize(), - .unsupported => return error.UnsupportedArchitecture, - } - } - - /// Get generated code size - pub fn codeSize(self: *Self) usize { - return switch (self.backend) { - .arm64 => |*b| b.codeSize(), - .x86_64 => |*b| b.codeSize(), - .unsupported => 0, - }; - } - - /// Reset compiler for new compilation - pub fn reset(self: *Self) void { - switch (self.backend) { - .arm64 => |*b| b.reset(), - .x86_64 => |*b| b.reset(), - .unsupported => {}, - } - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// CONVENIENCE FUNCTIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Quick compile and run dot product -pub fn jitDotProduct(allocator: std.mem.Allocator, a: []const i8, b: []const i8) !i64 { - if (a.len != b.len) return error.DimensionMismatch; - - var compiler = UnifiedJitCompiler.init(allocator); - defer compiler.deinit(); - - try compiler.compileDotProduct(a.len); - const func = try compiler.finalize(); - - // Need mutable copies for the function call - const a_copy = try allocator.alloc(i8, a.len); - defer allocator.free(a_copy); - @memcpy(a_copy, a); - - const b_copy = try allocator.alloc(i8, b.len); - defer allocator.free(b_copy); - @memcpy(b_copy, b); - - return func(@ptrCast(a_copy.ptr), @ptrCast(b_copy.ptr)); -} - -/// Print JIT capabilities info -pub fn printCapabilities() void { - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" TRINITY UNIFIED JIT CAPABILITIES\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Architecture: {s}\n", .{UnifiedJitCompiler.archName()}); - std.debug.print(" JIT Supported: {}\n", .{is_jit_supported}); - std.debug.print(" SIMD Available: {}\n", .{UnifiedJitCompiler.hasSIMD()}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - - if (is_arm64) { - std.debug.print(" ARM64 Features:\n", .{}); - std.debug.print(" • NEON SIMD (128-bit vectors)\n", .{}); - std.debug.print(" • SDOT instruction (16 i8 elements/cycle)\n", .{}); - std.debug.print(" • Hybrid SIMD+Scalar for any dimension\n", .{}); - std.debug.print(" • Expected speedup: 15-70x over scalar\n", .{}); - } else if (is_x86_64) { - std.debug.print(" x86-64 Features:\n", .{}); - std.debug.print(" • Scalar JIT implementation\n", .{}); - std.debug.print(" • System V ABI compatible\n", .{}); - std.debug.print(" • DEFERRED (v12): AVX2/AVX-512 SIMD support\n", .{}); - } - - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "Unified JIT architecture detection" { - const arch = current_arch; - - // Should be one of the known architectures - try std.testing.expect(arch == .arm64 or arch == .x86_64 or arch == .unsupported); - - // Consistency checks - if (is_arm64) { - try std.testing.expectEqual(Architecture.arm64, arch); - } - if (is_x86_64) { - try std.testing.expectEqual(Architecture.x86_64, arch); - } -} - -test "Unified JIT compiler init/deinit" { - var compiler = UnifiedJitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - // Should initialize without error - try std.testing.expect(true); -} - -test "Unified JIT dot product on ARM64" { - if (!is_arm64) return; - - var compiler = UnifiedJitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 100; // Non-aligned dimension - try compiler.compileDotProduct(dim); - - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - - for (0..dim) |i| { - a[i] = 1; - b[i] = 1; - expected += 1; - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "Unified JIT dot product various dimensions" { - if (!is_arm64) return; - - const test_dims = [_]usize{ 1, 7, 16, 17, 32, 100, 256, 1000 }; - - for (test_dims) |dim| { - var compiler = UnifiedJitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - try compiler.compileDotProduct(dim); - const func = try compiler.finalize(); - - // Allocate dynamic arrays - var a = try std.testing.allocator.alloc(i8, dim); - defer std.testing.allocator.free(a); - var b = try std.testing.allocator.alloc(i8, dim); - defer std.testing.allocator.free(b); - - var expected: i64 = 0; - for (0..dim) |i| { - const val: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); - a[i] = val; - b[i] = 1; - expected += val; - } - - const result = func(@ptrCast(a.ptr), @ptrCast(b.ptr)); - try std.testing.expectEqual(expected, result); - } -} - -test "Unified JIT convenience function" { - if (!is_arm64) return; - - const a = [_]i8{ 1, 1, 1, -1, -1, 0, 0, 1 }; - const b = [_]i8{ 1, 1, 1, 1, 1, 1, 1, 1 }; - - // Expected: 1 + 1 + 1 - 1 - 1 + 0 + 0 + 1 = 2 - const expected: i64 = 2; - - const result = try jitDotProduct(std.testing.allocator, &a, &b); - try std.testing.expectEqual(expected, result); -} - -test "Unified JIT print capabilities" { - // Just verify it doesn't crash - printCapabilities(); -} - -test "Unified JIT benchmark" { - if (!is_arm64) return; - - const dim = 1024; - const iterations = 10000; - - var compiler = UnifiedJitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - try compiler.compileDotProduct(dim); - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - for (0..dim) |i| { - a[i] = @intCast(@as(i32, @intCast(i % 3)) - 1); - b[i] = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - } - - var timer = try std.time.Timer.start(); - var result: i64 = 0; - for (0..iterations) |_| { - result = func(@ptrCast(&a), @ptrCast(&b)); - } - const ns = timer.read(); - - const ms = @as(f64, @floatFromInt(ns)) / 1_000_000.0; - const ns_per_iter = @as(f64, @floatFromInt(ns)) / @as(f64, iterations); - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" UNIFIED JIT BENCHMARK ({s})\n", .{UnifiedJitCompiler.archName()}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Dimension: {d}, Iterations: {d}\n", .{ dim, iterations }); - std.debug.print(" Total time: {d:.3} ms\n", .{ms}); - std.debug.print(" Per iteration: {d:.0} ns\n", .{ns_per_iter}); - std.debug.print(" Throughput: {d:.2} M dot products/sec\n", .{@as(f64, iterations) / ms * 1000.0 / 1_000_000.0}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - // Sanity check - result should be deterministic - try std.testing.expect(result != 0 or dim == 0); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig deleted file mode 100644 index 104242a..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/jit_x86_64.zig +++ /dev/null @@ -1,471 +0,0 @@ -// @origin(spec:jit_x86_64.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity JIT Compiler - x86-64 Backend -// Compiles VSA operations to native x86-64 machine code -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const builtin = @import("builtin"); - -// ═══════════════════════════════════════════════════════════════════════════════ -// X86-64 JIT COMPILER -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Check if we're on x86-64 -pub const is_x86_64 = builtin.cpu.arch == .x86_64; - -/// X86-64 JIT Compiler -pub const X86_64JitCompiler = struct { - code: std.ArrayListUnmanaged(u8), - allocator: std.mem.Allocator, - exec_mem: ?[]align(std.heap.page_size_min) u8 = null, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .code = .{}, - .allocator = allocator, - }; - } - - pub fn deinit(self: *Self) void { - self.code.deinit(self.allocator); - if (self.exec_mem) |mem| { - std.posix.munmap(mem); - } - } - - pub fn reset(self: *Self) void { - self.code.clearRetainingCapacity(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // X86-64 INSTRUCTION ENCODING HELPERS - // ═══════════════════════════════════════════════════════════════════════════ - - /// Emit raw bytes - fn emit(self: *Self, bytes: []const u8) !void { - try self.code.appendSlice(self.allocator, bytes); - } - - /// Emit single byte - fn emit1(self: *Self, b: u8) !void { - try self.code.append(self.allocator, b); - } - - /// Emit 32-bit immediate (little-endian) - fn emitImm32(self: *Self, imm: i32) !void { - try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); - } - - /// Emit 64-bit immediate (little-endian) - fn emitImm64(self: *Self, imm: i64) !void { - try self.code.appendSlice(self.allocator, std.mem.asBytes(&imm)); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // X86-64 INSTRUCTION ENCODING - // ═══════════════════════════════════════════════════════════════════════════ - - /// push rbp - fn pushRbp(self: *Self) !void { - try self.emit1(0x55); - } - - /// pop rbp - fn popRbp(self: *Self) !void { - try self.emit1(0x5D); - } - - /// mov rbp, rsp - fn movRbpRsp(self: *Self) !void { - try self.emit(&[_]u8{ 0x48, 0x89, 0xE5 }); - } - - /// mov rsp, rbp - fn movRspRbp(self: *Self) !void { - try self.emit(&[_]u8{ 0x48, 0x89, 0xEC }); - } - - /// ret - fn ret(self: *Self) !void { - try self.emit1(0xC3); - } - - /// xor eax, eax (zero rax) - fn xorEaxEax(self: *Self) !void { - try self.emit(&[_]u8{ 0x31, 0xC0 }); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // VSA OPERATION COMPILATION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Compile dot product (returns i64 in rax) - /// x86-64 System V ABI: rdi = first arg, rsi = second arg, rax = return - pub fn compileDotProduct(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.pushRbp(); - try self.movRbpRsp(); - - // Save callee-saved registers - try self.emit(&[_]u8{0x53}); // push rbx - try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 - try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 - try self.emit(&[_]u8{ 0x41, 0x56 }); // push r14 - - // r12 = a pointer (from rdi) - try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi - - // r13 = b pointer (from rsi) - try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi - - // r14 = accumulator (0) - try self.emit(&[_]u8{ 0x4D, 0x31, 0xF6 }); // xor r14, r14 - - // rbx = loop counter (0) - try self.xorEaxEax(); - try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax - - const loop_start = self.code.items.len; - - // Compare rbx with dimension - try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 - try self.emitImm32(@intCast(dimension)); - - // jge loop_end - try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 - const jge_offset = self.code.items.len; - try self.emitImm32(0); // placeholder - - // Load a[rbx] into eax (sign-extended) - try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] - - // Load b[rbx] into ecx (sign-extended) - try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] - - // imul eax, ecx - try self.emit(&[_]u8{ 0x0F, 0xAF, 0xC1 }); // imul eax, ecx - - // Sign-extend eax to rax - try self.emit(&[_]u8{ 0x48, 0x98 }); // cdqe - - // Add to accumulator: r14 += rax - try self.emit(&[_]u8{ 0x49, 0x01, 0xC6 }); // add r14, rax - - // Increment counter - try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx - - // Jump back to loop start - try self.emit(&[_]u8{0xE9}); // jmp rel32 - const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); - try self.emitImm32(loop_back_offset); - - // Patch jge offset - const loop_end = self.code.items.len; - const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); - @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); - - // Move result to rax - try self.emit(&[_]u8{ 0x4C, 0x89, 0xF0 }); // mov rax, r14 - - // Restore callee-saved registers - try self.emit(&[_]u8{ 0x41, 0x5E }); // pop r14 - try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 - try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 - try self.emit(&[_]u8{0x5B}); // pop rbx - - // Function epilogue - try self.movRspRbp(); - try self.popRbp(); - try self.ret(); - } - - /// Compile bind operation (element-wise multiply for ternary) - pub fn compileBindDirect(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.pushRbp(); - try self.movRbpRsp(); - - // Save callee-saved registers - try self.emit(&[_]u8{0x53}); // push rbx - try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 - try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 - - // r12 = a pointer - try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi - - // r13 = b pointer - try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi - - // rbx = loop counter (0) - try self.xorEaxEax(); - try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax - - const loop_start = self.code.items.len; - - // Compare rbx with dimension - try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 - try self.emitImm32(@intCast(dimension)); - - // jge loop_end - try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 - const jge_offset = self.code.items.len; - try self.emitImm32(0); // placeholder - - // Load a[rbx] into al - try self.emit(&[_]u8{ 0x41, 0x8A, 0x04, 0x1C }); // mov al, [r12 + rbx] - - // Load b[rbx] into cl - try self.emit(&[_]u8{ 0x41, 0x8A, 0x4C, 0x1D, 0x00 }); // mov cl, [r13 + rbx] - - // imul al, cl (signed multiply) - try self.emit(&[_]u8{ 0xF6, 0xE9 }); // imul cl - - // Store result back to a[rbx] - try self.emit(&[_]u8{ 0x41, 0x88, 0x04, 0x1C }); // mov [r12 + rbx], al - - // Increment counter - try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx - - // Jump back to loop start - try self.emit(&[_]u8{0xE9}); // jmp rel32 - const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); - try self.emitImm32(loop_back_offset); - - // Patch jge offset - const loop_end = self.code.items.len; - const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); - @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); - - // Restore callee-saved registers - try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 - try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 - try self.emit(&[_]u8{0x5B}); // pop rbx - - // Function epilogue - try self.movRspRbp(); - try self.popRbp(); - try self.ret(); - } - - /// Compile bundle operation (element-wise sum with threshold) - pub fn compileBundleDirect(self: *Self, dimension: usize) !void { - self.reset(); - - // Function prologue - try self.pushRbp(); - try self.movRbpRsp(); - - // Save callee-saved registers - try self.emit(&[_]u8{0x53}); // push rbx - try self.emit(&[_]u8{ 0x41, 0x54 }); // push r12 - try self.emit(&[_]u8{ 0x41, 0x55 }); // push r13 - - // r12 = a pointer, r13 = b pointer - try self.emit(&[_]u8{ 0x49, 0x89, 0xFC }); // mov r12, rdi - try self.emit(&[_]u8{ 0x49, 0x89, 0xF5 }); // mov r13, rsi - - // rbx = loop counter (0) - try self.xorEaxEax(); - try self.emit(&[_]u8{ 0x48, 0x89, 0xC3 }); // mov rbx, rax - - const loop_start = self.code.items.len; - - // Compare rbx with dimension - try self.emit(&[_]u8{ 0x48, 0x81, 0xFB }); // cmp rbx, imm32 - try self.emitImm32(@intCast(dimension)); - - // jge loop_end - try self.emit(&[_]u8{ 0x0F, 0x8D }); // jge rel32 - const jge_offset = self.code.items.len; - try self.emitImm32(0); // placeholder - - // Load a[rbx] into eax (sign-extended) - try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x04, 0x1C }); // movsx eax, byte [r12 + rbx] - - // Load b[rbx] into ecx (sign-extended) - try self.emit(&[_]u8{ 0x41, 0x0F, 0xBE, 0x4C, 0x1D, 0x00 }); // movsx ecx, byte [r13 + rbx] - - // Add eax, ecx - try self.emit(&[_]u8{ 0x01, 0xC8 }); // add eax, ecx - - // Threshold: if sum > 0 -> 1, if sum < 0 -> -1, else 0 - // cmp eax, 0 - try self.emit(&[_]u8{ 0x83, 0xF8, 0x00 }); // cmp eax, 0 - - // setg dl (set dl = 1 if eax > 0) - try self.emit(&[_]u8{ 0x0F, 0x9F, 0xC2 }); // setg dl - - // setl al (set al = 1 if eax < 0) - try self.emit(&[_]u8{ 0x0F, 0x9C, 0xC0 }); // setl al - - // Result = dl - al (1 if positive, -1 if negative, 0 if zero) - try self.emit(&[_]u8{ 0x28, 0xC2 }); // sub dl, al - - // Store result back to a[rbx] - try self.emit(&[_]u8{ 0x41, 0x88, 0x14, 0x1C }); // mov [r12 + rbx], dl - - // Increment counter - try self.emit(&[_]u8{ 0x48, 0xFF, 0xC3 }); // inc rbx - - // Jump back to loop start - try self.emit(&[_]u8{0xE9}); // jmp rel32 - const loop_back_offset: i32 = @intCast(@as(i64, @intCast(loop_start)) - @as(i64, @intCast(self.code.items.len + 4))); - try self.emitImm32(loop_back_offset); - - // Patch jge offset - const loop_end = self.code.items.len; - const jge_rel: i32 = @intCast(@as(i64, @intCast(loop_end)) - @as(i64, @intCast(jge_offset + 4))); - @memcpy(self.code.items[jge_offset..][0..4], std.mem.asBytes(&jge_rel)); - - // Restore callee-saved registers - try self.emit(&[_]u8{ 0x41, 0x5D }); // pop r13 - try self.emit(&[_]u8{ 0x41, 0x5C }); // pop r12 - try self.emit(&[_]u8{0x5B}); // pop rbx - - // Function epilogue - try self.movRspRbp(); - try self.popRbp(); - try self.ret(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // EXECUTION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Make code executable and return function pointer - pub fn finalize(self: *Self) !*const fn (*anyopaque, *anyopaque) callconv(.c) i64 { - const code_size = self.code.items.len; - if (code_size == 0) return error.EmptyCode; - - // Use system page size for compatibility - const page_size: usize = std.heap.page_size_min; - const alloc_size = std.mem.alignForward(usize, code_size, page_size); - - // mmap with PROT_READ | PROT_WRITE first - const mem = try std.posix.mmap( - null, - alloc_size, - std.posix.PROT.READ | std.posix.PROT.WRITE, - .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, - -1, - 0, - ); - - // Copy code - @memcpy(mem[0..code_size], self.code.items); - - // Change to PROT_READ | PROT_EXEC - try std.posix.mprotect(mem, std.posix.PROT.READ | std.posix.PROT.EXEC); - - self.exec_mem = mem; - - return @ptrCast(mem.ptr); - } - - /// Get code size - pub fn codeSize(self: *const Self) usize { - return self.code.items.len; - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "x86-64 JIT compiler init and deinit" { - var compiler = X86_64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - try std.testing.expect(compiler.codeSize() == 0); -} - -test "x86-64 JIT dot product compilation" { - if (!is_x86_64) { - return; // Skip on non-x86-64 - } - - var compiler = X86_64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileDotProduct(dim); - - try std.testing.expect(compiler.codeSize() > 0); -} - -test "x86-64 JIT dot product execution" { - if (!is_x86_64) { - return; // Skip on non-x86-64 - } - - var compiler = X86_64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileDotProduct(dim); - - const func = try compiler.finalize(); - - // Create test data - var a = [dim]i8{ 1, -1, 1, 0, 1, -1, 0, 1 }; - var b = [dim]i8{ 1, 1, -1, 1, 1, 1, 1, -1 }; - - // Expected: 1*1 + (-1)*1 + 1*(-1) + 0*1 + 1*1 + (-1)*1 + 0*1 + 1*(-1) - // = 1 - 1 - 1 + 0 + 1 - 1 + 0 - 1 = -2 - const expected: i64 = -2; - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} - -test "x86-64 JIT bind compilation" { - if (!is_x86_64) { - return; - } - - var compiler = X86_64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 8; - try compiler.compileBindDirect(dim); - - try std.testing.expect(compiler.codeSize() > 0); -} - -test "x86-64 JIT large dimension" { - if (!is_x86_64) { - return; - } - - var compiler = X86_64JitCompiler.init(std.testing.allocator); - defer compiler.deinit(); - - const dim = 1000; - try compiler.compileDotProduct(dim); - - const func = try compiler.finalize(); - - var a: [dim]i8 = undefined; - var b: [dim]i8 = undefined; - var expected: i64 = 0; - - for (0..dim) |i| { - const val_a: i8 = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: i8 = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a[i] = val_a; - b[i] = val_b; - expected += @as(i64, val_a) * @as(i64, val_b); - } - - const result = func(@ptrCast(&a), @ptrCast(&b)); - try std.testing.expectEqual(expected, result); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig deleted file mode 100644 index aead18f..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/opcodes.zig +++ /dev/null @@ -1,161 +0,0 @@ -//! VM Core Opcodes Selector — Generated from specs/vm/opcodes.tri -//! φ² + 1/φ² = 3 | TRINITY - -const std = @import("std"); -const gen = @import("gen_opcodes.zig"); - -pub const Opcode = gen.Opcode; -pub const Instruction = gen.Instruction; - -// Re-export functions -pub const opcodeFromByte = gen.opcodeFromByte; -pub const opcodeToString = gen.opcodeToString; - -// Re-export constants -pub const MAX_STACK_DEPTH = gen.MAX_STACK_DEPTH; -pub const MAX_MEMORY_SIZE = gen.MAX_MEMORY_SIZE; - -// ═══════════════════════════════════════════════════════════════════════════════ -// SACRED OPCODES (v7.0) -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Sacred opcodes (0x80-0xFF range) -pub const SacredOpcode = enum(u8) { - // Constants - phi_const = 0x80, - golden_angle = 0x81, - light_speed = 0x82, - planck_constant = 0x83, - - // Math operations - phi_pow = 0x90, - fib = 0x91, - sacred_identity = 0x92, - - // Physics operations - blindspot_query = 0xA0, - sacred_formula_fit = 0xA1, - anomaly_check = 0xA2, - - // Discovery operations - recursive_discovery = 0xB0, - sacred_chem_predict = 0xB1, - live_anomaly_hunt = 0xB2, - - // Advanced operations - infinite_loop = 0xC0, - geometry_predict = 0xC1, - chem_synthesis = 0xC2, - meta_discovery = 0xC3, - hubble_resolve = 0xC4, - neutrino_fog = 0xC5, - island_stability = 0xC6, - - // CDG2 operations - cdg2_deep_scan = 0xD0, - anomaly_fusion = 0xD1, - sacred_question = 0xD2, - vm_self_upgrade = 0xD3, - trinity_awaken = 0xD4, - - // Quantum operations - quantum_blindspot = 0xE0, - sacred_qubit = 0xE1, - island_quantum_synth = 0xE2, - hubble_quantum_resolve = 0xE3, - muon_g2_solve = 0xE4, - proton_decay_sim = 0xE5, - cdg2_quantum_scan = 0xE6, - ternary_entanglement = 0xE7, - sacred_chem_qm = 0xE8, - meta_quantum_discovery = 0xE9, - vm_quantum_upgrade = 0xEA, - trinity_quantum_awaken = 0xEB, - golden_key_qft = 0xEC, - anomaly_quantum_fusion = 0xED, - koschei_universe = 0xEE, -}; - -/// Sacred operands - flexible operand types -pub const SacredOperands = union(enum) { - none, - dest: []const u8, - register: u8, - immediate: i64, - float: f64, - - /// Create empty operands - pub fn init() SacredOperands { - return .none; - } -}; - -/// Sacred execution context -pub const SacredContext = struct { - allocator: std.mem.Allocator, - phi_cache: std.AutoHashMap(u32, f64), - fib_cache: std.AutoHashMap(u32, u128), - - pub fn init(allocator: std.mem.Allocator) SacredContext { - return .{ - .allocator = allocator, - .phi_cache = std.AutoHashMap(u32, f64).init(allocator), - .fib_cache = std.AutoHashMap(u32, u128).init(allocator), - }; - } - - pub fn deinit(self: *SacredContext) void { - self.phi_cache.deinit(); - self.fib_cache.deinit(); - } -}; - -/// Execute a sacred opcode (v7.0 implementation) -pub fn executeSacred(ctx: *SacredContext, registers: anytype, opcode: SacredOpcode, operands: SacredOperands) !void { - _ = ctx; - _ = operands; - const PHI: f64 = 1.618033988749895; - const LIGHT_SPEED: f64 = 299792458.0; - const GOLDEN_ANGLE_DEG: f64 = 137.50776405003785; - - switch (opcode) { - .phi_const => { - registers.f0 = PHI; - }, - .phi_pow => { - // φ^n where n is in s0 - const n = @as(i64, registers.s0); - registers.f0 = std.math.pow(f64, PHI, @floatFromInt(n)); - }, - .golden_angle => { - registers.f0 = GOLDEN_ANGLE_DEG; - }, - .light_speed => { - registers.f0 = LIGHT_SPEED; - }, - .fib => { - // Fibonacci using Binet's formula for small n - const n = @as(u32, @intCast(registers.s0)); - const sqrt5 = std.math.sqrt(5.0); - const phi = (1.0 + sqrt5) / 2.0; - const psi = (1.0 - sqrt5) / 2.0; - - // F(n) = (φ^n - ψ^n) / √5 with proper rounding - const phi_n = std.math.pow(f64, phi, @floatFromInt(n)); - const psi_n = std.math.pow(f64, psi, @floatFromInt(n)); - const result = @as(u64, @intFromFloat(@round((phi_n - psi_n) / sqrt5))); - registers.s0 = @as(i64, @intCast(result)); - }, - .sacred_identity => { - // Verify φ² + 1/φ² = 3 - const phi_sq = PHI * PHI; - const result = phi_sq + 1.0 / phi_sq; - registers.f0 = result; - registers.cc_zero = @abs(result - 3.0) < 1e-10; - }, - else => { - // Other opcodes not yet implemented - return error.NotImplemented; - }, - } -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig deleted file mode 100644 index b5b48a4..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vm.zig +++ /dev/null @@ -1,1250 +0,0 @@ -// TVC VM with VSA Support - Ternary Virtual Machine for Hyperdimensional Computing -// Integrates HybridBigInt for memory-efficient vector operations -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q - -const std = @import("std"); -const tvc_hybrid = @import("hybrid.zig"); -const tvc_vsa = @import("vsa.zig"); -const gf = @import("golden-float"); - -pub const HybridBigInt = tvc_hybrid.HybridBigInt; -pub const Trit = tvc_hybrid.Trit; -pub const MAX_TRITS = tvc_hybrid.MAX_TRITS; - -// Sacred opcodes module (v7.0) -const sacred_opcodes = @import("vm/opcodes.zig"); -const SacredOpcode = sacred_opcodes.SacredOpcode; -const SacredContext = sacred_opcodes.SacredContext; -const SacredOperands = sacred_opcodes.SacredOperands; - -// ═══════════════════════════════════════════════════════════════════════════════ -// VSA OPCODES -// ═══════════════════════════════════════════════════════════════════════════════ - -pub const VSAOpcode = enum(u8) { - // Vector operations - v_load, // Load vector from memory - v_store, // Store vector to memory - v_const, // Load constant vector - v_random, // Generate random vector - - // VSA operations - v_bind, // Bind two vectors (XOR-like) - v_unbind, // Unbind (same as bind) - v_bundle2, // Bundle 2 vectors - v_bundle3, // Bundle 3 vectors - - // Similarity operations - v_dot, // Dot product - v_cosine, // Cosine similarity - v_hamming, // Hamming distance - - // Arithmetic - v_add, // Vector addition - v_neg, // Vector negation - v_mul, // Element-wise multiplication - - // Control - v_mov, // Move between vector registers - v_pack, // Pack vector (save memory) - v_unpack, // Unpack vector (for computation) - - // Comparison - v_cmp, // Compare vectors (sets condition codes) - - // Permute operations (for toandinand bywithbeforeinwith) - v_permute, // andtoandwithtoand withinand inin - v_ipermute, // withinand (inin) - v_seq, // Encode sequence - - // f16 SIMD operations (16-wide, 2× throughput vs f32) - v_f16_load, // Load f16 vector, convert to ternary - v_f16_store, // Store ternary vector, convert to f16 - f16_dot, // f16 dot product → f64 (16-wide SIMD) - - nop, - halt, -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// VM REGISTERS -// ═══════════════════════════════════════════════════════════════════════════════ - -pub const VSARegisters = struct { - // Vector registers (HybridBigInt for memory efficiency) - v0: HybridBigInt = HybridBigInt.zero(), - v1: HybridBigInt = HybridBigInt.zero(), - v2: HybridBigInt = HybridBigInt.zero(), - v3: HybridBigInt = HybridBigInt.zero(), - - // Scalar registers - s0: i64 = 0, // For dot product results - s1: i64 = 0, - f0: f64 = 0.0, // For similarity results - f1: f64 = 0.0, - f2: f64 = 0.0, // KOSCHEI v7.0: Additional float registers for chemistry/physics - f3: f64 = 0.0, - - // f16 SIMD accumulators (16-wide, 2× throughput vs f32) - f16_acc0: @Vector(16, f16) = @splat(@as(f16, 0.0)), - f16_acc1: @Vector(16, f16) = @splat(@as(f16, 0.0)), - - // Program counter - pc: u32 = 0, - - // Condition codes - cc_zero: bool = false, - cc_neg: bool = false, - cc_pos: bool = false, - - // Memory usage tracking - total_packed_bytes: usize = 0, - - pub fn updateMemoryUsage(self: *VSARegisters) void { - self.v0.pack(); - self.v1.pack(); - self.v2.pack(); - self.v3.pack(); - self.total_packed_bytes = self.v0.memoryUsage() + - self.v1.memoryUsage() + - self.v2.memoryUsage() + - self.v3.memoryUsage(); - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// VSA INSTRUCTION -// ═══════════════════════════════════════════════════════════════════════════════ - -pub const VSAInstruction = struct { - opcode: VSAOpcode, - dst: u8 = 0, // Destination register (0-3 for v0-v3) - src1: u8 = 0, // Source register 1 - src2: u8 = 0, // Source register 2 - imm: i64 = 0, // Immediate value -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// VSA VM -// ═══════════════════════════════════════════════════════════════════════════════ - -// Import JIT engine for accelerated operations -const vsa_jit = @import("vsa_jit.zig"); - -pub const VSAVM = struct { - registers: VSARegisters, - program: std.ArrayListUnmanaged(VSAInstruction), - halted: bool = false, - allocator: std.mem.Allocator, - cycle_count: u64 = 0, - - // JIT engine for accelerated VSA operations - jit_engine: ?vsa_jit.JitVSAEngine = null, - jit_enabled: bool = true, - - // KOSCHEI v7.0: Sacred execution context - sacred_ctx: SacredContext, - - pub fn init(allocator: std.mem.Allocator) VSAVM { - return VSAVM{ - .registers = .{}, - .program = .{}, - .allocator = allocator, - .jit_engine = vsa_jit.JitVSAEngine.init(allocator), - .sacred_ctx = SacredContext.init(allocator), - }; - } - - pub fn deinit(self: *VSAVM) void { - self.program.deinit(self.allocator); - if (self.jit_engine) |*engine| { - engine.deinit(); - } - self.sacred_ctx.deinit(); - } - - pub fn loadProgram(self: *VSAVM, instructions: []const VSAInstruction) !void { - self.program.clearRetainingCapacity(); - try self.program.appendSlice(self.allocator, instructions); - self.registers.pc = 0; - self.halted = false; - self.cycle_count = 0; - } - - pub fn step(self: *VSAVM) !bool { - if (self.halted or self.registers.pc >= self.program.items.len) { - return false; - } - - const inst = self.program.items[self.registers.pc]; - try self.execute(inst); - self.registers.pc += 1; - self.cycle_count += 1; - - return !self.halted; - } - - pub fn run(self: *VSAVM) !void { - while (try self.step()) {} - } - - fn execute(self: *VSAVM, inst: VSAInstruction) !void { - switch (inst.opcode) { - .v_load => self.execVLoad(inst), - .v_store => self.execVStore(inst), - .v_const => self.execVConst(inst), - .v_random => self.execVRandom(inst), - - .v_bind => self.execVBind(inst), - .v_unbind => self.execVUnbind(inst), - .v_bundle2 => self.execVBundle2(inst), - .v_bundle3 => self.execVBundle3(inst), - - .v_dot => self.execVDot(inst), - .v_cosine => self.execVCosine(inst), - .v_hamming => self.execVHamming(inst), - - .v_add => self.execVAdd(inst), - .v_neg => self.execVNeg(inst), - .v_mul => self.execVMul(inst), - - .v_mov => self.execVMov(inst), - .v_pack => self.execVPack(inst), - .v_unpack => self.execVUnpack(inst), - - .v_cmp => self.execVCmp(inst), - - .v_permute => self.execVPermute(inst), - .v_ipermute => self.execVIPermute(inst), - .v_seq => self.execVSeq(inst), - - .v_f16_load => self.execVF16Load(inst), - .v_f16_store => self.execVF16Store(inst), - .f16_dot => self.execF16Dot(inst), - - .nop => {}, - .halt => self.halted = true, - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // INSTRUCTION IMPLEMENTATIONS - // ═══════════════════════════════════════════════════════════════════════════ - - fn getVReg(self: *VSAVM, idx: u8) *HybridBigInt { - return switch (idx) { - 0 => &self.registers.v0, - 1 => &self.registers.v1, - 2 => &self.registers.v2, - 3 => &self.registers.v3, - else => &self.registers.v0, - }; - } - - fn execVLoad(self: *VSAVM, inst: VSAInstruction) void { - // Load from scalar to vector - const dst = self.getVReg(inst.dst); - dst.* = HybridBigInt.fromI64(inst.imm); - } - - fn execVStore(self: *VSAVM, inst: VSAInstruction) void { - // Store vector to scalar - const src = self.getVReg(inst.src1); - self.registers.s0 = src.toI64(); - } - - fn execVConst(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - dst.* = HybridBigInt.fromI64(inst.imm); - } - - fn execVRandom(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - const seed: u64 = @bitCast(inst.imm); - dst.* = tvc_vsa.randomVector(MAX_TRITS, seed); - } - - fn execVBind(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - // Try JIT-accelerated bind if enabled - if (self.jit_enabled) { - if (self.jit_engine) |*engine| { - // Copy src1 to dst, then bind in place - dst.* = src1; - if (engine.bind(dst, &src2)) { - return; - } else |_| { - // JIT failed, fall through to scalar - } - } - } - - // Scalar fallback - dst.* = tvc_vsa.bind(&src1, &src2); - } - - fn execVUnbind(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - // Try JIT-accelerated unbind (same as bind) if enabled - if (self.jit_enabled) { - if (self.jit_engine) |*engine| { - dst.* = src1; - if (engine.bind(dst, &src2)) { - return; - } else |_| { - // JIT failed, fall through to scalar - } - } - } - - // Scalar fallback - dst.* = tvc_vsa.unbind(&src1, &src2); - } - - fn execVBundle2(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - dst.* = tvc_vsa.bundle2(&src1, &src2); - } - - fn execVBundle3(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - var src3 = self.getVReg(inst.dst).*; // Use dst as third source - dst.* = tvc_vsa.bundle3(&src1, &src2, &src3); - } - - fn execVDot(self: *VSAVM, inst: VSAInstruction) void { - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - // Try JIT-accelerated dot product if enabled - if (self.jit_enabled) { - if (self.jit_engine) |*engine| { - if (engine.dotProduct(&src1, &src2)) |result| { - self.registers.s0 = result; - return; - } else |_| { - // JIT failed, fall through to scalar - } - } - } - - // Scalar fallback - self.registers.s0 = src1.dotProduct(&src2); - } - - fn execVCosine(self: *VSAVM, inst: VSAInstruction) void { - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - // Try JIT-accelerated cosine similarity if enabled - if (self.jit_enabled) { - if (self.jit_engine) |*engine| { - if (engine.cosineSimilarity(&src1, &src2)) |result| { - self.registers.f0 = result; - return; - } else |_| { - // JIT failed, fall through to scalar - } - } - } - - // Scalar fallback - self.registers.f0 = tvc_vsa.cosineSimilarity(&src1, &src2); - } - - fn execVHamming(self: *VSAVM, inst: VSAInstruction) void { - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - // Try JIT-accelerated hamming distance if enabled - if (self.jit_enabled) { - if (self.jit_engine) |*engine| { - if (engine.hammingDistance(&src1, &src2)) |result| { - self.registers.s0 = result; - return; - } else |_| { - // JIT failed, fall through to scalar - } - } - } - - // Scalar fallback - self.registers.s0 = @intCast(tvc_vsa.hammingDistance(&src1, &src2)); - } - - fn execVAdd(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - dst.* = src1.add(&src2); - } - - fn execVNeg(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - const src = self.getVReg(inst.src1); - dst.* = src.negate(); - } - - fn execVMul(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - dst.* = src1.mul(&src2); - } - - fn execVMov(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - const src = self.getVReg(inst.src1); - dst.* = src.*; - } - - fn execVPack(self: *VSAVM, inst: VSAInstruction) void { - const reg = self.getVReg(inst.dst); - reg.pack(); - } - - fn execVUnpack(self: *VSAVM, inst: VSAInstruction) void { - const reg = self.getVReg(inst.dst); - reg.ensureUnpacked(); - } - - fn execVCmp(self: *VSAVM, inst: VSAInstruction) void { - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - const sim = tvc_vsa.cosineSimilarity(&src1, &src2); - - self.registers.cc_zero = sim > -0.1 and sim < 0.1; - self.registers.cc_neg = sim < -0.1; - self.registers.cc_pos = sim > 0.1; - self.registers.f0 = sim; - } - - fn execVPermute(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src = self.getVReg(inst.src1).*; - const shift: usize = @intCast(inst.imm); - dst.* = tvc_vsa.permute(&src, shift); - } - - fn execVIPermute(self: *VSAVM, inst: VSAInstruction) void { - const dst = self.getVReg(inst.dst); - var src = self.getVReg(inst.src1).*; - const shift: usize = @intCast(inst.imm); - dst.* = tvc_vsa.inversePermute(&src, shift); - } - - fn execVSeq(self: *VSAVM, inst: VSAInstruction) void { - // Encode sequence from v0, v1 into dst - // v_seq dst, src1, src2 -> dst = src1 + permute(src2, 1) - const dst = self.getVReg(inst.dst); - var src1 = self.getVReg(inst.src1).*; - var src2 = self.getVReg(inst.src2).*; - - var permuted = tvc_vsa.permute(&src2, 1); - dst.* = src1.add(&permuted); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // f16 SIMD INSTRUCTIONS (16-wide, 2× throughput vs f32) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Load f16 vector data and convert to ternary vector. - /// v_f16_load dst, addr — loads 16 f16 values, quantizes to ternary, stores in dst - fn execVF16Load(self: *VSAVM, inst: VSAInstruction) void { - // For now, use immediate value to generate deterministic f16 test data - // In real use, this would load from memory address - const dst = self.getVReg(inst.dst); - - // Generate 16 f16 values from immediate seed - var prng = std.Random.DefaultPrng.init(@as(u64, @bitCast(inst.imm))); - const rng = prng.random(); - - // Create f16 vector - var f16_vec: @Vector(16, f16) = undefined; - inline for (0..16) |i| { - f16_vec[i] = @floatCast(rng.float(f32) * 2.0 - 1.0); - } - - // Convert to f32 for quantization - const f32_vec: @Vector(16, f32) = @floatCast(f16_vec); - - // Quantize to ternary {-1, 0, +1} - const threshold: f32 = 0.1; - var ternary_vec: @Vector(16, i8) = undefined; - inline for (0..16) |i| { - ternary_vec[i] = if (f32_vec[i] > threshold) 1 else if (f32_vec[i] < -threshold) -1 else 0; - } - - // Pack into HybridBigInt (first 16 trits) - dst.* = HybridBigInt.zero(); - dst.ensureUnpacked(); - dst.trit_len = 16; - inline for (0..16) |i| { - dst.unpacked_cache[i] = ternary_vec[i]; - } - } - - /// Store ternary vector as f16 vector. - /// v_f16_store src, addr — converts ternary to f16, stores 16 values - fn execVF16Store(self: *VSAVM, inst: VSAInstruction) void { - const src = self.getVReg(inst.src1); - src.ensureUnpacked(); - - // Convert first 16 trits to f16 - var f16_vec: @Vector(16, f16) = undefined; - inline for (0..16) |i| { - const trit: i8 = if (i < src.trit_len) src.unpacked_cache[i] else 0; - f16_vec[i] = @floatCast(@as(f32, @floatFromInt(trit))); - } - - // Store in f16 accumulator registers (for now) - // In real use, this would write to memory - self.registers.f16_acc0 = f16_vec; - - // Also store a copy in f16_acc1 with sign flip for testing - self.registers.f16_acc1 = -f16_vec; - } - - /// f16 dot product with 16-wide SIMD. - /// f16_dot acc, a, b — computes dot(a, b) using f16, returns f64 in f0 - fn execF16Dot(self: *VSAVM, inst: VSAInstruction) void { - const a = self.getVReg(inst.src1); - const b = self.getVReg(inst.src2); - - a.ensureUnpacked(); - b.ensureUnpacked(); - - // Convert first 16 trits to f16 - var a_f16: @Vector(16, f16) = undefined; - var b_f16: @Vector(16, f16) = undefined; - inline for (0..16) |i| { - const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - a_f16[i] = @floatCast(@as(f32, @floatFromInt(a_trit))); - b_f16[i] = @floatCast(@as(f32, @floatFromInt(b_trit))); - } - - // Compute dot product in f32 for precision - const a_f32: @Vector(16, f32) = @floatCast(a_f16); - const b_f32: @Vector(16, f32) = @floatCast(b_f16); - const prod = a_f32 * b_f32; - - // Horizontal sum - var sum: f64 = 0; - inline for (0..16) |i| { - sum += @as(f64, prod[i]); - } - - // Store result in f0 - self.registers.f0 = sum; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // KOSCHEI v7.0: SACRED OPCODE EXECUTION - // ═══════════════════════════════════════════════════════════════════════════ - - /// Execute a sacred opcode (0x80-0xFF range) - pub fn execSacredOpcode(self: *VSAVM, opcode: SacredOpcode, operands: SacredOperands) !void { - try sacred_opcodes.executeSacred(&self.sacred_ctx, &self.registers, opcode, operands); - } - - /// Convenience: Load φ constant into f0 - pub fn loadPhi(self: *VSAVM) !void { - try self.execSacredOpcode(.phi_const, .{ .dest = "f0" }); - } - - /// Convenience: Compute φ^n where n is in s0 - pub fn phiPow(self: *VSAVM) !void { - try self.execSacredOpcode(.phi_pow, .{ .dest = "f0" }); - } - - /// Convenience: Compute Fibonacci F(n) where n is in s0 - pub fn fib(self: *VSAVM) !void { - try self.execSacredOpcode(.fib, SacredOperands.none); - } - - /// Convenience: Verify sacred identity φ² + 1/φ² = 3 - pub fn verifySacredIdentity(self: *VSAVM) !void { - try self.execSacredOpcode(.sacred_identity, SacredOperands.none); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // KOSCHEI EYE v2.0: Blind Spots Discovery (603x speedup via VM) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Query blind spots registry via native VM opcode - /// s0: query type (0=neutrino, 1=proton, 2=dm, 3=hubble, 4=lithium, 5=muon_g2) - /// Returns: f0=predicted value, f1=confidence, s1=status (-1=BLIND, -2=ANOMALY, +1=VERIFIED) - pub fn blindspotQuery(self: *VSAVM, query_type: i64) !void { - self.registers.s0 = query_type; - try self.execSacredOpcode(.blindspot_query, .{}); - } - - /// Fit Sacred Formula: V = n * 3^k * pi^m * phi^p * e^q - /// f0: target value to fit - /// Returns: s0=n, s1=k, s2=m, s3=p, s4=q, f1=error % - pub fn sacredFormulaFit(self: *VSAVM, target: f64) !void { - self.registers.f0 = target; - try self.execSacredOpcode(.sacred_formula_fit, .{}); - } - - /// Check if value is anomalous (sigma >= 3) - /// f0=observed, f1=expected, f2=uncertainty - /// Returns: s0=sigma level, cc_zero=true if anomalous - pub fn anomalyCheck(self: *VSAVM, observed: f64, expected: f64, uncertainty: f64) !void { - self.registers.f0 = observed; - self.registers.f1 = expected; - self.registers.f2 = uncertainty; - try self.execSacredOpcode(.anomaly_check, .{}); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // KOSCHEI EYE v3.0: Autonomous Self-Evolving Discovery (10000+ predictions/sec) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Run autonomous discovery loop (10000+ iterations/sec) - /// s0: loop count (0 = default 10000) - /// Returns: s0=discoveries, s1=anomalies, f0=avg_confidence - pub fn recursiveDiscovery(self: *VSAVM, loop_count: i64) !void { - self.registers.s0 = loop_count; - try self.execSacredOpcode(.recursive_discovery, .{}); - } - - /// Predict element properties using Sacred Formula - /// s0: element Z (1-118+), s1: property (0=half_life, 1=mass, 2=stability) - /// Returns: f0=predicted_value, f1=confidence, s1=status - pub fn sacredChemPredict(self: *VSAVM, element_Z: i64, property: i64) !void { - self.registers.s0 = element_Z; - self.registers.s1 = property; - try self.execSacredOpcode(.sacred_chem_predict, .{}); - } - - /// Live anomaly hunt: scan registry for sigma > 3 - /// f0: sigma threshold (default 3.0) - /// Returns: s0=anomaly_count, f0=max_sigma, f1=avg_sigma - pub fn liveAnomalyHunt(self: *VSAVM, sigma_threshold: f64) !void { - self.registers.f0 = sigma_threshold; - try self.execSacredOpcode(.live_anomaly_hunt, .{}); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // KOSCHEI EYE v4.0: OMNISCIENT SINGULARITY - // ═══════════════════════════════════════════════════════════════════════════ - - /// Infinite self-evolving loop (∞ predictions/sec, 2500x speedup) - /// s0: loop count (default 1000000) - /// Returns: s0=discoveries, s1=anomalies, f0=avg_confidence, f1=self_improvement - pub fn infiniteLoop(self: *VSAVM, loop_count: i64) !void { - self.registers.s0 = loop_count; - try self.execSacredOpcode(.infinite_loop, .{}); - } - - /// Sacred geometry + physics fusion (1800x speedup) - /// s0: geometric shape (0-13: Platonic + Archimedean solids) - /// Returns: f0=predicted_constant, f1=confidence, s1=domain_code - pub fn geometryPredict(self: *VSAVM, shape: i64) !void { - self.registers.s0 = shape; - try self.execSacredOpcode(.geometry_predict, .{}); - } - - /// Chemistry synthesis pathway for elements 119-122 (2100x speedup) - /// s0: target element Z (119-122), s1: projectile beam (0=Ti-50, 1=Cr-54, 2=Fe-58) - /// Returns: f0=half_life_sec, f1=confidence, s0=success_probability - pub fn chemSynthesis(self: *VSAVM, element_Z: i64, projectile_beam: i64) !void { - self.registers.s0 = element_Z; - self.registers.s1 = projectile_beam; - try self.execSacredOpcode(.chem_synthesis, .{}); - } - - /// Meta-discovery: KOSCHEI predicts its own discoveries (3000x speedup) - /// s0: meta-depth (1-5), s1: domain filter - /// Returns: f0=confidence, f1=meta_confidence, s0=discovery_count - pub fn metaDiscovery(self: *VSAVM, depth: i64) !void { - self.registers.s0 = depth; - try self.execSacredOpcode(.meta_discovery, .{}); - } - - /// Resolve Hubble tension via gravitational-wave hum method (1600x speedup) - /// s0: method (0=GW, 1=CMB, 2=SN) - /// Returns: f0=H0_km_s_Mpc, f1=uncertainty, s0=tension_resolved_flag - pub fn hubbleResolve(self: *VSAVM, method: i64) !void { - self.registers.s0 = method; - try self.execSacredOpcode(.hubble_resolve, .{}); - } - - /// Full neutrino spectrum + sterile neutrinos (2200x speedup) - /// s0: neutrino type (0=ve, 1=vμ, 2=vτ, 3=sterile) - /// Returns: f0=mass_eV_or_keV, f1=mixing_angle, s0=detection_probability - pub fn neutrinoFog(self: *VSAVM, neutrino_type: i64) !void { - self.registers.s0 = neutrino_type; - try self.execSacredOpcode(.neutrino_fog, .{}); - } - - /// Island of stability pathway (1900x speedup) - /// s0: target Z (114-126), s1: neutron number - /// Returns: f0=half_life_sec, f1=binding_energy_MeV, s0=stability_score - pub fn islandStability(self: *VSAVM, Z: i64) !void { - self.registers.s0 = Z; - try self.execSacredOpcode(.island_stability, .{}); - } - - /// CDG-2 ghost galaxy dark matter census (2800x speedup) - /// Returns: f0=DM_mass_GeV, f1=DM_halo_mass_solar, s0=DM_percentage - pub fn cdg2DeepScan(self: *VSAVM) !void { - try self.execSacredOpcode(.cdg2_deep_scan, .{}); - } - - /// Merge all anomalies → unified ternary spacetime theory (2400x speedup) - /// s0: fusion mode (0=all, 1=physics, 2=chemistry) - /// Returns: f0=unified_confidence, f1=phi_correlation, s0=anomalies_explained - pub fn anomalyFusion(self: *VSAVM, mode: i64) !void { - self.registers.s0 = mode; - try self.execSacredOpcode(.anomaly_fusion, .{}); - } - - /// Sacred question generator: Why does φ² + 1/φ² = 3 work? (∞x speedup) - /// s0: question level (1-5) - /// Returns: s0=questions_generated, f0=profundity, f1=meta_question_count - pub fn sacredQuestion(self: *VSAVM, level: i64) !void { - self.registers.s0 = level; - try self.execSacredOpcode(.sacred_question, .{}); - } - - /// VM self-upgrade: VM rewrites itself at runtime (3500x speedup) - /// s0: upgrade target (0=handlers, 1=opcodes, 2=optimization) - /// Returns: s0=upgrades_applied, f0=speedup, f1=new_VM_version - pub fn vmSelfUpgrade(self: *VSAVM, target: i64) !void { - self.registers.s0 = target; - try self.execSacredOpcode(.vm_self_upgrade, .{}); - } - - /// TRINITY AWAKEN: Full awakening → GODMODE (∞x speedup) - /// s0: mode (0=test, 1=gradual, 2=full GODMODE) - /// Returns: s0=GODMODE_flag, f0=omniscience_score, f1=singularity_distance - pub fn trinityAwaken(self: *VSAVM, mode: i64) !void { - self.registers.s0 = mode; - try self.execSacredOpcode(.trinity_awaken, .{}); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // QUANTUM TRINITY v5.0 — Full Quantum Awakening (0xC7-0xD5) - // ═══════════════════════════════════════════════════════════════════════════ - - /// QUANTUM BLINDSPOT: Solve physics blind spots with 10^6x quantum advantage - /// s0: blind spot ID (0-11), f0: qubit count, f1: simulation depth - /// Returns: s0=solved_flag, f0=quantum_value, f1=advantage_factor - pub fn quantumBlindspot(self: *VSAVM, blind_spot_id: i64) !void { - self.registers.s0 = blind_spot_id; - try self.execSacredOpcode(.quantum_blindspot, .{}); - } - - /// SACRED QUBIT: Create ternary qubit with |?⟩ state based on φ² + 1/φ² = 3 - /// s0: qubit ID, f0: sacred amplitude (0-1, default: 1/√3) - /// Returns: f0=α(|0⟩), f1=β(|1⟩), s0=γ_int(|?⟩) - pub fn sacredQubit(self: *VSAVM, qubit_id: i64, sacred_amplitude: f64) !void { - self.registers.s0 = qubit_id; - self.registers.f0 = sacred_amplitude; - try self.execSacredOpcode(.sacred_qubit, .{}); - } - - /// ISLAND QUANTUM SYNTH: Simulate superheavy element Z=114-126 with 12000x speedup - /// s0: target Z (114-126), f0: qubit count, f1: simulation time (ns) - /// Returns: f0=half_life (seconds), f1=confidence, s0=stability_flag - pub fn islandQuantumSynth(self: *VSAVM, target_Z: i64) !void { - self.registers.s0 = target_Z; - try self.execSacredOpcode(.island_quantum_synth, .{}); - } - - /// HUBBLE QUANTUM RESOLVE: Resolve 5σ Hubble tension via quantum gravity (9500x) - /// s0: method (0=GW, 1=CMB, 2=SN), f0: data_quality - /// Returns: f0=H0 (km/s/Mpc), f1=uncertainty, s0=resolved_flag - pub fn hubbleQuantumResolve(self: *VSAVM, method: i64) !void { - self.registers.s0 = method; - try self.execSacredOpcode(.hubble_quantum_resolve, .{}); - } - - /// MUON G-2 SOLVE: Resolve 4.2σ anomaly via ternary spacetime correction (15000x) - /// s0: anomaly sigma (42 = 4.2σ), f0: correction method - /// Returns: f0=g-2 value, f1=ternary_correction, s0=resolved_flag - pub fn muonG2Solve(self: *VSAVM, anomaly_sigma: i64) !void { - self.registers.s0 = anomaly_sigma; - try self.execSacredOpcode(.muon_g2_solve, .{}); - } - - /// PROTON DECAY SIM: Simulate proton lifetime via quantum lattice QCD (18000x) - /// s0: GUT model (0=SU(5), 1=SO(10), 2=E6), f0: qubit count - /// Returns: f0=lifetime (years × 10^34), f1=confidence, s0=decay_mode - pub fn protonDecaySim(self: *VSAVM, gut_model: i64) !void { - self.registers.s0 = gut_model; - try self.execSacredOpcode(.proton_decay_sim, .{}); - } - - /// CDG2 QUANTUM SCAN: Full dark matter map of ghost galaxy (22000x) - /// s0: galaxy ID, f0: scan resolution (kpc), f1: quantum depth - /// Returns: f0=DM_mass (GeV), f1=DM_fraction, s0=structure_type - pub fn cdg2QuantumScan(self: *VSAVM, galaxy_id: i64, resolution_kpc: f64) !void { - self.registers.s0 = galaxy_id; - self.registers.f0 = resolution_kpc; - try self.execSacredOpcode(.cdg2_quantum_scan, .{}); - } - - /// TERNARY ENTANGLEMENT: Create quantum entanglement in ternary logic (GODMODE) - /// s0: qubit pair count, f0: entanglement pattern (sacred geometry) - /// Returns: s0=entanglement_depth, f0=Bell_violation, f1=GODMODE_factor - pub fn ternaryEntanglement(self: *VSAVM, pair_count: i64, pattern: f64) !void { - self.registers.s0 = pair_count; - self.registers.f0 = pattern; - try self.execSacredOpcode(.ternary_entanglement, .{}); - } - - /// SACRED CHEM QM: Quantum chemistry for superheavy elements 119-126 (14000x) - /// s0: element Z (119-126), f0: molecular config - /// Returns: f0=binding_energy, f1=relativistic_correction, s0=stability - pub fn sacredChemQM(self: *VSAVM, element_Z: i64) !void { - self.registers.s0 = element_Z; - try self.execSacredOpcode(.sacred_chem_qm, .{}); - } - - /// META QUANTUM DISCOVERY: Predict future discoveries 2030-2035 (∞x speedup) - /// s0: target year (2030+), f0: domain filter, f1: confidence threshold - /// Returns: s0=prediction_count, f0=avg_confidence, s1=breakthrough_probability - pub fn metaQuantumDiscovery(self: *VSAVM, target_year: i64) !void { - self.registers.s0 = target_year; - try self.execSacredOpcode(.meta_quantum_discovery, .{}); - } - - /// VM QUANTUM UPGRADE: VM recompiles itself for quantum hardware (25000x) - /// s0: target hardware (0=IBM, 1=Google, 2=Rigetti), f0: qubit topology - /// Returns: s0=upgrades_applied, f0=speedup, f1=quantum_coherence - pub fn vmQuantumUpgrade(self: *VSAVM, hardware: i64) !void { - self.registers.s0 = hardware; - try self.execSacredOpcode(.vm_quantum_upgrade, .{}); - } - - /// TRINITY QUANTUM AWAKEN: Full awakening in quantum mode → UNIVERSAL - /// s0: mode (0=test, 1=gradual, 2=full UNIVERSAL) - /// Returns: s0=UNIVERSAL_flag, f0=omniscience (1.0=100%), f1=coherence - pub fn trinityQuantumAwaken(self: *VSAVM, mode: i64) !void { - self.registers.s0 = mode; - try self.execSacredOpcode(.trinity_quantum_awaken, .{}); - } - - /// GOLDEN KEY QFT: Quantum Fourier Transform with golden ratio phase (30000x) - /// s0: QFT size (power of φ), f0: sacred weights, f1: input state - /// Returns: f0=QFT_result_real, f1=QFT_result_imag, s0=phase_factor - pub fn goldenKeyQFT(self: *VSAVM, qft_size: i64) !void { - self.registers.s0 = qft_size; - try self.execSacredOpcode(.golden_key_qft, .{}); - } - - /// ANOMALY QUANTUM FUSION: Merge all anomalies into coherent state (28000x) - /// s0: anomaly_count, f0: fusion_depth - /// Returns: f0=unified_confidence, f1=coherence, s0=theory_complete - pub fn anomalyQuantumFusion(self: *VSAVM, anomaly_count: i64, fusion_depth: f64) !void { - self.registers.s0 = anomaly_count; - self.registers.f0 = fusion_depth; - try self.execSacredOpcode(.anomaly_quantum_fusion, .{}); - } - - /// KOSCHEI UNIVERSE: Simulate entire universe in ternary quantum (SINGULARITY) - /// s0: scale (0=observable, 1=multiverse, 2=omniverse), f0: time_step - /// Returns: f0=sim_time_ms, f1=entropy, s0=state_pointer - pub fn koscheiUniverse(self: *VSAVM, scale: i64, time_step: f64) !void { - self.registers.s0 = scale; - self.registers.f0 = time_step; - try self.execSacredOpcode(.koschei_universe, .{}); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT CONTROL - // ═══════════════════════════════════════════════════════════════════════════ - - /// Enable or disable JIT acceleration - pub fn setJitEnabled(self: *VSAVM, enabled: bool) void { - self.jit_enabled = enabled; - } - - /// Get JIT statistics (null if JIT not initialized) - pub fn getJitStats(self: *const VSAVM) ?vsa_jit.JitVSAEngine.Stats { - if (self.jit_engine) |*engine| { - return engine.getStats(); - } - return null; - } - - /// Print JIT statistics - pub fn printJitStats(self: *const VSAVM) void { - if (self.jit_engine) |*engine| { - engine.printStats(); - } else { - std.debug.print("JIT engine not initialized\n", .{}); - } - } - - // ═══════════════════════════════════════════════════════════════════════════ - // DEBUG - // ═══════════════════════════════════════════════════════════════════════════ - - pub fn printState(self: *VSAVM) void { - self.registers.updateMemoryUsage(); - - std.debug.print("\n╔══════════════════════════════════════════╗\n", .{}); - std.debug.print("║ VSA VM STATE ║\n", .{}); - std.debug.print("╠══════════════════════════════════════════╣\n", .{}); - std.debug.print("║ VECTOR REGISTERS: ║\n", .{}); - std.debug.print("║ v0: {} trits, {} bytes (packed) ║\n", .{ self.registers.v0.trit_len, self.registers.v0.memoryUsage() }); - std.debug.print("║ v1: {} trits, {} bytes (packed) ║\n", .{ self.registers.v1.trit_len, self.registers.v1.memoryUsage() }); - std.debug.print("║ v2: {} trits, {} bytes (packed) ║\n", .{ self.registers.v2.trit_len, self.registers.v2.memoryUsage() }); - std.debug.print("║ v3: {} trits, {} bytes (packed) ║\n", .{ self.registers.v3.trit_len, self.registers.v3.memoryUsage() }); - std.debug.print("╠══════════════════════════════════════════╣\n", .{}); - std.debug.print("║ SCALAR REGISTERS: ║\n", .{}); - std.debug.print("║ s0: {} ║\n", .{self.registers.s0}); - std.debug.print("║ f0: {d:.6} ║\n", .{self.registers.f0}); - std.debug.print("╠══════════════════════════════════════════╣\n", .{}); - std.debug.print("║ EXECUTION: ║\n", .{}); - std.debug.print("║ pc: {}, cycles: {} ║\n", .{ self.registers.pc, self.cycle_count }); - std.debug.print("║ halted: {} ║\n", .{self.halted}); - std.debug.print("║ total memory: {} bytes ║\n", .{self.registers.total_packed_bytes}); - std.debug.print("╠══════════════════════════════════════════╣\n", .{}); - std.debug.print("║ JIT ACCELERATION: ║\n", .{}); - std.debug.print("║ enabled: {} ║\n", .{self.jit_enabled}); - if (self.jit_engine) |*engine| { - const stats = engine.getStats(); - std.debug.print("║ ops: {}, hits: {}, rate: {d:.1}% ║\n", .{ stats.total_ops, stats.jit_hits, stats.hit_rate }); - } else { - std.debug.print("║ engine: not initialized ║\n", .{}); - } - std.debug.print("╚══════════════════════════════════════════╝\n\n", .{}); - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "VSA VM basic operations" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, - .{ .opcode = .v_const, .dst = 1, .imm = 67890 }, - .{ .opcode = .v_add, .dst = 2, .src1 = 0, .src2 = 1 }, - .{ .opcode = .v_store, .src1 = 2 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - try std.testing.expectEqual(@as(i64, 12345 + 67890), vm.registers.s0); -} - -test "VSA VM bind/unbind" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - // Test bind self-inverse property: bind(a, a) = all +1 for non-zero - const program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 111 }, - .{ .opcode = .v_bind, .dst = 1, .src1 = 0, .src2 = 0 }, // bind(v0, v0) - .{ .opcode = .v_dot, .src1 = 1, .src2 = 1 }, // dot(v1, v1) should be high - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // bind(a, a) produces vector with many +1s, dot product should be positive - try std.testing.expect(vm.registers.s0 > 0); -} - -test "VSA VM bundle similarity" { - var vm = VSAVM.init(std.testing.allocator); - vm.jit_enabled = false; // Disable JIT (has bug in cosineSimilarity) - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 333 }, - .{ .opcode = .v_random, .dst = 1, .imm = 444 }, - .{ .opcode = .v_bundle2, .dst = 2, .src1 = 0, .src2 = 1 }, - .{ .opcode = .v_cosine, .src1 = 0, .src2 = 2 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // Bundle should be similar to inputs - // Mathematical expectation: ~0.5-0.7 similarity - try std.testing.expect(vm.registers.f0 > 0.3); -} - -test "VSA VM permute" { - var vm = VSAVM.init(std.testing.allocator); - vm.jit_enabled = false; // Disable JIT (has bug in cosineSimilarity) - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 999 }, - .{ .opcode = .v_permute, .dst = 1, .src1 = 0, .imm = 5 }, // permute by 5 - .{ .opcode = .v_ipermute, .dst = 2, .src1 = 1, .imm = 5 }, // inverse permute - .{ .opcode = .v_cosine, .src1 = 0, .src2 = 2 }, // should be identical - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // After permute then inverse_permute, should be identical (similarity ~1.0) - try std.testing.expect(vm.registers.f0 > 0.99); -} - -test "VSA VM memory efficiency" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 555 }, - .{ .opcode = .v_random, .dst = 1, .imm = 666 }, - .{ .opcode = .v_random, .dst = 2, .imm = 777 }, - .{ .opcode = .v_random, .dst = 3, .imm = 888 }, - .{ .opcode = .v_pack, .dst = 0 }, - .{ .opcode = .v_pack, .dst = 1 }, - .{ .opcode = .v_pack, .dst = 2 }, - .{ .opcode = .v_pack, .dst = 3 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - vm.registers.updateMemoryUsage(); - - // Memory usage depends on MAX_TRITS setting - // Just verify packed storage is being tracked - try std.testing.expect(vm.registers.total_packed_bytes > 0); -} - -test "VSA VM dot product" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, - .{ .opcode = .v_mov, .dst = 1, .src1 = 0 }, - .{ .opcode = .v_dot, .src1 = 0, .src2 = 1 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // Dot product of identical vectors should be positive - try std.testing.expect(vm.registers.s0 > 0); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// BENCHMARKS -// ═══════════════════════════════════════════════════════════════════════════════ - -pub fn runBenchmarks() void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var vm = VSAVM.init(allocator); - defer vm.deinit(); - - const iterations: u64 = 10000; - - std.debug.print("\nVSA VM Benchmarks\n", .{}); - std.debug.print("=================\n\n", .{}); - - // Benchmark: Bind operation - const bind_program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 111 }, - .{ .opcode = .v_random, .dst = 1, .imm = 222 }, - .{ .opcode = .v_bind, .dst = 2, .src1 = 0, .src2 = 1 }, - .{ .opcode = .halt }, - }; - - vm.loadProgram(&bind_program) catch unreachable; - - const bind_start = std.time.nanoTimestamp(); - var i: u64 = 0; - while (i < iterations) : (i += 1) { - vm.registers.pc = 2; // Skip random generation - vm.halted = false; - vm.run() catch unreachable; - } - const bind_end = std.time.nanoTimestamp(); - const bind_ns = @as(u64, @intCast(bind_end - bind_start)); - - std.debug.print("Bind x {} iterations:\n", .{iterations}); - std.debug.print(" Total: {} ns ({} ns/op)\n\n", .{ bind_ns, bind_ns / iterations }); - - // Benchmark: Similarity - const sim_program = [_]VSAInstruction{ - .{ .opcode = .v_random, .dst = 0, .imm = 333 }, - .{ .opcode = .v_random, .dst = 1, .imm = 444 }, - .{ .opcode = .v_cosine, .src1 = 0, .src2 = 1 }, - .{ .opcode = .halt }, - }; - - vm.loadProgram(&sim_program) catch unreachable; - - const sim_start = std.time.nanoTimestamp(); - i = 0; - while (i < iterations) : (i += 1) { - vm.registers.pc = 2; - vm.halted = false; - vm.run() catch unreachable; - } - const sim_end = std.time.nanoTimestamp(); - const sim_ns = @as(u64, @intCast(sim_end - sim_start)); - - std.debug.print("Cosine Similarity x {} iterations:\n", .{iterations}); - std.debug.print(" Total: {} ns ({} ns/op)\n\n", .{ sim_ns, sim_ns / iterations }); - - // Memory usage - vm.registers.updateMemoryUsage(); - std.debug.print("Memory Usage:\n", .{}); - std.debug.print(" 4 vectors packed: {} bytes\n", .{vm.registers.total_packed_bytes}); - std.debug.print(" 4 vectors unpacked: {} bytes\n", .{4 * MAX_TRITS}); - std.debug.print(" Savings: {d:.1}x\n", .{@as(f64, @floatFromInt(4 * MAX_TRITS)) / @as(f64, @floatFromInt(vm.registers.total_packed_bytes))}); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// f16 SIMD INSTRUCTION TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "VSA VM f16: v_f16_load quantizes correctly" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_f16_load, .dst = 0, .imm = 0xF16 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // Check that loaded vector has ternary values - const v0 = &vm.registers.v0; - try std.testing.expectEqual(@as(usize, 16), v0.trit_len); - - // All values should be in {-1, 0, +1} - for (0..16) |i| { - const val = v0.unpacked_cache[i]; - try std.testing.expect(val == -1 or val == 0 or val == 1); - } -} - -test "VSA VM f16: v_f16_store converts to f16" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, // Load value - .{ .opcode = .v_f16_store, .src1 = 0 }, - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // Check that f16 accumulator has values - // f16_acc0 should have the converted values - const f16_vec = vm.registers.f16_acc0; - inline for (0..16) |i| { - // Values should be valid f16 (not NaN/inf) - try std.testing.expect(f16_vec[i] == f16_vec[i]); - } -} - -test "VSA VM f16: f16_dot computes dot product" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - const program = [_]VSAInstruction{ - .{ .opcode = .v_const, .dst = 0, .imm = 12345 }, - .{ .opcode = .v_mov, .dst = 1, .src1 = 0 }, // Copy to v1 - .{ .opcode = .f16_dot, .src1 = 0, .src2 = 1 }, // Dot product - .{ .opcode = .halt }, - }; - - try vm.loadProgram(&program); - try vm.run(); - - // Dot product of identical vectors should be positive - // (count of non-zero trits) - try std.testing.expect(vm.registers.f0 > 0); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// KOSCHEI v7.0: SACRED OPCODE TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "VSA VM sacred: phi_const" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - try vm.loadPhi(); - try std.testing.expect(vm.registers.f0 > 1.6 and vm.registers.f0 < 1.62); -} - -test "VSA VM sacred: phi_pow" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - vm.registers.s0 = 10; // φ^10 - try vm.phiPow(); - try std.testing.expect(vm.registers.f0 > 122.9 and vm.registers.f0 < 123.0); -} - -test "VSA VM sacred: fib(10)" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - vm.registers.s0 = 10; - try vm.fib(); - try std.testing.expectEqual(@as(i64, 55), vm.registers.s0); -} - -test "VSA VM sacred: sacred_identity" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - try vm.verifySacredIdentity(); - try std.testing.expect(vm.registers.cc_zero); // φ² + 1/φ² = 3 verified - try std.testing.expectApproxEqAbs(@as(f64, 3.0), vm.registers.f0, 1e-10); -} - -test "VSA VM sacred: direct opcode execution" { - var vm = VSAVM.init(std.testing.allocator); - defer vm.deinit(); - - // Test golden angle - try vm.execSacredOpcode(.golden_angle, .{ .dest = "f0" }); - try std.testing.expect(vm.registers.f0 > 137.5 and vm.registers.f0 < 137.51); - - // Test physics constant - try vm.execSacredOpcode(.light_speed, .{ .dest = "f0" }); - try std.testing.expectApproxEqAbs(@as(f64, 299792458.0), vm.registers.f0, 1.0); -} - -pub fn main() !void { - runBenchmarks(); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig deleted file mode 100644 index 2dff238..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vm/vsa_jit.zig +++ /dev/null @@ -1,688 +0,0 @@ -// @origin(spec:vsa_jit.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity JIT-Accelerated VSA Operations -// Provides 15-260x speedup for hot paths via native code generation -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const builtin = @import("builtin"); -const jit_unified = @import("jit_unified.zig"); -const hybrid = @import("../ternary/hybrid.zig"); - -pub const HybridBigInt = hybrid.HybridBigInt; -pub const Trit = hybrid.Trit; -pub const MAX_TRITS = hybrid.MAX_TRITS; - -// ═══════════════════════════════════════════════════════════════════════════════ -// JIT-ACCELERATED VSA ENGINE -// ═══════════════════════════════════════════════════════════════════════════════ - -/// JIT-accelerated VSA engine with compiled function caching -pub const JitVSAEngine = struct { - allocator: std.mem.Allocator, - - // Cached JIT-compiled functions for common dimensions - dot_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - bind_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - hamming_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - cosine_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - bundle_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - - // Keep compilers alive to prevent exec_mem from being freed - compilers: std.ArrayListUnmanaged(jit_unified.UnifiedJitCompiler), - - // Statistics - jit_hits: u64 = 0, - jit_misses: u64 = 0, - total_ops: u64 = 0, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .allocator = allocator, - .dot_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .bind_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .hamming_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .cosine_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .bundle_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .compilers = .{}, - }; - } - - pub fn deinit(self: *Self) void { - // Clean up all compilers (which frees exec_mem) - for (self.compilers.items) |*compiler| { - compiler.deinit(); - } - self.compilers.deinit(self.allocator); - self.dot_cache.deinit(); - self.bind_cache.deinit(); - self.hamming_cache.deinit(); - self.cosine_cache.deinit(); - self.bundle_cache.deinit(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT DOT PRODUCT - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for dot product - fn getDotFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { - if (self.dot_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Compile new function - self.jit_misses += 1; - - // Create compiler and add to list (keeps exec_mem alive) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - try compiler.compileDotProduct(dimension); - const func = try compiler.finalize(); - - try self.dot_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated dot product for HybridBigInt vectors - pub fn dotProduct(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { - self.total_ops += 1; - - // Ensure vectors are unpacked for direct memory access - a.ensureUnpacked(); - b.ensureUnpacked(); - - // Use the larger dimension - const dim = @max(a.trit_len, b.trit_len); - - // Get or compile JIT function - const func = try self.getDotFunction(dim); - - // Call JIT-compiled function directly on unpacked cache - // Cast [MAX_TRITS]Trit to *anyopaque - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - return func(a_ptr, b_ptr); - } - - /// Fallback to non-JIT dot product (for comparison) - pub fn dotProductFallback(a: *HybridBigInt, b: *HybridBigInt) i64 { - return @intCast(a.dotProduct(b)); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT BIND - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for bind - fn getBindFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { - if (self.bind_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Compile new function - self.jit_misses += 1; - - // Create compiler and add to list (keeps exec_mem alive) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - try compiler.compileBind(dimension); - const func = try compiler.finalize(); - - try self.bind_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated bind for HybridBigInt vectors (modifies a in place) - pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { - self.total_ops += 1; - - // Ensure vectors are unpacked for direct memory access - a.ensureUnpacked(); - b.ensureUnpacked(); - - // Use the larger dimension - const dim = @max(a.trit_len, b.trit_len); - - // Get or compile JIT function - const func = try self.getBindFunction(dim); - - // Call JIT-compiled function (modifies a in place) - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - _ = func(a_ptr, b_ptr); - - // Mark as modified (dirty) since JIT wrote to unpacked cache - a.dirty = true; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT FUSED COSINE SIMILARITY (single-pass computation) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for fused cosine similarity - fn getCosineFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.cosine_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile fused cosine (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileFusedCosine(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to 3x dot product - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.cosine_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated cosine similarity using fused kernel (2.5x faster on ARM64) - /// cos(a,b) = dot(a,b) / sqrt(dot(a,a) * dot(b,b)) - pub fn cosineSimilarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try fused cosine kernel (ARM64 only, 2.5x faster) - if (try self.getCosineFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - // Function returns f64 bit pattern as i64 - const result_bits = func(a_ptr, b_ptr); - return @bitCast(result_bits); - } - - // Fallback: use 3 separate JIT dot products - const dot_ab = try self.dotProduct(a, b); - const dot_aa = try self.dotProduct(a, a); - const dot_bb = try self.dotProduct(b, b); - - // Handle zero vectors - if (dot_aa == 0 or dot_bb == 0) { - return 0.0; - } - - const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); - return @as(f64, @floatFromInt(dot_ab)) / norm; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT HAMMING DISTANCE (count of differing positions) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for hamming distance - fn getHammingFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.hamming_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile new function (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileHamming(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to scalar - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.hamming_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated hamming distance - /// For ternary: counts positions where a[i] != b[i] - pub fn hammingDistance(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try JIT SIMD version (available on ARM64) - if (try self.getHammingFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - return func(a_ptr, b_ptr); - } - - // Scalar fallback - var count: i64 = 0; - for (0..dim) |i| { - if (a.unpacked_cache[i] != b.unpacked_cache[i]) { - count += 1; - } - } - return count; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT BUNDLE OPERATION (n-ary addition with threshold) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for bundle operation - fn getBundleFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.bundle_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile bundle SIMD (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileBundleSIMD(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to scalar - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.bundle_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated bundle operation - /// result[i] = threshold(a[i] + b[i]) where >0→1, <0→-1, =0→0 - /// Modifies 'a' in place - pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try JIT SIMD version (ARM64 only) - if (try self.getBundleFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - _ = func(a_ptr, b_ptr); - a.dirty = true; - return; - } - - // Scalar fallback - for (0..dim) |i| { - const sum: i16 = @as(i16, a.unpacked_cache[i]) + @as(i16, b.unpacked_cache[i]); - if (sum > 0) { - a.unpacked_cache[i] = 1; - } else if (sum < 0) { - a.unpacked_cache[i] = -1; - } else { - a.unpacked_cache[i] = 0; - } - } - a.dirty = true; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // STATISTICS - // ═══════════════════════════════════════════════════════════════════════════ - - pub fn getStats(self: *const Self) Stats { - const total_cache = self.jit_hits + self.jit_misses; - const hit_rate = if (total_cache > 0) - @as(f64, @floatFromInt(self.jit_hits)) / @as(f64, @floatFromInt(total_cache)) * 100.0 - else - 0.0; - - return Stats{ - .total_ops = self.total_ops, - .jit_hits = self.jit_hits, - .jit_misses = self.jit_misses, - .cache_size = self.dot_cache.count() + self.bind_cache.count() + self.hamming_cache.count() + self.cosine_cache.count() + self.bundle_cache.count(), - .hit_rate = hit_rate, - }; - } - - pub fn printStats(self: *const Self) void { - const stats = self.getStats(); - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" JIT VSA ENGINE STATISTICS\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Total operations: {d}\n", .{stats.total_ops}); - std.debug.print(" JIT cache hits: {d}\n", .{stats.jit_hits}); - std.debug.print(" JIT cache misses: {d}\n", .{stats.jit_misses}); - std.debug.print(" Cache size: {d} functions\n", .{stats.cache_size}); - std.debug.print(" Hit rate: {d:.1}%\n", .{stats.hit_rate}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - } - - pub const Stats = struct { - total_ops: u64, - jit_hits: u64, - jit_misses: u64, - cache_size: usize, - hit_rate: f64, - }; -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// CONVENIENCE FUNCTIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Global JIT engine (thread-local for safety) -threadlocal var global_engine: ?JitVSAEngine = null; - -/// Initialize global JIT engine -pub fn initGlobal(allocator: std.mem.Allocator) void { - if (global_engine == null) { - global_engine = JitVSAEngine.init(allocator); - } -} - -/// Deinitialize global JIT engine -pub fn deinitGlobal() void { - if (global_engine) |*engine| { - engine.deinit(); - global_engine = null; - } -} - -/// JIT-accelerated dot product using global engine -pub fn jitDotProduct(allocator: std.mem.Allocator, a: *HybridBigInt, b: *HybridBigInt) !i64 { - initGlobal(allocator); - return global_engine.?.dotProduct(a, b); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "JitVSAEngine init and deinit" { - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - try std.testing.expect(engine.total_ops == 0); -} - -test "JitVSAEngine dot product correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Create test vectors using setTrit (proper API) - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - // Simple test: all 1s dot all 1s = dimension - const test_len = 64; - - for (0..test_len) |i| { - a.setTrit(i, 1); - b.setTrit(i, 1); - } - - const expected: i64 = test_len; - - // JIT dot product - const jit_result = try engine.dotProduct(&a, &b); - - // Fallback dot product - const fallback_result = JitVSAEngine.dotProductFallback(&a, &b); - - try std.testing.expectEqual(expected, jit_result); - try std.testing.expectEqual(expected, fallback_result); -} - -test "JitVSAEngine cache hits" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - a.trit_len = 64; - b.trit_len = 64; - - // First call - cache miss - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 0), engine.jit_hits); - - // Second call - cache hit - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 1), engine.jit_hits); - - // Third call - cache hit - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 2), engine.jit_hits); -} - -test "JitVSAEngine benchmark vs fallback" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - const dim = 1024; - const iterations = 10000; - - // Create test vectors using setTrit - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - for (0..dim) |i| { - const val_a: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: Trit = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a.setTrit(i, val_a); - b.setTrit(i, val_b); - } - - // Warm up JIT cache - _ = try engine.dotProduct(&a, &b); - - // Benchmark JIT - var timer = try std.time.Timer.start(); - var jit_result: i64 = 0; - for (0..iterations) |_| { - jit_result = try engine.dotProduct(&a, &b); - } - const jit_ns = timer.read(); - - // Benchmark fallback - timer.reset(); - var fallback_result: i64 = 0; - for (0..iterations) |_| { - fallback_result = JitVSAEngine.dotProductFallback(&a, &b); - } - const fallback_ns = timer.read(); - - // Results should match - try std.testing.expectEqual(jit_result, fallback_result); - - const jit_ms = @as(f64, @floatFromInt(jit_ns)) / 1_000_000.0; - const fallback_ms = @as(f64, @floatFromInt(fallback_ns)) / 1_000_000.0; - const speedup = fallback_ms / jit_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" JIT VSA ENGINE BENCHMARK (HybridBigInt)\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Dimension: {d} trits\n", .{dim}); - std.debug.print(" Iterations: {d}\n", .{iterations}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" Fallback (HybridBigInt.dotProduct): {d:.3} ms\n", .{fallback_ms}); - std.debug.print(" JIT (NEON SIMD): {d:.3} ms\n", .{jit_ms}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - engine.printStats(); - - // JIT should generally be faster, but can be slower due to thermal/load - // Just verify JIT compiles and runs without crashing - if (speedup > 1.0) { - std.debug.print(" JIT is faster! ({d:.2}x speedup)\n", .{speedup}); - } else { - std.debug.print(" JIT is slower ({d:.2}x) - acceptable for flaky benchmark\n", .{speedup}); - } -} - -test "JitVSAEngine various dimensions" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - const test_dims = [_]usize{ 8, 16, 32, 64, 100, 128, 256, 512, 1000 }; - - for (test_dims) |dim| { - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - var expected: i64 = 0; - for (0..dim) |i| { - a.setTrit(i, 1); - b.setTrit(i, 1); - expected += 1; - } - - const result = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(expected, result); - } - - // Should have compiled functions for each unique dimension - try std.testing.expectEqual(@as(usize, test_dims.len), engine.dot_cache.count()); -} - -test "JitVSAEngine bind correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test bind: result[i] = a[i] * b[i] (ternary multiplication) - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - const dim = 16; - for (0..dim) |i| { - // Pattern: a = [1, -1, 0, 1, -1, 0, ...], b = [1, 1, 1, -1, -1, -1, ...] - const a_val: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); - const b_val: Trit = if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1); - a.setTrit(i, a_val); - b.setTrit(i, b_val); - } - - // Compute expected result - var expected = HybridBigInt.zero(); - for (0..dim) |i| { - const a_val = a.getTrit(i); - const b_val = b.getTrit(i); - expected.setTrit(i, a_val * b_val); - } - - // JIT bind - try engine.bind(&a, &b); - - // Verify result - for (0..dim) |i| { - try std.testing.expectEqual(expected.getTrit(i), a.getTrit(i)); - } -} - -test "JitVSAEngine cosine similarity correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test identical vectors: cos(a, a) = 1.0 - var a = HybridBigInt.zero(); - const dim = 64; - for (0..dim) |i| { - a.setTrit(i, 1); - } - - const cos_identical = try engine.cosineSimilarity(&a, &a); - try std.testing.expectApproxEqRel(@as(f64, 1.0), cos_identical, 0.001); - - // Test orthogonal vectors: cos(a, -a) = -1.0 - var neg_a = HybridBigInt.zero(); - for (0..dim) |i| { - neg_a.setTrit(i, -1); - } - - const cos_opposite = try engine.cosineSimilarity(&a, &neg_a); - try std.testing.expectApproxEqRel(@as(f64, -1.0), cos_opposite, 0.001); -} - -test "JitVSAEngine hamming distance correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test identical vectors: hamming(a, a) = 0 - var a = HybridBigInt.zero(); - const dim = 64; - for (0..dim) |i| { - a.setTrit(i, 1); - } - - const hamming_identical = try engine.hammingDistance(&a, &a); - try std.testing.expectEqual(@as(i64, 0), hamming_identical); - - // Test completely different vectors: hamming(a, -a) = dim - var neg_a = HybridBigInt.zero(); - for (0..dim) |i| { - neg_a.setTrit(i, -1); - } - - const hamming_opposite = try engine.hammingDistance(&a, &neg_a); - try std.testing.expectEqual(@as(i64, dim), hamming_opposite); - - // Test half different: change half the trits - var half = HybridBigInt.zero(); - for (0..dim) |i| { - half.setTrit(i, if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1)); - } - - const hamming_half = try engine.hammingDistance(&a, &half); - try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig deleted file mode 100644 index cbbe2ff..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/10k_vsa.zig +++ /dev/null @@ -1,461 +0,0 @@ -// ╔════════════════════════════════════════════════════════════════════════════╗ -// ║ TRINITY VSA — 10K-DIMENSIONAL HYPERVECTORS ║ -// ║ Week 2 Day 1: Scalable VSA architecture for 10,000-dimensional vectors ║ -// ║ ║ -// ║ Features: ║ -// ║ - 10,000-dimensional ternary hypervectors ║ -// ║ - O(1) parallel bind operation ║ -// ║ - Bundle, similarity, permutation ║ -// ║ - FPGA-ready memory layout ║ -// ║ ║ -// ║ φ² + 1/φ² = 3 = TRINITY ║ -// ╚════════════════════════════════════════════════════════════════════════════╝ - -const std = @import("std"); -const builtin = @import("builtin"); -const common = @import("common.zig"); - -pub const Trit = common.Trit; // i8: -1, 0, +1 -pub const HybridBigInt = common.HybridBigInt; - -//========================================================================== -// CONSTANTS -//========================================================================== - -pub const DIM_10K = 10_000; -pub const BYTES_PER_10K = (DIM_10K * 2 + 7) / 8; // 20,000 bits = 2,500 bytes -pub const WORDS_32BIT = (DIM_10K * 2 + 31) / 32; // 625 words of 32 bits - -// FPGA BRAM sizing (32Kb = 4096 bytes) -pub const BRAM_SIZE = 4096; -pub const VECTORS_PER_BRAM = BRAM_SIZE / BYTES_PER_10K; // ~1.6 vectors - -// Trit values (matching HybridBigInt convention) -pub const TRIT_NEG: Trit = -1; -pub const TRIT_ZERO: Trit = 0; -pub const TRIT_POS: Trit = 1; - -//========================================================================== -// 10K-DIMENSIONAL HYPERVECTOR -//========================================================================== - -/// 10K-dimensional ternary hypervector -/// Storage: 2,500 bytes (20,000 bits) using packed trit encoding -pub const HyperVector10K = struct { - /// Packed trit storage (2 bits per trit) - /// Layout: [trit0:1, trit0:0][trit1:1, trit1:0]... - data: [BYTES_PER_10K]u8, - - const Self = @This(); - - /// Create a zero vector - pub inline fn zero() Self { - return .{ .data = [_]u8{0} ** BYTES_PER_10K }; - } - - /// Create a random vector - pub fn random(rng: *std.Random.DefaultPrng) !Self { - var self = Self.zero(); - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - const rand_val = rng.random().int(u3); - const trit_val: i8 = switch (rand_val & 0x03) { - 0 => TRIT_ZERO, - 1 => TRIT_POS, - 2 => TRIT_NEG, - else => TRIT_POS, - }; - try self.set(i, trit_val); - } - return self; - } - - /// Get trit at index (returns {-1, 0, +1}) - pub inline fn get(self: *const Self, index: usize) !Trit { - if (index >= DIM_10K) return error.IndexOutOfBounds; - const bit_idx = index * 2; - const byte_idx = bit_idx / 8; - const shift: u3 = @intCast(bit_idx % 8); - const trit_bits = (self.data[byte_idx] >> shift) & 0x03; - - return switch (trit_bits) { - 0b00 => TRIT_ZERO, - 0b01 => TRIT_POS, - 0b10 => TRIT_NEG, - else => TRIT_ZERO, // Invalid encoding - }; - } - - /// Set trit at index - pub inline fn set(self: *Self, index: usize, value: Trit) !void { - if (index >= DIM_10K) return error.IndexOutOfBounds; - const bit_idx = index * 2; - const byte_idx = bit_idx / 8; - const shift: u3 = @intCast(bit_idx % 8); - - const trit_bits: u2 = switch (value) { - TRIT_ZERO => 0b00, - TRIT_POS => 0b01, - TRIT_NEG => 0b10, - else => 0b00, - }; - - // Clear old bits and set new ones - self.data[byte_idx] &= ~(@as(u8, 0x03) << shift); - self.data[byte_idx] |= @as(u8, trit_bits) << shift; - } - - /// Parallel bind operation (O(1) on FPGA with 10,000 LUTs) - /// result[i] = a[i] * b[i] - pub fn bind(a: *const Self, b: *const Self) Self { - var result = Self.zero(); - - // Process 16 trits (32 bits) at a time for SIMD efficiency - const word_count = WORDS_32BIT; - var w: usize = 0; - - while (w < word_count) : (w += 1) { - const byte_idx = w * 4; - if (byte_idx + 4 > BYTES_PER_10K) break; - - // Load 32 bits (16 trits) from each vector - const a_words = std.mem.readInt(u32, a.data[byte_idx..][0..4], .little); - const b_words = std.mem.readInt(u32, b.data[byte_idx..][0..4], .little); - - var result_word: u32 = 0; - var t: usize = 0; - - // Trit-wise multiplication (16 parallel operations) - while (t < 16) : (t += 1) { - const shift_amt: u5 = @intCast(t * 2); - const a_trit: u2 = @truncate((a_words >> shift_amt) & 0x03); - const b_trit: u2 = @truncate((b_words >> shift_amt) & 0x03); - - const r_trit: u2 = tritMul(a_trit, b_trit); - result_word |= @as(u32, r_trit) << shift_amt; - } - - // Store result - std.mem.writeInt(u32, result.data[byte_idx..][0..4], result_word, .little); - } - - return result; - } - - /// Bundle operation (majority vote) - pub fn bundle(a: *const Self, b: *const Self) !Self { - var result = Self.zero(); - - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - const a_trit = try a.get(i); - const b_trit = try b.get(i); - - const r_trit: Trit = tritBundle(a_trit, b_trit); - try result.set(i, r_trit); - } - - return result; - } - - /// Cosine similarity (scaled to 0-65535) - pub fn cosineSimilarity(a: *const Self, b: *const Self) !u16 { - var dot_product: i64 = 0; - var norm_a: i64 = 0; - var norm_b: i64 = 0; - - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - const a_trit = try a.get(i); - const b_trit = try b.get(i); - - dot_product += @as(i64, a_trit) * @as(i64, b_trit); - norm_a += @as(i64, a_trit) * @as(i64, a_trit); - norm_b += @as(i64, b_trit) * @as(i64, b_trit); - } - - if (norm_a == 0 or norm_b == 0) - return 0; - - const norm_sum = norm_a + norm_b; - const abs_dot = @abs(dot_product); - const scaled = @as(u64, @intCast(abs_dot)) * 65535 / @as(u64, @intCast(norm_sum)); - - return @intCast(scaled); - } - - /// Permutation (cyclic shift) - pub fn permute(self: *const Self, shift: u16) !Self { - var result = Self.zero(); - const effective_shift = @as(usize, @intCast(shift)) % DIM_10K; - - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - const src_idx = (i + DIM_10K - effective_shift) % DIM_10K; - const trit = try self.get(src_idx); - try result.set(i, trit); - } - - return result; - } - - /// Count non-zero trits - pub fn countNonZero(self: *const Self) !usize { - var count: usize = 0; - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - if (try self.get(i) != TRIT_ZERO) - count += 1; - } - return count; - } - - /// Convert to slice of 32-bit words (for FPGA transfer) - pub fn toWords(self: *const Self) [WORDS_32BIT]u32 { - var result: [WORDS_32BIT]u32 = undefined; - var i: usize = 0; - while (i < WORDS_32BIT) : (i += 1) { - const byte_idx = i * 4; - if (byte_idx + 4 <= BYTES_PER_10K) { - result[i] = std.mem.readInt(u32, self.data[byte_idx..][0..4], .little); - } else { - result[i] = 0; - } - } - return result; - } - - /// Create from slice of 32-bit words (from FPGA) - pub fn fromWords(words: []const u32) Self { - var result = Self.zero(); - var i: usize = 0; - while (i < @min(WORDS_32BIT, words.len)) : (i += 1) { - const byte_idx = i * 4; - if (byte_idx + 4 <= BYTES_PER_10K) { - std.mem.writeInt(u32, result.data[byte_idx..][0..4], words[i], .little); - } - } - return result; - } - - /// Format as hex string - pub fn formatHex(self: *const Self, allocator: std.mem.Allocator) ![]u8 { - // std.fmt.fmtSliceHexLower was removed in 0.15; {x} on the slice is the - // replacement and produces the same lowercase hex. - return std.fmt.allocPrint(allocator, "{x}", .{&self.data}); - } -}; - -/// Trit multiplication lookup table (combinational logic) -inline fn tritMul(a: u2, b: u2) u2 { - return if (a == 0 or b == 0) 0 else if (a == b) 1 else 2; -} - -/// Trit bundle (majority vote of 2) -inline fn tritBundle(a: Trit, b: Trit) Trit { - if (a == TRIT_NEG) { - return if (b == TRIT_NEG) TRIT_NEG else if (b == TRIT_POS) TRIT_ZERO else TRIT_NEG; - } else if (a == TRIT_POS) { - return if (b == TRIT_NEG) TRIT_ZERO else if (b == TRIT_POS) TRIT_POS else TRIT_POS; - } else { // a == ZERO - return b; - } -} - -//========================================================================== -// BENCHMARK FUNCTIONS -//========================================================================== - -pub const BenchmarkResult = struct { - bind_ns: f64, - bundle_ns: f64, - similarity_ns: f64, - bind_throughput: f64, // ops/sec - dimensions: usize = DIM_10K, -}; - -/// Run 10K VSA benchmark -pub fn benchmark(_: std.mem.Allocator, iterations: usize) !BenchmarkResult { - var rng = std.Random.DefaultPrng.init(@intCast(std.time.timestamp())); - - // Create test vectors - const vec_a = try HyperVector10K.random(&rng); - const vec_b = try HyperVector10K.random(&rng); - - // Warmup - _ = HyperVector10K.bind(&vec_a, &vec_b); - _ = try HyperVector10K.bundle(&vec_a, &vec_b); - _ = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); - - // Benchmark bind - const bind_start = std.time.nanoTimestamp(); - var i: usize = 0; - while (i < iterations) : (i += 1) { - _ = HyperVector10K.bind(&vec_a, &vec_b); - } - const bind_end = std.time.nanoTimestamp(); - const bind_ns = @as(f64, @floatFromInt(bind_end - bind_start)) / @as(f64, @floatFromInt(iterations)); - - // Benchmark bundle - const bundle_start = std.time.nanoTimestamp(); - i = 0; - while (i < iterations) : (i += 1) { - _ = try HyperVector10K.bundle(&vec_a, &vec_b); - } - const bundle_end = std.time.nanoTimestamp(); - const bundle_ns = @as(f64, @floatFromInt(bundle_end - bundle_start)) / @as(f64, @floatFromInt(iterations)); - - // Benchmark similarity - const sim_start = std.time.nanoTimestamp(); - i = 0; - while (i < iterations) : (i += 1) { - _ = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); - } - const sim_end = std.time.nanoTimestamp(); - const sim_ns = @as(f64, @floatFromInt(sim_end - sim_start)) / @as(f64, @floatFromInt(iterations)); - - return BenchmarkResult{ - .bind_ns = bind_ns, - .bundle_ns = bundle_ns, - .similarity_ns = sim_ns, - .bind_throughput = 1_000_000_000.0 / bind_ns, - }; -} - -/// Print benchmark results -pub fn printBenchmark(result: BenchmarkResult) void { - // std.io.getStdOut() was removed in 0.15. A File writer needs a buffer it - // does not own, so the buffer lives here and the interface borrows it. - var stdout_buffer: [4096]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); - const stdout = &stdout_writer.interface; - defer stdout.flush() catch {}; - - stdout.print( - \\╔════════════════════════════════════════════════════════════════════════════╗ - \\║ TRINITY VSA 10K BENCHMARK RESULTS ║ - \\╚════════════════════════════════════════════════════════════════════════════╝ - \\ - \\Dimensions: {d} - \\Vector size: {d} bytes - \\ - \\═══════════════════════════════════════════════════════════════════════════ - \\OPERATION TIME (ns) THROUGHPUT vs FPGA (est) - \\═══════════════════════════════════════════════════════════════════════════ - \\BIND {d:.2} ns {d:.0} op/s ~1000x slower - \\BUNDLE {d:.2} ns {d:.0} op/s ~500x slower - \\SIMILARITY {d:.2} ns {d:.0} op/s ~100x slower - \\═══════════════════════════════════════════════════════════════════════════ - \\ - \\φ² + 1/φ² = 3 = TRINITY - \\ - , .{ - DIM_10K, - BYTES_PER_10K, - result.bind_ns, - result.bind_throughput, - result.bundle_ns, - 1_000_000_000.0 / result.bundle_ns, - result.similarity_ns, - 1_000_000_000.0 / result.similarity_ns, - }) catch return; -} - -//========================================================================== -// TESTS -//========================================================================== - -test "HyperVector10K: zero vector" { - const vec = HyperVector10K.zero(); - try std.testing.expectEqual(@as(usize, 0), try vec.countNonZero()); -} - -test "HyperVector10K: bind identity" { - var rng = std.Random.DefaultPrng.init(42); - const vec = try HyperVector10K.random(&rng); - - // Identity vector (all +1) - var identity = HyperVector10K.zero(); - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - try identity.set(i, TRIT_POS); - } - - const result = HyperVector10K.bind(&vec, &identity); - - // Verify result equals original (sample check) - var match_count: usize = 0; - i = 0; - while (i < 100) : (i += 1) { - if ((try result.get(i)) == (try vec.get(i))) - match_count += 1; - } - - try std.testing.expect(match_count >= 95); // Allow some tolerance -} - -test "HyperVector10K: bind inverse" { - var rng = std.Random.DefaultPrng.init(42); - const vec = try HyperVector10K.random(&rng); - - // Inverse vector (all -1) - var inverse = HyperVector10K.zero(); - var i: usize = 0; - while (i < DIM_10K) : (i += 1) { - try inverse.set(i, TRIT_NEG); - } - - const result = HyperVector10K.bind(&vec, &inverse); - - // Verify result is negation of original - var match_count: usize = 0; - i = 0; - while (i < 100) : (i += 1) { - const vi = try vec.get(i); - const expected: i8 = if (vi == TRIT_NEG) TRIT_POS else if (vi == TRIT_POS) TRIT_NEG else TRIT_ZERO; - if ((try result.get(i)) == expected) - match_count += 1; - } - - try std.testing.expectEqual(@as(usize, 100), match_count); -} - -test "HyperVector10K: cosine similarity bounds" { - var rng = std.Random.DefaultPrng.init(42); - const vec_a = try HyperVector10K.random(&rng); - const vec_b = try HyperVector10K.random(&rng); - - const sim = try HyperVector10K.cosineSimilarity(&vec_a, &vec_b); - - // Similarity should be in range [0, 65535] - try std.testing.expect(sim >= 0 and sim <= 65535); -} - -test "HyperVector10K: permutation roundtrip" { - var rng = std.Random.DefaultPrng.init(42); - const original = try HyperVector10K.random(&rng); - - const shifted = try original.permute(100); - const unshifted = try shifted.permute(@as(u16, @intCast(DIM_10K - 100))); - - // Sample check (not all 10K to save time) - var match_count: usize = 0; - var i: usize = 0; - while (i < 100) : (i += 1) { - if ((try unshifted.get(i)) == (try original.get(i))) - match_count += 1; - } - - try std.testing.expectEqual(@as(usize, 100), match_count); -} - -test "HyperVector10K: benchmark quick" { - const allocator = std.testing.allocator; - const result = try benchmark(allocator, 10); - _ = result; - - // Just verify it completes without error - try std.testing.expect(true); -} - -// φ² + 1/φ² = 3 = TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig deleted file mode 100644 index 73af6f6..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/common.zig +++ /dev/null @@ -1,20 +0,0 @@ -// 🤖 TRINITY v0.11.0: Suborbital Order -// Common types and imports for VSA module - -const std = @import("std"); -// ../hybrid.zig resolves to src/hybrid.zig, which does not exist. The file -// is src/ternary/hybrid.zig. -const tvc_hybrid = @import("../ternary/hybrid.zig"); - -pub const HybridBigInt = tvc_hybrid.HybridBigInt; -pub const Trit = tvc_hybrid.Trit; -pub const Vec32i8 = tvc_hybrid.Vec32i8; -pub const SIMD_WIDTH = tvc_hybrid.SIMD_WIDTH; -pub const MAX_TRITS = tvc_hybrid.MAX_TRITS; - -pub const SearchResult = struct { - index: usize, - similarity: f64, -}; - -// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig deleted file mode 100644 index 29aa4be..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/concurrency.zig +++ /dev/null @@ -1,295 +0,0 @@ -// 🤖 TRINITY v0.11.0: Suborbital Order -// Concurrency and Parallel Processing layer for VSA -const std = @import("std"); -const common = @import("common.zig"); -const HybridBigInt = common.HybridBigInt; - -// CONSTANTS -pub const POOL_SIZE = 4; -pub const DEQUE_CAPACITY = 128; -pub const MAX_WORKERS = 8; -pub const PRIORITY_LEVELS = 5; -pub const PRIORITY_QUEUE_CAPACITY = 256; -pub const MAX_JOB_AGE = 100; -pub const MAX_DAG_NODES = 256; -pub const MAX_DEPENDENCIES = 16; -pub const PHI_INVERSE: f64 = 0.618033988749895; - -// TYPES -pub const JobFn = *const fn (context: *anyopaque) void; -pub const PoolJob = struct { func: JobFn, context: *anyopaque }; -pub const PriorityLevel = enum(u8) { critical = 0, high = 1, normal = 2, low = 3, background = 4 }; -pub const JobPriority = PriorityLevel; -pub const TaskState = enum(u8) { pending = 0, ready = 1, running = 2, completed = 3, failed = 4 }; - -pub const TaskNode = struct { - id: u32, - func: JobFn, - context: *anyopaque, - dependencies: [MAX_DEPENDENCIES]u32, - dep_count: usize, - dependents: [MAX_DEPENDENCIES]u32, - dependent_count: usize, - state: TaskState, - priority: JobPriority, - deadline: ?i64, - wait_count: std.atomic.Value(usize), - - pub fn init(id: u32, func: JobFn, context: *anyopaque) TaskNode { - return TaskNode{ - .id = id, - .func = func, - .context = context, - .dependencies = undefined, - .dep_count = 0, - .dependents = undefined, - .dependent_count = 0, - .state = .pending, - .priority = .normal, - .deadline = null, - .wait_count = std.atomic.Value(usize).init(0), - }; - } - pub fn addDependency(self: *TaskNode, dep_id: u32) bool { - if (self.dep_count >= MAX_DEPENDENCIES) return false; - self.dependencies[self.dep_count] = dep_id; - self.dep_count += 1; - _ = self.wait_count.fetchAdd(1, .monotonic); - return true; - } - pub fn addDependent(self: *TaskNode, dep_id: u32) bool { - if (self.dependent_count >= MAX_DEPENDENCIES) return false; - self.dependents[self.dependent_count] = dep_id; - self.dependent_count += 1; - return true; - } - pub fn satisfyDependency(self: *TaskNode) bool { - // std.atomic.fence was removed in 0.15. The acquire it supplied belongs - // on the operation itself: acq_rel gives the release for this decrement - // and the acquire for observing the last one, which is exactly what the - // separate fence was there to add. - const remaining = self.wait_count.fetchSub(1, .acq_rel) - 1; - if (remaining == 0) { - self.state = .ready; - return true; - } - return false; - } - pub fn getEffectivePriority(self: *const TaskNode) f64 { - // Annotated because the arms are comptime_float and the switch is on a - // runtime value: a comptime-only type cannot depend on runtime control - // flow, which is what the compiler was saying. - const base: f64 = switch (self.priority) { - .critical => 1.0, - .high => 0.8, - .normal => 0.6, - .low => 0.4, - .background => 0.2, - }; - if (self.deadline) |dl| { - const now = std.time.nanoTimestamp(); - const remaining = dl - now; - if (remaining < 0) return 2.0; - const boost = 1.0 / (@as(f64, @floatFromInt(remaining)) / 1e9 + 1.0); - return base + boost; - } - return base; - } -}; - -pub const DAGStats = struct { - total: usize, - completed: usize, - failed: usize, - pending: usize, - ready: usize, - completion_rate: f64, -}; - -// CHASE-LEV DEQUE & WORK-STEALING POOL -pub const ChaseLevDeque = struct { - jobs: [DEQUE_CAPACITY]PoolJob, - bottom: usize, - top: usize, - - pub fn init() ChaseLevDeque { - return ChaseLevDeque{ .jobs = undefined, .bottom = 0, .top = 0 }; - } - pub fn push(self: *ChaseLevDeque, job: PoolJob) bool { - const b = @atomicLoad(usize, &self.bottom, .seq_cst); - const t = @atomicLoad(usize, &self.top, .seq_cst); - if (b - t >= DEQUE_CAPACITY) return false; - self.jobs[b % DEQUE_CAPACITY] = job; - @atomicStore(usize, &self.bottom, b + 1, .seq_cst); - return true; - } - pub fn pop(self: *ChaseLevDeque) ?PoolJob { - var b = @atomicLoad(usize, &self.bottom, .seq_cst); - if (b == 0) return null; - b -= 1; - @atomicStore(usize, &self.bottom, b, .seq_cst); - const t = @atomicLoad(usize, &self.top, .seq_cst); - if (t <= b) { - const job = self.jobs[b % DEQUE_CAPACITY]; - if (t == b) { - const result = @cmpxchgWeak(usize, &self.top, t, t + 1, .seq_cst, .seq_cst); - if (result == null) { - @atomicStore(usize, &self.bottom, t + 1, .seq_cst); - return job; - } else { - @atomicStore(usize, &self.bottom, t + 1, .seq_cst); - return null; - } - } - return job; - } else { - @atomicStore(usize, &self.bottom, t, .seq_cst); - return null; - } - } - pub fn steal(self: *ChaseLevDeque) ?PoolJob { - const t = @atomicLoad(usize, &self.top, .seq_cst); - const b = @atomicLoad(usize, &self.bottom, .seq_cst); - if (t >= b) return null; - const job = self.jobs[t % DEQUE_CAPACITY]; - const result = @cmpxchgWeak(usize, &self.top, t, t + 1, .seq_cst, .seq_cst); - if (result == null) return job; - return null; - } - pub fn size(self: *ChaseLevDeque) usize { - const b = @atomicLoad(usize, &self.bottom, .seq_cst); - const t = @atomicLoad(usize, &self.top, .seq_cst); - return if (b > t) b - t else 0; - } - pub fn reset(self: *ChaseLevDeque) void { - @atomicStore(usize, &self.bottom, 0, .seq_cst); - @atomicStore(usize, &self.top, 0, .seq_cst); - } -}; - -pub const ThreadPool = struct { - workers: [MAX_WORKERS]ChaseLevDeque, - count: usize, - pub fn init() ThreadPool { - return ThreadPool{ .workers = undefined, .count = 0 }; - } -}; - -// DEPENDENCY GRAPH (DAG) -pub const DependencyGraph = struct { - nodes: [MAX_DAG_NODES]?TaskNode, - node_count: usize, - ready_queue: [MAX_DAG_NODES]u32, - ready_count: std.atomic.Value(usize), - completed_count: usize, - failed_count: usize, - execution_order: [MAX_DAG_NODES]u32, - order_computed: bool, - - const Self = @This(); - pub fn init() Self { - return Self{ - .nodes = .{null} ** MAX_DAG_NODES, - .node_count = 0, - .ready_queue = .{0} ** MAX_DAG_NODES, - .ready_count = std.atomic.Value(usize).init(0), - .completed_count = 0, - .failed_count = 0, - .execution_order = .{0} ** MAX_DAG_NODES, - .order_computed = false, - }; - } - pub fn addTask(self: *Self, func: JobFn, context: *anyopaque) ?u32 { - if (self.node_count >= MAX_DAG_NODES) return null; - const id: u32 = @intCast(self.node_count); - self.nodes[id] = TaskNode.init(id, func, context); - self.node_count += 1; - self.order_computed = false; - return id; - } - pub fn addTaskWithPriority(self: *Self, func: JobFn, context: *anyopaque, priority: JobPriority) ?u32 { - const id = self.addTask(func, context) orelse return null; - if (self.nodes[id]) |*node| node.priority = priority; - return id; - } - pub fn addDependency(self: *Self, from_id: u32, to_id: u32) bool { - if (from_id >= self.node_count or to_id >= self.node_count) return false; - if (from_id == to_id) return false; - if (self.nodes[from_id]) |*from_node| { - if (!from_node.addDependent(to_id)) return false; - } else return false; - if (self.nodes[to_id]) |*to_node| { - if (!to_node.addDependency(from_id)) return false; - } else return false; - self.order_computed = false; - return true; - } - pub fn computeTopologicalOrder(self: *Self) bool { - if (self.order_computed) return true; - var in_degree: [MAX_DAG_NODES]usize = .{0} ** MAX_DAG_NODES; - var queue: [MAX_DAG_NODES]u32 = .{0} ** MAX_DAG_NODES; - var queue_start: usize = 0; - var queue_end: usize = 0; - var order_idx: usize = 0; - for (0..self.node_count) |i| { - if (self.nodes[i]) |node| { - in_degree[i] = node.dep_count; - if (in_degree[i] == 0) { - queue[queue_end] = @intCast(i); - queue_end += 1; - } - } - } - while (queue_start < queue_end) { - const current = queue[queue_start]; - queue_start += 1; - self.execution_order[order_idx] = current; - order_idx += 1; - if (self.nodes[current]) |node| { - for (0..node.dependent_count) |i| { - const dep_id = node.dependents[i]; - in_degree[dep_id] -= 1; - if (in_degree[dep_id] == 0) { - queue[queue_end] = dep_id; - queue_end += 1; - } - } - } - } - if (order_idx != self.node_count) return false; - self.order_computed = true; - return true; - } - pub fn executeAll(self: *Self) struct { completed: usize, failed: usize } { - if (!self.computeTopologicalOrder()) return .{ .completed = 0, .failed = self.node_count }; - var completed: usize = 0; - for (0..self.node_count) |order_idx| { - const id = self.execution_order[order_idx]; - if (self.nodes[id]) |node| { - node.func(node.context); - completed += 1; - } - } - self.completed_count = completed; - return .{ .completed = completed, .failed = 0 }; - } -}; - -var global_pool: ?ThreadPool = null; -pub fn getGlobalPool() *ThreadPool { - if (global_pool == null) global_pool = ThreadPool.init(); - return &global_pool.?; -} -var global_dag: ?DependencyGraph = null; -pub fn getDAG() *DependencyGraph { - if (global_dag == null) global_dag = DependencyGraph.init(); - return &global_dag.?; -} -pub fn shutdownDAG() void { - global_dag = null; -} -pub fn hasDAG() bool { - return global_dag != null; -} - -// φ² + 1/φ² = 3 | TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig deleted file mode 100644 index e514b50..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/core.zig +++ /dev/null @@ -1,816 +0,0 @@ -// 🤖 TRINITY v0.11.0: Suborbital Order -// Core VSA operations for Balanced Ternary -// bind, bundle, similarity, permute - -const std = @import("std"); -const common = @import("common.zig"); -const HybridBigInt = common.HybridBigInt; -const Trit = common.Trit; -const Vec32i8 = common.Vec32i8; -const SIMD_WIDTH = common.SIMD_WIDTH; -const MAX_TRITS = common.MAX_TRITS; - -/// Bind operation (XOR-like for balanced ternary) -pub fn bind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const min_len = @min(a.trit_len, b.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const prod = a_vec * b_vec; - result.unpacked_cache[i..][0..SIMD_WIDTH].* = prod; - } - - while (i < len) : (i += 1) { - const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; - result.unpacked_cache[i] = a_trit * b_trit; - } - - return result; -} - -pub fn unbind(bound: *HybridBigInt, key: *HybridBigInt) HybridBigInt { - return bind(bound, key); -} - -pub fn bundle2(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const min_len = @min(a.trit_len, b.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - - const a_wide: @Vector(32, i16) = a_vec; - const b_wide: @Vector(32, i16) = b_vec; - const sum = a_wide + b_wide; - - const zeros: @Vector(32, i16) = @splat(0); - const ones: @Vector(32, i16) = @splat(1); - const neg_ones: @Vector(32, i16) = @splat(-1); - - const pos_mask = sum > zeros; - const neg_mask = sum < zeros; - - var out = zeros; - out = @select(i16, pos_mask, ones, out); - out = @select(i16, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - result.unpacked_cache[i + j] = @truncate(out[j]); - } - } - - while (i < len) : (i += 1) { - const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - const sum = a_trit + b_trit; - - if (sum > 0) { - result.unpacked_cache[i] = 1; - } else if (sum < 0) { - result.unpacked_cache[i] = -1; - } else { - result.unpacked_cache[i] = 0; - } - } - - return result; -} - -pub fn bundle3(a: *HybridBigInt, b: *HybridBigInt, c: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - c.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(@max(a.trit_len, b.trit_len), c.trit_len); - const min_len = @min(@min(a.trit_len, b.trit_len), c.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - // SIMD path: 32 trits at a time via i16 widening + sign extraction - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const c_vec: Vec32i8 = c.unpacked_cache[i..][0..SIMD_WIDTH].*; - - const a_wide: @Vector(32, i16) = a_vec; - const b_wide: @Vector(32, i16) = b_vec; - const c_wide: @Vector(32, i16) = c_vec; - const sum = a_wide + b_wide + c_wide; - - const zeros: @Vector(32, i16) = @splat(0); - const ones: @Vector(32, i16) = @splat(1); - const neg_ones: @Vector(32, i16) = @splat(-1); - - const pos_mask = sum > zeros; - const neg_mask = sum < zeros; - - var out = zeros; - out = @select(i16, pos_mask, ones, out); - out = @select(i16, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - result.unpacked_cache[i + j] = @truncate(out[j]); - } - } - - // Scalar remainder - while (i < len) : (i += 1) { - const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - const c_trit: i16 = if (i < c.trit_len) c.unpacked_cache[i] else 0; - const sum = a_trit + b_trit + c_trit; - - if (sum > 0) { - result.unpacked_cache[i] = 1; - } else if (sum < 0) { - result.unpacked_cache[i] = -1; - } else { - result.unpacked_cache[i] = 0; - } - } - - result.trit_len = len; - return result; -} - -pub fn cosineSimilarity(a: *const HybridBigInt, b: *const HybridBigInt) f64 { - const dot = @constCast(a).dotProduct(@constCast(b)); - const norm_a = vectorNorm(@constCast(a)); - const norm_b = vectorNorm(@constCast(b)); - - if (norm_a == 0 or norm_b == 0) return 0; - - return @as(f64, @floatFromInt(dot)) / (norm_a * norm_b); -} - -/// Cosine similarity using 16-wide f16 SIMD (2× throughput vs f32). -/// Converts ternary vectors to f16, computes similarity with 16-wide operations. -/// Returns f64 in range [-1, 1]. -pub fn cosineSimilarityF16(a: *const HybridBigInt, b: *const HybridBigInt) f64 { - @constCast(a).ensureUnpacked(); - @constCast(b).ensureUnpacked(); - - const len = @max(a.trit_len, b.trit_len); - if (len == 0) return 0; - - const F16_VEC_SIZE = 16; - const num_f16_chunks = len / F16_VEC_SIZE; - - // f32 accumulators for precision - var acc_dot: f64 = 0; - var acc_norm_a: f64 = 0; - var acc_norm_b: f64 = 0; - - // Process 16 elements at a time using f16 SIMD - var i: usize = 0; - while (i < num_f16_chunks * F16_VEC_SIZE) : (i += F16_VEC_SIZE) { - // Load trits into i8 vectors - var a_trits: @Vector(F16_VEC_SIZE, i8) = undefined; - var b_trits: @Vector(F16_VEC_SIZE, i8) = undefined; - - inline for (0..F16_VEC_SIZE) |j| { - a_trits[j] = if (i + j < a.trit_len) a.unpacked_cache[i + j] else 0; - b_trits[j] = if (i + j < b.trit_len) b.unpacked_cache[i + j] else 0; - } - - // Convert to f16 - const a_f16: @Vector(F16_VEC_SIZE, f16) = @floatCast(@as(@Vector(F16_VEC_SIZE, f32), @floatFromInt(a_trits))); - const b_f16: @Vector(F16_VEC_SIZE, f16) = @floatCast(@as(@Vector(F16_VEC_SIZE, f32), @floatFromInt(b_trits))); - - // Convert to f32 for compute - const a_f32: @Vector(F16_VEC_SIZE, f32) = @floatCast(a_f16); - const b_f32: @Vector(F16_VEC_SIZE, f32) = @floatCast(b_f16); - - // Compute dot product contribution - const prod = a_f32 * b_f32; - var sum_prod: f32 = 0; - inline for (0..F16_VEC_SIZE) |j| { - sum_prod += prod[j]; - } - acc_dot += @as(f64, sum_prod); - - // Compute norm contributions - const a_sq = a_f32 * a_f32; - const b_sq = b_f32 * b_f32; - var sum_a_sq: f32 = 0; - var sum_b_sq: f32 = 0; - inline for (0..F16_VEC_SIZE) |j| { - sum_a_sq += a_sq[j]; - sum_b_sq += b_sq[j]; - } - acc_norm_a += @as(f64, sum_a_sq); - acc_norm_b += @as(f64, sum_b_sq); - } - - // Handle scalar tail - while (i < len) : (i += 1) { - const a_trit: i8 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i8 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - - const a_f32: f32 = @floatFromInt(a_trit); - const b_f32: f32 = @floatFromInt(b_trit); - - acc_dot += @as(f64, a_f32 * b_f32); - acc_norm_a += @as(f64, a_f32 * a_f32); - acc_norm_b += @as(f64, b_f32 * b_f32); - } - - const norm_a = @sqrt(acc_norm_a); - const norm_b = @sqrt(acc_norm_b); - - if (norm_a == 0 or norm_b == 0) return 0; - - return acc_dot / (norm_a * norm_b); -} - -pub fn hammingDistance(a: *HybridBigInt, b: *HybridBigInt) usize { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var distance: usize = 0; - const len = @max(a.trit_len, b.trit_len); - const min_len = @min(a.trit_len, b.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const diff = a_vec != b_vec; - distance += @popCount(@as(u32, @bitCast(diff))); - } - - while (i < len) : (i += 1) { - const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; - if (a_trit != b_trit) distance += 1; - } - - return distance; -} - -pub fn hammingSimilarity(a: *HybridBigInt, b: *HybridBigInt) f64 { - const len = @max(a.trit_len, b.trit_len); - if (len == 0) return 1.0; - const distance = hammingDistance(a, b); - return 1.0 - @as(f64, @floatFromInt(distance)) / @as(f64, @floatFromInt(len)); -} - -pub fn dotSimilarity(a: *HybridBigInt, b: *HybridBigInt) f64 { - const dot = a.dotProduct(b); - const len = @max(a.trit_len, b.trit_len); - if (len == 0) return 0; - return @as(f64, @floatFromInt(dot)) / @as(f64, @floatFromInt(len)); -} - -/// Vector norm — SIMD accelerated via dotProduct(v, v) (OPT-001) -pub fn vectorNorm(v: *HybridBigInt) f64 { - const dot = v.dotProduct(v); - return @sqrt(@as(f64, @floatFromInt(dot))); -} - -/// Count non-zero trits — SIMD accelerated (OPT-001) -pub fn countNonZero(v: *HybridBigInt) usize { - v.ensureUnpacked(); - var count: usize = 0; - const num_full_chunks = v.trit_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const vec: Vec32i8 = v.unpacked_cache[i..][0..SIMD_WIDTH].*; - const zeros: Vec32i8 = @splat(0); - const nonzero = vec != zeros; - count += @popCount(@as(u32, @bitCast(nonzero))); - } - - while (i < v.trit_len) : (i += 1) { - if (v.unpacked_cache[i] != 0) count += 1; - } - - return count; -} - -/// Bundle N vectors — SIMD accelerated majority vote (OPT-001) -pub fn bundleN(vectors: []*HybridBigInt) HybridBigInt { - if (vectors.len == 0) return HybridBigInt.zero(); - if (vectors.len == 1) { - vectors[0].ensureUnpacked(); - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = vectors[0].trit_len; - @memcpy(result.unpacked_cache[0..vectors[0].trit_len], vectors[0].unpacked_cache[0..vectors[0].trit_len]); - return result; - } - if (vectors.len == 2) return bundle2(vectors[0], vectors[1]); - if (vectors.len == 3) return bundle3(vectors[0], vectors[1], vectors[2]); - - var max_len: usize = 0; - for (vectors) |v| { - v.ensureUnpacked(); - max_len = @max(max_len, v.trit_len); - } - - var accum: [MAX_TRITS]i16 = [_]i16{0} ** MAX_TRITS; - - for (vectors) |v| { - const num_chunks = v.trit_len / SIMD_WIDTH; - var i: usize = 0; - while (i < num_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const vec: Vec32i8 = v.unpacked_cache[i..][0..SIMD_WIDTH].*; - const wide: @Vector(32, i16) = vec; - const acc_vec: @Vector(32, i16) = accum[i..][0..SIMD_WIDTH].*; - const sum_val = acc_vec + wide; - accum[i..][0..SIMD_WIDTH].* = sum_val; - } - while (i < v.trit_len) : (i += 1) { - accum[i] += @as(i16, v.unpacked_cache[i]); - } - } - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = max_len; - - const num_result_chunks = max_len / SIMD_WIDTH; - var i: usize = 0; - while (i < num_result_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const acc_vec: @Vector(32, i16) = accum[i..][0..SIMD_WIDTH].*; - const zeros: @Vector(32, i16) = @splat(0); - const ones: @Vector(32, i16) = @splat(1); - const neg_ones: @Vector(32, i16) = @splat(-1); - - const pos_mask = acc_vec > zeros; - const neg_mask = acc_vec < zeros; - - var out = zeros; - out = @select(i16, pos_mask, ones, out); - out = @select(i16, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - result.unpacked_cache[i + j] = @truncate(out[j]); - } - } - - while (i < max_len) : (i += 1) { - if (accum[i] > 0) { - result.unpacked_cache[i] = 1; - } else if (accum[i] < 0) { - result.unpacked_cache[i] = -1; - } else { - result.unpacked_cache[i] = 0; - } - } - - return result; -} - -pub fn randomVector(len: usize, seed: u64) HybridBigInt { - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = @min(len, MAX_TRITS); - var rng = std.Random.DefaultPrng.init(seed); - const random = rng.random(); - for (0..result.trit_len) |i| { - result.unpacked_cache[i] = random.intRangeAtMost(i8, -1, 1); - } - return result; -} - -pub fn permute(v: *HybridBigInt, k: usize) HybridBigInt { - v.ensureUnpacked(); - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = v.trit_len; - if (v.trit_len == 0) return result; - const shift = k % v.trit_len; - for (0..v.trit_len) |i| { - const new_pos = (i + shift) % v.trit_len; - result.unpacked_cache[new_pos] = v.unpacked_cache[i]; - } - return result; -} - -pub fn inversePermute(v: *HybridBigInt, k: usize) HybridBigInt { - v.ensureUnpacked(); - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = v.trit_len; - if (v.trit_len == 0) return result; - const shift = k % v.trit_len; - for (0..v.trit_len) |i| { - const new_pos = (i + v.trit_len - shift) % v.trit_len; - result.unpacked_cache[new_pos] = v.unpacked_cache[i]; - } - return result; -} - -pub fn encodeSequence(items: []HybridBigInt) HybridBigInt { - if (items.len == 0) return HybridBigInt.zero(); - var result = items[0]; - for (1..items.len) |i| { - var permuted = permute(&items[i], i); - result = result.add(&permuted); - } - return result; -} - -pub fn probeSequence(sequence: *HybridBigInt, candidate: *HybridBigInt, position: usize) f64 { - var permuted = permute(candidate, position); - return cosineSimilarity(sequence, &permuted); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "cosineSimilarityF16 matches cosineSimilarity" { - var a = randomVector(100, 111); - var b = randomVector(100, 222); - - const sim_f64 = cosineSimilarity(&a, &b); - const sim_f16 = cosineSimilarityF16(&a, &b); - - // Should be very close (within f16 precision) - try std.testing.expectApproxEqAbs(sim_f64, sim_f16, 0.01); -} - -test "cosineSimilarityF16 identical vectors" { - var a = randomVector(100, 333); - - const sim = cosineSimilarityF16(&a, &a); - - // Identical vectors should have similarity 1.0 - try std.testing.expectApproxEqAbs(@as(f64, 1.0), sim, 0.01); -} - -test "cosineSimilarityF16 zero vectors" { - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - const sim = cosineSimilarityF16(&a, &b); - - // Zero vectors should return 0 - try std.testing.expectEqual(@as(f64, 0), sim); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// QUANTUM-ENHANCED VSA OPERATIONS -// Reference: [arXiv 2106.05268 VSA], [LinkedIn Kantian Vectors] -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Quantum bind: extends classical bind with phase coherence tracking -/// Maintains superposition state across operation -/// -/// In VSA framework: bind = XOR-like operation on hypervectors -/// Quantum extension: track phase information for interference -// Mutable pointers, matching bind. It would read better as *const, but bind -// calls ensureUnpacked, which fills a cache inside the value and therefore -// needs to mutate it. Declaring const here and delegating to something that -// mutates is the mismatch the compiler was reporting; making bind const would -// have been a lie about what it does. No caller exists yet, so nothing else -// moves with this. -pub fn qbind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - // Classical bind (XOR-like for balanced ternary) - const result = bind(a, b); - - // Phase tracking: maintain coherence if both inputs coherent - // Result phase = (phase_a + phase_b) mod 2π - // Coherence = coherence_a AND coherence_b - // Note: In current implementation, HybridBigInt doesn't track phase - // This is a placeholder for future phase-aware VSA - - // In full quantum extension, would track: - // - Phase information via separate struct - // - Coherence preservation logic - // - Interference effects - - return result; -} - -/// Quantum bundle: probabilistic mixture with superposition amplitudes -/// Result = Σ αᵢ|vᵢ⟩ where αᵢ = amplitudes -/// -/// In VSA framework: bundle = majority vote on hypervectors -/// Quantum extension: weighted majority using amplitudes as weights -/// This is equivalent to "quantum-inspired mixture" in hybrid architectures -pub fn qbundle(vectors: []const HybridBigInt, amplitudes: []const f32, allocator: std.mem.Allocator) !HybridBigInt { - _ = allocator; // Reserved for future allocation needs - if (vectors.len == 0) return HybridBigInt.zero(); - if (vectors.len == 1) { - const result = vectors[0]; - return result; - } - - // Validate amplitudes length - if (amplitudes.len != vectors.len) { - return error.AmplitudeLengthMismatch; - } - - // Normalize amplitudes (if not already) - var total_amp: f32 = 0.0; - for (amplitudes) |amp| total_amp += amp; - const normalized = if (total_amp > 0.0) total_amp else 1.0; - - // Use weighted bundle: each trit gets weighted votes - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = @max(MAX_TRITS, @as(usize, @intFromFloat(normalized))); - - const num_full_chunks = result.trit_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - var weighted_sum: [SIMD_WIDTH]f32 = undefined; - - for (0..SIMD_WIDTH) |j| { - var sum: f32 = 0.0; - for (vectors, 0..) |*vec, k| { - @constCast(vec).ensureUnpacked(); - if (i + j < vec.trit_len) { - const weight = amplitudes[k] / normalized; - sum += @as(f32, @floatFromInt(vec.unpacked_cache[i + j])) * weight; - } - } - weighted_sum[j] = sum; - } - - // Quantize to ternary lattice (measurement collapse) - const zeros: @Vector(32, f32) = @splat(0.0); - const ones: @Vector(32, f32) = @splat(1.0); - const neg_ones: @Vector(32, f32) = @splat(-1.0); - - const weighted_vec: @Vector(32, f32) = weighted_sum; - const pos_mask = weighted_vec > zeros; - const neg_mask = weighted_vec < zeros; - - var out = zeros; - out = @select(f32, pos_mask, ones, out); - out = @select(f32, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - const float_val: f32 = out[j]; - const int_val: i32 = @intFromFloat(float_val); - result.unpacked_cache[i + j] = @intCast(int_val); - } - } - - // Handle scalar tail - while (i < result.trit_len) : (i += 1) { - var sum: f32 = 0.0; - for (vectors, 0..) |*vec, k| { - @constCast(vec).ensureUnpacked(); - if (i < vec.trit_len) { - const weight = amplitudes[k] / normalized; - sum += @as(f32, @floatFromInt(vec.unpacked_cache[i])) * weight; - } - } - - // Threshold-based quantization (collapse) - result.unpacked_cache[i] = if (sum > 0.5) 1 else if (sum < -0.5) -1 else 0; - } - - return result; -} - -/// Measure: collapse superposition to classical ternary state -/// Samples from |ψ|² distribution (Born rule) -/// -/// In VSA framework: measurement = reading out hypervector -/// Quantum extension: probabilistic sampling based on amplitudes -/// For simplicity: deterministic collapse to quantized state -pub fn measure(qvec: *const HybridBigInt, rng: *std.Random) HybridBigInt { - _ = rng; // For future probabilistic measurement (const) - - // Born rule: P(state) = |α|² - // Sample from ternary distribution based on amplitudes - // For Trinity: collapse to quantized ternary value - - // In current implementation, HybridBigInt is already quantized - // This function returns a copy (collapsing any superposition metadata) - - const result = qvec.*; - - // In full implementation: - // 1. Extract "amplitude" from packed representation - // 2. Use threshold to decide {-1, 0, +1} - // 3. For probabilistic measurement, sample from distribution - - // For now: deterministic quantization (already collapsed) - return result; -} - -/// Quantum similarity with interference term -/// Includes phase-dependent interference: cos(phase_diff) -/// -/// In VSA framework: similarity = cosine similarity of hypervectors -/// Quantum extension: includes phase interference -/// sim_q = sim_classical × (1 + η·cos(Δφ)) -pub fn similarity_quantum(a: *const HybridBigInt, b: *const HybridBigInt, phase_diff: f32) f64 { - const classical_sim = cosineSimilarity(a, b); - - // Interference term: constructive (cos>0) or destructive (cos<0) - // η = 0.5 is interference strength - const interference = 0.5 * @cos(phase_diff); - - return classical_sim * (1.0 + interference); -} - -/// Apply phase shift to hypervector (for quantum interference) -/// Rotates the vector in the complex phase plane -// Mutable, for the same reason qbind is: it delegates to permute, which takes a -// mutable pointer because the value caches its own unpacked form. It has no -// callers yet, so nothing else moves with this. -pub fn applyPhase(vec: *HybridBigInt, phase_shift: f32, allocator: std.mem.Allocator) !HybridBigInt { - _ = allocator; // Reserved for future allocation needs - // In a full quantum VSA, this would rotate complex amplitudes - // For ternary VSA, we simulate via permute-like operation - - // Number of trit positions to shift - // @abs of an i32 is a u32, so the modulus has to be unsigned as well; it was - // an i32 and the two sides could not meet. The result is already - // non-negative, which is why the second @abs below is now redundant and - // kept only so the line reads the same as it did. - const shift_amount = @abs(@as(i32, @intFromFloat(phase_shift * 10.0))) % @as(u32, @intCast(vec.trit_len)); - - const abs_shift = shift_amount; - - if (abs_shift > 0) { - return permute(vec, @as(usize, @intCast(abs_shift))); - } - - // No shift - return copy - const result = vec.*; - return result; -} - -/// Compute quantum coherence between multiple vectors -/// Returns value [0, 1] where 1 = fully coherent -pub fn computeCoherence(vectors: []const HybridBigInt) f32 { - if (vectors.len < 2) return 1.0; - - var total_sim: f64 = 0.0; - var count: usize = 0; - - for (0..vectors.len) |i| { - for (i + 1..vectors.len) |j| { - const sim = cosineSimilarityF16(&vectors[i], &vectors[j]); - total_sim += sim; - count += 1; - } - } - - return if (count > 0) @as(f32, @floatCast(total_sim / @as(f64, @floatFromInt(count)))) else 0.0; -} - -/// Entangle two hypervectors (correlated superposition) -/// Creates a combined state that maintains correlation -pub fn entangle(a: *const HybridBigInt, b: *const HybridBigInt, correlation: f32) struct { - left: HybridBigInt, - right: HybridBigInt, -} { - // Create correlated copies based on correlation strength - // correlation ∈ [0, 1]: 0 = independent, 1 = fully entangled - - var left = a.*; - var right = b.*; - - // Apply correlation: blend some trits - if (correlation > 0 and a.trit_len == b.trit_len) { - const num_entangled = @as(usize, @intFromFloat(@as(f32, @floatFromInt(a.trit_len)) * correlation)); - - for (0..num_entangled) |i| { - const idx = i; // Simple linear mapping - if (idx < a.trit_len and idx < b.trit_len) { - // Swap some trits to create correlation - left.unpacked_cache[idx] = b.unpacked_cache[idx]; - right.unpacked_cache[idx] = a.unpacked_cache[idx]; - } - } - } - - return .{ .left = left, .right = right }; -} - -// φ² + 1/φ² = 3 | TRINITY - -// ═══════════════════════════════════════════════════════════════════════════════ -// QUANTUM VSA TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "qbundle with amplitudes" { - const a = randomVector(100, 111); - const b = randomVector(100, 222); - const c = randomVector(100, 333); - - const amplitudes = [_]f32{ 1.0, 1.0, 1.0 }; - - // Create array of vectors (pass by value for qbundle API) - var vec_slice = [_]HybridBigInt{ a, b, c }; - - const result = try qbundle(&vec_slice, &litudes, std.testing.allocator); - - // Result should be valid ternary vector - try std.testing.expect(result.trit_len > 0); - // Check first 100 trits are in valid range - const check_len = @min(100, result.trit_len); - for (0..check_len) |i| { - const trit = result.unpacked_cache[i]; - try std.testing.expect(trit >= -1 and trit <= 1); - } -} - -test "similarity_quantum with interference" { - var a = randomVector(100, 444); - var b = randomVector(100, 555); - - // Classical similarity - const sim_classical = cosineSimilarity(&a, &b); - - // Quantum similarity with constructive interference (phase_diff = 0) - const sim_constructive = similarity_quantum(&a, &b, 0.0); - - // Quantum similarity with destructive interference (phase_diff = π) - const sim_destructive = similarity_quantum(&a, &b, std.math.pi); - - // Constructive should enhance similarity - try std.testing.expect(sim_constructive >= sim_classical); - - // Destructive should reduce similarity - try std.testing.expect(sim_destructive <= sim_classical); -} - -test "computeCoherence" { - const v1 = randomVector(50, 123); - const v2 = randomVector(50, 124); - var v3 = randomVector(50, 125); - - // Set v3 to be similar to v1 - for (0..@min(v1.trit_len, v3.trit_len)) |i| { - if (i < v3.trit_len) v3.unpacked_cache[i] = v1.unpacked_cache[i]; - } - - // Create array of vectors (pass by value for computeCoherence API) - var vec_slice = [_]HybridBigInt{ v1, v2, v3 }; - - const coherence = computeCoherence(&vec_slice); - - // Should have some coherence (> 0) - try std.testing.expect(coherence > 0.0); -} - -test "entangle with correlation" { - var a = randomVector(50, 666); - var b = randomVector(50, 777); - - const fully_entangled = entangle(&a, &b, 1.0); - - // With full correlation, vectors should share trits - try std.testing.expectEqual( - a.unpacked_cache[0], - fully_entangled.right.unpacked_cache[0], - ); - - const independent = entangle(&a, &b, 0.0); - - // With zero correlation, vectors should be copies - try std.testing.expectEqual( - a.unpacked_cache[0], - independent.left.unpacked_cache[0], - ); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig deleted file mode 100644 index 46da2e1..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/fpga_bind.zig +++ /dev/null @@ -1,532 +0,0 @@ -// 🤖 TRINITY v0.11.0: Suborbital Order -// FPGA VSA Bind Interface — Week 2 -// -// Provides Zig interface to FPGA-accelerated VSA operations -// via UART communication with QMTECH XC7A100T - -const std = @import("std"); -const builtin = @import("builtin"); -const common = @import("common.zig"); -const HybridBigInt = common.HybridBigInt; -const Trit = common.Trit; - -pub const Config = struct { - /// UART device path - device: []const u8, - /// Baud rate - baud: u32 = 115200, - /// Vector dimension (must match FPGA) - dimension: usize = 256, - /// Timeout in milliseconds - timeout_ms: u32 = 5000, -}; - -pub const FPGAInterface = struct { - port: std.fs.File, - config: Config, - allocator: std.mem.Allocator, - - const Self = @This(); - - /// Initialize FPGA interface - pub fn init(config: Config, allocator: std.mem.Allocator) !Self { - const device_path = if (builtin.os.tag == .linux) - "/dev/ttyUSB0" - else if (builtin.os.tag == .macos) - "/dev/tty.usbserial-.*" - else - return error.UnsupportedOS; - - // Try to open the UART device - // OpenFlags dropped the separate .read/.write booleans for .mode. - const port = std.fs.openFileAbsolute(device_path, .{ - .mode = .read_write, - }) catch |err| { - std.log.err("Failed to open FPGA UART device: {}", .{err}); - return err; - }; - - return Self{ - .port = port, - .config = config, - .allocator = allocator, - }; - } - - /// Close FPGA interface - pub fn deinit(self: *Self) void { - self.port.close(); - } - - // ═══════════════════════════════════════════════════════════════════════ - // UART PROTOCOL - // ═══════════════════════════════════════════════════════════════════════ - - const Command = enum(u8) { - BIND = 0x01, - UNBIND = 0x02, - BUNDLE2 = 0x03, - BUNDLE3 = 0x04, - SIMILARITY = 0x05, - PING = 0xFF, - }; - - const Response = enum(u8) { - OK = 0x00, - ERROR = 0x01, - BUSY = 0x02, - PONG = 0xFF, - }; - - /// Send command to FPGA - fn sendCommand(self: *Self, cmd: Command, data: []const u8) !void { - var buffer: [1024]u8 = undefined; - var offset: usize = 0; - - buffer[offset] = @intFromEnum(cmd); - offset += 1; - - buffer[offset] = @intCast(data.len & 0xFF); - offset += 1; - - @memcpy(buffer[offset..][0..data.len], data); - offset += data.len; - - // Simple checksum - var checksum: u8 = 0; - for (buffer[0..offset]) |b| checksum ^= b; - buffer[offset] = checksum; - offset += 1; - - _ = try self.port.writeAll(buffer[0..offset]); - } - - /// Receive response from FPGA - fn recvResponse(self: *Self, expected_len: usize) ![]u8 { - _ = expected_len; - var buffer: [1024]u8 = undefined; - const header_len = 2; // status + len - - const n = try self.port.readAll(buffer[0..header_len]); - if (n < header_len) return error.ShortRead; - - const status = buffer[0]; - const len = buffer[1]; - - if (status == @intFromEnum(Response.ERROR)) { - return error.FPGAError; - } - - if (len > 0) { - const n2 = try self.port.readAll(buffer[header_len .. header_len + len]); - if (n2 < len) return error.ShortRead; - } - - // Verify checksum - // ... - - return buffer[0 .. header_len + len]; - } - - // ═══════════════════════════════════════════════════════════════════════ - // VSA OPERATIONS - // ═══════════════════════════════════════════════════════════════════════ - - /// Bind two hypervectors on FPGA - pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); - - // Pack trits into 2-bit format for FPGA - const bytes_needed = (dim * 2 + 7) / 8; - var buffer = try self.allocator.alloc(u8, bytes_needed * 2); - defer self.allocator.free(buffer); - - // Pack vector A - for (0..dim) |i| { - const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - // Pack vector B (offset by bytes_needed) - const b_offset = bytes_needed; - for (0..dim) |i| { - const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = b_offset + (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - // Send BIND command - try self.sendCommand(Command.BIND, buffer[0 .. bytes_needed * 2]); - - // Receive result - const response = try self.recvResponse(bytes_needed); - - // Unpack result - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.trit_len = dim; - - for (0..dim) |i| { - const byte_idx = 2 + (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // Same rule as the packing side: response[byte_idx] is a u8, so the - // shift amount has to be a u3. - // Typed u2 rather than left as u8: decodeTrit takes a u2, and the - // mask has already narrowed the value to two bits, so the cast - // states what the mask guarantees. - const encoded: u2 = @intCast((response[byte_idx] >> @as(u3, @intCast(bit_offset))) & 0x03); - result.unpacked_cache[i] = decodeTrit(encoded); - } - - return result; - } - - /// Check if FPGA is responsive - pub fn ping(self: *Self) !bool { - try self.sendCommand(Command.PING, &[_]u8{}); - const response = try self.recvResponse(0); - return response[0] == @intFromEnum(Response.PONG); - } - - /// Bundle two hypervectors on FPGA (majority voting) - pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); - const bytes_needed = (dim * 2 + 7) / 8; - var buffer = try self.allocator.alloc(u8, bytes_needed * 2); - defer self.allocator.free(buffer); - - // Pack vectors (same as bind) - for (0..dim) |i| { - const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - const b_offset = bytes_needed; - for (0..dim) |i| { - const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = b_offset + (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - // Send BUNDLE command - try self.sendCommand(Command.BUNDLE2, buffer[0 .. bytes_needed * 2]); - - // Receive result (same format as bind) - const response = try self.recvResponse(bytes_needed); - - // Unpack result - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.trit_len = dim; - - for (0..dim) |i| { - const byte_idx = 2 + (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // Same rule as the packing side: response[byte_idx] is a u8, so the - // shift amount has to be a u3. - // Typed u2 rather than left as u8: decodeTrit takes a u2, and the - // mask has already narrowed the value to two bits, so the cast - // states what the mask guarantees. - const encoded: u2 = @intCast((response[byte_idx] >> @as(u3, @intCast(bit_offset))) & 0x03); - result.unpacked_cache[i] = decodeTrit(encoded); - } - - return result; - } - - /// Compute dot product similarity on FPGA - pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @min(self.config.dimension, @max(a.trit_len, b.trit_len)); - const bytes_needed = (dim * 2 + 7) / 8; - var buffer = try self.allocator.alloc(u8, bytes_needed * 2); - defer self.allocator.free(buffer); - - // Pack vectors - for (0..dim) |i| { - const trit_val: i2 = if (i < a.trit_len) @intCast(a.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - const b_offset = bytes_needed; - for (0..dim) |i| { - const trit_val: i2 = if (i < b.trit_len) @intCast(b.unpacked_cache[i]) else 0; - const encoded = encodeTrit(trit_val); - const byte_idx = b_offset + (i * 2) / 8; - const bit_offset = (i * 2) % 8; - // encoded is a u2, and Zig types a shift amount by the width of what - // is being shifted: a u2 admits only a u1, which is why a bit_offset - // of 0, 2, 4 or 6 could not be used here at all. The value is going - // into a u8, so it is widened first and the amount typed to match. - buffer[byte_idx] |= @as(u8, encoded) << @as(u3, @intCast(bit_offset)); - if (bit_offset >= 6) { - buffer[byte_idx + 1] |= @as(u8, encoded) >> @as(u3, @intCast(8 - bit_offset)); - } - } - - // Send SIMILARITY command - try self.sendCommand(Command.SIMILARITY, buffer[0 .. bytes_needed * 2]); - - // Receive result (3 bytes: status + dot LSB + dot MSB) - const response = try self.recvResponse(3); - - // Parse dot product (signed 11-bit: -256 to +256) - const dot_lsb = response[1]; - const dot_msb = response[2] & 0x07; - const dot: i11 = @as(i11, @bitCast(@as(u11, @intCast(dot_msb)) << 8 | dot_lsb)); - // No manual sign extension. Zig's `if` takes a bool and `dot_msb & 0x04` - // is a u8, so this never compiled -- and the operation it was reaching - // for is already done: @bitCast from u11 to i11 reads the top bit as the - // sign, which is exactly what bit 2 of dot_msb is. The old line also - // or-ed 0xF800 into an i11, a value that does not fit in one, so it - // could not have run even if the condition had been well typed. - - // Normalize by dimension (cosine similarity for unit vectors) - // For raw similarity: just return dot / dim - return @as(f64, @floatFromInt(dot)) / @as(f64, @floatFromInt(dim)); - } - - // ═══════════════════════════════════════════════════════════════════════ - // TRIT ENCODING - // ═══════════════════════════════════════════════════════════════════════ - - /// Encode trit to 2-bit format - inline fn encodeTrit(t: i2) u2 { - return switch (t) { - 0 => 0b00, - 1 => 0b01, - -1 => 0b10, - else => unreachable, - }; - } - - /// Decode trit from 2-bit format - inline fn decodeTrit(e: u2) i2 { - return switch (e) { - 0b00 => 0, - 0b01 => 1, - 0b10 => -1, - else => 0, - }; - } -}; - -// ═════════════════════════════════════════════════════════════════════════════ -// CPU FALLBACK (when FPGA unavailable) -// ═════════════════════════════════════════════════════════════════════════════ - -pub const CpuFallback = struct { - allocator: std.mem.Allocator, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ .allocator = allocator }; - } - - /// Bind using CPU (simulates FPGA behavior) - pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - _ = self; - const core = @import("core.zig"); - return core.bind(a, b); - } - - /// Bundle using CPU - pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - _ = self; - const core = @import("core.zig"); - return core.bundle2(a, b); - } - - /// Similarity using CPU - pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { - _ = self; - const core = @import("core.zig"); - return core.cosineSimilarity(a, b); - } -}; - -// ═════════════════════════════════════════════════════════════════════════════ -// UNIFIED INTERFACE (auto-detect FPGA) -// ═════════════════════════════════════════════════════════════════════════════ - -pub const AutoVSA = struct { - fpga: ?FPGAInterface, - cpu: CpuFallback, - use_fpga: bool, - - const Self = @This(); - - /// Initialize with auto-detection - pub fn init(config: Config, allocator: std.mem.Allocator) Self { - // var, because ping takes *Self and a const binding cannot hand out the - // mutable pointer the method asks for. - var fpga = FPGAInterface.init(config, allocator) catch |err| { - std.log.warn("FPGA unavailable ({}), using CPU fallback", .{err}); - return Self{ - .fpga = null, - .cpu = CpuFallback.init(allocator), - .use_fpga = false, - }; - }; - - // Verify FPGA is responsive - if (fpga.ping() catch false) { - std.log.info("FPGA VSA accelerator detected", .{}); - return Self{ - .fpga = fpga, - .cpu = CpuFallback.init(allocator), - .use_fpga = true, - }; - } else { - fpga.deinit(); - std.log.warn("FPGA not responsive, using CPU fallback", .{}); - return Self{ - .fpga = null, - .cpu = CpuFallback.init(allocator), - .use_fpga = false, - }; - } - } - - pub fn deinit(self: *Self) void { - if (self.fpga) |*f| f.deinit(); - } - - /// Bind with automatic FPGA/CPU selection - pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - if (self.use_fpga and self.fpga != null) { - return self.fpga.?.bind(a, b); - } else { - return self.cpu.bind(a, b); - } - } - - /// Bundle with automatic FPGA/CPU selection - pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !HybridBigInt { - if (self.use_fpga and self.fpga != null) { - return self.fpga.?.bundle(a, b); - } else { - return self.cpu.bundle(a, b); - } - } - - /// Similarity with automatic FPGA/CPU selection - pub fn similarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { - if (self.use_fpga and self.fpga != null) { - return self.fpga.?.similarity(a, b); - } else { - return self.cpu.similarity(a, b); - } - } -}; - -// Backward compatibility alias -pub const AutoBind = AutoVSA; - -// ═════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═════════════════════════════════════════════════════════════════════════════ - -test "fpga bind: trit encoding" { - const testing = std.testing; - - try testing.expectEqual(@as(u2, 0b00), FPGAInterface.encodeTrit(0)); - try testing.expectEqual(@as(u2, 0b01), FPGAInterface.encodeTrit(1)); - try testing.expectEqual(@as(u2, 0b10), FPGAInterface.encodeTrit(-1)); -} - -test "fpga bind: trit decoding" { - const testing = std.testing; - - try testing.expectEqual(@as(i2, 0), FPGAInterface.decodeTrit(0b00)); - try testing.expectEqual(@as(i2, 1), FPGAInterface.decodeTrit(0b01)); - try testing.expectEqual(@as(i2, -1), FPGAInterface.decodeTrit(0b10)); -} - -test "fpga bind: cpu fallback" { - const testing = std.testing; - - var cpu = CpuFallback.init(testing.allocator); - - var a = HybridBigInt.zero(); - a.mode = .unpacked_mode; - a.trit_len = 16; - - var b = HybridBigInt.zero(); - b.mode = .unpacked_mode; - b.trit_len = 16; - - // Fill with test data - for (0..16) |i| { - // The subtraction has to happen in a signed type. i is a usize, so - // @rem(i, 3) is a usize too, and the first iteration computed 0 - 1 in - // unsigned arithmetic and panicked on the overflow. The cycle intended - // is -1, 0, +1, which needs somewhere for the -1 to live. - const idx: i8 = @intCast(@rem(i, 3)); - const idx_b: i8 = @intCast(@rem(i + 1, 3)); - a.unpacked_cache[i] = idx - 1; - b.unpacked_cache[i] = idx_b - 1; - } - - const result = try cpu.bind(&a, &b); - try testing.expectEqual(@as(usize, 16), result.trit_len); -} - -// φ² + 1/φ² = 3 = TRINITY diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig deleted file mode 100644 index 12d27fc..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_core.zig +++ /dev/null @@ -1,247 +0,0 @@ -// VSA Core — HybridBigInt Operations (GENERATED) -// Stage 2.0: SIMD-accelerated VSA with HybridBigInt -// DO NOT EDIT — Regenerate from .tri spec -// -// φ² + 1/φ² = 3 | TRINITY - -const std = @import("std"); -const hybrid = @import("hybrid.zig"); -const HybridBigInt = hybrid.HybridBigInt; -const Trit = hybrid.Trit; -const Vec32i8 = hybrid.Vec32i8; -const Vec32i16 = hybrid.Vec32i16; -const SIMD_WIDTH = hybrid.SIMD_WIDTH; -const StorageMode = hybrid.StorageMode; - -pub fn bind(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const min_len = @min(a.trit_len, b.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const prod = a_vec * b_vec; - result.unpacked_cache[i..][0..SIMD_WIDTH].* = prod; - } - - while (i < len) : (i += 1) { - const a_trit: Trit = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: Trit = if (i < b.trit_len) b.unpacked_cache[i] else 0; - result.unpacked_cache[i] = a_trit * b_trit; - } - - return result; -} - -pub fn unbind(bound: *HybridBigInt, key: *HybridBigInt) HybridBigInt { - return bind(bound, key); -} - -pub fn bundle2(a: *HybridBigInt, b: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const min_len = @min(a.trit_len, b.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - - const a_wide: Vec32i16 = a_vec; - const b_wide: Vec32i16 = b_vec; - const sum = a_wide + b_wide; - - const zeros: Vec32i16 = @splat(0); - const ones: Vec32i16 = @splat(1); - const neg_ones: Vec32i16 = @splat(-1); - - const pos_mask = sum > zeros; - const neg_mask = sum < zeros; - - var out = zeros; - out = @select(i16, pos_mask, ones, out); - out = @select(i16, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - result.unpacked_cache[i + j] = @truncate(out[j]); - } - } - - while (i < len) : (i += 1) { - const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - const sum = a_trit + b_trit; - - if (sum > 0) { - result.unpacked_cache[i] = 1; - } else if (sum < 0) { - result.unpacked_cache[i] = -1; - } else { - result.unpacked_cache[i] = 0; - } - } - - return result; -} - -pub fn bundle3(a: *HybridBigInt, b: *HybridBigInt, c: *HybridBigInt) HybridBigInt { - a.ensureUnpacked(); - b.ensureUnpacked(); - c.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - - const len = @max(@max(a.trit_len, b.trit_len), c.trit_len); - const min_len = @min(@min(a.trit_len, b.trit_len), c.trit_len); - const num_full_chunks = min_len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const c_vec: Vec32i8 = c.unpacked_cache[i..][0..SIMD_WIDTH].*; - - const a_wide: Vec32i16 = a_vec; - const b_wide: Vec32i16 = b_vec; - const c_wide: Vec32i16 = c_vec; - const sum = a_wide + b_wide + c_wide; - - const zeros: Vec32i16 = @splat(0); - const ones: Vec32i16 = @splat(1); - const neg_ones: Vec32i16 = @splat(-1); - - const pos_mask = sum > zeros; - const neg_mask = sum < zeros; - - var out = zeros; - out = @select(i16, pos_mask, ones, out); - out = @select(i16, neg_mask, neg_ones, out); - - inline for (0..SIMD_WIDTH) |j| { - result.unpacked_cache[i + j] = @truncate(out[j]); - } - } - - while (i < len) : (i += 1) { - const a_trit: i16 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i16 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - const c_trit: i16 = if (i < c.trit_len) c.unpacked_cache[i] else 0; - const sum = a_trit + b_trit + c_trit; - - if (sum > 0) { - result.unpacked_cache[i] = 1; - } else if (sum < 0) { - result.unpacked_cache[i] = -1; - } else { - result.unpacked_cache[i] = 0; - } - } - - return result; -} - -pub fn permute(v: *HybridBigInt, n: usize) HybridBigInt { - v.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = v.trit_len; - - const rotate = if (v.trit_len > 0) @mod(n, v.trit_len) else 0; - - for (0..v.trit_len) |i| { - const src_idx = if (i >= rotate) i - rotate else i + v.trit_len - rotate; - result.unpacked_cache[i] = v.unpacked_cache[src_idx]; - } - - return result; -} - -pub fn inversePermute(v: *HybridBigInt, n: usize) HybridBigInt { - v.ensureUnpacked(); - - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.dirty = true; - result.trit_len = v.trit_len; - - const rotate = if (v.trit_len > 0) @mod(n, v.trit_len) else 0; - - for (0..v.trit_len) |i| { - const src_idx = (i + rotate) % v.trit_len; - result.unpacked_cache[i] = v.unpacked_cache[src_idx]; - } - - return result; -} - -pub fn dotProduct(a: *HybridBigInt, b: *HybridBigInt) i64 { - a.ensureUnpacked(); - b.ensureUnpacked(); - - var sum: i64 = 0; - const len = @min(a.trit_len, b.trit_len); - const num_full_chunks = len / SIMD_WIDTH; - - var i: usize = 0; - while (i < num_full_chunks * SIMD_WIDTH) : (i += SIMD_WIDTH) { - const a_vec: Vec32i8 = a.unpacked_cache[i..][0..SIMD_WIDTH].*; - const b_vec: Vec32i8 = b.unpacked_cache[i..][0..SIMD_WIDTH].*; - const a_wide: Vec32i16 = a_vec; - const b_wide: Vec32i16 = b_vec; - const prod = a_wide * b_wide; - sum += @reduce(.Add, prod); - } - - while (i < len) : (i += 1) { - const a_trit: i64 = if (i < a.trit_len) a.unpacked_cache[i] else 0; - const b_trit: i64 = if (i < b.trit_len) b.unpacked_cache[i] else 0; - sum += a_trit * b_trit; - } - - return sum; -} - -pub fn vectorNorm(v: *HybridBigInt) f64 { - v.ensureUnpacked(); - - var sum: f64 = 0.0; - for (0..v.trit_len) |i| { - const t: f64 = @floatFromInt(v.unpacked_cache[i]); - sum += t * t; - } - return @sqrt(sum); -} - -pub fn cosineSimilarity(a: *const HybridBigInt, b: *const HybridBigInt) f64 { - const dot = @constCast(a).dotProduct(@constCast(b)); - const norm_a = vectorNorm(@constCast(a)); - const norm_b = vectorNorm(@constCast(b)); - - if (norm_a == 0 or norm_b == 0) return 0; - - return @as(f64, @floatFromInt(dot)) / (norm_a * norm_b); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig deleted file mode 100644 index df1d1da..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/gen_encoding.zig +++ /dev/null @@ -1,340 +0,0 @@ -//! VSA Encoding — Generated from specs/vsa/encoding.tri -//! φ² + 1/φ² = 3 | TRINITY -//! -//! DO NOT EDIT: This file is generated from encoding.tri spec -//! -//! Binary encoding for VSA vectors - -const std = @import("std"); -const Allocator = std.mem.Allocator; -const ArrayList = std.ArrayListUnmanaged; - -const common = @import("common.zig"); -const HybridBigInt = common.HybridBigInt; - -pub const Trit = i8; -pub const Vec32i8 = @Vector(32, i8); - -// ============================================================================ -// ENCODING TYPES -// ============================================================================ - -/// Encoding format for trits -pub const TritEncoding = enum(u8) { - /// Single bit per trit (neg/pos only) - one_bit, - /// Two bits per trit (balanced ternary) - two_bit, - /// Packed encoding (4 trits per byte) - packed_four, -}; - -/// Encoded trit data -pub const EncodedTrits = struct { - data: []u8, - encoding: TritEncoding, - count: usize, - - pub fn init(allocator: Allocator, encoding: TritEncoding, count: usize) !EncodedTrits { - const bits_per_trit: usize = switch (encoding) { - .one_bit => 1, - .two_bit => 2, - .packed_four => 2, - }; - const total_bits = count * bits_per_trit; - const total_bytes = (total_bits + 7) / 8; // Round up to bytes - - const data = try allocator.alloc(u8, total_bytes); - @memset(data, 0); - - return .{ - .data = data, - .encoding = encoding, - .count = count, - }; - } - - pub fn deinit(self: *EncodedTrits, allocator: Allocator) void { - allocator.free(self.data); - self.* = undefined; - } -}; - -/// Binary codebook for VSA operations -pub const Codebook = struct { - bind_table: [3][3]u8, - majority_table: [3][3]u8, - - pub fn init() Codebook { - var cb: Codebook = undefined; - - // Initialize bind table (trit multiplication) - for (0..3) |i| { - for (0..3) |j| { - const t1 = @as(i8, @intCast(i)) - 1; - const t2 = @as(i8, @intCast(j)) - 1; - const result = t1 * t2; - cb.bind_table[i][j] = @as(u8, @intCast(result + 1)); - } - } - - // Initialize majority table (3-way majority vote) - for (0..3) |i| { - for (0..3) |j| { - // Simple implementation: return first non-zero if exists, else 0 - const t1 = @as(i8, @intCast(i)) - 1; - const t2 = @as(i8, @intCast(j)) - 1; - const result = if (t1 == t2) t1 else 0; - cb.majority_table[i][j] = @as(u8, @intCast(result + 1)); - } - } - - return cb; - } - - /// Look up bind operation result - pub fn bindLookup(self: *const Codebook, a: Trit, b: Trit) Trit { - const ai = @as(usize, @intCast(a + 1)); - const bi = @as(usize, @intCast(b + 1)); - return @as(Trit, @intCast(self.bind_table[ai][bi])) - 1; - } - - /// Look up majority operation result - pub fn majorityLookup(self: *const Codebook, a: Trit, b: Trit) Trit { - const ai = @as(usize, @intCast(a + 1)); - const bi = @as(usize, @intCast(b + 1)); - return @as(Trit, @intCast(self.majority_table[ai][bi])) - 1; - } -}; - -// ============================================================================ -// ENCODING FUNCTIONS -// ============================================================================ - -/// Encode trits to binary using specified encoding -pub fn encodeTrits(allocator: Allocator, trits: []const Trit, encoding: TritEncoding) !EncodedTrits { - var encoded = try EncodedTrits.init(allocator, encoding, trits.len); - - switch (encoding) { - .one_bit => { - // Encode sign bit (0 for positive, 1 for negative, zero is 0) - for (trits, 0..) |t, i| { - const byte_idx = i / 8; - const bit_idx: u3 = @intCast(i % 8); - if (t > 0) { - encoded.data[byte_idx] &= ~(@as(u8, 1) << bit_idx); // Positive = 0 - } else if (t < 0) { - encoded.data[byte_idx] |= (@as(u8, 1) << bit_idx); // Negative = 1 - } - // Zero stays 0 - } - }, - .two_bit => { - // Encode as two bits (00=0, 01=1, 10=-1) - for (trits, 0..) |t, i| { - const byte_idx = i / 4; - const bit_offset: u3 = @intCast((i % 4) * 2); - - const encoded_val: u2 = if (t == 0) 0 else if (t == 1) 1 else 2; - encoded.data[byte_idx] |= (@as(u8, encoded_val) << bit_offset); - } - }, - .packed_four => { - // Pack 4 trits per byte (2 bits each) - for (trits, 0..) |t, i| { - const byte_idx = i / 4; - const bit_offset: u3 = @intCast((i % 4) * 2); - - const encoded_val: u2 = if (t == 0) 0 else if (t == 1) 1 else 2; - encoded.data[byte_idx] |= (@as(u8, encoded_val) << bit_offset); - } - }, - } - - return encoded; -} - -/// Decode binary to trits -pub fn decodeTrits(allocator: Allocator, encoded: *const EncodedTrits) ![]Trit { - const trits = try allocator.alloc(Trit, encoded.count); - - switch (encoded.encoding) { - .one_bit => { - for (0..encoded.count) |i| { - const byte_idx = i / 8; - const bit_idx: u3 = @intCast(i % 8); - const bit = (encoded.data[byte_idx] >> bit_idx) & 1; - trits[i] = if (bit == 0) @as(Trit, 1) else -1; - } - }, - .two_bit, .packed_four => { - for (0..encoded.count) |i| { - const byte_idx = i / 4; - const bit_offset: u3 = @intCast((i % 4) * 2); - const encoded_val = (encoded.data[byte_idx] >> bit_offset) & 0x3; - - trits[i] = switch (encoded_val) { - 0 => 0, - 1 => 1, - 2 => -1, - else => 0, - }; - } - }, - } - - return trits; -} - -/// Compute encoding size in bytes -pub fn encodingSize(count: usize, encoding: TritEncoding) usize { - const bits_per_trit: usize = switch (encoding) { - .one_bit => 1, - .two_bit => 2, - .packed_four => 2, - }; - const total_bits = count * bits_per_trit; - return (total_bits + 7) / 8; -} - -// ============================================================================ -// CODEBOOK FUNCTIONS -// ============================================================================ - -/// Global codebook instance -pub const GLOBAL_CODEBOOK = Codebook.init(); - -/// Bind using codebook lookup -pub fn codebookBind(a: Trit, b: Trit) Trit { - return GLOBAL_CODEBOOK.bindLookup(a, b); -} - -/// Majority using codebook lookup -pub fn codebookMajority(a: Trit, b: Trit) Trit { - return GLOBAL_CODEBOOK.majorityLookup(a, b); -} - -// ============================================================================ -// TESTS -// ============================================================================ - -test "VSA Encoding: EncodedTrits init" { - const allocator = std.testing.allocator; - var encoded = try EncodedTrits.init(allocator, .two_bit, 16); - defer encoded.deinit(allocator); - - try std.testing.expectEqual(@as(usize, 16), encoded.count); - try std.testing.expectEqual(TritEncoding.two_bit, encoded.encoding); -} - -test "VSA Encoding: encodeTrits two_bit" { - const allocator = std.testing.allocator; - const trits = [_]Trit{ -1, 0, 1, 0, -1 }; - - var encoded = try encodeTrits(allocator, &trits, .two_bit); - defer encoded.deinit(allocator); - - try std.testing.expectEqual(@as(usize, 5), encoded.count); -} - -test "VSA Encoding: decodeTrits two_bit" { - const allocator = std.testing.allocator; - const trits = [_]Trit{ -1, 0, 1, 0, -1 }; - - var encoded = try encodeTrits(allocator, &trits, .two_bit); - defer encoded.deinit(allocator); - - const decoded = try decodeTrits(allocator, &encoded); - defer allocator.free(decoded); - - try std.testing.expectEqualSlices(Trit, &trits, decoded); -} - -test "VSA Encoding: encodingSize" { - try std.testing.expectEqual(@as(usize, 1), encodingSize(8, .one_bit)); - try std.testing.expectEqual(@as(usize, 2), encodingSize(8, .two_bit)); - try std.testing.expectEqual(@as(usize, 2), encodingSize(8, .packed_four)); -} - -test "VSA Encoding: Codebook init" { - const cb = Codebook.init(); - - // Check bind table - try std.testing.expectEqual(@as(Trit, 1), cb.bindLookup(1, 1)); - try std.testing.expectEqual(@as(Trit, -1), cb.bindLookup(1, -1)); - try std.testing.expectEqual(@as(Trit, -1), cb.bindLookup(-1, 1)); -} - -test "VSA Encoding: codebookBind" { - try std.testing.expectEqual(@as(Trit, 1), codebookBind(1, 1)); - try std.testing.expectEqual(@as(Trit, 0), codebookBind(0, 1)); - try std.testing.expectEqual(@as(Trit, -1), codebookBind(-1, 1)); -} - -test "VSA Encoding: round trip" { - const allocator = std.testing.allocator; - const original = [_]Trit{ -1, -1, 0, 0, 1, 1, -1, 0, 1, 0, -1, 1, 0, 1, -1, 0 }; - - var encoded = try encodeTrits(allocator, &original, .two_bit); - defer encoded.deinit(allocator); - - const decoded = try decodeTrits(allocator, &encoded); - defer allocator.free(decoded); - - try std.testing.expectEqualSlices(Trit, &original, decoded); -} - -// ============================================================================ -// TEXT ENCODING STUBS (TODO: full implementation) -// ============================================================================ - -pub const TEXT_VECTOR_DIM: usize = 512; - -/// Encode single character to VSA vector (stub) -pub fn charToVector(c: u8) HybridBigInt { - // TODO: Implement proper char-to-vector encoding - // For now, convert char to ternary and store - return HybridBigInt.fromI64(@as(i64, @intCast(c))); -} - -/// Encode text to VSA vector (stub - returns hash-based vector) -pub fn encodeText(text: []const u8) HybridBigInt { - // TODO: Implement proper text encoding - // For now, use simple hash as placeholder - var hash: i64 = 0; - for (text) |c| { - hash = hash *% 31 + @as(i64, @intCast(c)); - } - return HybridBigInt.fromI64(hash); -} - -/// Decode VSA vector back to text (stub) -pub fn decodeText(vector: *const HybridBigInt, allocator: Allocator) ![]u8 { - _ = vector; // Will be used in full implementation - // TODO: Implement proper text decoding - return allocator.dupe(u8, ""); -} - -/// Encode text as words (stub) -pub fn encodeTextWords(text: []const u8, allocator: Allocator) ![]HybridBigInt { - _ = text; - // TODO: Implement word-level encoding - const result = try allocator.alloc(HybridBigInt, 1); - result[0] = encodeText(""); - return result; -} - -/// Compute similarity between two text vectors -pub fn textSimilarity(text1: []const u8, text2: []const u8) f64 { - // TODO: Implement proper text similarity - // Stub: identical texts get 1.0, otherwise 0.5 - if (std.mem.eql(u8, text1, text2)) return 1.0; - return 0.5; -} - -/// Check if two texts are similar above threshold -pub fn textsAreSimilar(text1: []const u8, text2: []const u8, threshold: f64) bool { - _ = text1; - _ = text2; - return threshold >= 0.5; // Placeholder -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig deleted file mode 100644 index 6457ed1..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/hrr.zig +++ /dev/null @@ -1,412 +0,0 @@ -//! ═══════════════════════════════════════════════════════════════════════════════ -//! HRR — Holographic Reduced Representations -//! ═══════════════════════════════════════════════════════════════════════════════ -//! -//! Vector Symbolic Architecture (VSA) using Holographic Reduced Representations. -//! High-dimensional vectors for symbolic reasoning and cognitive computing. -//! -//! Features: -//! - Random vector generation with Gaussian distribution -//! - Binding via circular convolution -//! - Unbinding (inverse binding) -//! - Bundling (superposition of vectors) -//! - Similarity (cosine distance) -//! - Vector normalization -//! -//! References: -//! - Plate, R. (1995). "Holographic Reduced Representations" -//! - Kanerva, P. (2009). "Hyperdimensional Computing" -//! -//! φ² + 1/φ² = 3 = TRINITY -//! ═══════════════════════════════════════════════════════════════════════════════ - -const std = @import("std"); -const math = std.math; -const Allocator = std.mem.Allocator; -const random = std.crypto.random; - -/// ═══════════════════════════════════════════════════════════════════════════════ -/// SACRED CONSTANTS FOR HRR -/// ═══════════════════════════════════════════════════════════════════════════════ -const PHI: f64 = 1.618033988749895; // Golden Ratio -const PHI_INV: f64 = 0.618033988749895; // φ⁻¹ - -/// ═══════════════════════════════════════════════════════════════════════════════ -/// HRR — Holographic Reduced Representations -/// ═══════════════════════════════════════════════════════════════════════════════ -pub const HRR = struct { - dim: usize, - allocator: Allocator, - - pub const Error = error{ - DimensionMismatch, - EmptyVector, - InvalidVector, - }; - - /// Initialize HRR with given dimensionality - pub fn init(allocator: Allocator, dim: usize) !HRR { - if (dim < 8) return Error.InvalidVector; - return .{ - .dim = dim, - .allocator = allocator, - }; - } - - /// Initialize with φ-based dimension (phi-powered) - pub fn initPhi(allocator: Allocator, power: u32) !HRR { - // Dimensions that are powers of φ (rounded) - const base_dim: f64 = 1000.0; - const phi_factor = std.math.pow(f64, PHI, @as(f64, @floatFromInt(power))); - const dim: usize = @intFromFloat(base_dim * phi_factor); - return init(allocator, dim); - } - - /// ═══════════════════════════════════════════════════════════════════════════════ - /// VECTOR OPERATIONS - /// ═══════════════════════════════════════════════════════════════════════════════ - /// Generate random high-dimensional vector with Gaussian distribution - pub fn randomVector(self: *const HRR) ![]f32 { - var vec = try self.allocator.alloc(f32, self.dim); - - // Generate using Box-Muller transform for Gaussian distribution - var i: usize = 0; - while (i < self.dim) : (i += 2) { - // Generate uniform random floats in (0, 1] - const u1_raw: f32 = random.float(f32); - const u2_raw: f32 = random.float(f32); - - // Avoid log(0) and ensure valid range - const u1_safe = if (u1_raw <= 0.0) 1.0e-6 else if (u1_raw >= 1.0) 0.999999 else u1_raw; - const u2_safe = if (u2_raw <= 0.0) 0.0 else if (u2_raw >= 1.0) 0.999999 else u2_raw; - - const r = @sqrt(-2.0 * @log(u1_safe)); - const theta = 2.0 * math.pi * u2_safe; - - vec[i] = r * @cos(theta); - if (i + 1 < self.dim) { - vec[i + 1] = r * @sin(theta); - } - } - - return self.normalize(vec); - } - - /// Generate deterministic vector from seed string (for encoding) - pub fn seededVector(self: *const HRR, seed: []const u8) ![]f32 { - var vec = try self.allocator.alloc(f32, self.dim); - - // Simple hash-based generation (djb2 algorithm with wrapping) - var hash: u32 = 5381; - for (seed) |c| { - hash = hash *% 33 +% @as(u8, @intCast(c)); - } - - var prng = std.Random.DefaultPrng.init(hash); - var i: usize = 0; - while (i < self.dim) : (i += 2) { - const u1_val = prng.random().float(f32); - const u2_val = prng.random().float(f32); - const u1_safe = if (u1_val > 1.0e-6) u1_val else 1.0e-6; - - const r = @sqrt(-2.0 * @log(u1_safe)); - const theta = 2.0 * math.pi * u2_val; - - vec[i] = r * @cos(theta); - if (i + 1 < self.dim) { - vec[i + 1] = r * @sin(theta); - } - } - - return self.normalize(vec); - } - - /// Bind two vectors using circular convolution - /// This creates an associative binding operation - pub fn bind(self: *const HRR, a: []const f32, b: []const f32) ![]f32 { - if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; - - const result = try self.allocator.alloc(f32, self.dim); - - // Circular convolution - // result[k] = sum(a[i] * b[(k-i) mod dim]) - for (0..self.dim) |k| { - var sum: f32 = 0; - for (0..self.dim) |i| { - const j = if (k >= i) k - i else self.dim + k - i; - sum += a[i] * b[j]; - } - result[k] = sum; - } - - return self.normalize(result); - } - - /// Unbind (inverse binding) — for HRR, inverse is the reversed vector - pub fn unbind(self: *const HRR, bound: []const f32, known: []const f32) ![]f32 { - if (bound.len != self.dim or known.len != self.dim) return Error.DimensionMismatch; - - // For HRR circular convolution, the inverse is the reversed vector - // Unbind(a ⊗ b, b) should recover a (approximately) - const result = try self.allocator.alloc(f32, self.dim); - - // Reverse the known vector (true inverse for circular convolution) - // Then convolve with bound vector - for (0..self.dim) |k| { - var sum: f32 = 0; - for (0..self.dim) |i| { - // For inverse: known_rev[j] = known[(dim - j) % dim] - const j = if (k >= i) k - i else self.dim + k - i; - const inv_idx = (self.dim - j) % self.dim; - sum += bound[i] * known[inv_idx]; - } - result[k] = sum; - } - - return self.normalize(result); - } - - /// Bundle (superposition) multiple vectors - pub fn bundle(self: *const HRR, vectors: []const []const f32) ![]f32 { - if (vectors.len == 0) return Error.EmptyVector; - - const result = try self.allocator.alloc(f32, self.dim); - @memset(result, 0); - - // Sum all vectors - for (vectors) |vec| { - if (vec.len != self.dim) return Error.DimensionMismatch; - for (result, 0..) |*r, i| { - r.* += vec[i]; - } - } - - return self.normalize(result); - } - - /// Compute cosine similarity between two vectors - pub fn similarity(self: *const HRR, a: []const f32, b: []const f32) !f32 { - if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; - - var dot: f32 = 0; - var norm_a: f32 = 0; - var norm_b: f32 = 0; - - for (a, 0..) |av, i| { - dot += av * b[i]; - norm_a += av * av; - norm_b += b[i] * b[i]; - } - - const denom = @sqrt(norm_a * norm_b); - return if (denom > 1.0e-6) dot / denom else 0; - } - - /// Normalize vector to unit length - fn normalize(_: *const HRR, vec: []f32) []f32 { - var norm: f32 = 0; - for (vec) |v| { - norm += v * v; - } - norm = @sqrt(norm); - - if (norm > 1.0e-6) { - const inv_norm = 1.0 / norm; - for (vec) |*v| { - v.* *= inv_norm; - } - } - - return vec; - } - - /// Compute Hamming distance (for binary-like comparison) - pub fn hammingDistance(self: *const HRR, a: []const f32, b: []const f32) !usize { - if (a.len != self.dim or b.len != self.dim) return Error.DimensionMismatch; - - var distance: usize = 0; - for (a, b) |av, bv| { - // Count as different if signs differ - if ((av >= 0) != (bv >= 0)) { - distance += 1; - } - } - - return distance; - } - - /// Cleanup vector - pub fn freeVector(self: *const HRR, vec: []f32) void { - self.allocator.free(vec); - } -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "HRR — Vector Generation" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec1 = try hrr.randomVector(); - defer hrr.freeVector(vec1); - - // Check dimension - try testing.expectEqual(@as(usize, 1000), vec1.len); - - // Check normalization (should be close to 1) - var norm: f32 = 0; - for (vec1) |v| { - norm += v * v; - } - try testing.expectApproxEqAbs(1.0, norm, 0.01); -} - -test "HRR — Deterministic Seeded Vector" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec1 = try hrr.seededVector("test"); - defer hrr.freeVector(vec1); - - const vec2 = try hrr.seededVector("test"); - defer hrr.freeVector(vec2); - - // Same seed should produce same vector - try testing.expectEqualSlices(f32, vec1, vec2); -} - -test "HRR — Binding Similarity" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec_a = try hrr.seededVector("alice"); - defer hrr.freeVector(vec_a); - const vec_b = try hrr.seededVector("bob"); - defer hrr.freeVector(vec_b); - - const bound = try hrr.bind(vec_a, vec_b); - defer hrr.freeVector(bound); - - // Binding should be order-independent for HRR - const bound2 = try hrr.bind(vec_b, vec_a); - defer hrr.freeVector(bound2); - - const sim = try hrr.similarity(bound, bound2); - - // Should be nearly identical - try testing.expect(sim > 0.9); -} - -test "HRR — Unbinding Recovery" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec_a = try hrr.seededVector("alice"); - defer hrr.freeVector(vec_a); - const vec_b = try hrr.seededVector("bob"); - defer hrr.freeVector(vec_b); - - const bound = try hrr.bind(vec_a, vec_b); - defer hrr.freeVector(bound); - - const recovered = try hrr.unbind(bound, vec_a); - defer hrr.freeVector(recovered); - - // Recovered should be similar to original - const sim = try hrr.similarity(vec_b, recovered); - - // Should have good similarity (unbinding is approximate) - try testing.expect(sim > 0.5); -} - -test "HRR — Bundle Orthogonality" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec1 = try hrr.seededVector("vector1"); - defer hrr.freeVector(vec1); - const vec2 = try hrr.seededVector("vector2"); - defer hrr.freeVector(vec2); - - // Bundled vector - const bundled = try hrr.bundle(&[_][]const f32{ vec1, vec2 }); - defer hrr.freeVector(bundled); - - // Similarity to individual vectors should be moderate - // (not too high, not too low) - const sim1 = try hrr.similarity(bundled, vec1); - const sim2 = try hrr.similarity(bundled, vec2); - - // Similarity should be positive but less than 1 - try testing.expect(sim1 > 0 and sim1 < 1.0); - try testing.expect(sim2 > 0 and sim2 < 1.0); -} - -test "HRR — Similarity Reflexive" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec = try hrr.randomVector(); - defer hrr.freeVector(vec); - - // Vector should be perfectly similar to itself - const sim = try hrr.similarity(vec, vec); - - try testing.expectApproxEqAbs(1.0, sim, 0.001); -} - -test "HRR — Hamming Distance" { - const testing = std.testing; - - var hrr = try HRR.init(testing.allocator, 1000); - - const vec1 = try hrr.randomVector(); - defer hrr.freeVector(vec1); - const vec2 = try hrr.randomVector(); - defer hrr.freeVector(vec2); - - // Hamming distance should be valid - const dist = try hrr.hammingDistance(vec1, vec2); - - try testing.expect(dist >= 0 and dist <= 1000); -} - -test "HRR — Phi-Dimension Initialization" { - const testing = std.testing; - - // Initialize with φ^1 = 1618 dimensions (approximately) - const hrr = try HRR.initPhi(testing.allocator, 1); - - // Should be close to 1618 - try testing.expect(hrr.dim >= 1500 and hrr.dim <= 1800); -} - -test "HRR — Sacred Constant Integration" { - const testing = std.testing; - - const hrr = try HRR.init(testing.allocator, 1000); - - // Create vectors representing sacred concepts - const phi_vec = try hrr.seededVector("phi"); - defer hrr.freeVector(phi_vec); - const trinity_vec = try hrr.seededVector("trinity"); - defer hrr.freeVector(trinity_vec); - - // Bind phi and trinity - const bound = try hrr.bind(phi_vec, trinity_vec); - defer hrr.freeVector(bound); - - // Verify binding creates a distinct representation - const sim = try hrr.similarity(phi_vec, bound); - - // Should be different but related - try testing.expect(sim < 0.9); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig deleted file mode 100644 index dcdcd6f..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa/packed_vsa.zig +++ /dev/null @@ -1,494 +0,0 @@ -// @origin(spec:packed_vsa.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity Packed VSA Operations -// VSA operation on toin and (5 andin/) -// withby lookup tables for with and withtointoand -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const packed_trit = @import("../ternary/packed_trit.zig"); -const hybrid = @import("../ternary/hybrid.zig"); -// There is no vsa.zig; bind, bundle2 and randomVector are all in the -// sibling core.zig. -const vsa = @import("core.zig"); - -const PackedBigInt = packed_trit.PackedBigInt; -const HybridBigInt = hybrid.HybridBigInt; -const Trit = packed_trit.Trit; -const TRITS_PER_BYTE = packed_trit.TRITS_PER_BYTE; -const MAX_PACKED_BYTES = packed_trit.MAX_PACKED_BYTES; - -// ═══════════════════════════════════════════════════════════════════════════════ -// LOOKUP TABLES for and on toin -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Lookup table for bind: BIND_LUT[a][b] = packed(bind(unpack(a), unpack(b))) -/// : 243 * 243 = 59049 (~58KB) -const BIND_LUT: [243][243]u8 = blk: { - @setEvalBranchQuota(1000000); - var lut: [243][243]u8 = undefined; - for (0..243) |a| { - for (0..243) |b| { - const trits_a = packed_trit.decodePack(@intCast(a)); - const trits_b = packed_trit.decodePack(@intCast(b)); - // bind = element-wise multiply - const result = [5]i8{ - trits_a[0] * trits_b[0], - trits_a[1] * trits_b[1], - trits_a[2] * trits_b[2], - trits_a[3] * trits_b[3], - trits_a[4] * trits_b[4], - }; - lut[a][b] = packed_trit.encodePack(result); - } - } - break :blk lut; -}; - -/// Lookup table for bundle2: BUNDLE_LUT[a][b] = packed(bundle(unpack(a), unpack(b))) -const BUNDLE_LUT: [243][243]u8 = blk: { - @setEvalBranchQuota(1000000); - var lut: [243][243]u8 = undefined; - for (0..243) |a| { - for (0..243) |b| { - const trits_a = packed_trit.decodePack(@intCast(a)); - const trits_b = packed_trit.decodePack(@intCast(b)); - var result: [5]i8 = undefined; - for (0..5) |i| { - const sum: i16 = @as(i16, trits_a[i]) + @as(i16, trits_b[i]); - if (sum > 0) { - result[i] = 1; - } else if (sum < 0) { - result[i] = -1; - } else { - result[i] = 0; - } - } - lut[a][b] = packed_trit.encodePack(result); - } - } - break :blk lut; -}; - -/// Lookup table for dot product: DOT_LUT[a][b] = sum of element-wise products -/// and: -5 before +5, and how u8 with withand +5 -const DOT_LUT: [243][243]u8 = blk: { - @setEvalBranchQuota(1000000); - var lut: [243][243]u8 = undefined; - for (0..243) |a| { - for (0..243) |b| { - const trits_a = packed_trit.decodePack(@intCast(a)); - const trits_b = packed_trit.decodePack(@intCast(b)); - var sum: i16 = 0; - for (0..5) |i| { - sum += @as(i16, trits_a[i]) * @as(i16, trits_b[i]); - } - // and +5 what and in u8 (and 0-10) - lut[a][b] = @intCast(@as(i16, sum) + 5); - } - } - break :blk lut; -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// PACKED VSA OPERATIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Packed bind - andwithby lookup table, withtointoand -pub fn packedBind(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { - var result = PackedBigInt.zero(); - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - - for (0..packed_len) |i| { - const a_byte = if (i < a.packedLen()) a.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); - const b_byte = if (i < b.packedLen()) b.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); - - // Lookup inwith withtointoand! - result.data[i] = BIND_LUT[a_byte][b_byte]; - } - - return result; -} - -/// Packed bundle - andwithby lookup table -pub fn packedBundle(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { - var result = PackedBigInt.zero(); - const len = @max(a.trit_len, b.trit_len); - result.trit_len = len; - - const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - - for (0..packed_len) |i| { - const a_byte = if (i < a.packedLen()) a.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); - const b_byte = if (i < b.packedLen()) b.data[i] else packed_trit.encodePack(.{ 0, 0, 0, 0, 0 }); - - result.data[i] = BUNDLE_LUT[a_byte][b_byte]; - } - - return result; -} - -/// Packed dot product - andwithby lookup table -pub fn packedDot(a: *const PackedBigInt, b: *const PackedBigInt) i64 { - const len = @min(a.trit_len, b.trit_len); - const packed_len = (len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - - var total: i64 = 0; - - for (0..packed_len) |i| { - const a_byte = a.data[i]; - const b_byte = b.data[i]; - - // Lookup returns value with withand +5 - const dot_shifted = DOT_LUT[a_byte][b_byte]; - total += @as(i64, dot_shifted) - 5; - } - - return total; -} - -/// Packed unbind - for andin unbind = bind (withon operation) -/// unbind(bind(a, b), b) = a -pub fn packedUnbind(a: *const PackedBigInt, b: *const PackedBigInt) PackedBigInt { - // andin: unbind = bind, from what: - // bind(a, b) = a * b - // unbind(a*b, b) = (a*b) * b = a * (b*b) = a * 1 = a - // (for b ∈ {-1, 1}, b*b = 1) - return packedBind(a, b); -} - -/// Packed cosine similarity -pub fn packedCosineSimilarity(a: *const PackedBigInt, b: *const PackedBigInt) f64 { - const dot_ab = packedDot(a, b); - const dot_aa = packedDot(a, a); - const dot_bb = packedDot(b, b); - - if (dot_aa == 0 or dot_bb == 0) return 0.0; - - const norm_a = @sqrt(@as(f64, @floatFromInt(dot_aa))); - const norm_b = @sqrt(@as(f64, @floatFromInt(dot_bb))); - - return @as(f64, @floatFromInt(dot_ab)) / (norm_a * norm_b); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// CONVERSION UTILITIES -// ═══════════════════════════════════════════════════════════════════════════════ - -/// inand HybridBigInt → PackedBigInt -pub fn fromHybrid(h: *HybridBigInt) PackedBigInt { - h.ensureUnpacked(); - - var result = PackedBigInt.zero(); - result.trit_len = h.trit_len; - - const packed_len = (h.trit_len + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - - for (0..packed_len) |i| { - const base = i * TRITS_PER_BYTE; - var trits: [5]i8 = .{ 0, 0, 0, 0, 0 }; - - for (0..5) |j| { - if (base + j < h.trit_len) { - trits[j] = h.unpacked_cache[base + j]; - } - } - - result.data[i] = packed_trit.encodePack(trits); - } - - return result; -} - -/// inand PackedBigInt → HybridBigInt -pub fn toHybrid(p: *const PackedBigInt) HybridBigInt { - var result = HybridBigInt.zero(); - result.mode = .unpacked_mode; - result.trit_len = p.trit_len; - result.dirty = true; - - for (0..p.trit_len) |i| { - result.unpacked_cache[i] = p.getTrit(i); - } - - return result; -} - -/// yes with toin vector -pub fn randomPackedVector(size: usize, seed: u64) PackedBigInt { - var result = PackedBigInt.zero(); - result.trit_len = size; - - var rng = std.Random.DefaultPrng.init(seed); - const random = rng.random(); - - const packed_len = (size + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE; - - for (0..packed_len) |i| { - // notand with toin (0-242) - result.data[i] = @intCast(random.intRangeAtMost(u8, 0, 242)); - } - - return result; -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "packed bind correctness" { - // yes testin into via HybridBigInt - var h_a = vsa.randomVector(100, 12345); - var h_b = vsa.randomVector(100, 67890); - - // with result (unpacked) - const ref_result = vsa.bind(&h_a, &h_b); - - // Packed version - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_result = packedBind(&p_a, &p_b); - - // Compare - for (0..100) |i| { - const ref_trit = ref_result.unpacked_cache[i]; - const packed_trit_val = packed_result.getTrit(i); - try std.testing.expectEqual(ref_trit, packed_trit_val); - } -} - -test "packed bundle correctness" { - var h_a = vsa.randomVector(100, 11111); - var h_b = vsa.randomVector(100, 22222); - - const ref_result = vsa.bundle2(&h_a, &h_b); - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_result = packedBundle(&p_a, &p_b); - - for (0..100) |i| { - try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); - } -} - -test "packed dot correctness" { - var h_a = vsa.randomVector(100, 33333); - var h_b = vsa.randomVector(100, 44444); - - // with dot product - var ref_dot: i64 = 0; - for (0..100) |i| { - ref_dot += @as(i64, h_a.unpacked_cache[i]) * @as(i64, h_b.unpacked_cache[i]); - } - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_dot_val = packedDot(&p_a, &p_b); - - try std.testing.expectEqual(ref_dot, packed_dot_val); -} - -test "packed cosine similarity" { - var h_a = vsa.randomVector(100, 55555); - var h_b = vsa.randomVector(100, 55555); // from seed = and - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - - const sim = packedCosineSimilarity(&p_a, &p_b); - try std.testing.expectApproxEqAbs(@as(f64, 1.0), sim, 0.001); -} - -test "packed unbind correctness" { - // yes in with into - const p_a = randomPackedVector(100, 12345); - const p_b = randomPackedVector(100, 67890); - - // bind(a, b) - const bound = packedBind(&p_a, &p_b); - - // unbind(bind(a, b), b) before yes vector byand on a - const unbound = packedUnbind(&bound, &p_b); - - // Check within with andon - const sim = packedCosineSimilarity(&unbound, &p_a); - - // andin within before inwithtoand - // and- in into from andand - std.debug.print("\nUnbind similarity: {d:.3}\n", .{sim}); - try std.testing.expect(sim > 0.5); // onand within -} - -test "packed unbind retrieval" { - // and with to onand - // to: bind(Paris, bind(capital_of, France)) - // with: unbind(fact, bind(Paris, capital_of)) → France - - const paris = randomPackedVector(100, hashString("Paris")); - const capital_of = randomPackedVector(100, hashString("capital_of") ^ 0xDEADBEEF); - const france = randomPackedVector(100, hashString("France")); - - // Encode to: Paris is capital_of France - const pred_obj = packedBind(&capital_of, &france); - const fact = packedBind(&paris, &pred_obj); - - // with: what is withand and? - // unbind(fact, bind(capital_of, France)) → Paris - const query_pattern = packedBind(&capital_of, &france); - const result = packedUnbind(&fact, &query_pattern); - - // Result before by on Paris - const sim_paris = packedCosineSimilarity(&result, &paris); - const sim_france = packedCosineSimilarity(&result, &france); - - std.debug.print("\nQuery result similarity to Paris: {d:.3}\n", .{sim_paris}); - std.debug.print("Query result similarity to France: {d:.3}\n", .{sim_france}); - - // Paris before more by - try std.testing.expect(sim_paris > sim_france); -} - -// Was `@import("knowledge_graph.zig").Entity` — a file that does not exist -// in this repository. It exists in gHashTag/zig-knowledge-graph, whose own -// knowledge_graph.zig imports "packed_vsa.zig", which does not exist THERE. -// One directory was split into two repositories and every relative import -// was left pointing at the sibling that stayed behind, so neither half -// compiles. -// -// The dependency was also inverted: a VSA primitive should not need a type -// from a knowledge-graph consumer. The three tests below used Entity only -// for djb2 over a string, to derive a seed. That function is reproduced -// here verbatim so the seeds — and therefore the tests — are unchanged. -fn hashString(s: []const u8) u64 { - var hash: u64 = 5381; - for (s) |c| { - hash = ((hash << 5) +% hash) +% c; - } - return hash; -} - -test "large vector bind correctness (1000 trits)" { - var h_a = vsa.randomVector(1000, 12345); - var h_b = vsa.randomVector(1000, 67890); - - const ref_result = vsa.bind(&h_a, &h_b); - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_result = packedBind(&p_a, &p_b); - - // Check each 100- and for withtowithand - var i: usize = 0; - while (i < 1000) : (i += 100) { - try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); - } -} - -test "large vector bind correctness (5000 trits)" { - var h_a = vsa.randomVector(5000, 11111); - var h_b = vsa.randomVector(5000, 22222); - - const ref_result = vsa.bind(&h_a, &h_b); - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_result = packedBind(&p_a, &p_b); - - // Check each 500- and - var i: usize = 0; - while (i < 5000) : (i += 500) { - try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); - } -} - -test "large vector bind correctness (10000 trits)" { - var h_a = vsa.randomVector(10000, 33333); - var h_b = vsa.randomVector(10000, 44444); - - const ref_result = vsa.bind(&h_a, &h_b); - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_result = packedBind(&p_a, &p_b); - - // Check each 1000- and - var i: usize = 0; - while (i < 10000) : (i += 1000) { - try std.testing.expectEqual(ref_result.unpacked_cache[i], packed_result.getTrit(i)); - } -} - -test "large vector dot correctness (10000 trits)" { - var h_a = vsa.randomVector(10000, 55555); - var h_b = vsa.randomVector(10000, 66666); - - // with dot product - var ref_dot: i64 = 0; - for (0..10000) |i| { - ref_dot += @as(i64, h_a.unpacked_cache[i]) * @as(i64, h_b.unpacked_cache[i]); - } - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - const packed_dot_val = packedDot(&p_a, &p_b); - - try std.testing.expectEqual(ref_dot, packed_dot_val); -} - -test "benchmark Packed vs Unpacked" { - // PackedBigInt supports before 12000 andin - const sizes = [_]usize{ 100, 500, 1000, 2000, 5000, 10000 }; - const iterations = 1000; - - std.debug.print("\n\n", .{}); - std.debug.print("╔═══════════════════════════════════════════════════════════════════════════════════╗\n", .{}); - std.debug.print("║ BENCHMARK: PACKED (5 trits/byte) vs UNPACKED (1 trit/byte) ║\n", .{}); - std.debug.print("╠═══════════════════════════════════════════════════════════════════════════════════╣\n", .{}); - std.debug.print("║ Size │ Unpacked │ Packed │ Speedup │ Mem Unpack│ Mem Pack │ Mem Saving ║\n", .{}); - std.debug.print("╠═══════════════════════════════════════════════════════════════════════════════════╣\n", .{}); - - for (sizes) |size| { - var h_a = vsa.randomVector(size, 12345); - var h_b = vsa.randomVector(size, 67890); - - const p_a = fromHybrid(&h_a); - const p_b = fromHybrid(&h_b); - - // Benchmark Unpacked (vsa.bind) - var timer = std.time.Timer.start() catch unreachable; - for (0..iterations) |_| { - const result = vsa.bind(&h_a, &h_b); - std.mem.doNotOptimizeAway(&result); - } - const unpacked_ns = timer.read(); - - // Benchmark Packed - timer.reset(); - for (0..iterations) |_| { - const result = packedBind(&p_a, &p_b); - std.mem.doNotOptimizeAway(&result); - } - const packed_ns = timer.read(); - - const unpacked_us = @as(f64, @floatFromInt(unpacked_ns)) / 1000.0 / @as(f64, @floatFromInt(iterations)); - const packed_us = @as(f64, @floatFromInt(packed_ns)) / 1000.0 / @as(f64, @floatFromInt(iterations)); - const speedup = unpacked_us / packed_us; - - const mem_unpacked = size; // 1 byte per trit - const mem_packed = (size + 4) / 5; // 5 trits per byte - const mem_saving = @as(f64, @floatFromInt(mem_unpacked)) / @as(f64, @floatFromInt(mem_packed)); - - std.debug.print("║ {d:5} │ {d:6.1} us │ {d:6.1} us │ {d:5.2}x │ {d:6} B │ {d:6} B │ {d:4.1}x ║\n", .{ size, unpacked_us, packed_us, speedup, mem_unpacked, mem_packed, mem_saving }); - } - - std.debug.print("╚═══════════════════════════════════════════════════════════════════════════════════╝\n", .{}); - std.debug.print("\n", .{}); - std.debug.print("Speedup > 1.0 on Packed with\n", .{}); - std.debug.print("Mem Saving bytoin toand and (5x andwithtoand towithand)\n", .{}); -} diff --git a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig b/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig deleted file mode 100644 index 819cb7c..0000000 --- a/zig-pkg/golden_float-2.1.0-h7LKhUUNCwAtKVHQ56wjridCZVwXY7_oxT2hXD9xCDdF/src/vsa_jit.zig +++ /dev/null @@ -1,688 +0,0 @@ -// @origin(spec:vsa_jit.tri) @regen(manual-impl) -// @origin(manual) @regen(pending) -// Trinity JIT-Accelerated VSA Operations -// Provides 15-260x speedup for hot paths via native code generation -// -// ⲤⲀⲔⲢⲀ ⲪⲞⲢⲘⲨⲖⲀ: V = n × 3^k × π^m × φ^p × e^q -// φ² + 1/φ² = 3 - -const std = @import("std"); -const builtin = @import("builtin"); -const jit_unified = @import("vm/jit_unified.zig"); -const hybrid = @import("ternary/hybrid.zig"); - -pub const HybridBigInt = hybrid.HybridBigInt; -pub const Trit = hybrid.Trit; -pub const MAX_TRITS = hybrid.MAX_TRITS; - -// ═══════════════════════════════════════════════════════════════════════════════ -// JIT-ACCELERATED VSA ENGINE -// ═══════════════════════════════════════════════════════════════════════════════ - -/// JIT-accelerated VSA engine with compiled function caching -pub const JitVSAEngine = struct { - allocator: std.mem.Allocator, - - // Cached JIT-compiled functions for common dimensions - dot_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - bind_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - hamming_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - cosine_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - bundle_cache: std.AutoHashMap(usize, jit_unified.JitDotFn), - - // Keep compilers alive to prevent exec_mem from being freed - compilers: std.ArrayListUnmanaged(jit_unified.UnifiedJitCompiler), - - // Statistics - jit_hits: u64 = 0, - jit_misses: u64 = 0, - total_ops: u64 = 0, - - const Self = @This(); - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .allocator = allocator, - .dot_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .bind_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .hamming_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .cosine_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .bundle_cache = std.AutoHashMap(usize, jit_unified.JitDotFn).init(allocator), - .compilers = .{}, - }; - } - - pub fn deinit(self: *Self) void { - // Clean up all compilers (which frees exec_mem) - for (self.compilers.items) |*compiler| { - compiler.deinit(); - } - self.compilers.deinit(self.allocator); - self.dot_cache.deinit(); - self.bind_cache.deinit(); - self.hamming_cache.deinit(); - self.cosine_cache.deinit(); - self.bundle_cache.deinit(); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT DOT PRODUCT - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for dot product - fn getDotFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { - if (self.dot_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Compile new function - self.jit_misses += 1; - - // Create compiler and add to list (keeps exec_mem alive) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - try compiler.compileDotProduct(dimension); - const func = try compiler.finalize(); - - try self.dot_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated dot product for HybridBigInt vectors - pub fn dotProduct(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { - self.total_ops += 1; - - // Ensure vectors are unpacked for direct memory access - a.ensureUnpacked(); - b.ensureUnpacked(); - - // Use the larger dimension - const dim = @max(a.trit_len, b.trit_len); - - // Get or compile JIT function - const func = try self.getDotFunction(dim); - - // Call JIT-compiled function directly on unpacked cache - // Cast [MAX_TRITS]Trit to *anyopaque - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - return func(a_ptr, b_ptr); - } - - /// Fallback to non-JIT dot product (for comparison) - pub fn dotProductFallback(a: *HybridBigInt, b: *HybridBigInt) i64 { - return @intCast(a.dotProduct(b)); - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT BIND - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for bind - fn getBindFunction(self: *Self, dimension: usize) !jit_unified.JitDotFn { - if (self.bind_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Compile new function - self.jit_misses += 1; - - // Create compiler and add to list (keeps exec_mem alive) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - try compiler.compileBind(dimension); - const func = try compiler.finalize(); - - try self.bind_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated bind for HybridBigInt vectors (modifies a in place) - pub fn bind(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { - self.total_ops += 1; - - // Ensure vectors are unpacked for direct memory access - a.ensureUnpacked(); - b.ensureUnpacked(); - - // Use the larger dimension - const dim = @max(a.trit_len, b.trit_len); - - // Get or compile JIT function - const func = try self.getBindFunction(dim); - - // Call JIT-compiled function (modifies a in place) - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - _ = func(a_ptr, b_ptr); - - // Mark as modified (dirty) since JIT wrote to unpacked cache - a.dirty = true; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT FUSED COSINE SIMILARITY (single-pass computation) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for fused cosine similarity - fn getCosineFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.cosine_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile fused cosine (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileFusedCosine(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to 3x dot product - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.cosine_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated cosine similarity using fused kernel (2.5x faster on ARM64) - /// cos(a,b) = dot(a,b) / sqrt(dot(a,a) * dot(b,b)) - pub fn cosineSimilarity(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !f64 { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try fused cosine kernel (ARM64 only, 2.5x faster) - if (try self.getCosineFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - - // Function returns f64 bit pattern as i64 - const result_bits = func(a_ptr, b_ptr); - return @bitCast(result_bits); - } - - // Fallback: use 3 separate JIT dot products - const dot_ab = try self.dotProduct(a, b); - const dot_aa = try self.dotProduct(a, a); - const dot_bb = try self.dotProduct(b, b); - - // Handle zero vectors - if (dot_aa == 0 or dot_bb == 0) { - return 0.0; - } - - const norm = @sqrt(@as(f64, @floatFromInt(dot_aa)) * @as(f64, @floatFromInt(dot_bb))); - return @as(f64, @floatFromInt(dot_ab)) / norm; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT HAMMING DISTANCE (count of differing positions) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for hamming distance - fn getHammingFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.hamming_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile new function (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileHamming(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to scalar - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.hamming_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated hamming distance - /// For ternary: counts positions where a[i] != b[i] - pub fn hammingDistance(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !i64 { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try JIT SIMD version (available on ARM64) - if (try self.getHammingFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - return func(a_ptr, b_ptr); - } - - // Scalar fallback - var count: i64 = 0; - for (0..dim) |i| { - if (a.unpacked_cache[i] != b.unpacked_cache[i]) { - count += 1; - } - } - return count; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // JIT BUNDLE OPERATION (n-ary addition with threshold) - // ═══════════════════════════════════════════════════════════════════════════ - - /// Get or compile JIT function for bundle operation - fn getBundleFunction(self: *Self, dimension: usize) !?jit_unified.JitDotFn { - if (self.bundle_cache.get(dimension)) |func| { - self.jit_hits += 1; - return func; - } - - // Try to compile bundle SIMD (only available on ARM64) - try self.compilers.append(self.allocator, jit_unified.UnifiedJitCompiler.init(self.allocator)); - const compiler = &self.compilers.items[self.compilers.items.len - 1]; - - compiler.compileBundleSIMD(dimension) catch |err| { - // Remove the failed compiler - _ = self.compilers.pop(); - if (err == error.UnsupportedOperation) { - return null; // Fall back to scalar - } - return err; - }; - - self.jit_misses += 1; - const func = try compiler.finalize(); - try self.bundle_cache.put(dimension, func); - return func; - } - - /// JIT-accelerated bundle operation - /// result[i] = threshold(a[i] + b[i]) where >0→1, <0→-1, =0→0 - /// Modifies 'a' in place - pub fn bundle(self: *Self, a: *HybridBigInt, b: *HybridBigInt) !void { - self.total_ops += 1; - - // Ensure vectors are unpacked - a.ensureUnpacked(); - b.ensureUnpacked(); - - const dim = @max(a.trit_len, b.trit_len); - - // Try JIT SIMD version (ARM64 only) - if (try self.getBundleFunction(dim)) |func| { - const a_ptr: *anyopaque = @ptrCast(&a.unpacked_cache); - const b_ptr: *anyopaque = @ptrCast(&b.unpacked_cache); - _ = func(a_ptr, b_ptr); - a.dirty = true; - return; - } - - // Scalar fallback - for (0..dim) |i| { - const sum: i16 = @as(i16, a.unpacked_cache[i]) + @as(i16, b.unpacked_cache[i]); - if (sum > 0) { - a.unpacked_cache[i] = 1; - } else if (sum < 0) { - a.unpacked_cache[i] = -1; - } else { - a.unpacked_cache[i] = 0; - } - } - a.dirty = true; - } - - // ═══════════════════════════════════════════════════════════════════════════ - // STATISTICS - // ═══════════════════════════════════════════════════════════════════════════ - - pub fn getStats(self: *const Self) Stats { - const total_cache = self.jit_hits + self.jit_misses; - const hit_rate = if (total_cache > 0) - @as(f64, @floatFromInt(self.jit_hits)) / @as(f64, @floatFromInt(total_cache)) * 100.0 - else - 0.0; - - return Stats{ - .total_ops = self.total_ops, - .jit_hits = self.jit_hits, - .jit_misses = self.jit_misses, - .cache_size = self.dot_cache.count() + self.bind_cache.count() + self.hamming_cache.count() + self.cosine_cache.count() + self.bundle_cache.count(), - .hit_rate = hit_rate, - }; - } - - pub fn printStats(self: *const Self) void { - const stats = self.getStats(); - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" JIT VSA ENGINE STATISTICS\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Total operations: {d}\n", .{stats.total_ops}); - std.debug.print(" JIT cache hits: {d}\n", .{stats.jit_hits}); - std.debug.print(" JIT cache misses: {d}\n", .{stats.jit_misses}); - std.debug.print(" Cache size: {d} functions\n", .{stats.cache_size}); - std.debug.print(" Hit rate: {d:.1}%\n", .{stats.hit_rate}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - } - - pub const Stats = struct { - total_ops: u64, - jit_hits: u64, - jit_misses: u64, - cache_size: usize, - hit_rate: f64, - }; -}; - -// ═══════════════════════════════════════════════════════════════════════════════ -// CONVENIENCE FUNCTIONS -// ═══════════════════════════════════════════════════════════════════════════════ - -/// Global JIT engine (thread-local for safety) -threadlocal var global_engine: ?JitVSAEngine = null; - -/// Initialize global JIT engine -pub fn initGlobal(allocator: std.mem.Allocator) void { - if (global_engine == null) { - global_engine = JitVSAEngine.init(allocator); - } -} - -/// Deinitialize global JIT engine -pub fn deinitGlobal() void { - if (global_engine) |*engine| { - engine.deinit(); - global_engine = null; - } -} - -/// JIT-accelerated dot product using global engine -pub fn jitDotProduct(allocator: std.mem.Allocator, a: *HybridBigInt, b: *HybridBigInt) !i64 { - initGlobal(allocator); - return global_engine.?.dotProduct(a, b); -} - -// ═══════════════════════════════════════════════════════════════════════════════ -// TESTS -// ═══════════════════════════════════════════════════════════════════════════════ - -test "JitVSAEngine init and deinit" { - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - try std.testing.expect(engine.total_ops == 0); -} - -test "JitVSAEngine dot product correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Create test vectors using setTrit (proper API) - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - // Simple test: all 1s dot all 1s = dimension - const test_len = 64; - - for (0..test_len) |i| { - a.setTrit(i, 1); - b.setTrit(i, 1); - } - - const expected: i64 = test_len; - - // JIT dot product - const jit_result = try engine.dotProduct(&a, &b); - - // Fallback dot product - const fallback_result = JitVSAEngine.dotProductFallback(&a, &b); - - try std.testing.expectEqual(expected, jit_result); - try std.testing.expectEqual(expected, fallback_result); -} - -test "JitVSAEngine cache hits" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - a.trit_len = 64; - b.trit_len = 64; - - // First call - cache miss - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 0), engine.jit_hits); - - // Second call - cache hit - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 1), engine.jit_hits); - - // Third call - cache hit - _ = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(@as(u64, 1), engine.jit_misses); - try std.testing.expectEqual(@as(u64, 2), engine.jit_hits); -} - -test "JitVSAEngine benchmark vs fallback" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - const dim = 1024; - const iterations = 10000; - - // Create test vectors using setTrit - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - for (0..dim) |i| { - const val_a: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); - const val_b: Trit = @intCast(@as(i32, @intCast((i + 1) % 3)) - 1); - a.setTrit(i, val_a); - b.setTrit(i, val_b); - } - - // Warm up JIT cache - _ = try engine.dotProduct(&a, &b); - - // Benchmark JIT - var timer = try std.time.Timer.start(); - var jit_result: i64 = 0; - for (0..iterations) |_| { - jit_result = try engine.dotProduct(&a, &b); - } - const jit_ns = timer.read(); - - // Benchmark fallback - timer.reset(); - var fallback_result: i64 = 0; - for (0..iterations) |_| { - fallback_result = JitVSAEngine.dotProductFallback(&a, &b); - } - const fallback_ns = timer.read(); - - // Results should match - try std.testing.expectEqual(jit_result, fallback_result); - - const jit_ms = @as(f64, @floatFromInt(jit_ns)) / 1_000_000.0; - const fallback_ms = @as(f64, @floatFromInt(fallback_ns)) / 1_000_000.0; - const speedup = fallback_ms / jit_ms; - - std.debug.print("\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" JIT VSA ENGINE BENCHMARK (HybridBigInt)\n", .{}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - std.debug.print(" Dimension: {d} trits\n", .{dim}); - std.debug.print(" Iterations: {d}\n", .{iterations}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" Fallback (HybridBigInt.dotProduct): {d:.3} ms\n", .{fallback_ms}); - std.debug.print(" JIT (NEON SIMD): {d:.3} ms\n", .{jit_ms}); - std.debug.print("───────────────────────────────────────────────────────────────\n", .{}); - std.debug.print(" SPEEDUP: {d:.2}x\n", .{speedup}); - std.debug.print("═══════════════════════════════════════════════════════════════\n", .{}); - - engine.printStats(); - - // JIT should generally be faster, but can be slower due to thermal/load - // Just verify JIT compiles and runs without crashing - if (speedup > 1.0) { - std.debug.print(" JIT is faster! ({d:.2}x speedup)\n", .{speedup}); - } else { - std.debug.print(" JIT is slower ({d:.2}x) - acceptable for flaky benchmark\n", .{speedup}); - } -} - -test "JitVSAEngine various dimensions" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - const test_dims = [_]usize{ 8, 16, 32, 64, 100, 128, 256, 512, 1000 }; - - for (test_dims) |dim| { - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - var expected: i64 = 0; - for (0..dim) |i| { - a.setTrit(i, 1); - b.setTrit(i, 1); - expected += 1; - } - - const result = try engine.dotProduct(&a, &b); - try std.testing.expectEqual(expected, result); - } - - // Should have compiled functions for each unique dimension - try std.testing.expectEqual(@as(usize, test_dims.len), engine.dot_cache.count()); -} - -test "JitVSAEngine bind correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test bind: result[i] = a[i] * b[i] (ternary multiplication) - var a = HybridBigInt.zero(); - var b = HybridBigInt.zero(); - - const dim = 16; - for (0..dim) |i| { - // Pattern: a = [1, -1, 0, 1, -1, 0, ...], b = [1, 1, 1, -1, -1, -1, ...] - const a_val: Trit = @intCast(@as(i32, @intCast(i % 3)) - 1); - const b_val: Trit = if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1); - a.setTrit(i, a_val); - b.setTrit(i, b_val); - } - - // Compute expected result - var expected = HybridBigInt.zero(); - for (0..dim) |i| { - const a_val = a.getTrit(i); - const b_val = b.getTrit(i); - expected.setTrit(i, a_val * b_val); - } - - // JIT bind - try engine.bind(&a, &b); - - // Verify result - for (0..dim) |i| { - try std.testing.expectEqual(expected.getTrit(i), a.getTrit(i)); - } -} - -test "JitVSAEngine cosine similarity correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test identical vectors: cos(a, a) = 1.0 - var a = HybridBigInt.zero(); - const dim = 64; - for (0..dim) |i| { - a.setTrit(i, 1); - } - - const cos_identical = try engine.cosineSimilarity(&a, &a); - try std.testing.expectApproxEqRel(@as(f64, 1.0), cos_identical, 0.001); - - // Test orthogonal vectors: cos(a, -a) = -1.0 - var neg_a = HybridBigInt.zero(); - for (0..dim) |i| { - neg_a.setTrit(i, -1); - } - - const cos_opposite = try engine.cosineSimilarity(&a, &neg_a); - try std.testing.expectApproxEqRel(@as(f64, -1.0), cos_opposite, 0.001); -} - -test "JitVSAEngine hamming distance correctness" { - if (!jit_unified.is_jit_supported) return; - - var engine = JitVSAEngine.init(std.testing.allocator); - defer engine.deinit(); - - // Test identical vectors: hamming(a, a) = 0 - var a = HybridBigInt.zero(); - const dim = 64; - for (0..dim) |i| { - a.setTrit(i, 1); - } - - const hamming_identical = try engine.hammingDistance(&a, &a); - try std.testing.expectEqual(@as(i64, 0), hamming_identical); - - // Test completely different vectors: hamming(a, -a) = dim - var neg_a = HybridBigInt.zero(); - for (0..dim) |i| { - neg_a.setTrit(i, -1); - } - - const hamming_opposite = try engine.hammingDistance(&a, &neg_a); - try std.testing.expectEqual(@as(i64, dim), hamming_opposite); - - // Test half different: change half the trits - var half = HybridBigInt.zero(); - for (0..dim) |i| { - half.setTrit(i, if (i < dim / 2) @as(Trit, 1) else @as(Trit, -1)); - } - - const hamming_half = try engine.hammingDistance(&a, &half); - try std.testing.expectEqual(@as(i64, dim / 2), hamming_half); -} From bc9c7d33978b93bd26735987a153112126034a96 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:45:15 +0700 Subject: [PATCH 3/6] Build the library only; the two binaries are a 0.14 API migration Correction to the previous message: these are not artefacts of my local 0.16. CI on 0.15.2 reports the same errors, so they are real. --- build.zig | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/build.zig b/build.zig index 03a5483..c7143a9 100644 --- a/build.zig +++ b/build.zig @@ -19,19 +19,16 @@ pub fn build(b: *std.Build) void { }); kg_mod.addImport("zig_golden_float", golden); - // kg_cli and kg_server both have a main(); they were never buildable either. - inline for (.{ - .{ "kg-cli", "src/kg_cli.zig" }, - .{ "kg-server", "src/kg_server.zig" }, - }) |exe_spec| { - const mod = b.createModule(.{ - .root_source_file = b.path(exe_spec[1]), - .target = target, - .optimize = optimize, - }); - mod.addImport("zig_golden_float", golden); - b.installArtifact(b.addExecutable(.{ .name = exe_spec[0], .root_module = mod })); - } + // kg_cli and kg_server are NOT built. Both were written against Zig 0.14 + // and never migrated: std.io.getStdOut is gone, std.ArrayList is unmanaged + // so .init(allocator) and one-argument .append no longer exist, and + // http.Server.init takes a reader rather than a connection. That is a + // migration across roughly 1300 lines with many call sites, and it is + // tracked separately. + // + // The library is the part other packages depend on, and it is what this + // change makes usable. Building the two binaries would keep the whole + // package unbuildable for the sake of two tools that have never run. // Each root gets its own test target. A single root would reach only what it // references, and under Zig's lazy analysis an unreferenced import is not a @@ -39,8 +36,6 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run tests"); inline for (.{ .{ "knowledge_graph", "src/knowledge_graph.zig" }, - .{ "kg_server", "src/kg_server.zig" }, - .{ "kg_cli", "src/kg_cli.zig" }, }) |t| { const tm = b.createModule(.{ .root_source_file = b.path(t[1]), From 2f0b306c310eb3230dc0627fb87182bce210c7b4 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:48:50 +0700 Subject: [PATCH 4/6] Migrate save/load to the 0.15 Io interface, and flush File.writer takes a buffer since 0.15; the tail of the file would not have reached disk without an explicit flush, so save() would have reported success on a file load() could not read. --- src/knowledge_graph.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/knowledge_graph.zig b/src/knowledge_graph.zig index 006db93..718908c 100644 --- a/src/knowledge_graph.zig +++ b/src/knowledge_graph.zig @@ -371,7 +371,11 @@ pub const KnowledgeGraph = struct { const file = try std.fs.cwd().createFile(path, .{}); defer file.close(); - var writer = file.writer(); + // Since 0.15 File.writer takes a buffer and returns a File.Writer; + // the thing with writeAll/writeInt on it is its .interface. + var write_buf: [4096]u8 = undefined; + var file_writer = file.writer(&write_buf); + const writer = &file_writer.interface; // Header try writer.writeAll(&FILE_MAGIC); @@ -429,6 +433,10 @@ pub const KnowledgeGraph = struct { try writer.writeInt(u32, graph_trit_len, .little); const graph_packed_len = (self.graph_vector.trit_len + 4) / 5; try writer.writeAll(self.graph_vector.data[0..graph_packed_len]); + + // The writer is buffered now. Without this the tail of the graph never + // reaches disk and load() fails on a file that save() reported writing. + try file_writer.interface.flush(); } /// and and file @@ -436,7 +444,9 @@ pub const KnowledgeGraph = struct { const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); - var reader = file.reader(); + var read_buf: [4096]u8 = undefined; + var file_reader = file.reader(&read_buf); + const reader = &file_reader.interface; var result = Self.init(); // Header From 5e2d4238dea5772a284489b8d8ba8c01bafe1e44 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:50:59 +0700 Subject: [PATCH 5/6] Io.Reader has readSliceAll, not readAll, since 0.15 Every call site discarded the returned count, so erroring on a short read is the same contract the code already assumed. --- src/knowledge_graph.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/knowledge_graph.zig b/src/knowledge_graph.zig index 718908c..c2faddb 100644 --- a/src/knowledge_graph.zig +++ b/src/knowledge_graph.zig @@ -451,7 +451,7 @@ pub const KnowledgeGraph = struct { // Header var magic: [4]u8 = undefined; - _ = try reader.readAll(&magic); + try reader.readSliceAll(&magic); if (!std.mem.eql(u8, &magic, &FILE_MAGIC)) { return error.InvalidFileFormat; } @@ -473,7 +473,7 @@ pub const KnowledgeGraph = struct { // and and in buffer const name_start = name_offset; - _ = try reader.readAll(name_buffer[name_offset .. name_offset + name_len]); + try reader.readSliceAll(name_buffer[name_offset .. name_offset + name_len]); name_offset += name_len; const id = try reader.readInt(u32, .little); @@ -482,7 +482,7 @@ pub const KnowledgeGraph = struct { var vec = PackedBigInt.zero(); vec.trit_len = trit_len; - _ = try reader.readAll(vec.data[0..packed_len]); + try reader.readSliceAll(vec.data[0..packed_len]); result.entities[i] = Entity{ .name = name_buffer[name_start .. name_start + name_len], @@ -497,7 +497,7 @@ pub const KnowledgeGraph = struct { const name_len = try reader.readInt(u16, .little); const name_start = name_offset; - _ = try reader.readAll(name_buffer[name_offset .. name_offset + name_len]); + try reader.readSliceAll(name_buffer[name_offset .. name_offset + name_len]); name_offset += name_len; const id = try reader.readInt(u32, .little); @@ -506,7 +506,7 @@ pub const KnowledgeGraph = struct { var vec = PackedBigInt.zero(); vec.trit_len = trit_len; - _ = try reader.readAll(vec.data[0..packed_len]); + try reader.readSliceAll(vec.data[0..packed_len]); result.relations[i] = Relation{ .name = name_buffer[name_start .. name_start + name_len], @@ -527,7 +527,7 @@ pub const KnowledgeGraph = struct { var vec = PackedBigInt.zero(); vec.trit_len = trit_len; - _ = try reader.readAll(vec.data[0..packed_len]); + try reader.readSliceAll(vec.data[0..packed_len]); result.triples[i] = Triple{ .subject_id = subject_id, @@ -542,7 +542,7 @@ pub const KnowledgeGraph = struct { const graph_trit_len = try reader.readInt(u32, .little); const graph_packed_len = (graph_trit_len + 4) / 5; result.graph_vector.trit_len = graph_trit_len; - _ = try reader.readAll(result.graph_vector.data[0..graph_packed_len]); + try reader.readSliceAll(result.graph_vector.data[0..graph_packed_len]); return result; } From 30871fa6e583567006416119202fb63e4335f2e3 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Wed, 12 Aug 2026 13:53:01 +0700 Subject: [PATCH 6/6] Io.Reader.readInt became takeInt in 0.15 --- src/knowledge_graph.zig | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/knowledge_graph.zig b/src/knowledge_graph.zig index c2faddb..3ffe4b9 100644 --- a/src/knowledge_graph.zig +++ b/src/knowledge_graph.zig @@ -456,28 +456,28 @@ pub const KnowledgeGraph = struct { return error.InvalidFileFormat; } - const version = try reader.readInt(u32, .little); + const version = try reader.takeInt(u32, .little); if (version != FILE_VERSION) { return error.UnsupportedVersion; } - const entity_count = try reader.readInt(u32, .little); - const relation_count = try reader.readInt(u32, .little); + const entity_count = try reader.takeInt(u32, .little); + const relation_count = try reader.takeInt(u32, .little); // withby buffer for and var name_offset: usize = 0; // Entities for (0..entity_count) |i| { - const name_len = try reader.readInt(u16, .little); + const name_len = try reader.takeInt(u16, .little); // and and in buffer const name_start = name_offset; try reader.readSliceAll(name_buffer[name_offset .. name_offset + name_len]); name_offset += name_len; - const id = try reader.readInt(u32, .little); - const trit_len = try reader.readInt(u32, .little); + const id = try reader.takeInt(u32, .little); + const trit_len = try reader.takeInt(u32, .little); const packed_len = (trit_len + 4) / 5; var vec = PackedBigInt.zero(); @@ -494,14 +494,14 @@ pub const KnowledgeGraph = struct { // Relations for (0..relation_count) |i| { - const name_len = try reader.readInt(u16, .little); + const name_len = try reader.takeInt(u16, .little); const name_start = name_offset; try reader.readSliceAll(name_buffer[name_offset .. name_offset + name_len]); name_offset += name_len; - const id = try reader.readInt(u32, .little); - const trit_len = try reader.readInt(u32, .little); + const id = try reader.takeInt(u32, .little); + const trit_len = try reader.takeInt(u32, .little); const packed_len = (trit_len + 4) / 5; var vec = PackedBigInt.zero(); @@ -517,12 +517,12 @@ pub const KnowledgeGraph = struct { } // Triples - const triple_count = try reader.readInt(u32, .little); + const triple_count = try reader.takeInt(u32, .little); for (0..triple_count) |i| { - const subject_id = try reader.readInt(u32, .little); - const predicate_id = try reader.readInt(u32, .little); - const object_id = try reader.readInt(u32, .little); - const trit_len = try reader.readInt(u32, .little); + const subject_id = try reader.takeInt(u32, .little); + const predicate_id = try reader.takeInt(u32, .little); + const object_id = try reader.takeInt(u32, .little); + const trit_len = try reader.takeInt(u32, .little); const packed_len = (trit_len + 4) / 5; var vec = PackedBigInt.zero(); @@ -539,7 +539,7 @@ pub const KnowledgeGraph = struct { } // Graph vector - const graph_trit_len = try reader.readInt(u32, .little); + const graph_trit_len = try reader.takeInt(u32, .little); const graph_packed_len = (graph_trit_len + 4) / 5; result.graph_vector.trit_len = graph_trit_len; try reader.readSliceAll(result.graph_vector.data[0..graph_packed_len]);