@fraczak/k 6.5.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -455,6 +455,307 @@ const htmlContent = `<!DOCTYPE html>
455
455
  margin: 4px 0 4px 16px;
456
456
  }
457
457
 
458
+ /* Tree View & Toolbar */
459
+ .k-entry-container {
460
+ display: flex;
461
+ flex-direction: column;
462
+ gap: 6px;
463
+ margin-top: 4px;
464
+ }
465
+
466
+ .k-output-toolbar {
467
+ display: flex;
468
+ align-items: center;
469
+ justify-content: space-between;
470
+ gap: 8px;
471
+ margin-bottom: 2px;
472
+ flex-wrap: wrap;
473
+ }
474
+
475
+ .k-mode-group, .k-action-group {
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 4px;
479
+ }
480
+
481
+ .k-btn-mode, .k-btn-action {
482
+ background: var(--bg-surface);
483
+ border: 1px solid var(--border-color);
484
+ color: var(--text-muted);
485
+ border-radius: 4px;
486
+ padding: 2px 8px;
487
+ font-size: 11px;
488
+ font-family: inherit;
489
+ cursor: pointer;
490
+ display: inline-flex;
491
+ align-items: center;
492
+ gap: 4px;
493
+ transition: all 0.15s ease;
494
+ }
495
+
496
+ .k-btn-mode:hover, .k-btn-action:hover {
497
+ background: var(--bg-subtle);
498
+ color: var(--text-main);
499
+ border-color: #8b949e;
500
+ }
501
+
502
+ .k-btn-mode.active {
503
+ background: rgba(88, 166, 255, 0.2);
504
+ border-color: var(--color-prompt);
505
+ color: var(--color-prompt);
506
+ font-weight: 600;
507
+ }
508
+
509
+ .k-tree-view {
510
+ display: flex;
511
+ flex-direction: column;
512
+ gap: 6px;
513
+ }
514
+
515
+ .k-tree-section {
516
+ background: rgba(22, 27, 34, 0.6);
517
+ border: 1px solid var(--border-color);
518
+ border-radius: 6px;
519
+ overflow: hidden;
520
+ }
521
+
522
+ .k-tree-section-summary {
523
+ background: rgba(33, 38, 45, 0.5);
524
+ padding: 6px 10px;
525
+ cursor: pointer;
526
+ list-style: none;
527
+ display: flex;
528
+ align-items: center;
529
+ gap: 8px;
530
+ font-size: 12px;
531
+ user-select: none;
532
+ border-bottom: 1px solid transparent;
533
+ transition: background 0.15s;
534
+ }
535
+
536
+ .k-tree-section[open] > .k-tree-section-summary {
537
+ border-bottom-color: rgba(48, 54, 61, 0.5);
538
+ }
539
+
540
+ .k-tree-section-summary::-webkit-details-marker {
541
+ display: none;
542
+ }
543
+
544
+ .k-tree-section-summary:hover {
545
+ background: rgba(33, 38, 45, 0.8);
546
+ }
547
+
548
+ .k-section-title {
549
+ font-weight: 700;
550
+ color: var(--color-prompt);
551
+ white-space: nowrap;
552
+ }
553
+
554
+ .k-section-preview {
555
+ color: var(--text-muted);
556
+ font-size: 11px;
557
+ overflow: hidden;
558
+ text-overflow: ellipsis;
559
+ white-space: nowrap;
560
+ flex: 1;
561
+ }
562
+
563
+ .k-tree-section-body {
564
+ padding: 8px 10px;
565
+ font-size: 13px;
566
+ line-height: 1.6;
567
+ overflow-x: auto;
568
+ }
569
+
570
+ /* Tree Node & Summary */
571
+ .k-tree-node {
572
+ margin-left: 12px;
573
+ border-left: 1px dashed rgba(48, 54, 61, 0.6);
574
+ padding-left: 8px;
575
+ }
576
+
577
+ .k-tree-summary {
578
+ cursor: pointer;
579
+ list-style: none;
580
+ display: inline-flex;
581
+ align-items: baseline;
582
+ gap: 6px;
583
+ outline: none;
584
+ user-select: none;
585
+ padding: 1px 4px;
586
+ border-radius: 4px;
587
+ transition: background 0.15s;
588
+ }
589
+
590
+ .k-tree-summary::-webkit-details-marker {
591
+ display: none;
592
+ }
593
+
594
+ .k-tree-summary:hover {
595
+ background: rgba(88, 166, 255, 0.1);
596
+ }
597
+
598
+ .k-tree-arrow {
599
+ display: inline-block;
600
+ font-size: 8px;
601
+ width: 10px;
602
+ height: 10px;
603
+ line-height: 10px;
604
+ text-align: center;
605
+ transition: transform 0.15s ease;
606
+ color: var(--text-muted);
607
+ }
608
+
609
+ details[open] > summary > .k-tree-arrow {
610
+ transform: rotate(90deg);
611
+ }
612
+
613
+ .k-tree-children {
614
+ display: flex;
615
+ flex-direction: column;
616
+ gap: 2px;
617
+ }
618
+
619
+ .k-tree-row {
620
+ display: flex;
621
+ align-items: baseline;
622
+ gap: 6px;
623
+ padding-left: 8px;
624
+ margin: 1px 0;
625
+ }
626
+
627
+ .k-tree-child-val, .k-tree-child-pat {
628
+ flex: 1;
629
+ }
630
+
631
+ .k-tree-leaf {
632
+ display: inline-flex;
633
+ align-items: baseline;
634
+ gap: 4px;
635
+ padding: 1px 4px;
636
+ }
637
+
638
+ /* Syntax Highlighting */
639
+ .k-val-brace, .k-pat-brace {
640
+ color: var(--color-yellow);
641
+ font-weight: 600;
642
+ }
643
+
644
+ .k-pat-union {
645
+ color: var(--color-cyan);
646
+ font-weight: 600;
647
+ }
648
+
649
+ .k-val-pipe {
650
+ color: var(--text-muted);
651
+ font-weight: 700;
652
+ }
653
+
654
+ .k-val-tag {
655
+ color: var(--color-purple);
656
+ font-weight: 600;
657
+ }
658
+
659
+ .k-pat-tag {
660
+ color: var(--color-cyan);
661
+ font-weight: 600;
662
+ }
663
+
664
+ .k-tree-field-key, .k-pat-field-key {
665
+ color: var(--color-blue);
666
+ font-weight: 600;
667
+ }
668
+
669
+ .k-val-empty, .k-pat-empty {
670
+ color: var(--text-muted);
671
+ font-style: italic;
672
+ }
673
+
674
+ .k-pat-var {
675
+ color: var(--color-green);
676
+ font-weight: 700;
677
+ }
678
+
679
+ .k-pat-vardef {
680
+ color: var(--color-green);
681
+ font-size: 11px;
682
+ font-weight: 600;
683
+ background: rgba(63, 185, 80, 0.15);
684
+ border-radius: 3px;
685
+ padding: 0 4px;
686
+ }
687
+
688
+ .k-pat-any {
689
+ color: var(--color-yellow);
690
+ font-style: italic;
691
+ }
692
+
693
+ .k-pat-ellipsis {
694
+ color: var(--text-muted);
695
+ letter-spacing: 2px;
696
+ }
697
+
698
+ .k-val-preview {
699
+ color: var(--text-muted);
700
+ font-size: 11px;
701
+ max-width: 320px;
702
+ overflow: hidden;
703
+ text-overflow: ellipsis;
704
+ white-space: nowrap;
705
+ }
706
+
707
+ .k-tree-badge {
708
+ font-size: 10px;
709
+ background: rgba(139, 148, 158, 0.15);
710
+ color: var(--text-muted);
711
+ padding: 1px 5px;
712
+ border-radius: 10px;
713
+ font-weight: 500;
714
+ }
715
+
716
+ .k-badge-cycle {
717
+ background: rgba(248, 81, 73, 0.2);
718
+ color: var(--color-red);
719
+ font-weight: 600;
720
+ }
721
+
722
+ .k-codecs-box {
723
+ background: rgba(22, 27, 34, 0.8);
724
+ border: 1px solid var(--border-color);
725
+ border-left: 3px solid var(--color-green);
726
+ border-radius: 0 6px 6px 0;
727
+ padding: 6px 12px;
728
+ display: flex;
729
+ flex-direction: column;
730
+ gap: 3px;
731
+ }
732
+
733
+ .k-codec-row {
734
+ color: var(--color-green);
735
+ font-weight: 500;
736
+ font-size: 13px;
737
+ }
738
+
739
+ .k-timing-row {
740
+ color: var(--text-muted);
741
+ font-size: 11px;
742
+ font-style: italic;
743
+ padding: 2px 4px;
744
+ }
745
+
746
+ .k-raw-pre {
747
+ background: rgba(13, 17, 23, 0.8);
748
+ border: 1px solid var(--border-color);
749
+ border-radius: 6px;
750
+ padding: 8px 12px;
751
+ font-family: inherit;
752
+ font-size: 13px;
753
+ color: var(--color-green);
754
+ white-space: pre-wrap;
755
+ word-break: break-word;
756
+ user-select: text;
757
+ }
758
+
458
759
  /* Welcome banner */
459
760
  .welcome-banner {
460
761
  padding: 16px;
@@ -74,7 +74,7 @@ const arm64OptLevel = process.env.ARM64_OPT || "-O2";
74
74
 
75
75
  // LLVM options
76
76
  const llvmWarmupIterations = parseNonNegativeIntEnv("LLVM_WARMUP_ITERATIONS", 1);
77
- const llvmRuntimeMode = process.env.K_LLVM_IEEE_RUNTIME_MODE || "compact";
77
+ const llvmRuntimeMode = process.env.K_LLVM_IEEE_RUNTIME_MODE || "fast";
78
78
 
79
79
  function getClangOptLevels() {
80
80
  for (const arg of process.argv.slice(2)) {
@@ -156,7 +156,8 @@ await evaluateInput(perfKSource, state);
156
156
 
157
157
  const ieeeRaw = fs.readFileSync(ieeePath, "utf8");
158
158
  // Exclude trailing unassigned expression and comment block to ensure clean parse
159
- const ieeeBase = ieeeRaw.split("\n").slice(0, 15584).join("\n");
159
+ const endIdx = ieeeRaw.indexOf("\n()\n");
160
+ const ieeeBase = endIdx !== -1 ? ieeeRaw.slice(0, endIdx) : ieeeRaw;
160
161
  const fullSource = `${ieeeBase}\n${perfKSource}\nperf_ieee`;
161
162
 
162
163
  console.log("==> Preparing relation perf_ieee");
@@ -375,6 +376,8 @@ if (runLLVM) {
375
376
  console.log(`==> Running LLVM (${opt}) (${iterations} iterations)...`);
376
377
  const result = await llvmRunner.run(iterations);
377
378
  llvmResults.set(opt, result);
379
+ } catch (err) {
380
+ console.log(`==> LLVM (${opt}) execution failed: ${err.message?.split("\n")[0]}`);
378
381
  } finally {
379
382
  llvmRunner.close();
380
383
  }
@@ -9,6 +9,7 @@ import {
9
9
  createState,
10
10
  decodeWire,
11
11
  evaluateInput,
12
+ executeKVM,
12
13
  exportPatternGraph,
13
14
  patternToPropertyList,
14
15
  propertyListToPattern,
@@ -38,6 +39,7 @@ import {
38
39
  runTimedIterations,
39
40
  runTimedIterationsAsync,
40
41
  shouldStrictFail,
42
+ safeDeepEqual,
41
43
  toPlainObject,
42
44
  tryCompileCase,
43
45
  wireInput,
@@ -77,6 +79,9 @@ const iterations = parsePositiveIntEnv("ITERATIONS", 3);
77
79
  // ARM64 options
78
80
  const arm64WarmupIterations = parseNonNegativeIntEnv("ARM64_WARMUP_ITERATIONS", 1);
79
81
  const arm64OptLevel = process.env.ARM64_OPT || "-O2";
82
+ if (!process.env.K_ARENA_CAPACITY_MB && !process.env.K_ARENA_MB) {
83
+ process.env.K_ARENA_CAPACITY_MB = "4096";
84
+ }
80
85
 
81
86
  // LLVM options
82
87
  const llvmWarmupIterations = parseNonNegativeIntEnv("LLVM_WARMUP_ITERATIONS", 1);
@@ -529,3 +534,329 @@ console.log(`Conformance validation: ${validationSummary.join(", ")} cases match
529
534
  if (runLLVM && shouldStrictFail(testSuite)) process.exitCode = 1;
