metanum 2.0.0 → 2.0.2

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.
package/test_mixed.js CHANGED
@@ -1,4 +1,4 @@
1
- if (!this.MetaNum) MetaNum = require("./metanum.js");
1
+ if (!this.MetaNum) MetaNum = require("./metanum.js");
2
2
  if (!this.format) format = require("./format-metanum.js").format;
3
3
  //if (!this.FORMAT_OPTIONS) FORMAT_OPTIONS = require("./format-metanum.js").FORMAT_OPTIONS;
4
4
 
@@ -244,9 +244,7 @@ checkOp("pent_root semantic b=3", MetaNum.pentate(2,3).pentate_root(3), 2, 0.1);
244
244
  checkOp("pent_log small", MetaNum(1e10).pentate_log(2), 2.795, 0.1);
245
245
  checkOp("pent_root small", MetaNum(1e10).pentate_root(2), 2.956, 0.1);
246
246
 
247
- // ─────────────────────────────────────
248
- // hyper_log / hyper_root (a{b}c = d inverses)
249
- // ─────────────────────────────────────
247
+ console.log("\n=== hyper_log/root ===");
250
248
  function checkHL(label, d, a, b, want) {
251
249
  checkBool("hyper_log " + label, MetaNum(d).hyper_log(a)(b).eq(want), true);
252
250
  }
@@ -272,7 +270,7 @@ checkHR("10↑↑↑3, c=3, b=3", MetaNum(10).arrow(3)(3), 3, 3, 10);
272
270
  checkHR("2{4}3, c=3, b=4", MetaNum(2).arrow(4)(3), 3, 4, 2);
273
271
 
274
272
  //hyperoperation definition from https://googology.fandom.com/wiki/Template:ExtendedOps
275
-
273
+ console.log("\n=== hyperoperation definition ===")
276
274
  // ─── 1. aperiote (ω): x{ω}y = x{y}x (rule 3: n{λ}b = n{λ[b]}n)
277
275
  // rule 3: x{ω}y = x{ω[y]}x = x{y}x (final operand is the base)
278
276
  // aperiote(3,0): 3{0}3 = 3*3 = 9
@@ -515,20 +513,13 @@ checkOp("epsl(3,4)", m3.epsilonate(4));
515
513
  checkOp("epsl(4,3)", m4.epsilonate(3));
516
514
  checkBool("epsl NaN", MetaNum.epsilonate(m3, MetaNum.NaN).isNaN(), true);
517
515
 
518
- // ─── 34b. ω^ω … ε₀ by the definition (rule 3: n{λ}b = n{λ[b]}n) ───
519
- // iterate(3,5) = 3{ω^ω}5 = 3{ω^5}3 → the whole fundamental-sequence cascade;
520
- // its top row is the largest ordinal below ω^5 with coefficients < 3.
521
- console.log("\n=== 34b. iterate..epsilonate (rule 3 + layer markers) ===");
522
- var it35 = m3.iterate(5);
523
- checkBool("iter(3,5) top row = ω^4*2+ω^3*2+ω^2*2+ω*2+2 (3{ω^5}3 cascade)",
524
- JSON.stringify(it35.array[it35.array.length - 1]) === "[1,2,2,2,2,2]", true);
525
-
526
516
  // λ[y] layer markers in STANDARD form (finite rows merged into r0, layer
527
517
  // lowered as far as it goes — README L17: layer L ⇒ ω^ω^…((L-1) ω^'s)^(bracket)):
528
518
  // iter → 10{ω^y}10 (L1 [1,y]); itmu → 10{ω^ω+(y-1)}10 (L1 [1,0,1], r0=y-1)
529
519
  // cube → 10{ω^ω+ω^y}10 (L1 r0[…,1@y] + [1,0,1]); expo → 10{ω^ω·y}10 (L1 [y,0,1])
530
520
  // tria → 10{ω^(ω+y)}10 (L1 [1,y,1]); trix → 10{ω^(ω·y+10)}10 (L1 [1,10,y])
531
521
  // apix → 10{ω^(ω^y)}10 (L1 [1,0…0,1] with y zeros)
522
+ console.log("\n=== λ[y] layer markers in STANDARD form ===");
532
523
  var wOps = ["iterate","itermult","cuboiter","expoiter","trioterate","trixxate","aperixxate"];
