metanum 2.0.1 → 2.0.3

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];
@@ -1027,26 +1018,74 @@ checkBool("h31(10,5.1) < h31(10,5.5) < h31(10,5.9) < h31(10,6)",
1027
1018
  MetaNum(10).h31(5.1).lt(MetaNum(10).h31(5.5)) &&
1028
1019
  MetaNum(10).h31(5.5).lt(MetaNum(10).h31(5.9)) &&
1029
1020
  MetaNum(10).h31(5.9).lt(MetaNum(10).h31(6)), true);
1030
- // the expanded form keeps the count law: every row below the top one carries
1031
- // x-2, and the α-1 row carries y-2 — a fractional y-2, which is what makes
1032
- // h21(10,2+ε) start at 10{ω*2}10 and grow to 10{ω*2}10{ω*2}10 at y = 3
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
1033
1025
  (function () {
1034
1026
  var ok = true;
1035
1027
  for (var k = 0; k < 6; k++) {
1036
1028
  var fy = [2.001, 2.1, 2.5, 3.5, 10.25, 100.75][k];
1037
1029
  var rr = MetaNum(10).h21(fy).array.slice(1);
1038
1030
  for (var i = 0; i < rr.length - 1; i++) if (rr[i][0] !== 8) ok = false;
1039
- if (Math.abs(rr[rr.length - 1][0] - (fy - 2)) > 1e-9) ok = false;
1031
+ if (rr[rr.length - 1][0] !== Math.ceil(fy) - 2) ok = false;
1040
1032
  }
1041
- checkBool("h21(10, fractional): lower rows = x-2 = 8, ω*2 row = y-2", ok, true);
1042
- // the endpoints: 2+ε is (just above) h21(10,2) = 10{ω*2}10, 3-ε → h21(10,3)
1043
- checkBool("h21(10,2+1e-15) = 10{ω*2}10 cascade + a vanishing ω*2 row",
1044
- MetaNum(10).h21(2 + 1e-15).array.length === 12 &&
1045
- MetaNum(10).h21(2 + 1e-15).array[11][0] < 1e-9, true);
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);
1046
1039
  checkBool("h21(10,2.999999) → h21(10,3) from below",
1047
1040
  MetaNum(10).h21(2.999999).lt(MetaNum(10).h21(3)) &&
1048
1041
  MetaNum(10).h21(2.999999).gt(MetaNum(10).h21(2)), true);
1049
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
+
1050
1089
  // row coefficients stay integer (a fractional constant is carried to the base)
1051
1090
  (function () {
1052
1091
  var ok = true;
@@ -1243,8 +1282,12 @@ checkBool("hardy(1e15) < hardy(1e16) < hardy(1e17)",
1243
1282
  // -- MetaNum-object input (v2.0): structures shadow to their ordinal --
1244
1283
  // power tower 10^^k → ω^ω^…^ω (k ω's) → H there = 10.epsilonate(k);
1245
1284
  // letter/ordinal values sit at their own Hardy position → themselves
1246
- checkBool("hardy(10^^100) = 10{ω^…^ω(100 ωs)}10 = epsilonate(100)",
1247
- 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);
1248
1291
  checkBool("hardy(MetaNum G600) = Infinity",
1249
1292
  MetaNum.hardy(MetaNum("G600")).eq(MetaNum.infinity), true);
1250
1293
  checkBool("hardy(MetaNum arrow(3,9,3)) = Infinity",
@@ -1255,12 +1298,533 @@ checkBool("hardy(10{3}10) = Infinity (> 10^^MSI)",
1255
1298
  // -- the engine limit: hardy(10^^MSI) = 10{ω^…^ω(MSI ω's)}10 (v2.0) --
1256
1299
  (function () {
1257
1300
  var MSI = 9007199254740991;
1258
- checkBool("hardy(10^^MSI) = MetaNum limit 10{ω^…^ω(MSI ω's)}10",
1259
- MetaNum.hardy(MetaNum(10).tetr(MSI)).eq(MetaNum(10).epsilonate(MSI)), true);
1260
- checkBool("hardy(10^^(MSI-1)) = epsilonate(MSI-1) (just below the cap)",
1261
- 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);
1397
+ })();
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)
1262
1600
  })();
1263
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
+
1264
1828
  // ─────────────────────────────────────
1265
1829
  // fractional hyperoperation levels
1266
1830
  // level n+f interpolates geometrically (README rule i): 10{n+f}10 =
@@ -1282,6 +1846,44 @@ console.log("\n=== fractional levels ===");
1282
1846
  MetaNum(3).arrow(2.5)(3).lt(MetaNum(3).arrow(3)(3)), true);
1283
1847
  })();
1284
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
+
1285
1887
  // ─────────────────────────────────────
1286
1888
  // non-integer arguments at ordinal levels
1287
1889
  // expa(10,2.1) = 10{ω+1}2.1 = 10{ω}10{ω}(10^0.1)
@@ -1325,7 +1927,8 @@ function checkFormat(name, input, expected) {
1325
1927
  }
1326
1928
 
1327
1929
  // Small values & regular numbers
1328
- 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
1329
1932
  checkFormat("1E-100", "1E-100", "1.000E-100");
