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/metanum.js CHANGED
@@ -2916,7 +2916,7 @@
2916
2916
  // enumeration representation (one row per ordinal below α, count = y-2)
2917
2917
  // - everything else (any operand beyond MSI, x = 2, or enumeration too large):
2918
2918
  // unified ordinal engine (README rules 1-4)
2919
- Q._hyperopFromOrdinalRaw = function (x, y, coeffs) {
2919
+ Q._hyperopFromOrdinalRaw = function (x, y, coeffs, skipFrac) {
2920
2920
  x = new MetaNum(x);
2921
2921
  y = new MetaNum(y);
2922
2922
  if (x.isNaN() || y.isNaN()) return MetaNum.NaN.clone();
@@ -2924,8 +2924,29 @@
2924
2924
  if (y.lte(MetaNum.ZERO)) return MetaNum.NaN.clone();
2925
2925
  if (x.eq(MetaNum.ONE)) return MetaNum.ONE.clone();
2926
2926
  if (y.eq(MetaNum.ONE)) return x.clone();
2927
+
2927
2928
  var cf0 = trimCoeffs((coeffs || []).slice());
2928
2929
  if (cf0.length === 0) return x.mul(y); // rule 1: x{0}y = x*y
2930
+ // Fractional level coefficients come from a fundamental sequence taken at a
2931
+ // non-integer index. On the diagonal ω = x, so ω^(k)·(c+f) = ω^(k)·c +
2932
+ // ω^(k-1)·(x·f) — resolve top-down until only the constant term can still be
2933
+ // fractional, then turn that leftover into a fractional argument one level
2934
+ // higher: x{γ+f}x = x{γ+1}(2·(x/2)^f) (README: 10{ω*2}2.1 = 10{ω+2.1}10 =
2935
+ // 10{ω+3}2·5^0.1; continuous at f=0 since x{γ+1}2 = x{γ}x).
2936
+ if (coeffsHaveFraction(cf0)) {
2937
+ var xnf = x.toNumber();
2938
+ if (isFinite(xnf) && xnf >= 2) {
2939
+ var rs = Q._resolveFracLevel(cf0, xnf);
2940
+ if (rs.frac > 0) {
2941
+ var up = rs.ord.slice();
2942
+ while (up.length < 1) up.push(0);
2943
+ up[0] = (up[0] || 0) + 1;
2944
+ var argF = 2 * Math.pow(xnf / 2, rs.frac);
2945
+ return Q._hyperopFromOrdinal(x, new MetaNum(argF), trimCoeffs(up));
2946
+ }
2947
+ cf0 = rs.ord;
2948
+ }
2949
+ }
2929
2950
  // Non-integer argument at ordinal levels (issues.md addition 3):
2930
2951
  // successor level α (c0 > 0): x{α}(m+f) = x{α-1} applied m times to x^f
2931
2952
  // (e.g. expa(10,2.1) = 10{ω+1}2.1 = 10{ω}10{ω}(10^0.1); continuous at
@@ -2944,13 +2965,33 @@
2944
2965
  // exactly and composition identities stay stable across paths
2945
2966
  var yf = new MetaNum(Number((yFracNum - ym).toPrecision(15)));
2946
2967
  if ((cf0[0] || 0) > 0) {
2968
+ var xnfS = x.toNumber();
2969
+ var xOkS = isFinite(xnfS) && xnfS >= 3 && xnfS <= MAX_SAFE_INTEGER;
2970
+ // α-1 ≥ ω*2 only: for the ω+1/ω+2/ω+3 levels the applications after the
2971
+ // first still have an operand inside MSI, so the engine evaluates them by
2972
+ // the definition (10{ω}(10{ω}(10^0.1))) and that identity is exact.
2973
+ var pred = succCoeffs(cf0);
2974
+ var bigLevel = (pred[1] || 0) >= 2;
2975
+ for (var bi = 2; bi < pred.length; bi++) if ((pred[bi] || 0) > 0) bigLevel = true;
2976
+ if (xOkS && bigLevel && ym <= 100000) {
2977
+ // successor rule written in the EXPANDED form (the same generator the
2978
+ // integer arguments use): x{α}(m+f) = x{α-1}^m(x^f) means the α-1 row
2979
+ // carries m applications — ceil(y)-2 = m-1 — and the cascade below it
2980
+ // takes its first fundamental sequence at the fractional start value
2981
+ // x^f, which is what interpolates the cascade depth between the two
2982
+ // neighbouring integers (exactly what the finite levels do: 10{3}2.1
2983
+ // starts from 10^0.1). The base is x{x^f}x when that descent is the
2984
+ // ω one (ω+1 levels), x{x}x otherwise.
2985
+ return Q._expandedHyperop(x, new MetaNum(ym + 1), cf0,
2986
+ Math.pow(xnfS, Number((yFracNum - ym).toPrecision(15))));
2987
+ }
2947
2988
  if (ym <= 100000) {
2948
2989
  // successor rule: x{α}(m+f) = x{α-1} applied m times to x^f
2949
2990
  var succ = cf0.slice();
2950
2991
  succ[0] = succ[0] - 1;
2951
2992
  succ = trimCoeffs(succ);
2952
2993
  var v = x.pow(yf);
2953
- for (var it = 0; it < ym; it++) v = Q._hyperopFromOrdinalRaw(x, v, succ);
2994
+ for (var it = 0; it < ym; it++) v = Q._hyperopFromOrdinalRaw(x, v, succ, true);
2954
2995
  return v;
2955
2996
  }
2956
2997
  // Successor level with an iteration count beyond direct evaluation:
@@ -2959,9 +3000,16 @@
2959
3000
  return Q._ordinalHyperop(x, y, cf0);
2960
3001
  }
2961
3002
  if (ym >= 1) {
2962
- // limit level: fundamental sequence at m = floor(y); rule 3 puts the
2963
- // base x as the final operand: x{λ}y = x{λ[m]}x
2964
- return Q._hyperopFromOrdinalRaw(x, x, fsCoeffs(cf0, ym));
3003
+ // limit level: rule 3 with the FULL (fractional) index — x{λ}y =
3004
+ // x{λ[y]}x. λ[y] can carry fractional coefficients (10{ω*2}2.1 =
3005
+ // 10{ω+2.1}10), which the fractional-level rule above resolves, so the
3006
+ // fraction of y is never thrown away (flooring it would make
3007
+ // h20(10,2.1) = h20(10,2)).
3008
+ // Nested (skipFrac: the successor rule is already iterating a fractional
3009
+ // start value) the index is floored — otherwise every application would
3010
+ // take another fractional fundamental sequence and recurse forever.
3011
+ var fsIdx = skipFrac ? Math.floor(yFracNum) : yFracNum;
3012
+ return Q._hyperopFromOrdinalRaw(x, x, fsCoeffs(cf0, fsIdx), skipFrac);
2965
3013
  }
2966
3014
  }
2967
3015
  var cf = cf0;
@@ -2970,9 +3018,11 @@
2970
3018
  if (deg >= 1 && x.array.length === 1 && x.layer === 0
2971
3019
  && y.array.length === 1 && y.layer === 0) {
2972
3020
  var xC = metaFiniteCount(x), yC = metaFiniteCount(y);
2973
- if (xC >= 3 && xC <= MAX_SAFE_INTEGER && yC >= 3 && yC <= MAX_SAFE_INTEGER
2974
- && Math.pow(yC, deg) <= MetaNum.maxRows) {
2975
- return Q._enumHyperop(x, y, cf);
3021
+ // Finite operands in [3, MSI] always take the EXPANDED cascade form — a
3022
+ // limit operation keeps its fundamental-sequence rows all the way up to
3023
+ // MSI (100 < y ≤ MSI included), truncated to the largest maxRows-1 rows.
3024
+ if (xC >= 3 && xC <= MAX_SAFE_INTEGER && yC >= 3 && yC <= MAX_SAFE_INTEGER) {
3025
+ return Q._expandedHyperop(x, y, cf);
2976
3026
  }
2977
3027
  }
2978
3028
  return Q._ordinalHyperop(x, y, cf);
@@ -2992,214 +3042,146 @@
2992
3042
  return r;
2993
3043
  };
2994
3044
 
2995
- // Legacy (HEAD-compatible) enumeration-based hyperoperation for small operands.
2996
- Q._enumHyperop = function (x, y, coeffs) {
2997
- var xNum = x.toNumber();
2998
- var yNum = y.toNumber();
2999
- var maxRows = MetaNum.maxRows; // 100
3000
- var baseArrowLevel = Math.max(Math.floor(isFinite(xNum) ? xNum : 3), 3);
3001
- var baseResult = x.arrow(baseArrowLevel)(x);
3002
- var rows = Q._generateOrdinalRows(coeffs,
3003
- isFinite(xNum) ? Math.floor(xNum) : baseArrowLevel,
3004
- isFinite(yNum) ? Math.floor(yNum) : baseArrowLevel,
3005
- maxRows);
3045
+ // ---------------------------------------------------------------------------
3046
+ // Expanded (cascade) form of x{α}y for FINITE operands in [3, MSI] — README
3047
+ // rules 1-4 written out row by row. The rows are generated TOP-DOWN so a
3048
+ // huge fundamental sequence (y up to MSI) costs nothing: generation stops as
3049
+ // soon as the array budget is filled, keeping the LARGEST rows.
3050
+ //
3051
+ // successor α = β + m (m = c0 > 0): x{α}y = x{β+m-1}^(y-2)(x{β+m-1}x),
3052
+ // so the row at β+m-1 carries y-2 applications and every row below it
3053
+ // (β+m-2 … β, then the cascade of β) comes from decomposing with the
3054
+ // BASE x as operand and therefore carries x-2 (h13(10,20) → [8,0,1],
3055
+ // [8,1,1], [18,2,1] — only the top row follows y).
3056
+ // limit α (c0 = 0): rule 3 gives x{α}opd = x{α[opd]}x, so the operand turns
3057
+ // into the base x and every row carries x-2 — including for 100 < y ≤ MSI
3058
+ // (h20(10,1000) stays an expanded cascade, not a one-row marker).
3059
+ // ---------------------------------------------------------------------------
3060
+ // `fsOpd` is the operand of the FIRST fundamental-sequence step. The default
3061
+ // is the base x; the fractional successor rule passes the fractional start
3062
+ // value x^f instead (x{α}(m+f) = x{α-1}^m(x^f)), which is what interpolates the
3063
+ // cascade depth between two integers. baseIdx (returned as `.baseIdx`) is the
3064
+ // index used by the last descent — the finite level the cascade bottoms out at.
3065
+ Q._cascadeRows = function (xn, cf, yn, budget, fsOpd) {
3066
+ var rows = [];
3067
+ if (!(budget >= 1)) return { rows: rows, baseIdx: xn };
3068
+ var cfs = trimCoeffs((cf || []).slice());
3069
+ var opd = Math.min(yn, MAX_SAFE_INTEGER);
3070
+ var first = (cfs[0] || 0) > 0; // successor: the outer count is y-2
3071
+ var fracOpd = (fsOpd === undefined) ? null : fsOpd;
3072
+ var baseIdx = xn;
3073
+ var fracRem = 0; // fractional constant carried to the base
3074
+ var guard = 0;
3075
+ while (rows.length < budget && guard++ < 10000) {
3076
+ if (!coeffsHaveOmegaPart(cfs)) break; // finite level: no row
3077
+ if ((cfs[0] || 0) > 0) {
3078
+ var mRaw = cfs[0];
3079
+ var m = Math.floor(mRaw);
3080
+ // a fractional constant term (from a fundamental sequence taken at a
3081
+ // fractional index) is not a valid CNF coefficient — keep the integer
3082
+ // part as rows and carry the remainder into the base's finite level
3083
+ // index (x{10.16}x), which is exactly how the ω level interpolates
3084
+ var mFr = Number((mRaw - m).toPrecision(15));
3085
+ if (mFr > 1e-9) fracRem = mFr;
3086
+ var beta = trimCoeffs(cfs.slice());
3087
+ beta[0] = 0;
3088
+ beta = trimCoeffs(beta);
3089
+ var steps = Math.min(m, budget - rows.length + 1);
3090
+ for (var s = 0; s < steps; s++) {
3091
+ var j = m - 1 - s; // rows β+m-1 … β
3092
+ var cnt = (first ? opd : xn) - 2;
3093
+ first = false;
3094
+ if (cnt < 1) continue;
3095
+ if (cnt > MAX_SAFE_INTEGER) cnt = MAX_SAFE_INTEGER;
3096
+ var ord = beta.slice();
3097
+ if (j > 0) ord[0] = (ord[0] || 0) + j; // β + j
3098
+ ord = trimCoeffs(ord);
3099
+ if (!coeffsHaveOmegaPart(ord)) continue; // finite: lives in r0
3100
+ rows.push([cnt].concat(ord));
3101
+ }
3102
+ cfs = beta; // continue below β
3103
+ opd = Math.min(xn, MAX_SAFE_INTEGER); // everything below the top
3104
+ continue; // row is base-operand
3105
+ }
3106
+ // limit: fundamental sequence at the current operand (rule 3)
3107
+ var idx = (fracOpd !== null) ? fracOpd : opd;
3108
+ fracOpd = null;
3109
+ baseIdx = idx;
3110
+ var nxt = fsCoeffs(cfs, idx);
3111
+ if (coeffsHaveFraction(nxt)) {
3112
+ // a fractional index makes the coefficients fractional (ω^k·(c+f)) —
3113
+ // resolve them against the base so every row coefficient stays an
3114
+ // integer, carrying the leftover constant fraction into the base
3115
+ var rs2 = Q._resolveFracLevel(nxt, xn);
3116
+ cfs = rs2.ord;
3117
+ if (rs2.frac > 0) fracRem = rs2.frac;
3118
+ } else {
3119
+ cfs = trimCoeffs(nxt);
3120
+ }
3121
+ opd = Math.min(xn, MAX_SAFE_INTEGER); // operand becomes x
3122
+ }
3123
+ rows.baseIdx = baseIdx + fracRem; // descending by ordinal
3124
+ return rows;
3125
+ };
3126
+
3127
+ Q._expandedHyperop = function (x, y, cf, fsOpd) {
3128
+ var maxRows = MetaNum.maxRows;
3129
+ var xn = Math.floor(metaFiniteCount(x));
3130
+ var yn = Math.floor(metaFiniteCount(y));
3131
+ var budget = maxRows - 1; // ordinal rows the array holds
3132
+ var desc = Q._cascadeRows(xn, cf, yn, budget + 1, fsOpd); // +1 detects truncation
3133
+ var truncated = desc.length > budget;
3134
+ if (truncated) desc = desc.slice(0, budget); // keep the LARGEST rows
3135
+ var rows = desc.reverse(); // ascending
3136
+ // base r0 = x{idx}x (rule 3 at the ω level: x{ω}x = x{x}x) — the finite part;
3137
+ // idx is the operand of the descent that bottoms out, i.e. the fractional
3138
+ // start value x^f when the cascade came from the fractional successor rule
3139
+ var baseIdx = (desc.baseIdx === undefined) ? xn : desc.baseIdx;
3140
+ // arrow() evaluates fractional levels too (10{10.2589}10 ≠ 10{10}10), so the
3141
+ // carried fraction survives into the base
3142
+ var baseResult = x.arrow(Math.max(baseIdx, 3))(x);
3006
3143
  var result = baseResult.clone();
3007
- var maxOrdinalRows = maxRows - 1;
3008
- if (rows.length > maxOrdinalRows) {
3009
- // Truncation: keep the largest maxOrdinalRows rows (rows ascend), the
3010
- // base defaults to [10] and the first kept row's count marks it with +1
3011
- rows = rows.slice(rows.length - maxOrdinalRows);
3144
+ if (truncated) {
3012
3145
  result.array = [[10]];
3013
- rows[0][0] = (rows[0][0] || 0) + 1;
3146
+ rows[0][0] = (rows[0][0] || 0) + 1; // truncation marker
3014
3147
  if (rows[0][0] > MAX_SAFE_INTEGER) rows[0][0] = MAX_SAFE_INTEGER;
3015
3148
  } else {
3016
3149
  result.array = [baseResult.array[0].slice()];
3017
3150
  }
3018
3151
  for (var i = 0; i < rows.length; i++) result.array.push(rows[i]);
3019
- if (result.array.length > 1) result.layer = 0;
3020
- return result;
3152
+ result.layer = 0;
3153
+ return result.normalize();
3021
3154
  };
3022
3155
 
3023
- // Legacy (HEAD-compatible) ordinal row enumeration: one row per ordinal below
3024
- // `coeffs` (coefficients bounded by `cardinal`), each with count = cardinal-2.
3025
- Q._generateOrdinalRows = function (coeffs, base, cardinal, maxRows) {
3026
- var rows = [];
3027
- var m = base;
3028
- var n = cardinal;
3029
-
3030
- function compareOrd(a, b) {
3031
- var maxLen = Math.max(a.length, b.length);
3032
- for (var i = maxLen - 1; i >= 0; i--) {
3033
- var va = (i < a.length) ? (a[i] || 0) : 0;
3034
- var vb = (i < b.length) ? (b[i] || 0) : 0;
3035
- if (va < vb) return -1;
3036
- if (va > vb) return 1;
3037
- }
3038
- return 0;
3039
- }
3040
-
3041
- function addOrd(a, b) {
3042
- if (!b || b.length === 0 || (b.length === 1 && b[0] === 0)) return a.slice();
3043
- if (!a || a.length === 0 || (a.length === 1 && a[0] === 0)) return b.slice();
3044
- var result = a.slice();
3045
- var k = 0;
3046
- while (k < result.length && (result[k] || 0) === 0) k++;
3047
- if (k >= result.length) {
3048
- return b.slice();
3049
- } else if (k === 0) {
3050
- result[0] = (result[0] || 0) + (b[0] || 0);
3051
- for (var i = 1; i < b.length; i++) {
3052
- result[i] = (result[i] || 0) + (b[i] || 0);
3053
- }
3054
- } else {
3055
- result[k] = 0;
3056
- result = result.slice(0, k);
3057
- for (var i = 0; i < b.length; i++) {
3058
- result.push(b[i] || 0);
3059
- }
3060
- }
3061
- while (result.length > 0 && result[result.length - 1] === 0) result.pop();
3062
- return result;
3063
- }
3064
-
3065
- function isZero(ord) {
3066
- if (!ord || ord.length === 0) return true;
3067
- for (var i = 0; i < ord.length; i++) {
3068
- if ((ord[i] || 0) !== 0) return false;
3069
- }
3070
- return true;
3071
- }
3072
-
3073
- function buildRow(ord, count) {
3074
- var maxIdx = ord.length - 1;
3075
- while (maxIdx >= 0 && (ord[maxIdx] || 0) === 0) maxIdx--;
3076
- if (maxIdx < 0) return null;
3077
- var row = [count];
3078
- for (var i = 0; i <= maxIdx - 1; i++) row.push(ord[i] || 0);
3079
- row.push(ord[maxIdx]);
3080
- return row;
3081
- }
3082
-
3083
- // Enumerate all ordinals below ω^k with coefficients < n
3084
- function enumBelowOmegaK(k, n) {
3085
- if (k === 0) {
3086
- return [[0]];
3087
- }
3088
- if (k === 1) {
3089
- var result = [];
3090
- for (var i = 0; i < n; i++) {
3091
- result.push([i]);
3092
- }
3093
- return result;
3094
- }
3095
- var result = [];
3096
- for (var a = 0; a < n; a++) {
3097
- var sub = enumBelowOmegaK(k - 1, n);
3098
- for (var i = 0; i < sub.length; i++) {
3099
- var combined = sub[i].slice();
3100
- while (combined.length < k) combined.push(0);
3101
- combined[k - 1] = a;
3102
- result.push(combined);
3103
- }
3104
- }
3105
- return result;
3106
- }
3107
-
3108
- // Enumerate all ordinals below a given ordinal
3109
- function enumBelow(alpha, n) {
3110
- if (isZero(alpha)) return [];
3111
-
3112
- if ((alpha[0] || 0) > 0) {
3113
- var pred = alpha.slice();
3114
- pred[0]--;
3115
- while (pred.length > 0 && pred[pred.length - 1] === 0) pred.pop();
3116
- if (pred.length === 0) pred = [0];
3117
- var result = enumBelow(pred, n);
3118
- result.push(pred.slice());
3119
- return result;
3120
- }
3121
-
3122
- var k = alpha.length - 1;
3123
- while (k >= 0 && (alpha[k] || 0) === 0) k--;
3124
- if (k < 0) return [];
3125
-
3126
- var ck = alpha[k] || 0;
3127
- var rest = alpha.slice(0, k);
3128
- while (rest.length > 0 && rest[rest.length - 1] === 0) rest.pop();
3129
-
3130
- var result = [];
3131
-
3132
- for (var j = 0; j < ck; j++) {
3133
- var sub = enumBelowOmegaK(k, n);
3134
- for (var i = 0; i < sub.length; i++) {
3135
- var combined = sub[i].slice();
3136
- while (combined.length <= k) combined.push(0);
3137
- combined[k] = j;
3138
- result.push(combined);
3139
- }
3140
- }
3141
-
3142
- if (!isZero(rest)) {
3143
- var restSub = enumBelow(rest, n);
3144
- for (var i = 0; i < restSub.length; i++) {
3145
- var combined = restSub[i].slice();
3146
- while (combined.length <= k) combined.push(0);
3147
- combined[k] = ck;
3148
- result.push(combined);
3149
- }
3150
- }
3151
-
3152
- return result;
3156
+ // Any coefficient with a non-zero fractional part?
3157
+ function coeffsHaveFraction(coeffs) {
3158
+ for (var i = 0; i < coeffs.length; i++) {
3159
+ var c = coeffs[i] || 0;
3160
+ if (c !== Math.floor(c)) return true;
3153
3161
  }
3162
+ return false;
3163
+ }
3154
3164
 
3155
- var count = n - 2;
3156
- if (count < 1) return rows;
3157
-
3158
- var allOrdinals = enumBelow(coeffs, n);
3159
-
3160
- var seen = {};
3161
- var uniqueOrdinals = [];
3162
- for (var i = 0; i < allOrdinals.length; i++) {
3163
- var ord = allOrdinals[i];
3164
- if (isZero(ord)) continue;
3165
-
3166
- // Normalize: remove trailing zeros
3167
- var normalized = ord.slice();
3168
- while (normalized.length > 0 && normalized[normalized.length - 1] === 0) {
3169
- normalized.pop();
3165
+ // Resolve fractional CNF coefficients against the base x (see the comment in
3166
+ // _hyperopFromOrdinalRaw): ω^(k)·(c+f) = ω^(k)·c + ω^(k-1)·(x·f) top-down.
3167
+ // Returns the integer level plus the fractional part left in the constant term.
3168
+ Q._resolveFracLevel = function (cf, xn) {
3169
+ var r = trimCoeffs((cf || []).slice());
3170
+ for (var k = r.length - 1; k >= 1; k--) {
3171
+ var c = r[k] || 0;
3172
+ var fl = Math.floor(c);
3173
+ var fr = Number((c - fl).toPrecision(15));
3174
+ if (fr > 1e-9) {
3175
+ r[k] = fl;
3176
+ r[k - 1] = (r[k - 1] || 0) + xn * fr;
3170
3177
  }
3171
-
3172
- // Skip ordinals below ω (finite ordinals: only constant term, length <= 1)
3173
- if (normalized.length <= 1) continue;
3174
-
3175
- var key = normalized.join(',');
3176
- if (seen[key]) continue;
3177
- seen[key] = true;
3178
- uniqueOrdinals.push(normalized);
3179
- }
3180
-
3181
- uniqueOrdinals.sort(function (a, b) {
3182
- return compareOrd(a, b);
3183
- });
3184
-
3185
- var allRows = [];
3186
- for (var i = 0; i < uniqueOrdinals.length; i++) {
3187
- var row = buildRow(uniqueOrdinals[i], count);
3188
- if (row) allRows.push(row);
3189
- }
3190
-
3191
- // When enumerating more rows than fit, keep the largest maxRows-1
3192
- // ordinals (rows are sorted ascending, so drop from the front)
3193
- if (allRows.length > maxRows - 1) {
3194
- var maxOrdinalRows = maxRows - 1;
3195
- allRows = allRows.slice(allRows.length - maxOrdinalRows);
3196
- }
3197
-
3198
- for (var i = 0; i < allRows.length; i++) {
3199
- rows.push(allRows[i]);
3200
3178
  }
3201
-
3202
- return rows;
3179
+ var c0 = r[0] || 0;
3180
+ var fl0 = Math.floor(c0);
3181
+ var fr0 = Number((c0 - fl0).toPrecision(15));
3182
+ if (!(fr0 > 1e-9)) fr0 = 0;
3183
+ r[0] = fl0;
3184
+ return { ord: trimCoeffs(r), frac: fr0 };
3203
3185
  };
3204
3186
 
3205
3187
  // Helper: number of leading iterate applications (of level `beta`) starting
@@ -3217,6 +3199,26 @@
3217
3199
  return steps + 1;
3218
3200
  }
3219
3201
 
3202
+ // Inverse of a LIMIT level taken through the expanded cascade: x{λ}y = x{λ[y]}x
3203
+ // makes every row depend on y only through the fundamental sequence, so instead
3204
+ // of reading a count row we search the (monotone) forward map.
3205
+ Q._invExpandedLimit = function (base, value, coeffs) {
3206
+ var lo = 3, hi = MAX_SAFE_INTEGER, best = 0;
3207
+ var v3 = Q._expandedHyperop(base, new MetaNum(3), coeffs);
3208
+ if (value.lt(v3)) return base.clone();
3209
+ while (lo <= hi) {
3210
+ var mid = lo + Math.floor((hi - lo) / 2);
3211
+ if (Q._expandedHyperop(base, new MetaNum(mid), coeffs).lte(value)) {
3212
+ best = mid; lo = mid + 1;
3213
+ } else {
3214
+ hi = mid - 1;
3215
+ }
3216
+ }
3217
+ if (best === 0) return base.clone();
3218
+ var vb = Q._expandedHyperop(base, new MetaNum(best), coeffs);
3219
+ return vb.eq(value) ? new MetaNum(best) : base.clone();
3220
+ };
3221
+
3220
3222
  // Helper: generic inverse hyperoperation from ordinal level.
3221
3223
  // Forward paths and the rows they leave:
3222
3224
  // - small x ∈ [3, MSI] & small y (enumeration path): rows for each ordinal
@@ -3249,14 +3251,29 @@
3249
3251
  }
3250
3252
  if (cf.length === 0) return z;
3251
3253
 
3252
- // enumeration path (base z is a small finite number ≥ 3): top row count = y-2
3254
+ // expanded cascade path (base z is a small finite number ≥ 3): the forward
3255
+ // operation wrote the rule-1..4 cascade rows.
3256
+ // successor α: the row at α-1 carries exactly y-2 applications → +2
3257
+ // limit α: every row carries x-2 and y only appears inside the
3258
+ // fundamental-sequence rows, so it is recovered from the forward map
3253
3259
  var zC = metaFiniteCount(z);
3254
3260
  if (z.array.length === 1 && z.layer === 0 && zC >= 3 && zC <= MAX_SAFE_INTEGER) {
3255
- var lastRow = x.array[x.array.length - 1];
3256
- if (lastRow && lastRow.length >= 2 && (lastRow[0] || 0) > 0) {
3257
- return new MetaNum((lastRow[0] || 0) + 2);
3261
+ if ((cf[0] || 0) > 0) {
3262
+ var pred = succCoeffs(cf);
3263
+ var predRow = null;
3264
+ for (var pi = x.array.length - 1; pi >= 1; pi--) {
3265
+ var prow = x.array[pi];
3266
+ if (prow && prow.length >= 2 && (prow[0] || 0) > 0 &&
3267
+ cmpCoeffsLevel(rowToCoeffs(prow), pred) === 0) { predRow = prow; break; }
3268
+ }
3269
+ if (predRow) return new MetaNum((predRow[0] || 0) + 2);
3270
+ var lastRow = x.array[x.array.length - 1];
3271
+ if (lastRow && lastRow.length >= 2 && (lastRow[0] || 0) > 0) {
3272
+ return new MetaNum((lastRow[0] || 0) + 2);
3273
+ }
3274
+ return z;
3258
3275
  }
3259
- return z;
3276
+ return Q._invExpandedLimit(z, x, cf);
3260
3277
  }
3261
3278
 
3262
3279
  // engine path: count row [c | β] with β = α-1 (successor α only)
@@ -3636,8 +3653,21 @@
3636
3653
  return Q._layerMarker([10], 1, [[1, m]]);
3637
3654
  }
3638
3655
  if (yc <= MAX_SAFE_INTEGER) {
3639
- // fractional y: rule 3 takes the fundamental sequence at floor(y)
3640
- return P.iterate.call(x, new MetaNum(Math.max(2, Math.floor(yc))));
3656
+ // fractional y: (ω^ω)[y] = ω^y = ω^(m+f) = ω^m·ω^f, and on the diagonal
3657
+ // ω^f reads as x^f — so the level is ω^m·(x^f), whose coefficients carry
3658
+ // x^f at index m (README: 10{ω^ω}2.1 = 10{ω^2.1}10 = 10{ω^2*10^0.1}10).
3659
+ // Flooring y would lose the fraction and make iterate(10,2.1)=iterate(10,2).
3660
+ var mf = Math.max(2, Math.floor(yc));
3661
+ var ff = Number((yc - Math.floor(yc)).toPrecision(15));
3662
+ var xnf = x.toNumber();
3663
+ var cfac = (isFinite(xnf) && xnf >= 2 && ff > 0) ? Math.pow(xnf, ff) : 1;
3664
+ if (mf < MetaNum.maxCols) {
3665
+ var cfw = [];
3666
+ for (var cw = 0; cw < mf; cw++) cfw.push(0);
3667
+ cfw.push(cfac);
3668
+ return Q._hyperopFromOrdinal(x, x, cfw);
3669
+ }
3670
+ return Q._layerMarker([10], 1, [[cfac, mf]]);
3641
3671
  }
3642
3672
  // ordinal / out-of-range operand: y plus one ω^ω marker row
3643
3673
  return Q._ordinalOperandOp(y, 1, [1, 0, 1]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metanum",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A javascript library that handles numbers as large as f_ε₀(9e15)",
5
5
  "main": "metanum.js",
6
6
  "files": [