533
524
  var mk35 = {
534
525
  iterate: { layer: 0, rows: null }, // exact cascade, layer 0
@@ -561,12 +552,12 @@ for (var wy = 2; wy <= 9; wy++) {
561
552
  checkBool("ops strictly grow at y=" + wy + " (iter<itmu<cube<expo<tria<trix<apix)", wmono, true);
562
553
  }
563
554
 
564
- // ─── 34c. precision budget: exact while it fits maxRows/maxCols ───
555
+ // precision budget: exact while it fits maxRows/maxCols
565
556
  // arrow's most precise form holds maxRows+maxCols-2 finite hyperoperation
566
557
  // levels: r0 = [a0 … a(maxCols-1)] plus rows [count, level] for levels
567
558
  // maxCols … maxRows+maxCols-2. Beyond that: keep the largest maxRows-1
568
559
  // ordinal rows sorted ascending, array[1][0] += 1, array[0] = [10].
569
- console.log("\n=== 34c. precision budget (maxRows/maxCols) ===");
560
+ console.log("\n=== precision budget (maxRows/maxCols) ===");
570
561
  (function () {
571
562
  var a39 = MetaNum.arrow(10, 39, 10);
572
563
  checkBool("arrow(10,39,10) exact: maxCols r0 entries + maxRows-1 rows, top level 38",
@@ -746,7 +737,7 @@ var hyperOpList = [
746
737
  ];
747
738
 
748
739
  // ─── double nested iteration ───
749
- console.log("\n=== hyperoperation iteration tests (not fully implemented) ===");
740
+ console.log("\n=== hyperoperation iteration tests ===");
750
741
  for (var hi = 0; hi < hyperOpList.length; hi++) {
751
742
  var shortName = hyperOpList[hi][0];
752
743
  var methodName = hyperOpList[hi][1];
@@ -872,6 +863,289 @@ checkBool("inv_powerexpande(1e16{ω+3}5,1e16)=5", mL16.powerexpande(5).inv_power
872
863
  // Small-x finite branch (base has no ω-row): inv_expande is exact too.
873
864
  checkBool("inv_expande(3{ω+1}5,3)=5", MetaNum(3).expande(5).inv_expande(MetaNum(3)).eq(MetaNum(5)), true);
874
865
 
866
+ // ─────────────────────────────────────
867
+ // h11/h12/h13 (ω+1 / ω+2 / ω+3): the TOP row carries y-2 applications
868
+ // (rule 2: x{α}y = x{α-1}^(y-2)(x{α-1}x)), and every row BELOW it comes from
869
+ // decomposing x{α-1}x — whose operand is the BASE x — so it carries x-2,
870
+ // the same count the finite r0 coefficients carry.
871
+ // 10{ω+3}20 = 10{ω+2}^18 10{ω+1}^8 10{ω}^8 10{ω}10
872
+ // → [8,0,1] [8,1,1] [18,2,1] (not three 18-rows)
873
+ // ─────────────────────────────────────
874
+ console.log("\n=== h11/h12/h13 count law ===");
875
+ checkBool("h13(10,20) = 10{ω+3}20 → [8,0,1] [8,1,1] [18,2,1]",
876
+ JSON.stringify(MetaNum(10).h13(20).array.slice(1)) === "[[8,0,1],[8,1,1],[18,2,1]]", true);
877
+ checkBool("h12(10,20) = 10{ω+2}20 → [8,0,1] [18,1,1]",
878
+ JSON.stringify(MetaNum(10).h12(20).array.slice(1)) === "[[8,0,1],[18,1,1]]", true);
879
+ checkBool("h11(10,20) = 10{ω+1}20 → [18,0,1] (single row stays y-2)",
880
+ JSON.stringify(MetaNum(10).h11(20).array.slice(1)) === "[[18,0,1]]", true);
881
+ // the lower rows are the BASE's count (x-2) and never move with y
882
+ var hRows = { 3: 1, 5: 3, 10: 8 };
883
+ for (var hx in hRows) {
884
+ var lowC = hRows[hx];
885
+ for (var hy = 3; hy <= 20; hy++) {
886
+ var hr = MetaNum(Number(hx)).h13(hy).array.slice(1);
887
+ var hok = JSON.stringify(hr) === JSON.stringify(
888
+ [[lowC, 0, 1], [lowC, 1, 1], [hy - 2, 2, 1]]);
889
+ if (!hok) { checkBool("h13(" + hx + "," + hy + ") rows", false, true); break; }
890
+ }
891
+ checkBool("h13(" + hx + ",3..20): lower rows fixed at x-2=" + lowC + ", top row y-2", true, true);
892
+ }
893
+ // y-monotone and level-monotone on the enumeration path
894
+ for (var hy2 = 3; hy2 <= 20; hy2++) {
895
+ var hprev = null, hch = true;
896
+ for (var hi = 0; hi < 3; hi++) {
897
+ var hv = MetaNum(10)[["h11", "h12", "h13"][hi]](hy2);
898
+ if (hprev && !hv.gt(hprev)) hch = false;
899
+ hprev = hv;
900
+ }
901
+ if (!hch) { checkBool("h11<h12<h13 at y=" + hy2, false, true); break; }
902
+ }
903
+ checkBool("h11 < h12 < h13 for y = 3..20", true, true);
904
+ checkBool("inv_powerexpande(10{ω+3}20,10) = 20",
905
+ MetaNum(10).h13(20).inv_powerexpande(MetaNum(10)).eq(MetaNum(20)), true);
906
+
907
+ // ─────────────────────────────────────
908
+ // Every level from ω*2 up follows the same law:
909
+ // successor α = β+1 → rows of x{β}x, each x-2, + one β-row carrying y-2
910
+ // limit α → rule 3 turns y into the fundamental-sequence index, so
911
+ // EVERY row carries x-2 (stay expanded up to MSI)
912
+ // ─────────────────────────────────────
913
+ console.log("\n=== h20+ : expanded form & count law ===");
914
+ // successor h21 (ω*2+1): 10{ω*2+1}20 = the ω..ω+9 cascade (x-2 = 8) + ω*2 row y-2
915
+ checkBool("h21(10,20) = [8,0..9,1] rows + [18,0,2]",
916
+ JSON.stringify(MetaNum(10).h21(20).array.slice(1)) ===
917
+ "[[8,0,1],[8,1,1],[8,2,1],[8,3,1],[8,4,1],[8,5,1],[8,6,1],[8,7,1],[8,8,1],[8,9,1],[18,0,2]]", true);
918
+ // h22 (ω*2+2): one more row at ω*2 (x-2), top row ω*2+1 carries y-2
919
+ checkBool("h22(10,20) = cascade + [8,0,2] + [18,1,2]",
920
+ JSON.stringify(MetaNum(10).h22(20).array.slice(1)) ===
921
+ "[[8,0,1],[8,1,1],[8,2,1],[8,3,1],[8,4,1],[8,5,1],[8,6,1],[8,7,1],[8,8,1],[8,9,1],[8,0,2],[18,1,2]]", true);
922
+ // base-driven lower rows: x=3 → the cascade stops at ω+2 and every count is 1
923
+ checkBool("h21(3,20): lower rows carry x-2 = 1",
924
+ JSON.stringify(MetaNum(3).h21(20).array.slice(1)) === "[[1,0,1],[1,1,1],[1,2,1],[18,0,2]]", true);
925
+ // and the lower rows never move with y
926
+ (function () {
927
+ var lowRows = JSON.stringify(MetaNum(10).h21(3).array.slice(1, 11));
928
+ var ok = true;
929
+ for (var y = 4; y <= 20; y++) {
930
+ var r = MetaNum(10).h21(y).array.slice(1);
931
+ if (JSON.stringify(r.slice(0, 10)) !== lowRows || r[10][0] !== y - 2) ok = false;
932
+ }
933
+ checkBool("h21(10,3..20): lower rows fixed at 8, ω*2 row = y-2", ok, true);
934
+ })();
935
+
936
+ // limit ops stay EXPANDED for 100 < y ≤ MSI (no one-row marker)
937
+ (function () {
938
+ var MSI = 9007199254740991;
939
+ for (var i = 0; i < 3; i++) {
940
+ var lf = ["h20", "h30", "h100"][i];
941
+ for (var k = 0; k < 3; k++) {
942
+ var ly = [101, 1000, MSI][k];
943
+ var lr = MetaNum(10)[lf](ly);
944
+ // maxRows-1 ordinal rows, base collapsed to [10], first kept row marked +1
945
+ checkBool(lf + "(10," + ly + ") stays expanded (" + (lr.array.length - 1) + " rows)",
946
+ lr.array.length - 1 === MetaNum.maxRows - 1 && lr.array[0].length === 1 &&
947
+ lr.array[1][0] === 9, true);
948
+ }
949
+ }
950
+ // top row carries the fundamental-sequence index y-1
951
+ checkBool("h20(10,1000) top row = ω+999",
952
+ JSON.stringify(MetaNum(10).h20(1000).array[19]) === "[8,999,1]", true);
953
+ checkBool("h20(10,MSI) top row = ω+(MSI-1)",
954
+ MetaNum(10).h20(MSI).array[19][1] === MSI - 1, true);
955
+ })();
956
+
957
+ // ─────────────────────────────────────
958
+ // Fractional y for every level from ω*2 up (README "Non-integer arguments"):
959
+ // a limit level takes its fundamental sequence at the FULL fractional index and
960
+ // the fractional coefficients resolve against the base x (ω^(k)·(c+f) =
961
+ // ω^(k)·c + ω^(k-1)·(x·f)); a leftover fractional constant becomes a fractional
962
+ // argument one level up: x{γ+f}x = x{γ+1}(2·(x/2)^f).
963
+ // ─────────────────────────────────────
964
+ console.log("\n=== h20+ with fractional y ===");
965
+ checkBool("10{ω*2}2.1 = 10{ω+3}(2*5^0.1)",
966
+ MetaNum(10).h20(2.1).eq(MetaNum(10).powerexpande(2 * Math.pow(5, 0.1))), true);
967
+ checkBool("3{ω*2}2.1 = 3{ω+3}(2*1.5^0.1)",
968
+ MetaNum(3).h20(2.1).eq(MetaNum(3).powerexpande(2 * Math.pow(1.5, 0.1))), true);
969
+ checkBool("10{ω^2}2.1 = 10{ω*2+1}10",
970
+ MetaNum(10).h100(2.1).eq(MetaNum(10).h21(10)), true);
971
+ checkBool("3{ω^2}2.1 = 3{ω*2+1}(2*1.5^0.3)",
972
+ MetaNum(3).h100(2.1).eq(MetaNum(3).h21(2 * Math.pow(1.5, 0.3))), true);
973
+ // 10{ω^ω}2.1 = 10{ω^2.1}10 = 10{ω^2*10^0.1}10 → ω^2+ω*2+5.8925… → next level
974
+ (function () {
975
+ var rf = MetaNum._resolveFracLevel([0, 0, Math.pow(10, 0.1)], 10);
976
+ var up = rf.ord.slice(); up[0] += 1;
977
+ checkBool("10{ω^ω}2.1 = 10{ω^2+ω*2+6}(2*5^0.8925…) (level " +
978
+ JSON.stringify(up) + ")",
979
+ JSON.stringify(up) === "[6,2,1]" &&
980
+ MetaNum(10).iterate(2.1).eq(
981
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(2 * Math.pow(5, rf.frac)), up)), true);
982
+ })();
983
+ // the fraction is not thrown away: y and floor(y) differ
984
+ checkBool("h20(10,2.1) != h20(10,2) and lies strictly between 2 and 3",
985
+ MetaNum(10).h20(2.1).gt(MetaNum(10).h20(2)) &&
986
+ MetaNum(10).h20(2.1).lt(MetaNum(10).h20(3)), true);
987
+ checkBool("h100(10,10.25) lies strictly between 10 and 11",
988
+ MetaNum(10).h100(10.25).gt(MetaNum(10).h100(10)) &&
989
+ MetaNum(10).h100(10.25).lt(MetaNum(10).h100(11)), true);
990
+ // monotone in y across the integers for the LIMIT levels
991
+ (function () {
992
+ var limOps = ["h20", "h30", "h100", "h110", "h1000", "h10000", "iterate"];
993
+ var bad = 0, firstBad = "";
994
+ for (var i = 0; i < limOps.length; i++) {
995
+ for (var b = 0; b < 2; b++) {
996
+ var bx = [3, 10][b];
997
+ for (var m = 2; m <= 10; m++) {
998
+ var a = MetaNum(bx)[limOps[i]](m), c = MetaNum(bx)[limOps[i]](m + 0.001);
999
+ var d = MetaNum(bx)[limOps[i]](m + 0.5), e = MetaNum(bx)[limOps[i]](m + 1);
1000
+ if (!(c.gte(a) && d.gte(c) && e.gte(d))) {
1001
+ bad++; if (!firstBad) firstBad = limOps[i] + "(" + bx + "," + m + ")";
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ checkBool("limit levels monotone across fractional y", bad === 0, true,
1007
+ bad === 0 ? "" : " first failure: " + firstBad);
1008
+ })();
1009
+ // the SUCCESSOR rule with a fractional argument is written in the same expanded
1010
+ // form: the α-1 row carries the m applications (ceil(y)-2 = m-1) and the cascade
1011
+ // below it is generated at the fundamental-sequence index x^f, so the value grows
1012
+ // with f inside the interval and converges to the next integer at f → 1
1013
+ checkBool("h21(10,2.001) < h21(10,2.5) < h21(10,2.99) < h21(10,3)",
1014
+ MetaNum(10).h21(2.001).lt(MetaNum(10).h21(2.5)) &&
1015
+ MetaNum(10).h21(2.5).lt(MetaNum(10).h21(2.99)) &&
1016
+ MetaNum(10).h21(2.99).lt(MetaNum(10).h21(3)), true);
1017
+ checkBool("h31(10,5.1) < h31(10,5.5) < h31(10,5.9) < h31(10,6)",
1018
+ MetaNum(10).h31(5.1).lt(MetaNum(10).h31(5.5)) &&
1019
+ MetaNum(10).h31(5.5).lt(MetaNum(10).h31(5.9)) &&
1020
+ MetaNum(10).h31(5.9).lt(MetaNum(10).h31(6)), true);
1021
+ // the expanded form keeps the count law with INTEGER counts (only array[0][0]
1022
+ // may be fractional): every row below the top one carries x-2 = 8 and the α-1
1023
+ // row carries ceil(y)-2; the fraction lives in the cascade depth, so 2+ε starts
1024
+ // with the shallowest cascade and 3-ε has the full 10{ω*2}10 cascade
1025
+ (function () {
1026
+ var ok = true;
1027
+ for (var k = 0; k < 6; k++) {
1028
+ var fy = [2.001, 2.1, 2.5, 3.5, 10.25, 100.75][k];
1029
+ var rr = MetaNum(10).h21(fy).array.slice(1);
1030
+ for (var i = 0; i < rr.length - 1; i++) if (rr[i][0] !== 8) ok = false;
1031
+ if (rr[rr.length - 1][0] !== Math.ceil(fy) - 2) ok = false;
1032
+ }
1033
+ checkBool("h21(10, fractional): lower rows = 8, ω*2 row = ceil(y)-2 (integer)", ok, true);
1034
+ // the endpoints: 2+ε is just above h21(10,2) = 10{ω*2}10, 3-ε → h21(10,3)
1035
+ checkBool("h21(10,2+1e-15) = shallowest cascade + one ω*2 row, above h21(10,2)",
1036
+ MetaNum(10).h21(2 + 1e-15).array.length === 3 &&
1037
+ JSON.stringify(MetaNum(10).h21(2 + 1e-15).array[2]) === "[1,0,2]" &&
1038
+ MetaNum(10).h21(2 + 1e-15).gt(MetaNum(10).h21(2)), true);
1039
+ checkBool("h21(10,2.999999) → h21(10,3) from below",
1040
+ MetaNum(10).h21(2.999999).lt(MetaNum(10).h21(3)) &&
1041
+ MetaNum(10).h21(2.999999).gt(MetaNum(10).h21(2)), true);
1042
+ })();
1043
+ // the CONTRACT for fractional operands: only array[0][0] may be non-integer —
1044
+ // every row count, every row coefficient and every other r0 entry is an integer
1045
+ // (exactly like MetaNum.arrow with a fractional level/argument)
1046
+ (function () {
1047
+ var bad = [], n = 0;
1048
+ var fOps = ["h11","h13","h20","h21","h31","h100","h101","h1001","iterate",
1049
+ "cuboiter","trioterate","epsilonate"];
1050
+ for (var f of fOps) {
1051
+ for (var b = 0; b < 3; b++) {
1052
+ var bx = [3, 10, 100][b];
1053
+ for (var k = 0; k < 3; k++) {
1054
+ var by = [2.1, 3.5, 100.75][k];
1055
+ n++;
1056
+ var arr = MetaNum(bx)[f](by).array;
1057
+ for (var i = 0; i < arr.length; i++) {
1058
+ for (var j = 0; j < arr[i].length; j++) {
1059
+ if (i === 0 && j === 0) continue;
1060
+ var c = arr[i][j];
1061
+ if (typeof c !== "number" || !isFinite(c) || c !== Math.floor(c)) {
1062
+ if (bad.length < 4) bad.push(f + "(" + bx + "," + by + ") [" + i + "][" + j + "]=" + c);
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+ checkBool("only array[0][0] is fractional (" + n + " results checked)", bad.length === 0,
1070
+ true, bad.join(" | "));
1071
+ // arrow() itself obeys the same contract
1072
+ var bad2 = 0;
1073
+ for (var b2 = 0; b2 < 3; b2++) {
1074
+ var bx2 = [3, 10, 100][b2];
1075
+ for (var k2 = 0; k2 < 7; k2++) {
1076
+ var lv = [1.5, 2.1, 2.5, 3.5, 10.25, 100.75, 1e9 + 0.4][k2];
1077
+ var arr2 = MetaNum(bx2).arrow(lv)(10).array;
1078
+ for (var i2 = 0; i2 < arr2.length; i2++) {
1079
+ for (var j2 = 0; j2 < arr2[i2].length; j2++) {
1080
+ if (i2 === 0 && j2 === 0) continue;
1081
+ if (arr2[i2][j2] !== Math.floor(arr2[i2][j2])) bad2++;
1082
+ }
1083
+ }
1084
+ }
1085
+ }
1086
+ checkBool("arrow() with fractional levels: only array[0][0] is fractional", bad2 === 0, true);
1087
+ })();
1088
+
1089
+ // row coefficients stay integer (a fractional constant is carried to the base)
1090
+ (function () {
1091
+ var ok = true;
1092
+ for (var f of ["h21", "h22", "h31", "h101", "h111", "h1001"]) {
1093
+ for (var k = 0; k < 5; k++) {
1094
+ var fy = [2.001, 2.5, 3.5, 10.25, 100.75][k];
1095
+ var arr = MetaNum(10)[f](fy).array;
1096
+ for (var i = 1; i < arr.length; i++) {
1097
+ for (var j = 1; j < arr[i].length; j++) {
1098
+ if (arr[i][j] !== Math.floor(arr[i][j])) ok = false;
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ checkBool("fractional y: every ordinal row coefficient is an integer", ok, true);
1104
+ })();
1105
+
1106
+ // no crash / NaN over the whole range with fractional operands
1107
+ (function () {
1108
+ var allOps = ["h20","h21","h22","h30","h31","h100","h101","h110","h111",
1109
+ "h1000","h1001","h10000","iterate"];
1110
+ var bad = 0;
1111
+ for (var i = 0; i < allOps.length; i++) {
1112
+ for (var b = 0; b < 4; b++) {
1113
+ var bx = [3, 10, 1e6, 1e16][b];
1114
+ for (var k = 0; k < 5; k++) {
1115
+ var by = [2.0001, 2.5, 3.5, 10.25, 1000.5][k];
1116
+ try {
1117
+ var v = MetaNum(bx)[allOps[i]](by);
1118
+ if (v.isNaN()) bad++;
1119
+ } catch (e) { bad++; }
1120
+ }
1121
+ }
1122
+ }
1123
+ checkBool("no throw/NaN for fractional y over h20..h10000 + iterate", bad === 0, true);
1124
+ })();
1125
+
1126
+ // inverse round-trip over the whole small-operand range, successors and limits
1127
+ (function () {
1128
+ var pairs = [["h11","inv_expande"],["h12","inv_multiexpande"],["h13","inv_powerexpande"],
1129
+ ["h20","inv_aperioexpande"],["h21","inv_explode"],["h22","inv_multiexplode"],
1130
+ ["h30","inv_aperioexplode"],["h31","inv_detonate"],["h100","inv_aperionate"],
1131
+ ["h101","inv_megote"],["h110","inv_aperimegote"],["h111","inv_megoexpande"],
1132
+ ["h1000","inv_aperiatote"],["h1001","inv_powiainate"]];
1133
+ var bad = 0, firstBad = "";
1134
+ for (var i = 0; i < pairs.length; i++) {
1135
+ for (var b = 0; b < 3; b++) {
1136
+ var bx = [3, 5, 10][b];
1137
+ for (var y = 3; y <= 20; y++) {
1138
+ var v = MetaNum(bx)[pairs[i][0]](y);
1139
+ if (!v[pairs[i][1]](MetaNum(bx)).eq(MetaNum(y))) {
1140
+ bad++; if (!firstBad) firstBad = pairs[i][0] + "(" + bx + "," + y + ")";
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+ checkBool("inverse round-trip for h11..h1001 (x=3/5/10, y=3..20)", bad === 0,
1146
+ true, bad === 0 ? "" : " first failure: " + firstBad);
1147
+ })();
1148
+
875
1149
  // ─────────────────────────────────────
876
1150
  // engine arrays: the (y-2) count law
877
1151
  // x{L}y = x{L-1}^{y-2}(x{L-1}x): 10{25}10 → count-8 rows/cols (the
@@ -1008,8 +1282,12 @@ checkBool("hardy(1e15) < hardy(1e16) < hardy(1e17)",
1008
1282
  // -- MetaNum-object input (v2.0): structures shadow to their ordinal --
1009
1283
  // power tower 10^^k → ω^ω^…^ω (k ω's) → H there = 10.epsilonate(k);
1010
1284
  // letter/ordinal values sit at their own Hardy position → themselves
1011
- checkBool("hardy(10^^100) = 10{ω^…^ω(100 ωs)}10 = epsilonate(100)",
1012
- MetaNum.hardy(MetaNum(10).tetr(100)).eq(MetaNum(10).epsilonate(100)), true);
1285
+ checkBool("hardy(10^^100) = H_{ω^…^ω(100 ωs)}(10), layer 100-2 = 98 (one per 10^ level)",
1286
+ (function () {
1287
+ var v = MetaNum.hardy(MetaNum(10).tetr(100));
1288
+ return v.layer === 98 &&
1289
+ JSON.stringify(v.array) === JSON.stringify(MetaNum.hardy(1e10).array);
1290
+ })(), true);
1013
1291
  checkBool("hardy(MetaNum G600) = Infinity",
1014
1292
  MetaNum.hardy(MetaNum("G600")).eq(MetaNum.infinity), true);
1015
1293
  checkBool("hardy(MetaNum arrow(3,9,3)) = Infinity",
@@ -1020,12 +1298,533 @@ checkBool("hardy(10{3}10) = Infinity (> 10^^MSI)",
1020
1298
  // -- the engine limit: hardy(10^^MSI) = 10{ω^…^ω(MSI ω's)}10 (v2.0) --
1021
1299
  (function () {
1022
1300
  var MSI = 9007199254740991;
1023
- checkBool("hardy(10^^MSI) = MetaNum limit 10{ω^…^ω(MSI ω's)}10",
1024
- MetaNum.hardy(MetaNum(10).tetr(MSI)).eq(MetaNum(10).epsilonate(MSI)), true);
1025
- checkBool("hardy(10^^(MSI-1)) = epsilonate(MSI-1) (just below the cap)",
1026
- MetaNum.hardy(MetaNum(10).tetr(MSI - 1)).eq(MetaNum(10).epsilonate(MSI - 1)), true);
1301
+ // the tower height k caps the Hardy hierarchy: layer = k - 2 (one per 10^)
1302
+ checkBool("hardy(10^^MSI) = the MetaNum limit (layer MSI-2, base rows kept)",
1303
+ MetaNum.hardy(MetaNum(10).tetr(MSI)).layer === MSI - 2 &&
1304
+ JSON.stringify(MetaNum.hardy(MetaNum(10).tetr(MSI)).array) ===
1305
+ JSON.stringify(MetaNum.hardy(1e10).array), true);
1306
+ checkBool("hardy(10^^(MSI-1)) is just below the cap",
1307
+ MetaNum.hardy(MetaNum(10).tetr(MSI - 1)).lt(MetaNum.hardy(MetaNum(10).tetr(MSI))), true);
1308
+ })();
1309
+
1310
+ // ─────────────────────────────────────
1311
+ // v2.0.2 hardy fixes:
1312
+ // - a plain number beyond the double range is NOT a power tower: read its
1313
+ // decimal expansion (hardy("1.8e308") stays layer 0 with the ordinary
1314
+ // digit path, last row [8,7,0,3] like hardy("1.79e308"))
1315
+ // - ee10 / eee10 / eeee10 map to ω-towers of DIFFERENT heights (layers
1316
+ // 2/3/4), no longer the same value
1317
+ // - fractional n: the ordinal comes from the integer part, the base
1318
+ // argument becomes 10+f (H_ω(10.38) = 10.38·2 = 20.76)
1319
+ // ─────────────────────────────────────
1320
+ console.log("\n=== v2.0.2 hardy fixes ===");
1321
+ checkBool("hardy(\"1.8e308\") layer 0, top row [1,8,0,3] (ω^(ω²·3+8), one application)",
1322
+ (function () {
1323
+ var h = MetaNum.hardy(MetaNum("1.8e308"));
1324
+ return h.layer === 0 &&
1325
+ JSON.stringify(h.array[h.array.length - 1]) === "[1,8,0,3]";
1326
+ })(), true);
1327
+ // (the row budget resolves differences down to ~1e307 here, so the pair is
1328
+ // 1.7e308/1.8e308 — one exponent step apart, not one hundredth of one)
1329
+ checkBool("hardy(\"1.7e308\") < hardy(\"1.8e308\") (monotone over the double limit)",
1330
+ MetaNum.hardy(MetaNum("1.7e308")).lt(MetaNum.hardy(MetaNum("1.8e308"))), true);
1331
+ checkBool("hardy(ee10) < hardy(eee10) < hardy(eeee10) (distinct tower heights)",
1332
+ MetaNum.hardy(MetaNum("ee10")).lt(MetaNum.hardy(MetaNum("eee10"))) &&
1333
+ MetaNum.hardy(MetaNum("eee10")).lt(MetaNum.hardy(MetaNum("eeee10"))), true);
1334
+ checkBool("hardy(ee10)/eee10/eeee10 sit at layers 1/2/3 (one layer per 10^ level)",
1335
+ MetaNum.hardy(MetaNum("ee10")).layer === 1 &&
1336
+ MetaNum.hardy(MetaNum("eee10")).layer === 2 &&
1337
+ MetaNum.hardy(MetaNum("eeee10")).layer === 3, true);
1338
+ checkBool("hardy(10.38) = 20.76 (H_ω(10.38) = 10.38·2)",
1339
+ MetaNum.hardy(10.38).eq(MetaNum(20.76)), true);
1340
+ checkBool("hardy(100.1) = 11084.709858935355 (H_{ω²}(10.1) = 10.1·2^10.1)",
1341
+ MetaNum.hardy(100.1).eq(MetaNum(11084.709858935355)), true);
1342
+ checkBool("hardy(10) < hardy(10.38) < hardy(11) (continuous in n)",
1343
+ MetaNum.hardy(10).lt(MetaNum.hardy(10.38)) &&
1344
+ MetaNum.hardy(10.38).lt(MetaNum.hardy(11)), true);
1345
+ checkBool("hardy(100) < hardy(100.1) < hardy(101)",
1346
+ MetaNum.hardy(100).lt(MetaNum.hardy(100.1)) &&
1347
+ MetaNum.hardy(100.1).lt(MetaNum.hardy(101)), true);
1348
+ // ─────────────────────────────────────
1349
+ // non-integer inputs above 1000: the fundamental-sequence step writes the
1350
+ // running base n into a coefficient, so a coefficient can turn FRACTIONAL —
1351
+ // the old "-1" step then went negative and left no positive term at all
1352
+ // (hardy(1000.5) threw "Cannot read properties of undefined (reading 'gt')").
1353
+ // 0 < c_j < 1 is now a PARTIAL step: ω^j·f contributes ω^(j-1)·(n·f).
1354
+ // ─────────────────────────────────────
1355
+ (function () {
1356
+ var vals = [1000.5, 1000.1, 1000.9999, 1001.1, 10000.5, 1000000.75, 1e10 + 0.5, 1e15 + 0.5];
1357
+ var bad = [], nan = 0;
1358
+ for (var i = 0; i < vals.length; i++) {
1359
+ var v;
1360
+ try { v = MetaNum.hardy(vals[i]); } catch (e) { bad.push(vals[i] + " threw " + e.message.slice(0, 40)); continue; }
1361
+ if (v.isNaN()) nan++;
1362
+ }
1363
+ checkBool("hardy(non-integer > 1000) does not throw", bad.length === 0 && nan === 0, true,
1364
+ bad.join(" | "));
1365
+ // monotone in the integer and in the fraction
1366
+ var mono = true;
1367
+ for (var m = 0; m < 5; m++) {
1368
+ var base = [1000, 1001, 10000, 12345, 99999][m];
1369
+ for (var k = 0; k < 5; k++) {
1370
+ var f = [0.001, 0.25, 0.5, 0.75, 0.999][k];
1371
+ if (!(MetaNum.hardy(base + f).gte(MetaNum.hardy(base + f - 0.0001)) &&
1372
+ MetaNum.hardy(base + 1).gte(MetaNum.hardy(base + f)))) mono = false;
1373
+ }
1374
+ }
1375
+ checkBool("hardy(n) monotone across non-integer n > 1000", mono, true);
1376
+ checkBool("hardy(1000) < hardy(1000.5) < hardy(1001)",
1377
+ MetaNum.hardy(1000).lt(MetaNum.hardy(1000.5)) &&
1378
+ MetaNum.hardy(1000.5).lt(MetaNum.hardy(1001)), true);
1379
+ checkBool("hardy(10000) < hardy(10000.5) < hardy(10001)",
1380
+ MetaNum.hardy(10000).lt(MetaNum.hardy(10000.5)) &&
1381
+ MetaNum.hardy(10000.5).lt(MetaNum.hardy(10001)), true);
1382
+ // the array contract still holds for those results
1383
+ var cbad = 0;
1384
+ for (var b2 = 0; b2 < 4; b2++) {
1385
+ var bv = [1000, 10000, 1e6, 1e10][b2];
1386
+ for (var k2 = 0; k2 < 3; k2++) {
1387
+ var arr = MetaNum.hardy(bv + [0.1, 0.5, 0.9][k2]).array;
1388
+ for (var i2 = 0; i2 < arr.length; i2++) {
1389
+ for (var j2 = 0; j2 < arr[i2].length; j2++) {
1390
+ if (i2 === 0 && j2 === 0) continue;
1391
+ if (arr[i2][j2] !== Math.floor(arr[i2][j2])) cbad++;
1392
+ }
1393
+ }
1394
+ }
1395
+ }
1396
+ checkBool("hardy(non-integer > 1000): only array[0][0] is fractional", cbad === 0, true);
1027
1397
  })();
1028
1398
 
1399
+ // ─────────────────────────────────────
1400
+ // hardy above 1e10 with a non-zero "rest": the from-below base is H_{ω^n1}(n1),
1401
+ // whose tower sits at level n1-1 — array[0] holds n1 entries, so the step-by-step
1402
+ // expansion only fits while n1 ≤ maxCols. Above that the coefficients the limit
1403
+ // steps write turn structured and the expansion collapsed into a destroyed base
1404
+ // (array[0] = [1]) with a bogus row: hardy(1e10+11) was [[1],[2,20]].
1405
+ // The engine value 10{n1-1}(n1+1) is used there instead.
1406
+ // ─────────────────────────────────────
1407
+ console.log("\n=== hardy(n ≥ 1e10) : base & cascade ===");
1408
+ (function () {
1409
+ // H_{ω^ω+ω+1}(10) = H_{ω^ω}(22): the exact tower [c0, 21×20] no longer fits
1410
+ // maxCols = 20, so the TRUNCATED cascade [3086.036…, 21×19] is kept — the
1411
+ // [非整数, 21, 21, …, 21] single-row shape (was the engine value
1412
+ // 10{20}22 = [[1e10,8×19],[21,20]]).
1413
+ var v11 = MetaNum.hardy(1e10 + 11); // H_{ω^ω+ω+1}(10) = H_{ω^ω}(22)
1414
+ checkBool("hardy(1e10+11): single truncated tower [c0, 21×19] — [非整数,21,21,…]",
1415
+ v11.array.length === 1 && v11.array[0].length === MetaNum.maxCols &&
1416
+ v11.array[0][1] === 21 && v11.array[0][MetaNum.maxCols - 1] === 21 &&
1417
+ v11.array[0][0] > 1 && v11.array[0][0] !== Math.floor(v11.array[0][0]), true);
1418
+ checkBool("hardy(1e10+11) < 10{21}23 (from below on the ω^22 tower)",
1419
+ v11.lt(MetaNum.arrow(10, 21, 23)), true);
1420
+ // the same at the top of the range: H_{ω^ω}(48) → level 47
1421
+ var v22 = MetaNum.hardy(1e10 + 22);
1422
+ checkFormat("hardy(1e10+22) = H_{ω^ω+ω*2+2}(10) → X49", v22, "X49");
1423
+ checkBool("hardy(1e10+22): single truncated tower [c0, 47×19]",
1424
+ v22.array.length === 1 && v22.array[0][1] === 47 &&
1425
+ v22.array[0][MetaNum.maxCols - 1] === 47, true);
1426
+ checkBool("hardy(1e10+10) < hardy(1e10+11) < hardy(1e10+22)",
1427
+ MetaNum.hardy(1e10 + 10).lt(MetaNum.hardy(1e10 + 11)) &&
1428
+ MetaNum.hardy(1e10 + 11).lt(MetaNum.hardy(1e10 + 22)), true);
1429
+ // the whole 1e10+m family keeps growing, and never collapses again
1430
+ var broke = 0, collapsed = 0;
1431
+ for (var m = 0; m <= 30; m++) {
1432
+ var vm = MetaNum.hardy(1e10 + m);
1433
+ if (m > 0 && !vm.gt(MetaNum.hardy(1e10 + m - 1))) broke++;
1434
+ if (vm.array[0].length === 1 && vm.array[0][0] === 1) collapsed++;
1435
+ }
1436
+ checkBool("hardy(1e10+0..30) is strictly monotone", broke === 0, true);
1437
+ checkBool("hardy(1e10+0..30) never collapses array[0] to [1]", collapsed === 0, true);
1438
+ })();
1439
+
1440
+ // The leading term ω^β·dk is dk APPLICATIONS of H_{ω^β}, one after the other on
1441
+ // the running value, and an application is a row [1, β] — never a bump of the
1442
+ // ω-row count ([9,0,1] / [16,0,1] conflated dk with the rest-part):
1443
+ // hardy(1.1e11) = H_{ω^(ω+1)}(H_{ω^ω}(10)) = [3086.036,9×8] [1,1,1]
1444
+ // hardy(2e11) = H_{ω^(ω+1)}(H_{ω^(ω+1)}(10)) = [3086.036,9×8] [8,0,1] [1,1,1]
1445
+ (function () {
1446
+ var a = MetaNum.hardy(1e11), b = MetaNum.hardy(1.1e11), c = MetaNum.hardy(2e11);
1447
+ var base9 = "[3086.036065328153,9,9,9,9,9,9,9,9]";
1448
+ checkBool("hardy(1e11) = base + [8,0,1] (one H_{ω^(ω+1)}, from below)",
1449
+ JSON.stringify(a.array) === "[" + base9 + ",[8,0,1]]", true);
1450
+ checkBool("hardy(1.1e11) = [3086.036,9×8] [1,1,1] (H_{ω^ω} then H_{ω^(ω+1)})",
1451
+ JSON.stringify(b.array) === "[" + base9 + ",[1,1,1]]", true);
1452
+ checkBool("hardy(2e11) = [3086.036,9×8] [8,0,1] [1,1,1] (H_{ω^(ω+1)} twice)",
1453
+ JSON.stringify(c.array) === "[" + base9 + ",[8,0,1],[1,1,1]]", true);
1454
+ checkBool("hardy(3e11) = … [8,0,1] [2,1,1] (three applications)",
1455
+ JSON.stringify(MetaNum.hardy(3e11).array) === "[" + base9 + ",[8,0,1],[2,1,1]]", true);
1456
+ checkBool("hardy(1e11) < hardy(1.1e11) < hardy(2e11) < hardy(3e11)",
1457
+ a.lt(b) && b.lt(c) && c.lt(MetaNum.hardy(3e11)), true);
1458
+ var ok = true;
1459
+ for (var dk = 1; dk <= 9; dk++) {
1460
+ var vd = MetaNum.hardy(dk * 1e11);
1461
+ if (dk > 1 && !vd.gt(MetaNum.hardy((dk - 1) * 1e11))) ok = false;
1462
+ }
1463
+ checkBool("hardy(dk·1e11) strictly monotone in dk = 1..9", ok, true);
1464
+ // the same at ω+2: hardy(1.1e12) = H_{ω^(ω+2)}(H_{ω^(ω+1)}(10))
1465
+ checkBool("hardy(1.1e12) = [3086.036,9×8] [8,0,1] [1,2,1]",
1466
+ JSON.stringify(MetaNum.hardy(1.1e12).array) === "[" + base9 + ",[8,0,1],[1,2,1]]", true);
1467
+ })();
1468
+
1469
+ // hardy("1e100000") and above: 10^v is a PLAIN number (a power tower has the
1470
+ // same r0 = [v,1] shape but v ≥ 1e10), so the digit path must be taken — the
1471
+ // old "exponent < 1e5" guard pushed it into the tower branch (layer 1).
1472
+ (function () {
1473
+ var h100k = MetaNum.hardy("1e100000");
1474
+ checkBool("hardy(\"1e100000\") = H_{ω^(ω^5)}(10), layer 0", h100k.layer === 0, true);
1475
+ checkBool("hardy(\"1e100000\"): top row is the ω^5 cascade [8,9,9,9,9,9]",
1476
+ JSON.stringify(h100k.array[h100k.array.length - 1]) === "[8,9,9,9,9,9]", true);
1477
+ checkBool("hardy(\"1e10000\") top row = [8,9,9,9,9] (ω^4 cascade)",
1478
+ JSON.stringify(MetaNum.hardy("1e10000").array[19]) === "[8,9,9,9,9]", true);
1479
+ checkBool("hardy(\"1e1000\") top row = [8,9,9,9] (ω^3 cascade)",
1480
+ JSON.stringify(MetaNum.hardy("1e1000").array[19]) === "[8,9,9,9]", true);
1481
+ checkBool("hardy(1e100) top row = [8,9,9] (ω^2 cascade)",
1482
+ JSON.stringify(MetaNum.hardy(1e100).array[19]) === "[8,9,9]", true);
1483
+ checkBool("hardy(1e100) < hardy(\"1e1000\") < hardy(\"1e10000\") < hardy(\"1e100000\")",
1484
+ MetaNum.hardy(1e100).lt(MetaNum.hardy("1e1000")) &&
1485
+ MetaNum.hardy("1e1000").lt(MetaNum.hardy("1e10000")) &&
1486
+ MetaNum.hardy("1e10000").lt(MetaNum.hardy("1e100000")), true);
1487
+ // ee6..ee9: the ordinal is read off the value itself (10^W → ω^ord(W)),
1488
+ // so these are layer-0 cascades with ordinal rows that follow the input;
1489
+ // ee10 (W = 1e10 exactly) is the height-3 band → layerUp(hardy(1e10))
1490
+ checkBool("hardy(ee6..ee9) stay layer 0 with ordinal rows; ee10 → layer 1",
1491
+ (function () {
1492
+ for (var k = 6; k <= 9; k++) {
1493
+ var v = MetaNum.hardy("ee" + k);
1494
+ if (v.layer !== 0 || v.array.length - 1 !== 19) return false;
1495
+ }
1496
+ var v10 = MetaNum.hardy("ee10");
1497
+ return v10.layer === 1 && v10.array.length === 1;
1498
+ })(), true);
1499
+ checkBool("hardy(ee6) < hardy(ee7) < … < hardy(ee10) (rows follow the input)",
1500
+ (function () {
1501
+ for (var k = 7; k <= 10; k++) {
1502
+ if (!MetaNum.hardy("ee" + (k - 1)).lt(MetaNum.hardy("ee" + k))) return false;
1503
+ }
1504
+ return true;
1505
+ })(), true);
1506
+ checkBool("hardy(ee10) = layerUp(hardy(1e10)): the same rows at layer 1",
1507
+ MetaNum.hardy("ee10").layer === 1 &&
1508
+ JSON.stringify(MetaNum.hardy("ee10").array) ===
1509
+ JSON.stringify(MetaNum.hardy(1e10).array), true);
1510
+ // just above ee10 the level no longer fits into rows → layer 1
1511
+ checkBool("hardy(10^(1e10+1)) = H_{ω^(ω^ω+1)}(10) → layer 1",
1512
+ MetaNum.hardy(MetaNum(10).pow(MetaNum(1e10).add(MetaNum(1)))).layer === 1, true);
1513
+ checkBool("hardy(ee10) < hardy(10^(1e10+1)) < hardy(ee20)",
1514
+ MetaNum.hardy("ee10").lt(MetaNum.hardy(MetaNum(10).pow(MetaNum(1e10).add(MetaNum(1))))) &&
1515
+ MetaNum.hardy(MetaNum(10).pow(MetaNum(1e10).add(MetaNum(1)))).lt(MetaNum.hardy("ee20")), true);
1516
+ checkBool("hardy(ee10) < hardy(ee20) < hardy(ee100) < hardy(eee5) < hardy(eee10)",
1517
+ MetaNum.hardy("ee10").lt(MetaNum.hardy("ee20")) &&
1518
+ MetaNum.hardy("ee20").lt(MetaNum.hardy("ee100")) &&
1519
+ MetaNum.hardy("ee100").lt(MetaNum.hardy("eee5")) &&
1520
+ MetaNum.hardy("eee5").lt(MetaNum.hardy("eee10")), true);
1521
+ checkBool("above eee10 the layer is 2, above eeee10 it is 3 (one per 10^)",
1522
+ MetaNum.hardy("eee20").layer === 2 && MetaNum.hardy("eeee10").layer === 3 &&
1523
+ MetaNum.hardy("eeeee10").layer === 4, true);
1524
+ checkBool("hardy(eee10) < hardy(eee20) < hardy(eeee10) < hardy(eeeee10)",
1525
+ MetaNum.hardy("eee10").lt(MetaNum.hardy("eee20")) &&
1526
+ MetaNum.hardy("eee20").lt(MetaNum.hardy("eeee10")) &&
1527
+ MetaNum.hardy("eeee10").lt(MetaNum.hardy("eeeee10")), true);
1528
+ })();
1529
+
1530
+ // ─────────────────────────────────────
1531
+ // A number beyond Number.MAX_VALUE is stored as 10^v; the reconstructed mantissa
1532
+ // carries a rounding tail (2e1000 → 10^1000.301029995664 → 10^0.301… =
1533
+ // 1.9999999999… instead of 2), which used to spell "19999…9" and made hardy
1534
+ // read a garbage "rest" — hardy("2e1000") returned Infinity.
1535
+ (function () {
1536
+ var vals = ["2e1000", "5e1001", "9e999", "3.5e500"];
1537
+ var bad = 0;
1538
+ for (var i = 0; i < vals.length; i++) {
1539
+ var v = MetaNum.hardy(vals[i]);
1540
+ if (v.isNaN() || v.array[0].length === 1 && v.array[0][0] === null) bad++;
1541
+ }
1542
+ checkBool("hardy(\"2e1000\")/\"5e1001\"/… are finite (no rounding-tail Infinity)",
1543
+ bad === 0, true);
1544
+ checkBool("hardy(\"9e999\") < hardy(\"1e1000\") < hardy(\"2e1000\") monotone",
1545
+ MetaNum.hardy("9e999").lt(MetaNum.hardy("1e1000")) &&
1546
+ MetaNum.hardy("1e1000").lt(MetaNum.hardy("2e1000")), true);
1547
+ checkBool("hardy(\"2e1000\") top row follows the input (ω^1000 cascade)",
1548
+ MetaNum.hardy("2e1000").layer === 0 &&
1549
+ MetaNum.hardy("2e1000").array.length - 1 === 19, true);
1550
+ })();
1551
+
1552
+ // ─────────────────────────────────────
1553
+ // Levels above the letter grid (ω*2+100, ω*50, ω²*50, …) read as level-arg:
1554
+ // the snapped level with the fundamental-sequence index that recovers the
1555
+ // ordinal — the LARGEST coefficient was used before, which collapsed
1556
+ // [100,100,50], [100,100,60] and [100,100,100] into one string.
1557
+ (function () {
1558
+ var pairs = [
1559
+ [[100, 100, 50], "2.000Aaaa51"],
1560
+ [[100, 100, 60], "2.000Aaaa61"],
1561
+ [[100, 100, 100], "2.000Aaaa101"],
1562
+ [[100, 100, 200], "2.000Aaaa201"],
1563
+ [[100, 30], "2.000Aaa31"],
1564
+ [[100, 50], "2.000Aaa51"],
1565
+ [[100, 2], "2.000Ca100"]
1566
+ ]
1567
+ var ok = true, seen = {}
1568
+ for (var i = 0; i < pairs.length; i++) {
1569
+ var f = format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), pairs[i][0]))
1570
+ if (f !== pairs[i][1]) { ok = false; console.log(" " + f + " ≠ " + pairs[i][1]) }
1571
+ if (seen[f]) { ok = false; console.log(" collision: " + f) }
1572
+ seen[f] = true
1573
+ }
1574
+ checkBool("unnamed levels keep distinct level-args", ok, true)
1575
+ // the ω+c / ω*N families must not collide either
1576
+ var ok2 = true, seen2 = {}
1577
+ var fam = [[100, 3], [100, 4], [100, 5], [0, 40], [100, 40]]
1578
+ for (var j = 0; j < fam.length; j++) {
1579
+ var g = format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), fam[j]))
1580
+ if (seen2[g]) { ok2 = false; console.log(" collision: " + g + " " + JSON.stringify(fam[j])) }
1581
+ seen2[g] = true
1582
+ }
1583
+ checkBool("ω+c / ω*N families keep distinct level-args", ok2, true)
1584
+ })();
1585
+
1586
+ // The carried !αAaβ form takes the ω-EXPONENT as β (README: !αAaβ = ω^β plus
1587
+ // the digits of α). ω³*50+… snaps to ω⁴, whose letter "Aaaaa" (5) exceeds
1588
+ // multiLetterLimit(4), so it reads "!2.000Aa4" — "!2.000Aa51" would claim the
1589
+ // ω⁵¹ level.
1590
+ (function () {
1591
+ var deep = format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 100, 100, 50]))
1592
+ checkFormat("ω³*50+… → !1.000H51 (single-letter carried form)",
1593
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 100, 100, 50]), "!1.000H51")
1594
+ checkFormat("ω³*100+… → !1.000H101",
1595
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 100, 100, 100]), "!1.000H101")
1596
+ checkFormat("ω³*200+… → !1.000H201",
1597
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 100, 100, 200]), "!1.000H201")
1598
+ checkBool("the level-arg index is not used as the !-form exponent",
1599
+ deep.indexOf("Aa51") < 0, true)
1600
+ })();
1601
+
1602
+ // ─────────────────────────────────────
1603
+ // format: repeatLetterThreshold caps how many times ONE letter repeats — an
1604
+ // inner level must not promote to the outer letter (3{3}6 = FFF(E^7.6e12) is
1605
+ // "FFFEEE9.007E15", not "FFFF7.626E12"), and the saturated form is followed by
1606
+ // the next-letter (G) form.
1607
+ (function () {
1608
+ checkFormat("arrow(3,3,5) spells three F", MetaNum.arrow(3, 3, 5), "FFF7.626E12");
1609
+ // a fourth F would break repeatLetterThreshold, so the value renders at the
1610
+ // next letter by its real argument — not a saturated FFFEEE{…} form
1611
+ checkFormat("arrow(3,3,6) → 2.045G5 (next letter, real argument)",
1612
+ MetaNum.arrow(3, 3, 6), "2.045G5");
1613
+ checkFormat("arrow(3,3,7) → 2.045G6", MetaNum.arrow(3, 3, 7), "2.045G6");
1614
+ checkFormat("arrow(3,3,8) → 2.045G7", MetaNum.arrow(3, 3, 8), "2.045G7");
1615
+ for (var an = 3; an <= 8; an++) {
1616
+ var fs = format(MetaNum.arrow(3, 3, an))
1617
+ if (/(.)\1{3}/.test(fs.replace(/[\d,.]/g, ''))) {
1618
+ checkBool("arrow(3,3," + an + ") keeps within repeatLetterThreshold(3)", false, true)
1619
+ }
1620
+ }
1621
+ checkBool("no more than repeatLetterThreshold(3) copies of one letter", true, true)
1622
+ })();
1623
+
1624
+ // A diagonal letter (name ending in "a": Aa, Ba, Ca, Aaa, …) carries the
1625
+ // mantissa α = 2·5^f ∈ [2,10) — never the [1,10) of the other letters.
1626
+ (function () {
1627
+ var diag = ["2.000Ca100", "2.000Aaa100", "2.000Aaaa101", "2.000Ba100"];
1628
+ var got = [
1629
+ format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 2])),
1630
+ format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [0, 100])),
1631
+ format(MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(1000), [100, 100, 100])),
1632
+ format(MetaNum(10).aperioexpande(100))
1633
+ ];
1634
+ var ok = true;
1635
+ for (var i = 0; i < diag.length; i++) {
1636
+ if (got[i] !== diag[i]) { ok = false; console.log(" " + got[i] + " ≠ " + diag[i]); }
1637
+ var m = parseFloat(got[i]);
1638
+ if (!(m >= 2 && m < 10)) ok = false;
1639
+ }
1640
+ checkBool("diagonal-letter mantissa stays in [2,10)", ok, true);
1641
+ })();
1642
+
1643
+ // Truncation must keep hardy's definitional from-below base top as array[0][0],
1644
+ // not collapse it to the [10] marker (the string "1e16+1e13" form used to parse
1645
+ // to 0 → hardy(0) = 10 → the bare [10] base).
1646
+ (function () {
1647
+ var baseTop = 3086.036065328153;
1648
+ checkBool("hardy(1e10+22): truncated tower keeps the base top 3086.036…, not [10]",
1649
+ MetaNum.hardy(1e10 + 22).array[0][0] === baseTop, true);
1650
+ checkBool("hardy(1e10+40): truncated tower keeps the base top 3086.036…, not [10]",
1651
+ MetaNum.hardy(1e10 + 40).array[0][0] === baseTop, true);
1652
+ checkBool("hardy(\"1e16+1e13\"): the string form keeps the base and rows",
1653
+ (function () {
1654
+ var v = MetaNum.hardy("1e16+1e13");
1655
+ return v.array[0][0] === baseTop &&
1656
+ JSON.stringify(v.array.slice(1)) === "[[8,0,1],[8,1,1],[8,2,1],[1,6,1]]";
1657
+ })(), true);
1658
+ var n10 = 0, mono = true, prev = null;
1659
+ for (var m = 0; m <= 40; m++) {
1660
+ var vm = MetaNum.hardy(1e10 + m);
1661
+ if (vm.array[0].length === 1 && vm.array[0][0] === 10) n10++;
1662
+ if (prev && !vm.gt(prev)) mono = false;
1663
+ prev = vm;
1664
+ }
1665
+ checkBool("hardy(1e10+0..40): never a [10] base", n10 === 0, true);
1666
+ checkBool("hardy(1e10+0..40): strictly monotone across the truncation point", mono, true);
1667
+ checkBool("hardy(\"1e100000\") keeps the base too",
1668
+ JSON.stringify(MetaNum.hardy("1e100000").array[0]) === "[3086.036065328153,9,9,9,9,9,9,9,9]", true);
1669
+ })();
1670
+
1671
+ // hardy(1e16+1e13) = H_{ω^(ω+6)+ω^(ω+3)}(10): the plain number is stored as 10^v
1672
+ // from 1e16 on, and the way back through the double lost the low digits
1673
+ // (10010000000000000 → 10010000000000014), which made the digit path read 14
1674
+ // instead of 1e13 as the "rest" — the base then collapsed to [1].
1675
+ (function () {
1676
+ var v = MetaNum.hardy(1e16 + 1e13);
1677
+ checkBool("hardy(1e16+1e13): array[0] is a real base, not the [1] marker",
1678
+ v.array[0].length > 1, true);
1679
+ checkBool("hardy(1e16+1e13) = H_{ω^(ω+6)}(H_{ω^(ω+3)}(10)) → … [8,2,1] [1,6,1]",
1680
+ JSON.stringify(v.array.slice(1)) ===
1681
+ "[[8,0,1],[8,1,1],[8,2,1],[1,6,1]]", true);
1682
+ checkBool("hardy(1e16) < hardy(1e16+1e13) < hardy(2e16)",
1683
+ MetaNum.hardy(1e16).lt(v) && v.lt(MetaNum.hardy(2e16)), true);
1684
+ })();
1685
+
1686
+ // ─────────────────────────────────────
1687
+ // v2.0.2 hardy(n > 1e10) round 2:
1688
+ // 1. fractional inputs 1e10+f collapse into ONE plain row (the fractional
1689
+ // hyper-operation result) and stay monotone across the integers;
1690
+ // 2. n1 > maxCols+1 keeps the truncated exact tower [c0, (n1-1)×(maxCols-1)]
1691
+ // instead of the engine value;
1692
+ // 3. "AeB+C" strings parse to the exact sum (hardy("1e16+1e13") is no longer
1693
+ // hardy(0) = 10);
1694
+ // 4. for 1e10 ≤ x < e1e10, hardy(10^x) = layerUp(hardy(x)) — one layer apart,
1695
+ // same array rows.
1696
+ // ─────────────────────────────────────
1697
+ console.log("\n=== v2.0.2 hardy(n > 1e10) round 2 ===");
1698
+ (function () {
1699
+ // 1. single-row fractional results
1700
+ checkBool("hardy(1e10) < hardy(1e10+0.5) < hardy(1e10+1), all single-row",
1701
+ (function () {
1702
+ var a = MetaNum.hardy(1e10), b = MetaNum.hardy("1e10+0.5"), c = MetaNum.hardy(1e10 + 1);
1703
+ return a.lt(b) && b.lt(c) &&
1704
+ a.array.length === 1 && b.array.length === 1 && c.array.length === 1;
1705
+ })(), true);
1706
+ checkBool("hardy(1e10+0.5).array[0][0] is the fractional hyper-op result",
1707
+ (function () {
1708
+ var b = MetaNum.hardy("1e10+0.5").array[0][0];
1709
+ return b > 3086.036 && b < 6785.435 && b !== Math.floor(b);
1710
+ })(), true);
1711
+ checkBool("hardy(1e10+k+0.5) single-row and monotone for k = 0..9",
1712
+ (function () {
1713
+ var prev = MetaNum.hardy(1e10);
1714
+ for (var k = 0; k <= 9; k++) {
1715
+ var v = MetaNum.hardy(1e10 + k + 0.5);
1716
+ if (v.array.length !== 1) return false;
1717
+ if (!v.gt(prev)) return false;
1718
+ prev = v;
1719
+ var vi = MetaNum.hardy(1e10 + k + 1);
1720
+ if (!vi.gt(v)) return false;
1721
+ prev = vi;
1722
+ }
1723
+ return true;
1724
+ })(), true);
1725
+ // 2. truncated towers
1726
+ checkBool("hardy(1e10+11) < hardy(1e10+11.5) ≤ hardy(1e10+12) (fraction keeps order)",
1727
+ MetaNum.hardy("1e10+11").lt(MetaNum.hardy("1e10+11.5")) &&
1728
+ MetaNum.hardy("1e10+11.5").lt(MetaNum.hardy(1e10 + 12)), true);
1729
+ checkBool("hardy(1e10+m) tower level = hardy(m)-1 for m = 12..40",
1730
+ (function () {
1731
+ for (var m = 12; m <= 40; m += 7) {
1732
+ var lv = MetaNum.hardy(1e10 + m).array[0][1];
1733
+ if (lv !== MetaNum.hardy(m).toNumber() - 1) return false;
1734
+ }
1735
+ return true;
1736
+ })(), true);
1737
+ // 3. the string sum form
1738
+ checkBool("M(\"1e10+0.5\") parses to the exact 10000000000.5",
1739
+ JSON.stringify(MetaNum("1e10+0.5").array) === "[[10000000000.5]]", true);
1740
+ checkBool("M(\"1e10+11\") parses to the exact 10000000011",
1741
+ JSON.stringify(MetaNum("1e10+11").array) === "[[10000000011]]", true);
1742
+ checkBool("hardy(\"1e10+11\") = hardy(10000000011) (string = number form)",
1743
+ JSON.stringify(MetaNum.hardy("1e10+11").array) ===
1744
+ JSON.stringify(MetaNum.hardy(10000000011).array), true);
1745
+ checkBool("hardy(\"1e10+22\") format X49 (level 47 chain, promoted)",
1746
+ MetaNum.hardy("1e10+22").format() === "X49", true);
1747
+ // 4. the tower layer rule: every extra 10^ level adds ONE layer, the array
1748
+ // rows stay those of the innermost readable expansion
1749
+ checkBool("hardy(e1e10) = layerUp(hardy(1e10))",
1750
+ (function () {
1751
+ var a = MetaNum.hardy("e1e10"), b = MetaNum.hardy(1e10);
1752
+ return a.layer === b.layer + 1 &&
1753
+ JSON.stringify(a.array) === JSON.stringify(b.array);
1754
+ })(), true);
1755
+ checkBool("hardy(1e308) = layer 0, the digit-path cascade (…[8,6,0,3],[8,7,0,3])",
1756
+ (function () {
1757
+ var v = MetaNum.hardy("1e308");
1758
+ return v.layer === 0 && v.array.length > 2 &&
1759
+ JSON.stringify(v.array[v.array.length - 2]) === "[8,6,0,3]" &&
1760
+ JSON.stringify(v.array[v.array.length - 1]) === "[8,7,0,3]";
1761
+ })(), true);
1762
+ checkBool("hardy(e1e308)/ee1e308/eee1e308 = the same rows at layers 1/2/3",
1763
+ (function () {
1764
+ var v0 = MetaNum.hardy("1e308");
1765
+ var chain = ["e1e308", "ee1e308", "eee1e308"];
1766
+ for (var i = 0; i < chain.length; i++) {
1767
+ var v = MetaNum.hardy(chain[i]);
1768
+ if (v.layer !== i + 1) return false;
1769
+ if (JSON.stringify(v.array) !== JSON.stringify(v0.array)) return false;
1770
+ }
1771
+ return true;
1772
+ })(), true);
1773
+ checkBool("hardy(10^^100) keeps the rows: layer 98 over hardy(1e10)'s array",
1774
+ (function () {
1775
+ var v = MetaNum.hardy(MetaNum(10).tetr(100));
1776
+ return v.layer === 98 &&
1777
+ JSON.stringify(v.array) === JSON.stringify(MetaNum.hardy(1e10).array);
1778
+ })(), true);
1779
+ checkBool("hardy(10^x) = layerUp(hardy(x)) for x = 1e10+1, 2e10, 1e11, 1e15",
1780
+ (function () {
1781
+ var xs = [1e10 + 1, 2e10, 1e11, 1e15];
1782
+ for (var i = 0; i < xs.length; i++) {
1783
+ var hx = MetaNum.hardy(xs[i]);
1784
+ var htx = MetaNum.hardy(MetaNum(10).pow(MetaNum(xs[i])));
1785
+ if (htx.layer !== hx.layer + 1) return false;
1786
+ if (JSON.stringify(htx.array) !== JSON.stringify(hx.array)) return false;
1787
+ }
1788
+ return true;
1789
+ })(), true);
1790
+ checkBool("hardy(10^x) monotone in x across the height-3 band",
1791
+ (function () {
1792
+ var xs = [2e10, 5e10, 1e11, 1e12, 1e15, 1e18];
1793
+ for (var i = 1; i < xs.length; i++) {
1794
+ if (!MetaNum.hardy(MetaNum(10).pow(MetaNum(xs[i - 1])))
1795
+ .lt(MetaNum.hardy(MetaNum(10).pow(MetaNum(xs[i]))))) return false;
1796
+ }
1797
+ return true;
1798
+ })(), true);
1799
+ checkBool("hardy(eee10) keeps the definitional base in its layer-1 marker",
1800
+ JSON.stringify(MetaNum.hardy("eee10").array[0]) ===
1801
+ "[3086.036065328153,9,9,9,9,9,9,9,9]", true);
1802
+ })();
1803
+
1804
+ // ─────────────────────────────────────
1805
+ // v2.0.2 unnamed-level format: a level whose coefficients exceed the letter
1806
+ // grid (ω*2+100, ω*100, ω²*100+ω*100+100, …) rounds UP to the next nameable
1807
+ // level and reads as level-arg — never a wrapped/wrong letter, never the
1808
+ // summed repeat count
1809
+ // ─────────────────────────────────────
1810
+ console.log("\n=== v2.0.2 unnamed-level format ===");
1811
+ // a diagonal letter ("Ca", "Ba", "Aaa", …) carries α = 2·5^f ∈ [2,10)
1812
+ checkFormat("10{ω*2+100}100 → 2.000Ca100",
1813
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 2]), "2.000Ca100");
1814
+ checkFormat("10{ω*100}100 → 2.000Aaa100",
1815
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [0, 100]), "2.000Aaa100");
1816
+ checkFormat("10{ω²*100+ω*100+100}1000 → 2.000Aaaa101",
1817
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(1000), [100, 100, 100]), "2.000Aaaa101");
1818
+ checkFormat("h20(10,1000) → 2.000Ba1,000 (ω*2 with y=1000)",
1819
+ MetaNum(10).h20(1000), "2.000Ba1,000");
1820
+ checkFormat("h100(10,1000) → 2.000Aaa1,000 (ω² with y=1000)",
1821
+ MetaNum(10).h100(1000), "2.000Aaa1,000");
1822
+ checkFormat("h30(10,100) → 2.000Ca100 (ω*3 with y=100)",
1823
+ MetaNum(10).h30(100), "2.000Ca100");
1824
+ // nameable levels keep their ordinary cascade display
1825
+ checkFormat("h21(10,1000) → Bb999 (nameable top row, unchanged)",
1826
+ MetaNum(10).h21(1000), "1.000Bb999");
1827
+
1029
1828
  // ─────────────────────────────────────
1030
1829
  // fractional hyperoperation levels
1031
1830
  // level n+f interpolates geometrically (README rule i): 10{n+f}10 =
@@ -1047,6 +1846,44 @@ console.log("\n=== fractional levels ===");
1047
1846
  MetaNum(3).arrow(2.5)(3).lt(MetaNum(3).arrow(3)(3)), true);