1330
1933
  checkFormat("0.123", "0.123", "0.123");
1331
1934
  checkFormat("456789", "456789", "456,789");
@@ -1366,7 +1969,7 @@ checkFormat("Defg200", "Defg200", "1.000Defg200");
1366
1969
  // Symbols and ε:
1367
1970
  checkFormat("!Aa300", "!Aa300", "!2.000Aa300");
1368
1971
  checkFormat("@Bb400", "@Bb400", "@1.000Bb400");
1369
- checkFormat("1ε500", "1ε500", "1.000ε500");
1972
+ checkFormat("1ε500", "1ε500", "1.000000ε500"); // epsilonSignificantDigits = 6
1370
1973
 
1371
1974
  // Reciprocal of large numbers (sign=2) format tests
1372
1975
  // smallNotationUseE=true → E-<formatted mag>
@@ -1384,7 +1987,7 @@ check("1/F10 parse", "1/F10", [[10000000000, 8]]);
1384
1987
  // 1/F500 = 10^(-log10(F500)) = 10^(-F499) → "E-1.000F499"
1385
1988
  // 1/G200 = 10^(-log10(G200)) ≈ 10^(-G200) → "E-1.000G200" (TMSI: G200>TMSI)
1386
1989
  // 1/F10 = 10^(-log10(F10)) = 10^(-F9) → "E-1.000F9"
1387
- 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 α
1388
1991
  checkFormat("1/F500 (useE=true)", "1/F500", "E-1.000F499");
1389
1992
  checkFormat("1/G200 (useE=true)", "1/G200", "E-1.000G200");
1390
1993
  checkFormat("1/F10 (useE=true)", "1/F10", "E-1.000F9");
@@ -1397,7 +2000,7 @@ checkFormat("1/F10 (useE=true)", "1/F10", "E-1.000F9");
1397
2000
  var _FMT = (typeof FORMAT_OPTIONS !== "undefined" && FORMAT_OPTIONS) ? FORMAT_OPTIONS : require("./format-metanum.js").FORMAT_OPTIONS;
1398
2001
  var _savedUseE = _FMT.smallNotationUseE;
1399
2002
  _FMT.smallNotationUseE = false;
1400
- checkFormat("E-EE1000 (useE=false)", "E-EE1000", "EE1.000E1,000⁻¹");
2003
+ checkFormat("E-EE1000 (useE=false)", "E-EE1000", "EE1E1,000⁻¹"); // integer α (sciDecimalThreshold)
1401
2004
  checkFormat("1/F500 (useE=false)", "1/F500", "1.000F500⁻¹");
1402
2005
  checkFormat("1/G200 (useE=false)", "1/G200", "1.000G200⁻¹");
1403
2006
  checkFormat("1/F10 (useE=false)", "1/F10", "1.000F10⁻¹");
@@ -1416,10 +2019,12 @@ checkFormat("canonical all-8s keeps α=1 (G600)", "G600", "1.000G600");
1416
2019
  // and a combination longer than multiLetterLimit switches to !αAaβ
1417
2020
  // ─────────────────────────────────────
1418
2021
  checkFormat("20-row cascade compresses to two letter types", MetaNum.h10000(3, 10), "Iccc1.000Iccb10");
1419
- checkFormat("10-letter level carries to !…Aa9 (definition digits)", MetaNum.iter(3, 10), "!2.222Aa9");
1420
- checkFormat("5-letter level carries to !…Aa4 (definition digits)", m3.iter(5), "!2.222Aa4");
1421
- checkFormat("symbol carry stacks: apix(3,10) → @…Aa10", m3.aperixxate(10), "@2.000Aa10");
1422
- checkFormat("symbol carry stacks: apix(3,5) → @…Aa5", m3.aperixxate(5), "@2.000Aa5");
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");
1423
2028
  checkFormat("short diagonal keeps the letter form", m3.aperixxate(3), "!2.000Aaaa10");
1424
2029
  checkFormat("cascade inner α stays [1,10) (AbAa600)", "AbAa600", "Ab1.000Aa600");
1425
2030
 
@@ -1456,9 +2061,10 @@ for (var si = 0; si < stacked.length; si++) {
1456
2061
  checkFormat("apea^3(1e16) nests further", p3, "BaBaBa1.000E16");
1457
2062
  checkBool("apea stacking is strictly monotone", p2.gt(p1) && p3.gt(p2), true);
1458
2063
  })();
1459
- // a row coefficient above 25 (ω+81 from a ω*2 fundamental sequence) has no
1460
- // letter in the grid — clamp down instead of wrapping into a higher letter
1461
- checkFormat("ω*2 level reads Ba (no coefficient wrap)", MetaNum(10).aperioexpande(100), "1.000Ba154");
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");
1462
2068
 
1463
2069
  // Γ-canonical law test: long descending count-1 chains
1464
2070
  // format at their bisect-exact letter on the engine's smooth arrow curve;
@@ -1700,6 +2306,97 @@ console.log("\n=== MetaNum(...).format() instance method ===");
1700
2306
  MetaNum("FE400").format(), format(MetaNum("FE400")));
1701
2307
  })();
1702
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
+
1703
2400
  console.log("\n=== format tests Done ===");
1704
2401
 
1705
2402