metanum 2.0.0 → 2.0.1

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
@@ -872,6 +872,241 @@ checkBool("inv_powerexpande(1e16{ω+3}5,1e16)=5", mL16.powerexpande(5).inv_power
872
872
  // Small-x finite branch (base has no ω-row): inv_expande is exact too.
873
873
  checkBool("inv_expande(3{ω+1}5,3)=5", MetaNum(3).expande(5).inv_expande(MetaNum(3)).eq(MetaNum(5)), true);
874
874
 
875
+ // ─────────────────────────────────────
876
+ // h11/h12/h13 (ω+1 / ω+2 / ω+3): the TOP row carries y-2 applications
877
+ // (rule 2: x{α}y = x{α-1}^(y-2)(x{α-1}x)), and every row BELOW it comes from
878
+ // decomposing x{α-1}x — whose operand is the BASE x — so it carries x-2,
879
+ // the same count the finite r0 coefficients carry.
880
+ // 10{ω+3}20 = 10{ω+2}^18 10{ω+1}^8 10{ω}^8 10{ω}10
881
+ // → [8,0,1] [8,1,1] [18,2,1] (not three 18-rows)
882
+ // ─────────────────────────────────────
883
+ console.log("\n=== h11/h12/h13 count law ===");
884
+ checkBool("h13(10,20) = 10{ω+3}20 → [8,0,1] [8,1,1] [18,2,1]",
885
+ JSON.stringify(MetaNum(10).h13(20).array.slice(1)) === "[[8,0,1],[8,1,1],[18,2,1]]", true);
886
+ checkBool("h12(10,20) = 10{ω+2}20 → [8,0,1] [18,1,1]",
887
+ JSON.stringify(MetaNum(10).h12(20).array.slice(1)) === "[[8,0,1],[18,1,1]]", true);
888
+ checkBool("h11(10,20) = 10{ω+1}20 → [18,0,1] (single row stays y-2)",
889
+ JSON.stringify(MetaNum(10).h11(20).array.slice(1)) === "[[18,0,1]]", true);
890
+ // the lower rows are the BASE's count (x-2) and never move with y
891
+ var hRows = { 3: 1, 5: 3, 10: 8 };
892
+ for (var hx in hRows) {
893
+ var lowC = hRows[hx];
894
+ for (var hy = 3; hy <= 20; hy++) {
895
+ var hr = MetaNum(Number(hx)).h13(hy).array.slice(1);
896
+ var hok = JSON.stringify(hr) === JSON.stringify(
897
+ [[lowC, 0, 1], [lowC, 1, 1], [hy - 2, 2, 1]]);
898
+ if (!hok) { checkBool("h13(" + hx + "," + hy + ") rows", false, true); break; }
899
+ }
900
+ checkBool("h13(" + hx + ",3..20): lower rows fixed at x-2=" + lowC + ", top row y-2", true, true);
901
+ }
902
+ // y-monotone and level-monotone on the enumeration path
903
+ for (var hy2 = 3; hy2 <= 20; hy2++) {
904
+ var hprev = null, hch = true;
905
+ for (var hi = 0; hi < 3; hi++) {
906
+ var hv = MetaNum(10)[["h11", "h12", "h13"][hi]](hy2);
907
+ if (hprev && !hv.gt(hprev)) hch = false;
908
+ hprev = hv;
909
+ }
910
+ if (!hch) { checkBool("h11<h12<h13 at y=" + hy2, false, true); break; }
911
+ }
912
+ checkBool("h11 < h12 < h13 for y = 3..20", true, true);
913
+ checkBool("inv_powerexpande(10{ω+3}20,10) = 20",
914
+ MetaNum(10).h13(20).inv_powerexpande(MetaNum(10)).eq(MetaNum(20)), true);
915
+
916
+ // ─────────────────────────────────────
917
+ // Every level from ω*2 up follows the same law:
918
+ // successor α = β+1 → rows of x{β}x, each x-2, + one β-row carrying y-2
919
+ // limit α → rule 3 turns y into the fundamental-sequence index, so
920
+ // EVERY row carries x-2 (stay expanded up to MSI)
921
+ // ─────────────────────────────────────
922
+ console.log("\n=== h20+ : expanded form & count law ===");
923
+ // successor h21 (ω*2+1): 10{ω*2+1}20 = the ω..ω+9 cascade (x-2 = 8) + ω*2 row y-2
924
+ checkBool("h21(10,20) = [8,0..9,1] rows + [18,0,2]",
925
+ JSON.stringify(MetaNum(10).h21(20).array.slice(1)) ===
926
+ "[[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);
927
+ // h22 (ω*2+2): one more row at ω*2 (x-2), top row ω*2+1 carries y-2
928
+ checkBool("h22(10,20) = cascade + [8,0,2] + [18,1,2]",
929
+ JSON.stringify(MetaNum(10).h22(20).array.slice(1)) ===
930
+ "[[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);
931
+ // base-driven lower rows: x=3 → the cascade stops at ω+2 and every count is 1
932
+ checkBool("h21(3,20): lower rows carry x-2 = 1",
933
+ JSON.stringify(MetaNum(3).h21(20).array.slice(1)) === "[[1,0,1],[1,1,1],[1,2,1],[18,0,2]]", true);
934
+ // and the lower rows never move with y
935
+ (function () {
936
+ var lowRows = JSON.stringify(MetaNum(10).h21(3).array.slice(1, 11));
937
+ var ok = true;
938
+ for (var y = 4; y <= 20; y++) {
939
+ var r = MetaNum(10).h21(y).array.slice(1);
940
+ if (JSON.stringify(r.slice(0, 10)) !== lowRows || r[10][0] !== y - 2) ok = false;
941
+ }
942
+ checkBool("h21(10,3..20): lower rows fixed at 8, ω*2 row = y-2", ok, true);
943
+ })();
944
+
945
+ // limit ops stay EXPANDED for 100 < y ≤ MSI (no one-row marker)
946
+ (function () {
947
+ var MSI = 9007199254740991;
948
+ for (var i = 0; i < 3; i++) {
949
+ var lf = ["h20", "h30", "h100"][i];
950
+ for (var k = 0; k < 3; k++) {
951
+ var ly = [101, 1000, MSI][k];
952
+ var lr = MetaNum(10)[lf](ly);
953
+ // maxRows-1 ordinal rows, base collapsed to [10], first kept row marked +1
954
+ checkBool(lf + "(10," + ly + ") stays expanded (" + (lr.array.length - 1) + " rows)",
955
+ lr.array.length - 1 === MetaNum.maxRows - 1 && lr.array[0].length === 1 &&
956
+ lr.array[1][0] === 9, true);
957
+ }
958
+ }
959
+ // top row carries the fundamental-sequence index y-1
960
+ checkBool("h20(10,1000) top row = ω+999",
961
+ JSON.stringify(MetaNum(10).h20(1000).array[19]) === "[8,999,1]", true);
962
+ checkBool("h20(10,MSI) top row = ω+(MSI-1)",
963
+ MetaNum(10).h20(MSI).array[19][1] === MSI - 1, true);
964
+ })();
965
+
966
+ // ─────────────────────────────────────
967
+ // Fractional y for every level from ω*2 up (README "Non-integer arguments"):
968
+ // a limit level takes its fundamental sequence at the FULL fractional index and
969
+ // the fractional coefficients resolve against the base x (ω^(k)·(c+f) =
970
+ // ω^(k)·c + ω^(k-1)·(x·f)); a leftover fractional constant becomes a fractional
971
+ // argument one level up: x{γ+f}x = x{γ+1}(2·(x/2)^f).
972
+ // ─────────────────────────────────────
973
+ console.log("\n=== h20+ with fractional y ===");
974
+ checkBool("10{ω*2}2.1 = 10{ω+3}(2*5^0.1)",
975
+ MetaNum(10).h20(2.1).eq(MetaNum(10).powerexpande(2 * Math.pow(5, 0.1))), true);
976
+ checkBool("3{ω*2}2.1 = 3{ω+3}(2*1.5^0.1)",
977
+ MetaNum(3).h20(2.1).eq(MetaNum(3).powerexpande(2 * Math.pow(1.5, 0.1))), true);
978
+ checkBool("10{ω^2}2.1 = 10{ω*2+1}10",
979
+ MetaNum(10).h100(2.1).eq(MetaNum(10).h21(10)), true);
980
+ checkBool("3{ω^2}2.1 = 3{ω*2+1}(2*1.5^0.3)",
981
+ MetaNum(3).h100(2.1).eq(MetaNum(3).h21(2 * Math.pow(1.5, 0.3))), true);
982
+ // 10{ω^ω}2.1 = 10{ω^2.1}10 = 10{ω^2*10^0.1}10 → ω^2+ω*2+5.8925… → next level
983
+ (function () {
984
+ var rf = MetaNum._resolveFracLevel([0, 0, Math.pow(10, 0.1)], 10);
985
+ var up = rf.ord.slice(); up[0] += 1;
986
+ checkBool("10{ω^ω}2.1 = 10{ω^2+ω*2+6}(2*5^0.8925…) (level " +
987
+ JSON.stringify(up) + ")",
988
+ JSON.stringify(up) === "[6,2,1]" &&
989
+ MetaNum(10).iterate(2.1).eq(
990
+ MetaNum._hyperopFromOrdinal(MetaNum(10), MetaNum(2 * Math.pow(5, rf.frac)), up)), true);
991
+ })();
992
+ // the fraction is not thrown away: y and floor(y) differ
993
+ checkBool("h20(10,2.1) != h20(10,2) and lies strictly between 2 and 3",
994
+ MetaNum(10).h20(2.1).gt(MetaNum(10).h20(2)) &&
995
+ MetaNum(10).h20(2.1).lt(MetaNum(10).h20(3)), true);
996
+ checkBool("h100(10,10.25) lies strictly between 10 and 11",
997
+ MetaNum(10).h100(10.25).gt(MetaNum(10).h100(10)) &&
998
+ MetaNum(10).h100(10.25).lt(MetaNum(10).h100(11)), true);
999
+ // monotone in y across the integers for the LIMIT levels
1000
+ (function () {
1001
+ var limOps = ["h20", "h30", "h100", "h110", "h1000", "h10000", "iterate"];
1002
+ var bad = 0, firstBad = "";
1003
+ for (var i = 0; i < limOps.length; i++) {
1004
+ for (var b = 0; b < 2; b++) {
1005
+ var bx = [3, 10][b];
1006
+ for (var m = 2; m <= 10; m++) {
1007
+ var a = MetaNum(bx)[limOps[i]](m), c = MetaNum(bx)[limOps[i]](m + 0.001);
1008
+ var d = MetaNum(bx)[limOps[i]](m + 0.5), e = MetaNum(bx)[limOps[i]](m + 1);
1009
+ if (!(c.gte(a) && d.gte(c) && e.gte(d))) {
1010
+ bad++; if (!firstBad) firstBad = limOps[i] + "(" + bx + "," + m + ")";
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ checkBool("limit levels monotone across fractional y", bad === 0, true,
1016
+ bad === 0 ? "" : " first failure: " + firstBad);
1017
+ })();
1018
+ // the SUCCESSOR rule with a fractional argument is written in the same expanded
1019
+ // form: the α-1 row carries the m applications (ceil(y)-2 = m-1) and the cascade
1020
+ // below it is generated at the fundamental-sequence index x^f, so the value grows
1021
+ // with f inside the interval and converges to the next integer at f → 1
1022
+ checkBool("h21(10,2.001) < h21(10,2.5) < h21(10,2.99) < h21(10,3)",
1023
+ MetaNum(10).h21(2.001).lt(MetaNum(10).h21(2.5)) &&
1024
+ MetaNum(10).h21(2.5).lt(MetaNum(10).h21(2.99)) &&
1025
+ MetaNum(10).h21(2.99).lt(MetaNum(10).h21(3)), true);
1026
+ checkBool("h31(10,5.1) < h31(10,5.5) < h31(10,5.9) < h31(10,6)",
1027
+ MetaNum(10).h31(5.1).lt(MetaNum(10).h31(5.5)) &&
1028
+ MetaNum(10).h31(5.5).lt(MetaNum(10).h31(5.9)) &&
1029
+ 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
1033
+ (function () {
1034
+ var ok = true;
1035
+ for (var k = 0; k < 6; k++) {
1036
+ var fy = [2.001, 2.1, 2.5, 3.5, 10.25, 100.75][k];
1037
+ var rr = MetaNum(10).h21(fy).array.slice(1);
1038
+ 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;
1040
+ }
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);
1046
+ checkBool("h21(10,2.999999) → h21(10,3) from below",
1047
+ MetaNum(10).h21(2.999999).lt(MetaNum(10).h21(3)) &&
1048
+ MetaNum(10).h21(2.999999).gt(MetaNum(10).h21(2)), true);
1049
+ })();
1050
+ // row coefficients stay integer (a fractional constant is carried to the base)
1051
+ (function () {
1052
+ var ok = true;
1053
+ for (var f of ["h21", "h22", "h31", "h101", "h111", "h1001"]) {
1054
+ for (var k = 0; k < 5; k++) {
1055
+ var fy = [2.001, 2.5, 3.5, 10.25, 100.75][k];
1056
+ var arr = MetaNum(10)[f](fy).array;
1057
+ for (var i = 1; i < arr.length; i++) {
1058
+ for (var j = 1; j < arr[i].length; j++) {
1059
+ if (arr[i][j] !== Math.floor(arr[i][j])) ok = false;
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+ checkBool("fractional y: every ordinal row coefficient is an integer", ok, true);
1065
+ })();
1066
+
1067
+ // no crash / NaN over the whole range with fractional operands
1068
+ (function () {
1069
+ var allOps = ["h20","h21","h22","h30","h31","h100","h101","h110","h111",
1070
+ "h1000","h1001","h10000","iterate"];
1071
+ var bad = 0;
1072
+ for (var i = 0; i < allOps.length; i++) {
1073
+ for (var b = 0; b < 4; b++) {
1074
+ var bx = [3, 10, 1e6, 1e16][b];
1075
+ for (var k = 0; k < 5; k++) {
1076
+ var by = [2.0001, 2.5, 3.5, 10.25, 1000.5][k];
1077
+ try {
1078
+ var v = MetaNum(bx)[allOps[i]](by);
1079
+ if (v.isNaN()) bad++;
1080
+ } catch (e) { bad++; }
1081
+ }
1082
+ }
1083
+ }
1084
+ checkBool("no throw/NaN for fractional y over h20..h10000 + iterate", bad === 0, true);
1085
+ })();
1086
+
1087
+ // inverse round-trip over the whole small-operand range, successors and limits
1088
+ (function () {
1089
+ var pairs = [["h11","inv_expande"],["h12","inv_multiexpande"],["h13","inv_powerexpande"],
1090
+ ["h20","inv_aperioexpande"],["h21","inv_explode"],["h22","inv_multiexplode"],
1091
+ ["h30","inv_aperioexplode"],["h31","inv_detonate"],["h100","inv_aperionate"],
1092
+ ["h101","inv_megote"],["h110","inv_aperimegote"],["h111","inv_megoexpande"],
1093
+ ["h1000","inv_aperiatote"],["h1001","inv_powiainate"]];
1094
+ var bad = 0, firstBad = "";
1095
+ for (var i = 0; i < pairs.length; i++) {
1096
+ for (var b = 0; b < 3; b++) {
1097
+ var bx = [3, 5, 10][b];
1098
+ for (var y = 3; y <= 20; y++) {
1099
+ var v = MetaNum(bx)[pairs[i][0]](y);
1100
+ if (!v[pairs[i][1]](MetaNum(bx)).eq(MetaNum(y))) {
1101
+ bad++; if (!firstBad) firstBad = pairs[i][0] + "(" + bx + "," + y + ")";
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+ checkBool("inverse round-trip for h11..h1001 (x=3/5/10, y=3..20)", bad === 0,
1107
+ true, bad === 0 ? "" : " first failure: " + firstBad);
1108
+ })();
1109
+
875
1110
  // ─────────────────────────────────────
876
1111
  // engine arrays: the (y-2) count law
877
1112
  // x{L}y = x{L-1}^{y-2}(x{L-1}x): 10{25}10 → count-8 rows/cols (the
@@ -1180,7 +1415,7 @@ checkFormat("canonical all-8s keeps α=1 (G600)", "G600", "1.000G600");
1180
1415
  // carry — a cascade that would spell 20 letter types keeps its top two levels,
1181
1416
  // and a combination longer than multiLetterLimit switches to !αAaβ
1182
1417
  // ─────────────────────────────────────
1183
- checkFormat("20-row cascade compresses to one type", MetaNum.h10000(3, 10), "1.000Iccc19");
1418
+ checkFormat("20-row cascade compresses to two letter types", MetaNum.h10000(3, 10), "Iccc1.000Iccb10");
1184
1419
  checkFormat("10-letter level carries to !…Aa9 (definition digits)", MetaNum.iter(3, 10), "!2.222Aa9");
1185
1420
  checkFormat("5-letter level carries to !…Aa4 (definition digits)", m3.iter(5), "!2.222Aa4");
1186
1421
  checkFormat("symbol carry stacks: apix(3,10) → @…Aa10", m3.aperixxate(10), "@2.000Aa10");
@@ -1188,6 +1423,43 @@ checkFormat("symbol carry stacks: apix(3,5) → @…Aa5", m3.aperixxate(5), "@2.
1188
1423
  checkFormat("short diagonal keeps the letter form", m3.aperixxate(3), "!2.000Aaaa10");
1189
1424
  checkFormat("cascade inner α stays [1,10) (AbAa600)", "AbAa600", "Ab1.000Aa600");
1190
1425
 
1426
+ // ─────────────────────────────────────
1427
+ // issue #14: stacking the SAME function must nest the letter — the rows are a
1428
+ // composition, so the top row leads and the row below it (which collapses from
1429
+ // its repeat count) keeps the single α and β:
1430
+ // poea(10,poea(10,100)) = Ad(Ad(99)) → "Ad1.000Ad99" (not "1.000Ad114")
1431
+ // ─────────────────────────────────────
1432
+ var stacked = [
1433
+ ["expande (ω+1)", "expande", "2.398Ab99", "Ab2.398Ab99"],
1434
+ ["multiexpande (ω+2)", "multiexpande", "1.000Ac99", "Ac1.000Ac99"],
1435
+ ["powerexpande (ω+3)", "powerexpande", "1.000Ad99", "Ad1.000Ad99"],
1436
+ ["explode (ω*2+1)", "explode", "1.000Bb99", "Bb1.000Bb99"],
1437
+ ["multiexplode (ω*2+2)", "multiexplode", "1.000Bc99", "Bc1.000Bc99"],
1438
+ ["megoexpande (ω²+1)", "megoexpande", "1.000Abb99", "Abb1.000Abb99"],
1439
+ ["multimegote (ω²+2)", "multimegote", "1.000Aac99", "Aac1.000Aac99"],
1440
+ ["powiainate (ω³+1)", "powiainate", "1.000Aaab99", "Aaab1.000Aaab99"]
1441
+ ];
1442
+ for (var si = 0; si < stacked.length; si++) {
1443
+ var sf = stacked[si][1], s1 = MetaNum(10)[sf](100), s2 = MetaNum(10)[sf](s1), s3 = MetaNum(10)[sf](s2);
1444
+ checkFormat("stacked " + stacked[si][0] + " (1st)", s1, stacked[si][2]);
1445
+ checkFormat("stacked " + stacked[si][0] + " (x2)", s2, stacked[si][3]);
1446
+ checkBool("stacked " + stacked[si][0] + " grows and keeps nesting",
1447
+ s2.gt(s1) && s3.gt(s2) && format(s3).indexOf(format(s2).slice(0, 2)) === 0, true);
1448
+ }
1449
+ // apea (ω*2) with an operand above MSI: apea(apea(a)) must stay visible
1450
+ (function () {
1451
+ var p1 = MetaNum(10).aperioexpande(MetaNum(1e16));
1452
+ var p2 = MetaNum(10).aperioexpande(p1);
1453
+ var p3 = MetaNum(10).aperioexpande(p2);
1454
+ checkFormat("apea(1e16) (ω*2 level = Ba)", p1, "Ba1.000E16");
1455
+ checkFormat("apea(apea(1e16)) nests", p2, "BaBa1.000E16");
1456
+ checkFormat("apea^3(1e16) nests further", p3, "BaBaBa1.000E16");
1457
+ checkBool("apea stacking is strictly monotone", p2.gt(p1) && p3.gt(p2), true);
1458
+ })();
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");
1462
+
1191
1463
  // Γ-canonical law test: long descending count-1 chains
1192
1464
  // format at their bisect-exact letter on the engine's smooth arrow curve;
1193
1465
  // ordinal-row levels use Aa-notation (the engine's fractional-arg curve is