1048
1847
  })();
1049
1848
 
1849
+ // ─────────────────────────────────────
1850
+ // arrow(x,y,y): level and argument move together, so the interpolation anchor
1851
+ // θ0 (the c with x{n+1}c = x{n}y) has to be a CONTINUOUS inverse — the integer
1852
+ // bisection of hyper_log overshoots it (2 instead of 1.0414), which makes the
1853
+ // geometric step y/θ0 < 1 run backwards and breaks monotonicity.
1854
+ // ─────────────────────────────────────
1855
+ (function () {
1856
+ var ys = [1, 1.1, 1.5, 1.9, 2];
1857
+ for (var b = 0; b < 2; b++) {
1858
+ var bx = [3, 10][b], prev = null, ok = true, firstBad = "";
1859
+ for (var i = 0; i < ys.length; i++) {
1860
+ var v = MetaNum.arrow(bx, ys[i], ys[i]);
1861
+ if (prev && !v.gte(prev)) { ok = false; if (!firstBad) firstBad = "y=" + ys[i]; }
1862
+ prev = v;
1863
+ }
1864
+ checkBool("arrow(" + bx + ",y,y) monotone for y = 1, 1.1, 1.5, 1.9, 2", ok, true,
1865
+ ok ? "" : " first break: " + firstBad);
1866
+ }
1867
+ // fine sweep across the whole 1 → 3 range, both endpoints included
1868
+ var fine = [1, 1.01, 1.25, 1.5, 1.75, 1.99, 2, 2.1, 2.5, 2.9, 3];
1869
+ for (var b2 = 0; b2 < 2; b2++) {
1870
+ var bx2 = [3, 10][b2], p2 = null, ok2 = true;
1871
+ for (var j = 0; j < fine.length; j++) {
1872
+ var v2 = MetaNum.arrow(bx2, fine[j], fine[j]);
1873
+ if (p2 && !v2.gte(p2)) ok2 = false;
1874
+ p2 = v2;
1875
+ }
1876
+ checkBool("arrow(" + bx2 + ",y,y) monotone over y = 1 … 3", ok2, true);
1877
+ }
1878
+ // the ends of an interval meet the neighbouring integers
1879
+ checkBool("10{1.99}1.99 < 10{2}2 = 10^^2",
1880
+ MetaNum.arrow(10, 1.99, 1.99).lt(MetaNum.arrow(10, 2, 2)), true);
1881
+ checkBool("10{2}2 < 10{2.1}2.1",
1882
+ MetaNum.arrow(10, 2, 2).lt(MetaNum.arrow(10, 2.1, 2.1)), true);
1883
+ checkBool("3{1.99}1.99 < 3{2}2 = 27",
1884
+ MetaNum.arrow(3, 1.99, 1.99).lt(MetaNum.arrow(3, 2, 2)), true);
1885
+ })();
1886
+
1050
1887
  // ─────────────────────────────────────