530
535
  if (runWasm && testSuite.some(tc => tc.wasmConformance !== "ok")) process.exitCode = 1;
531
536
  if (runARM64 && testSuite.some(tc => tc.arm64Conformance !== "ok")) process.exitCode = 1;
537
+
538
+ // =========================================================================
539
+ // Squaring Scale & Memory Profiling Benchmark
540
+ // Expression: s = {()x,()y}times; 987654321 s s s s s s s s s s
541
+ // =========================================================================
542
+
543
+ function countBitsValue(val) {
544
+ let count = 0;
545
+ let cur = val?.tag === "+" || val?.tag === "-" ? val.value : val;
546
+ while (cur && (cur.tag === "0" || cur.tag === "1")) {
547
+ count++;
548
+ cur = cur.value;
549
+ }
550
+ return count;
551
+ }
552
+
553
+ function getProcessMemory(pid) {
554
+ try {
555
+ const status = fs.readFileSync(`/proc/${pid}/status`, "utf8");
556
+ const vmrss = status.match(/VmRSS:\s+(\d+)\s+kB/);
557
+ const vmhwm = status.match(/VmHWM:\s+(\d+)\s+kB/);
558
+ return {
559
+ rssMb: vmrss ? Number((parseInt(vmrss[1], 10) / 1024).toFixed(1)) : null,
560
+ peakRssMb: vmhwm ? Number((parseInt(vmhwm[1], 10) / 1024).toFixed(1)) : null
561
+ };
562
+ } catch {
563
+ return { rssMb: null, peakRssMb: null };
564
+ }
565
+ }
566
+
567
+ function getNodeMemory() {
568
+ const mem = process.memoryUsage();
569
+ return {
570
+ heapMb: Number((mem.heapUsed / (1024 * 1024)).toFixed(1)),
571
+ rssMb: Number((mem.rss / (1024 * 1024)).toFixed(1))
572
+ };
573
+ }
574
+
575
+ const squaringSteps = parsePositiveIntEnv("SQUARING_STEPS", 10);
576
+ const squaringInputText = process.env.SQUARING_INPUT || "987654321";
577
+ const squaringStepTimeoutMs = parsePositiveIntEnv("SQUARING_STEP_TIMEOUT_MS", 60000);
578
+
579
+ console.log("\n=================== SQUARING SCALE & MEMORY BENCHMARK ===================");
580
+ console.log(`Expression: s = {()x,()y}times; ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
581
+ console.log(`Evaluation: ${squaringSteps} successive squarings doubling bit length each step`);
582
+
583
+ await evaluateInput("s = {()x,()y}times;", state);
584
+ codes.load(state.codes);
585
+ const sRel = prepareRelation(state, "s", {
586
+ source: `${arithmeticsLibrarySource}\ns = {()x,()y}times;\ns`,
587
+ sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
588
+ });
589
+
590
+ const sInitialVal = intValue(squaringInputText);
591
+ const { inputWire: sInitWire, inputPattern: sInputPattern } = wireInput(sInitialVal);
592
+
593
+ // Build executables for persistent servers
594
+ let sLlvmServer = null;
595
+ if (runLLVM) {
596
+ const sLlvmCompiled = tryCompileCase({
597
+ object: sRel.object,
598
+ relationName: sRel.relationName,
599
+ relHash: sRel.relHash,
600
+ inputPattern: sInputPattern,
601
+ cacheDir,
602
+ sourceLabel: "@fraczak/k/Examples/arithmetics.k#s"
603
+ });
604
+ if (sLlvmCompiled.status === "ok") {
605
+ sLlvmServer = new PersistentExecutable(sLlvmCompiled.exePath);
606
+ }
607
+ }
608
+
609
+ let sWasmServer = null;
610
+ if (runWasm) {
611
+ try {
612
+ const sWasmPath = path.join(cacheDir, "perf_int_wasm_s.wasm");
613
+ const sWasmExe = path.join(cacheDir, "perf_int_wasm_s.exe");
614
+ const wasmBuf = await compileWasmArtifactFromObject(sRel.object, { entry: "__main__" });
615
+ fs.writeFileSync(sWasmPath, wasmBuf);
616
+ const runBin = fileURLToPath(import.meta.resolve("../backends/wasm/bin/k-wasm-run.mjs"));
617
+ fs.writeFileSync(sWasmExe, `#!/bin/sh\nexec node "${runBin}" "${sWasmPath}" "$@"\n`, { mode: 0o755 });
618
+ sWasmServer = new PersistentExecutable(sWasmExe);
619
+ } catch (e) {
620
+ console.log("Failed to build Wasm executable for 's':", e.message);
621
+ }
622
+ }
623
+
624
+ let sArm64Server = null;
625
+ if (runARM64) {
626
+ try {
627
+ const sArm64Exe = path.join(cacheDir, "perf_int_arm64_s");
628
+ compileARM64ArtifactFromObject(sRel.object, {
629
+ entry: "__main__",
630
+ outputPath: sArm64Exe,
631
+ optLevel: arm64OptLevel
632
+ });
633
+ sArm64Server = new PersistentExecutable(sArm64Exe);
634
+ } catch (e) {
635
+ console.log("Failed to build ARM64 executable for 's':", e.message);
636
+ }
637
+ }
638
+
639
+ const squaringLanes = [];
640
+ if (runBaselines) {
641
+ squaringLanes.push({ id: "jsAware", name: "Native JS (Aware)", type: "jsAware", memLabel: "heap" });
642
+ squaringLanes.push({ id: "jsFree", name: "Native JS (Free)", type: "jsFree", memLabel: "heap" });
643
+ squaringLanes.push({ id: "kvm", name: "kVM (Env-Free)", type: "kvm", memLabel: "heap" });
644
+ }
645
+ if (runLLVM && sLlvmServer) {
646
+ squaringLanes.push({ id: "llvm", name: llvmLaneName(), type: "persistent", server: sLlvmServer, memLabel: "peak RSS" });
647
+ }
648
+ if (runWasm && sWasmServer) {
649
+ squaringLanes.push({ id: "wasm", name: wasmLaneName(), type: "persistent", server: sWasmServer, memLabel: "peak RSS" });
650
+ }
651
+ if (runARM64 && sArm64Server) {
652
+ squaringLanes.push({ id: "arm64", name: arm64LaneName(arm64OptLevel), type: "persistent", server: sArm64Server, memLabel: "peak RSS" });
653
+ }
654
+
655
+ const squaringResults = {};
656
+ for (const lane of squaringLanes) squaringResults[lane.id] = [];
657
+ const stepMetadata = [];
658
+
659
+ try {
660
+ // 1. Persistent child processes (ARM64, Wasm, LLVM)
661
+ for (const lane of squaringLanes.filter(l => l.type === "persistent")) {
662
+ console.log(`==> Running ${lane.name} squaring lane...`);
663
+ let curWire = sInitWire;
664
+ for (let step = 1; step <= squaringSteps; step++) {
665
+ try {
666
+ const t0 = performance.now();
667
+ curWire = await lane.server.request(curWire);
668
+ const t1 = performance.now();
669
+ const timeMs = t1 - t0;
670
+ const mem = getProcessMemory(lane.server.child.pid);
671
+ squaringResults[lane.id].push({ status: "ok", timeMs, memMb: mem.peakRssMb, wire: curWire });
672
+ if (!stepMetadata[step - 1]) {
673
+ stepMetadata[step - 1] = {
674
+ wireLen: curWire.length,
675
+ bits: null,
676
+ canonicalWire: curWire
677
+ };
678
+ }
679
+ if (timeMs > squaringStepTimeoutMs) break;
680
+ } catch (e) {
681
+ squaringResults[lane.id].push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
682
+ break;
683
+ }
684
+ }
685
+ }
686
+
687
+ // 2. In-process baselines (JS Aware, JS Free, kVM)
688
+ if (runBaselines) {
689
+ // JS Aware
690
+ console.log("==> Running Native JS (Envelope-Aware) squaring lane...");
691
+ let curValAware = sInitialVal;
692
+ run.defs = state;
693
+ for (let step = 1; step <= squaringSteps; step++) {
694
+ try {
695
+ const t0 = performance.now();
696
+ curValAware = run(codes.find, sRel.relDef.def, curValAware, sRel.relDef.typePatternGraph);
697
+ const t1 = performance.now();
698
+ const timeMs = t1 - t0;
699
+ const mem = getNodeMemory();
700
+ squaringResults.jsAware.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValAware });
701
+ if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
702
+ stepMetadata[step - 1].bits = countBitsValue(curValAware);
703
+ }
704
+ if (timeMs > squaringStepTimeoutMs) break;
705
+ } catch (e) {
706
+ squaringResults.jsAware.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
707
+ break;
708
+ }
709
+ }
710
+
711
+ // JS Free
712
+ console.log("==> Running Native JS (Envelope-Free) squaring lane...");
713
+ let curValFree = sInitialVal;
714
+ run_converged.defs = state;
715
+ for (let step = 1; step <= squaringSteps; step++) {
716
+ try {
717
+ const t0 = performance.now();
718
+ curValFree = run_converged(codes.find, sRel.relDef.def, curValFree, sRel.relDef.typePatternGraph);
719
+ const t1 = performance.now();
720
+ const timeMs = t1 - t0;
721
+ const mem = getNodeMemory();
722
+ squaringResults.jsFree.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValFree });
723
+ if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
724
+ stepMetadata[step - 1].bits = countBitsValue(curValFree);
725
+ }
726
+ if (timeMs > squaringStepTimeoutMs) break;
727
+ } catch (e) {
728
+ squaringResults.jsFree.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
729
+ break;
730
+ }
731
+ }
732
+
733
+ // kVM
734
+ console.log("==> Running kVM Interpreter (Env-Free) squaring lane...");
735
+ let curValKvm = sInitialVal;
736
+ const kvmContext = { rels: state.rels, findCode: codes.find, options: { envelopeFree: true } };
737
+ for (let step = 1; step <= squaringSteps; step++) {
738
+ try {
739
+ const t0 = performance.now();
740
+ curValKvm = executeKVM(sRel.kvmFunc, curValKvm, kvmContext);
741
+ const t1 = performance.now();
742
+ const timeMs = t1 - t0;
743
+ const mem = getNodeMemory();
744
+ squaringResults.kvm.push({ status: "ok", timeMs, memMb: mem.heapMb, value: curValKvm });
745
+ if (stepMetadata[step - 1] && stepMetadata[step - 1].bits == null) {
746
+ stepMetadata[step - 1].bits = countBitsValue(curValKvm);
747
+ }
748
+ if (timeMs > squaringStepTimeoutMs) break;
749
+ } catch (e) {
750
+ squaringResults.kvm.push({ status: "failed", error: e.message?.split("\n")[0] || String(e) });
751
+ break;
752
+ }
753
+ }
754
+ }
755
+
756
+ } finally {
757
+ if (sArm64Server) sArm64Server.close();
758
+ if (sWasmServer) sWasmServer.close();
759
+ if (sLlvmServer) sLlvmServer.close();
760
+ }
761
+
762
+ // For any remaining steps without bit counts, decode canonicalWire safely to get bits:
763
+ for (let step = 1; step <= squaringSteps; step++) {
764
+ const meta = stepMetadata[step - 1];
765
+ if (meta && meta.bits == null && meta.canonicalWire) {
766
+ try {
767
+ const decoded = decodeWire(meta.canonicalWire).value;
768
+ meta.bits = countBitsValue(decoded);
769
+ } catch {}
770
+ }
771
+ }
772
+
773
+ // Display results table
774
+ console.log("\n======================== SQUARING SCALING & MEMORY BENCHMARK ========================");
775
+ console.log(`Expression: s = {()x,()y}times; ${squaringInputText} ${"s ".repeat(squaringSteps).trim()}`);
776
+ console.log(`Initial: ${squaringInputText} (${countBitsValue(sInitialVal)} bits)`);
777
+ console.log("----------------------------------------------------------------------------------------------------------------------------------");
778
+ const headerCols = ["Step", "Bits", "Wire(B)", ...squaringLanes.map(l => `${l.name} (${l.memLabel})`)];
779
+ const colWidths = [4, 6, 7, ...squaringLanes.map(l => Math.max(22, l.name.length + l.memLabel.length + 5))];
780
+ console.log(headerCols.map((h, i) => i < 3 ? h.padStart(colWidths[i]) : h.padEnd(colWidths[i])).join(" | "));
781
+ console.log(colWidths.map(w => "-".repeat(w)).join("-|-"));
782
+
783
+ for (let step = 1; step <= squaringSteps; step++) {
784
+ const meta = stepMetadata[step - 1] || { bits: "?", wireLen: "?" };
785
+ const cols = [
786
+ String(step).padStart(colWidths[0]),
787
+ String(meta.bits ?? "?").padStart(colWidths[1]),
788
+ String(meta.wireLen ?? "?").padStart(colWidths[2])
789
+ ];
790
+ for (let li = 0; li < squaringLanes.length; li++) {
791
+ const lane = squaringLanes[li];
792
+ const res = squaringResults[lane.id][step - 1];
793
+ const w = colWidths[3 + li];
794
+ if (!res) {
795
+ cols.push("-".padEnd(w));
796
+ } else if (res.status === "ok") {
797
+ const t = res.timeMs < 10 ? `${res.timeMs.toFixed(2)}ms` : `${res.timeMs.toFixed(1)}ms`;
798
+ const m = res.memMb != null ? `${res.memMb}MB` : "";
799
+ cols.push(`${t} / ${m}`.padEnd(w));
800
+ } else {
801
+ let errText = res.error;
802
+ if (errText.includes("Maximum call stack")) {
803
+ errText = "Stack overflow (V8)";
804
+ } else if (errText.includes("status null") || errText.includes("failed with status")) {
805
+ errText = "Arena limit reached";
806
+ }
807
+ cols.push(errText.slice(0, w - 1).padEnd(w));
808
+ }
809
+ }
810
+ console.log(cols.join(" | "));
811
+ }
812
+ console.log("==================================================================================================================================");
813
+
814
+ // Conformance check across all computed steps
815
+ let squaringConformanceOk = true;
816
+ for (let step = 1; step <= squaringSteps; step++) {
817
+ const meta = stepMetadata[step - 1];
818
+ if (!meta?.canonicalWire) continue;
819
+ for (const lane of squaringLanes) {
820
+ const res = squaringResults[lane.id][step - 1];
821
+ if (res?.status === "ok") {
822
+ if (res.wire) {
823
+ if (Buffer.compare(res.wire, meta.canonicalWire) !== 0) {
824
+ squaringConformanceOk = false;
825
+ console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
826
+ }
827
+ } else if (res.value) {
828
+ try {
829
+ const wireVal = decodeWire(meta.canonicalWire).value;
830
+ if (!safeDeepEqual(res.value, wireVal)) {
831
+ squaringConformanceOk = false;
832
+ console.log(`Squaring conformance mismatch on step ${step} (${lane.name})`);
833
+ }
834
+ } catch {}
835
+ }
836
+ }
837
+ }
838
+ }
839
+
840
+ console.log("\nSquaring Performance & Scaling Summary:");
841
+ for (const lane of squaringLanes) {
842
+ const okSteps = squaringResults[lane.id].filter(r => r.status === "ok");
843
+ const maxStep = okSteps.length;
844
+ const lastOk = okSteps[okSteps.length - 1];
845
+ const peakMem = okSteps.reduce((max, r) => Math.max(max, r.memMb || 0), 0);
846
+ const failureReason = squaringResults[lane.id][maxStep]?.error;
847
+ let statusDetail = "";
848
+ if (failureReason) {
849
+ if (failureReason.includes("Maximum call stack")) statusDetail = " (V8 call stack overflow)";
850
+ else if (failureReason.includes("status null") || failureReason.includes("failed with status")) statusDetail = " (arena capacity limit reached)";
851
+ else statusDetail = ` (${failureReason.slice(0, 30)})`;
852
+ }
853
+ const timeStr = lastOk ? ` (step ${maxStep} in ${lastOk.timeMs < 10 ? lastOk.timeMs.toFixed(2) : lastOk.timeMs.toFixed(1)}ms)` : "";
854
+ console.log(` - ${lane.name.padEnd(32)}: reached Step ${String(maxStep).padStart(2)}/${squaringSteps}${timeStr}, peak ${lane.memLabel}: ${peakMem}MB${statusDetail}`);
855
+ }
856
+
857
+ if (squaringConformanceOk) {
858
+ console.log("\nSquaring Conformance: All completed squaring steps match expected values across all backends!\n");
859
+ } else {
860
+ console.log("\nSquaring Conformance: Warning, some step outputs differed between backends!\n");
861
+ process.exitCode = 1;
862
+ }
@@ -26,6 +26,7 @@ const tests = [
26
26
  ["conformance/run.mjs", node, ["conformance/run.mjs"]],
27
27
  ["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
28
28
  ["tests/test-repl-html.mjs", node, ["tests/test-repl-html.mjs"]],
29
+ ["tests/test-tree-view.mjs", node, ["tests/test-tree-view.mjs"]],
29
30
  ["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
30
31
  ["tests/test-kvm-polymorphic.mjs", node, ["tests/test-kvm-polymorphic.mjs"]],
31
32
  ["tests/test-arithmetics.mjs", node, ["tests/test-arithmetics.mjs"]],