1051
1888
  // non-integer arguments at ordinal levels
1052
1889
  // expa(10,2.1) = 10{ω+1}2.1 = 10{ω}10{ω}(10^0.1)
@@ -1090,7 +1927,8 @@ function checkFormat(name, input, expected) {
1090
1927
  }
1091
1928
 
1092
1929
  // Small values & regular numbers
1093
- checkFormat("1E-1000000", "1E-1000000", "1.000E-1,000,000"); // sign=2 reciprocal
1930
+ // β = 1e6 ≥ 10^sciDecimalThreshold(3) → α keeps only its integer part
1931
+ checkFormat("1E-1000000", "1E-1000000", "1E-1,000,000"); // sign=2 reciprocal
1094
1932
  checkFormat("1E-100", "1E-100", "1.000E-100");
1095
1933
  checkFormat("0.123", "0.123", "0.123");
1096
1934
  checkFormat("456789", "456789", "456,789");
@@ -1131,7 +1969,7 @@ checkFormat("Defg200", "Defg200", "1.000Defg200");
1131
1969
  // Symbols and ε:
1132
1970
  checkFormat("!Aa300", "!Aa300", "!2.000Aa300");
1133
1971
  checkFormat("@Bb400", "@Bb400", "@1.000Bb400");
1134
- checkFormat("1ε500", "1ε500", "1.000ε500");
1972
+ checkFormat("1ε500", "1ε500", "1.000000ε500"); // epsilonSignificantDigits = 6
1135
1973
 
1136
1974
  // Reciprocal of large numbers (sign=2) format tests
1137
1975
  // smallNotationUseE=true → E-<formatted mag>
@@ -1149,7 +1987,7 @@ check("1/F10 parse", "1/F10", [[10000000000, 8]]);
1149
1987
  // 1/F500 = 10^(-log10(F500)) = 10^(-F499) → "E-1.000F499"
1150
1988
  // 1/G200 = 10^(-log10(G200)) ≈ 10^(-G200) → "E-1.000G200" (TMSI: G200>TMSI)
1151
1989
  // 1/F10 = 10^(-log10(F10)) = 10^(-F9) → "E-1.000F9"
1152
- checkFormat("E-EE1000 (useE=true)", "E-EE1000", "E-E1.000E1,000");
1990
+ checkFormat("E-EE1000 (useE=true)", "E-EE1000", "E-E1E1,000"); // β=1e6 ≥ 10^sciDecimalThreshold → integer α
1153
1991
  checkFormat("1/F500 (useE=true)", "1/F500", "E-1.000F499");
1154
1992
  checkFormat("1/G200 (useE=true)", "1/G200", "E-1.000G200");
1155
1993
  checkFormat("1/F10 (useE=true)", "1/F10", "E-1.000F9");
@@ -1162,7 +2000,7 @@ checkFormat("1/F10 (useE=true)", "1/F10", "E-1.000F9");
1162
2000
  var _FMT = (typeof FORMAT_OPTIONS !== "undefined" && FORMAT_OPTIONS) ? FORMAT_OPTIONS : require("./format-metanum.js").FORMAT_OPTIONS;
1163
2001
  var _savedUseE = _FMT.smallNotationUseE;
1164
2002
  _FMT.smallNotationUseE = false;
1165
- checkFormat("E-EE1000 (useE=false)", "E-EE1000", "EE1.000E1,000⁻¹");
2003
+ checkFormat("E-EE1000 (useE=false)", "E-EE1000", "EE1E1,000⁻¹"); // integer α (sciDecimalThreshold)
1166
2004
  checkFormat("1/F500 (useE=false)", "1/F500", "1.000F500⁻¹");
1167
2005
  checkFormat("1/G200 (useE=false)", "1/G200", "1.000G200⁻¹");
1168
2006
  checkFormat("1/F10 (useE=false)", "1/F10", "1.000F10⁻¹");
@@ -1180,14 +2018,54 @@ checkFormat("canonical all-8s keeps α=1 (G600)", "G600", "1.000G600");
1180
2018
  // carry — a cascade that would spell 20 letter types keeps its top two levels,
1181
2019
  // and a combination longer than multiLetterLimit switches to !αAaβ
1182
2020
  // ─────────────────────────────────────
1183
- checkFormat("20-row cascade compresses to one type", MetaNum.h10000(3, 10), "1.000Iccc19");
1184
- checkFormat("10-letter level carries to !…Aa9 (definition digits)", MetaNum.iter(3, 10), "!2.222Aa9");
1185
- checkFormat("5-letter level carries to !…Aa4 (definition digits)", m3.iter(5), "!2.222Aa4");
1186
- checkFormat("symbol carry stacks: apix(3,10) → @…Aa10", m3.aperixxate(10), "@2.000Aa10");
1187
- checkFormat("symbol carry stacks: apix(3,5) → @…Aa5", m3.aperixxate(5), "@2.000Aa5");
2021
+ checkFormat("20-row cascade compresses to two letter types", MetaNum.h10000(3, 10), "Iccc1.000Iccb10");
2022
+ // a level whose letter name is longer than multiLetterLimit carries to the
2023
+ // SINGLE-letter form !αΓβ — "!" adds one ω^ layer on top of the level Γ names
2024
+ checkFormat("10-letter level carries to !1.000M9", MetaNum.iter(3, 10), "!1.000M9");
2025
+ checkFormat("5-letter level carries to !1.000H4", m3.iter(5), "!1.000H4");
2026
+ checkFormat("symbol carry stacks: apix(3,10) → @1.000N10", m3.aperixxate(10), "@1.000N10");
2027
+ checkFormat("symbol carry stacks: apix(3,5) → @1.000I10", m3.aperixxate(5), "@1.000I10");
1188
2028
  checkFormat("short diagonal keeps the letter form", m3.aperixxate(3), "!2.000Aaaa10");
1189
2029
  checkFormat("cascade inner α stays [1,10) (AbAa600)", "AbAa600", "Ab1.000Aa600");
1190
2030
 
2031
+ // ─────────────────────────────────────
2032
+ // issue #14: stacking the SAME function must nest the letter — the rows are a
2033
+ // composition, so the top row leads and the row below it (which collapses from
2034
+ // its repeat count) keeps the single α and β:
2035
+ // poea(10,poea(10,100)) = Ad(Ad(99)) → "Ad1.000Ad99" (not "1.000Ad114")
2036
+ // ─────────────────────────────────────
2037
+ var stacked = [
2038
+ ["expande (ω+1)", "expande", "2.398Ab99", "Ab2.398Ab99"],
2039
+ ["multiexpande (ω+2)", "multiexpande", "1.000Ac99", "Ac1.000Ac99"],
2040
+ ["powerexpande (ω+3)", "powerexpande", "1.000Ad99", "Ad1.000Ad99"],
2041
+ ["explode (ω*2+1)", "explode", "1.000Bb99", "Bb1.000Bb99"],
2042
+ ["multiexplode (ω*2+2)", "multiexplode", "1.000Bc99", "Bc1.000Bc99"],
2043
+ ["megoexpande (ω²+1)", "megoexpande", "1.000Abb99", "Abb1.000Abb99"],
2044
+ ["multimegote (ω²+2)", "multimegote", "1.000Aac99", "Aac1.000Aac99"],
2045
+ ["powiainate (ω³+1)", "powiainate", "1.000Aaab99", "Aaab1.000Aaab99"]
2046
+ ];
2047
+ for (var si = 0; si < stacked.length; si++) {
2048
+ var sf = stacked[si][1], s1 = MetaNum(10)[sf](100), s2 = MetaNum(10)[sf](s1), s3 = MetaNum(10)[sf](s2);
2049
+ checkFormat("stacked " + stacked[si][0] + " (1st)", s1, stacked[si][2]);
2050
+ checkFormat("stacked " + stacked[si][0] + " (x2)", s2, stacked[si][3]);
2051
+ checkBool("stacked " + stacked[si][0] + " grows and keeps nesting",
2052
+ s2.gt(s1) && s3.gt(s2) && format(s3).indexOf(format(s2).slice(0, 2)) === 0, true);
2053
+ }
2054
+ // apea (ω*2) with an operand above MSI: apea(apea(a)) must stay visible
2055
+ (function () {
2056
+ var p1 = MetaNum(10).aperioexpande(MetaNum(1e16));
2057
+ var p2 = MetaNum(10).aperioexpande(p1);
2058
+ var p3 = MetaNum(10).aperioexpande(p2);
2059
+ checkFormat("apea(1e16) (ω*2 level = Ba)", p1, "Ba1.000E16");
2060
+ checkFormat("apea(apea(1e16)) nests", p2, "BaBa1.000E16");
2061
+ checkFormat("apea^3(1e16) nests further", p3, "BaBaBa1.000E16");
2062
+ checkBool("apea stacking is strictly monotone", p2.gt(p1) && p3.gt(p2), true);
2063
+ })();
2064
+ // a row coefficient above the grid (ω+81..99 from a ω*2 fundamental sequence)
2065
+ // has no letter — the level rounds UP to the next nameable one and the value
2066
+ // reads as level-arg: 10{ω*2}100 → Ba with the operation's own argument 100
2067
+ checkFormat("ω*2 level reads Ba (no coefficient wrap)", MetaNum(10).aperioexpande(100), "2.000Ba100");
2068
+
1191
2069
  // Γ-canonical law test: long descending count-1 chains
1192
2070
  // format at their bisect-exact letter on the engine's smooth arrow curve;
1193
2071
  // ordinal-row levels use Aa-notation (the engine's fractional-arg curve is
@@ -1428,6 +2306,97 @@ console.log("\n=== MetaNum(...).format() instance method ===");
1428
2306
  MetaNum("FE400").format(), format(MetaNum("FE400")));
1429
2307
  })();
1430
2308
 
2309
+ // ─────────────────────────────────────
2310
+ // FORMAT_OPTIONS: every option must take effect. Each check flips the option
2311
+ // and asserts the display changes along the option's documented meaning.
2312
+ // ─────────────────────────────────────
2313
+ console.log("\n=== FORMAT_OPTIONS take effect ===");
2314
+ (function () {
2315
+ var OPT = _FMT;
2316
+ var saved = {};
2317
+ function set(k, v) { if (!(k in saved)) saved[k] = OPT[k]; OPT[k] = v; }
2318
+ function restore() { for (var k in saved) OPT[k] = saved[k]; saved = {}; }
2319
+ function after(name, fn) {
2320
+ var r;
2321
+ try { r = fn(); } catch (e) { r = "threw " + e.message.slice(0, 40); }
2322
+ restore();
2323
+ checkBool(name, r === true, true, typeof r === "string" ? r : "");
2324
+ }
2325
+
2326
+ after("opt smallNotationUseE: ⁻¹ when false, E- when true", function () {
2327
+ set("smallNotationThreshold", 4);
2328
+ set("smallNotationUseE", false); var a = format(MetaNum(1e-10));
2329
+ set("smallNotationUseE", true); var b = format(MetaNum(1e-10));
2330
+ return a.indexOf("⁻¹") >= 0 && b.indexOf("E-") >= 0;
2331
+ });
2332
+ after("opt smallNotationThreshold moves the small-value boundary", function () {
2333
+ set("smallNotationThreshold", 3); var a = /E-4|⁻¹/.test(format(MetaNum(1e-4)));
2334
+ set("smallNotationThreshold", 5); var b = /E-4|⁻¹/.test(format(MetaNum(1e-4)));
2335
+ return a && !b;
2336
+ });
2337
+ after("opt decimalPlaces: 0 → '1', 2 → '1.40'", function () {
2338
+ set("decimalPlaces", 0); var a = format(MetaNum(1.4));
2339
+ set("decimalPlaces", 2); var b = format(MetaNum(1.4));
2340
+ return a === "1" && b === "1.40";
2341
+ });
2342
+ after("opt decimalThreshold: decimals drop past 10^n", function () {
2343
+ set("decimalThreshold", 3); var a = format(MetaNum(1234.56)).indexOf(".") < 0;
2344
+ set("decimalThreshold", 5); var b = format(MetaNum(1234.56)).indexOf(".") >= 0;
2345
+ return a && b;
2346
+ });
2347
+ after("opt useCommas toggles grouping", function () {
2348
+ set("useCommas", true); var a = format(MetaNum(1234567));
2349
+ set("useCommas", false); var b = format(MetaNum(1234567));
2350
+ return a === "1,234,567" && b === "1234567";
2351
+ });
2352
+ after("opt sciThreshold moves the E boundary", function () {
2353
+ set("sciThreshold", 9); var a = /E/.test(format(MetaNum(1e11)));
2354
+ set("sciThreshold", 12); var b = /E/.test(format(MetaNum(1e11)));
2355
+ return a && !b;
2356
+ });
2357
+ after("opt sciSignificantDigits: 1 → 1.2E10", function () {
2358
+ set("sciSignificantDigits", 1);
2359
+ return /^1\.2E/.test(format(MetaNum(1.2345e10)));
2360
+ });
2361
+ after("opt sciDecimalThreshold: β ≥ 10^n → integer α", function () {
2362
+ set("sciDecimalThreshold", 3); var a = /^1E100,000$/.test(format(MetaNum("1e100000")));
2363
+ set("sciDecimalThreshold", 9); var b = /^1\.000E100,000$/.test(format(MetaNum("1e100000")));
2364
+ return a && b;
2365
+ });
2366
+ after("opt singleLetterDigits: 1 → 2.0G, 3 → 2.045G", function () {
2367
+ set("singleLetterDigits", 1); var a = /^2\.0G/.test(format(MetaNum.arrow(3, 3, 7)));
2368
+ set("singleLetterDigits", 3); var b = /^2\.045G/.test(format(MetaNum.arrow(3, 3, 7)));
2369
+ return a && b;
2370
+ });
2371
+ after("opt repeatLetterThreshold: 3 collapses arrow(3,3,6), 5 spells FFFF", function () {
2372
+ set("repeatLetterThreshold", 3); var a = format(MetaNum.arrow(3, 3, 6)) === "2.045G5";
2373
+ set("repeatLetterThreshold", 5); var b = /^FFFF/.test(format(MetaNum.arrow(3, 3, 6)));
2374
+ return a && b;
2375
+ });
2376
+ after("opt multiLetterDigits: 1 → 2.0Aa, 3 → 2.000Aa", function () {
2377
+ set("multiLetterDigits", 1); var a = /^2\.0Aa/.test(format(MetaNum(10).arrow(23)(MetaNum(10))));
2378
+ set("multiLetterDigits", 3); var b = /^2\.000Aa/.test(format(MetaNum(10).arrow(23)(MetaNum(10))));
2379
+ return a && b;
2380
+ });
2381
+ after("opt multiLetterRepeatThreshold: 2 collapses 3×Aa, 5 keeps it", function () {
2382
+ set("multiLetterRepeatThreshold", 2); var a = /^1\.841Ab/.test(format(MetaNum("AaAaAa100")));
2383
+ set("multiLetterRepeatThreshold", 5); var b = /^AaAa/.test(format(MetaNum("AaAaAa100")));
2384
+ return a && b;
2385
+ });
2386
+ after("opt multiLetterLimit: 4 keeps Aaaa, 3 carries to !", function () {
2387
+ var v = MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(100), [100, 100, 50]);
2388
+ set("multiLetterLimit", 4); var a = /Aaaa/.test(format(v));
2389
+ set("multiLetterLimit", 3); var b = format(v).indexOf("!") === 0;
2390
+ return a && b;
2391
+ });
2392
+ after("opt epsilonSignificantDigits: 6 → 1.000000ε12, 0 → 1ε12", function () {
2393
+ var v = MetaNum("!!!!!!!!!!!!2.000Aa10");
2394
+ set("epsilonSignificantDigits", 6); var a = format(v) === "1.000000ε12";
2395
+ set("epsilonSignificantDigits", 0); var b = format(v) === "1ε12";
2396
+ return a && b;
2397
+ });
2398
+ })();
2399
+
1431
2400
  console.log("\n=== format tests Done ===");
1432
2401
 
1433
2402