metanum 1.4.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/README.md +474 -114
- package/format-metanum.js +934 -115
- package/metanum.js +1462 -497
- package/package.json +32 -23
- package/test_mixed.js +878 -47
- package/.gitattributes +0 -1
package/format-metanum.js
CHANGED
|
@@ -16,9 +16,9 @@ const FORMAT_OPTIONS = {
|
|
|
16
16
|
sciSignificantDigits: 3, // 7. 科学计数法有效位数(=n则αEβ的α的小数部分保留n位)
|
|
17
17
|
sciDecimalThreshold: 3, // 8. 科学计数法小数阈值(=n则αEβ的β>=10^n时不显示小数部分)
|
|
18
18
|
singleLetterDigits: 3, // 9. 单字母计数法有效位数(αFβ,αGβ...αZβ中α的小数位数)
|
|
19
|
-
repeatLetterThreshold: 3, // 10. 单字母重复阈值(=n则出现n个重复的单字母时用下一个字母计数法,n<2时以2计算)
|
|
19
|
+
repeatLetterThreshold: 3, // 10. 单字母重复阈值(=n则出现n+1个重复的单字母时用下一个字母计数法,n<2时以2计算)
|
|
20
20
|
multiLetterDigits: 3, // 11. 多字母计数法有效位数(及以上的α的小数位数)
|
|
21
|
-
multiLetterRepeatThreshold: 3, // 12. 多字母组合重复阈值(=n则出现n个重复的多字母组合时用下一个字母计数法,n<2时以2计算)
|
|
21
|
+
multiLetterRepeatThreshold: 3, // 12. 多字母组合重复阈值(=n则出现n+1个重复的多字母组合时用下一个字母计数法,n<2时以2计算)
|
|
22
22
|
multiLetterLimit: 4, // 13. 多字母组合最大位数(=n则多字母组合的长度不超过n,超过则切换下一种计数法,n<2时以2计算)
|
|
23
23
|
epsilonSignificantDigits: 6 // 14. epsilon有效位数(=n则αεβ中α的小数位数为n)
|
|
24
24
|
}
|
|
@@ -288,6 +288,36 @@ function letterName(height) {
|
|
|
288
288
|
return result
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
// ─── Letter token with the multiLetterLimit symbol carry ──────────
|
|
292
|
+
// A k-letter combination (Aa, Aaa, Aaaa, …) sits at level ω^(k-1). Once the
|
|
293
|
+
// combination would be longer than FORMAT_OPTIONS.multiLetterLimit the
|
|
294
|
+
// notation switches to the next one instead: the symbol form !αAaβ. Per its
|
|
295
|
+
// definition (!1.2345Aa4 = 10{ω^4+ω^3*2+ω^2*3+ω*4+5}10) β is the top
|
|
296
|
+
// ω-exponent (k-1) and α's digits are the CNF coefficients — exactly the
|
|
297
|
+
// digits of the letter itself (uppercase index, then the lowercase indices),
|
|
298
|
+
// which keeps the diagonal mantissa inside [2,10) for the cascades.
|
|
299
|
+
function letterTokenOf(height) {
|
|
300
|
+
let plain = letterName(height)
|
|
301
|
+
let limit = Math.max(2, FORMAT_OPTIONS.multiLetterLimit | 0)
|
|
302
|
+
if (plain.length <= limit) return { sym: "", letter: plain, arg: 0, mant: null }
|
|
303
|
+
let mant = plain.charCodeAt(0) - 64 // coefficient of ω^(k-1)
|
|
304
|
+
for (let i = 1; i < plain.length; i++) {
|
|
305
|
+
mant += (plain.charCodeAt(i) - 97) * Math.pow(10, -i)
|
|
306
|
+
}
|
|
307
|
+
if (mant < 2) mant = 2 // diagonal [2,10) anchor
|
|
308
|
+
if (mant >= 10) mant = 9 + (mant - 10) / 170 // keep α ∈ [2,10)
|
|
309
|
+
return { sym: "!", letter: "Aa", arg: plain.length - 1, mant: mant }
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Emit α·letter·β for a token: the plain form is αΓβ, the carried form is the
|
|
313
|
+
// symbol-first !αAaβ whose α is the definition mantissa and β the ω-exponent.
|
|
314
|
+
function emitLetterToken(tok, alphaStr, betaStr, precision) {
|
|
315
|
+
if (!tok.sym) return alphaStr + tok.letter + betaStr
|
|
316
|
+
let a = (tok.mant === null || tok.mant === undefined) ? alphaStr
|
|
317
|
+
: regularFormat(tok.mant, precision)
|
|
318
|
+
return tok.sym + a + tok.letter + formatR0Arg(tok.arg, precision)
|
|
319
|
+
}
|
|
320
|
+
|
|
291
321
|
// ─── Symbol Name: Map layer to symbol ────────────────────────────
|
|
292
322
|
const SYMBOLS = "!@#$%&~<>?"
|
|
293
323
|
function symbolName(layer) {
|
|
@@ -343,9 +373,219 @@ function getOrdinalLetter(ordRows) {
|
|
|
343
373
|
return tokens.join("")
|
|
344
374
|
}
|
|
345
375
|
|
|
376
|
+
// ─── Reference Polarize (verbatim port of PowiainaNum's myPolarize) ──
|
|
377
|
+
// Recovers the display α ("bottom") and the diagonal level ("arrows") of an
|
|
378
|
+
// r0 chain STRUCTURALLY, by climbing log10 level by level — the only method
|
|
379
|
+
// that stays exact at high levels where the engine's own fractional-arg
|
|
380
|
+
// bisect quantizes (level ≥ 20). Ground truth: format-powiainanum.js.
|
|
381
|
+
// their array = [base, [level, count, 1, 1], ...] sorted top-level-first
|
|
382
|
+
// ours: r0 = [base, cnt1, cnt2, ...] where cnt_i = count at level i
|
|
383
|
+
function refPolarizeArraySort(array) {
|
|
384
|
+
array.sort(function (a, b) {
|
|
385
|
+
if (typeof a == 'number') return
|
|
386
|
+
else if (a[3] > b[3]) return -1
|
|
387
|
+
else if (a[3] < b[3]) return 1
|
|
388
|
+
else if (a[2] == 'x' && b[2] != 'x') return -1
|
|
389
|
+
else if (a[2] != 'x' && b[2] == 'x') return 1
|
|
390
|
+
else if (a[2] == 'x' && b[2] == 'x') return -1
|
|
391
|
+
else if (a[2] > b[2]) return -1
|
|
392
|
+
else if (a[2] < b[2]) return 1
|
|
393
|
+
else if (a[0] == 'x' && b[0] != 'x') return -1
|
|
394
|
+
else if (a[0] != 'x' && b[0] == 'x') return 1
|
|
395
|
+
else if (a[0] == 'x' && b[0] == 'x') return -1
|
|
396
|
+
else if (a[0] > b[0]) return -1
|
|
397
|
+
else if (a[0] < b[0]) return 1
|
|
398
|
+
else if (a[1] > b[1]) return -1
|
|
399
|
+
else if (a[1] < b[1]) return 1
|
|
400
|
+
return -1
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
function refPolarizeArrayMerge(array) {
|
|
404
|
+
let elemOffset = 0
|
|
405
|
+
for (let i = 0; i < array.length - 2; ++i) {
|
|
406
|
+
if (array[i][0] == array[i + 1][0] &&
|
|
407
|
+
array[i][2] == array[i + 1][2] &&
|
|
408
|
+
array[i][3] == array[i + 1][3]) {
|
|
409
|
+
array[i][1] += array[i + 1][1]
|
|
410
|
+
array.splice(i + 1, 1)
|
|
411
|
+
--i
|
|
412
|
+
elemOffset++
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return elemOffset
|
|
416
|
+
}
|
|
417
|
+
// our r0 + [count, level] finite rows → their [base, [level, count, 1, 1]...]
|
|
418
|
+
// (their row level = our level verbatim: their 3{21}3 rows top at [20,1] and their polarize
|
|
419
|
+
// returns arrows 20, displayed "…20" — no off-by-one)
|
|
420
|
+
function refPolarizeInput(r0, finiteRows) {
|
|
421
|
+
let arr = [r0[0]]
|
|
422
|
+
for (let i = 1; i < r0.length; i++) {
|
|
423
|
+
if (r0[i] > 0) arr.push([i, r0[i], 1, 1])
|
|
424
|
+
}
|
|
425
|
+
if (finiteRows) {
|
|
426
|
+
for (let i = 0; i < finiteRows.length; i++) {
|
|
427
|
+
let fr = finiteRows[i]
|
|
428
|
+
if (fr && fr.length === 2 && fr[0] > 0) arr.push([fr[1], fr[0], 1, 1])
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return arr
|
|
432
|
+
}
|
|
433
|
+
function refPolarize(array, hasOperationRepeat = true) {
|
|
434
|
+
refPolarizeArraySort(array)
|
|
435
|
+
refPolarizeArrayMerge(array)
|
|
436
|
+
let ptr = array.length - 2
|
|
437
|
+
let b = () => array[array.length - 1]
|
|
438
|
+
let c = (x) => { array[array.length - 1] = x }
|
|
439
|
+
let repeatResult = 1000001
|
|
440
|
+
while (--repeatResult >= 0) {
|
|
441
|
+
if (b() >= 10) {
|
|
442
|
+
let a = Math.log10(b())
|
|
443
|
+
array.push([1, 1, 1, 1])
|
|
444
|
+
ptr++
|
|
445
|
+
refPolarizeArraySort(array)
|
|
446
|
+
let offset = refPolarizeArrayMerge(array)
|
|
447
|
+
ptr -= offset
|
|
448
|
+
c(a)
|
|
449
|
+
} else {
|
|
450
|
+
if (ptr == 0 && typeof array[ptr + 1] == 'number' &&
|
|
451
|
+
(hasOperationRepeat || (!hasOperationRepeat && array[ptr][1] == 1))) break
|
|
452
|
+
if (ptr != 0 && typeof array[ptr + 1] == 'number' && typeof array[ptr][0] == 'number' &&
|
|
453
|
+
array[ptr][1] == 1 &&
|
|
454
|
+
(array[ptr - 1][0] == 'x' || array[ptr - 1][2] > array[ptr][2] || array[ptr - 1][3] > array[ptr][3]) &&
|
|
455
|
+
array[ptr][0] > 2 && (ptr != 0 || array[ptr - 1][0] == 'x')) {
|
|
456
|
+
let arrow_count = array[ptr][0]
|
|
457
|
+
let base = b()
|
|
458
|
+
let Jx
|
|
459
|
+
if (arrow_count == 3) Jx = base
|
|
460
|
+
else Jx = arrow_count - 1 + Math.log(base / 2) / Math.log(5)
|
|
461
|
+
array[ptr][0] = 'x'
|
|
462
|
+
c(Jx)
|
|
463
|
+
refPolarizeArraySort(array)
|
|
464
|
+
let offset = refPolarizeArrayMerge(array)
|
|
465
|
+
ptr -= offset
|
|
466
|
+
}
|
|
467
|
+
if (typeof array[ptr + 1] == 'number' && array[ptr][0] == 'x' && b() < 10 && ptr != 0) {
|
|
468
|
+
let base = b()
|
|
469
|
+
let JRepeation = array[ptr][1]
|
|
470
|
+
let Kx = JRepeation + Math.log10(base)
|
|
471
|
+
array[ptr][1] = 1
|
|
472
|
+
array[ptr][0] = 1
|
|
473
|
+
array[ptr][2]++
|
|
474
|
+
c(Kx)
|
|
475
|
+
refPolarizeArraySort(array)
|
|
476
|
+
let offset = refPolarizeArrayMerge(array)
|
|
477
|
+
ptr -= offset
|
|
478
|
+
}
|
|
479
|
+
if (((ptr == 0 && !hasOperationRepeat && array[ptr][1] != 1) || ptr != 0) &&
|
|
480
|
+
array[ptr][0] != 'x' && b() < 10) {
|
|
481
|
+
if (b() == 1 && ptr > 0 && array[ptr - 1][0] > array[ptr][0] && array[ptr][1] == 1) {
|
|
482
|
+
array[ptr][0] = array[ptr - 1][0]
|
|
483
|
+
} else {
|
|
484
|
+
let right = array[ptr][1] + Math.log10(b())
|
|
485
|
+
array[ptr][0]++
|
|
486
|
+
array[ptr][1] = 1
|
|
487
|
+
c(right)
|
|
488
|
+
}
|
|
489
|
+
refPolarizeArraySort(array)
|
|
490
|
+
let offset = refPolarizeArrayMerge(array)
|
|
491
|
+
ptr -= offset
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
bottom: b(),
|
|
497
|
+
repeation: array[ptr][1],
|
|
498
|
+
arrows: array[ptr][0],
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Convert a finite-chain polarize triple (bottom, repeation, arrows) into the
|
|
502
|
+
// dlsdl diagonal-letter display pair (mantissa, β) for a diagonal letter
|
|
503
|
+
// (Aa, Ba, Ca, Aaa, … — multi-letter tokens ending in 'a').
|
|
504
|
+
// The value behaves as 10{arrows+1}(t) with t = log10(bottom)+repeation.
|
|
505
|
+
// Pure dlsdl convention for diagonal letters (α = 2·5^f ∈ [2,10)):
|
|
506
|
+
// t ∈ [2,10) -> mantissa t, β = arrows
|
|
507
|
+
// (3{100}3 -> 2.376Aa99; 10{100}(10{93}10)-> 2.002Aa100)
|
|
508
|
+
// t ≥ 10 -> the count climbs one level per geometric smooth-log step
|
|
509
|
+
// f → 1+log10(f); the exact anchor t=10 is mantissa 2 at
|
|
510
|
+
// β=arrows+1 (10{100}10 -> 2.000Aa100, Aa100 itself)
|
|
511
|
+
// t ∈ [1,2) -> below the β anchor: mantissa 2·5^(t-1), β=arrows-1
|
|
512
|
+
// Truncation-marker normalization for finite rows: when the engine truncated
|
|
513
|
+
// an overflow cascade it reset r0 to [10] and bumped the LOWEST kept row's
|
|
514
|
+
// count by 1 over the uniform per-level fill (xCnt). That +1 marks the
|
|
515
|
+
// dropped tail, not a real operation — read the row as carrying xCnt so
|
|
516
|
+
// truncated displays match the untruncated anchor (cross-config stability).
|
|
517
|
+
function normalizedFiniteRows(r0, finiteRows) {
|
|
518
|
+
if (!finiteRows || finiteRows.length === 0 || r0[0] !== 10) return finiteRows
|
|
519
|
+
let sorted = finiteRows.slice().sort((a, b) => a[1] - b[1])
|
|
520
|
+
let c0 = sorted[0][0] || 0
|
|
521
|
+
let c1 = sorted.length > 1 ? (sorted[1][0] || 0) : 0
|
|
522
|
+
if (c0 === c1 + 1) {
|
|
523
|
+
let copy = finiteRows.slice()
|
|
524
|
+
for (let i = 0; i < copy.length; i++) {
|
|
525
|
+
if (copy[i][1] === sorted[0][1]) copy[i] = [c1, copy[i][1]]
|
|
526
|
+
}
|
|
527
|
+
return copy
|
|
528
|
+
}
|
|
529
|
+
return finiteRows
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function aaDiagonalPair(pol) {
|
|
533
|
+
let t = Math.log10(pol.bottom) + pol.repeation
|
|
534
|
+
let beta = pol.arrows
|
|
535
|
+
// discrete integer argument (10{arrows+1}(t) with integer t): climb the
|
|
536
|
+
// raw count C=t-1 at level arrows; C ≤ 9 is the anchor band (one smooth
|
|
537
|
+
// application 10{arrows+1}2 … through arg 9) -> mantissa 2 at β=arrows+1
|
|
538
|
+
let discrete = Math.abs(pol.bottom - 1) < 1e-12 && pol.repeation === Math.floor(pol.repeation)
|
|
539
|
+
if (discrete && t - 1 <= 9 + 1e-9 && t >= 2) {
|
|
540
|
+
return { mant: 2, beta: pol.arrows + 1 }
|
|
541
|
+
}
|
|
542
|
+
if (t >= 9.999999999) {
|
|
543
|
+
// v = 10{arrows+1}(t) = {arrows}^{t-1}(10): the raw level-(arrows)
|
|
544
|
+
// application count is (t-1); run the geometric smooth-log climb
|
|
545
|
+
// from a scalar count all the way to the diagonal band at
|
|
546
|
+
// β = arrows+1. Integer arguments far below the next anchor land at
|
|
547
|
+
// mantissa 2.000 (10{100}10 … 10{100}100 are all 2.000Aa100).
|
|
548
|
+
let n = pol.arrows
|
|
549
|
+
let s = Math.log10(Math.max(t - 1, 1))
|
|
550
|
+
for (let level = 1; level < n + 2; level++) {
|
|
551
|
+
s = (s >= 9.999999999) ? 2 : 1 + Math.log10(s)
|
|
552
|
+
}
|
|
553
|
+
let mant = s < 2 ? 1 + s : s
|
|
554
|
+
return { mant: Math.min(mant, 10), beta: n + 1 }
|
|
555
|
+
}
|
|
556
|
+
if (t < 2) return { mant: 2 * Math.pow(5, t - 1), beta: beta - 1 }
|
|
557
|
+
// t in [2,10): genuinely smooth (fractional compact row, or a non-10 base
|
|
558
|
+
// fixed point such as 3{100}3 -> 2.376) is shown directly; the discrete
|
|
559
|
+
// integer-argument anchor case was handled at the top of the function.
|
|
560
|
+
return { mant: t, beta: beta }
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Structural α/diagonal recovery for a value whose finite-level chain (r0
|
|
564
|
+
// cols + [count, level] rows) reaches the Aa diagonal region. Mirrors the
|
|
565
|
+
// reference convention: 3{24}3 → bottom 2.375812, arrows 23 (the "2.376Aa23"
|
|
566
|
+
// display); 3{100}3 → bottom 2.375812, arrows 99 ("Aa2.376Aa99" inner part).
|
|
567
|
+
function polarizeFiniteChain(r0, finiteRows) {
|
|
568
|
+
try {
|
|
569
|
+
finiteRows = normalizedFiniteRows(r0, finiteRows)
|
|
570
|
+
let arr = refPolarizeInput(r0.slice(0), finiteRows)
|
|
571
|
+
if (arr.length === 1) return null
|
|
572
|
+
return refPolarize(arr, true)
|
|
573
|
+
} catch (e) {
|
|
574
|
+
return null
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
346
578
|
// ─── Main Format Function ────────────────────────────────────────
|
|
347
579
|
|
|
348
|
-
|
|
580
|
+
// Parse an already-formatted r0 argument back to a number (e.g. "1,000"
|
|
581
|
+
// → 1000, "1.000E1,000" → null: carries its own E structure)
|
|
582
|
+
function formatR0ArgStr(argStr) {
|
|
583
|
+
var s = String(argStr).replace(/,/g, "")
|
|
584
|
+
if (/^\d+(\.\d+)?$/.test(s)) return Number(s)
|
|
585
|
+
return null
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function format(num, precision=3, small=false) {
|
|
349
589
|
if (MetaNum.isNaN(num)) return "NaN"
|
|
350
590
|
let sciSigDigits = FORMAT_OPTIONS.sciSignificantDigits // for E notation
|
|
351
591
|
let sciPrecision = Math.max(sciSigDigits, precision)
|
|
@@ -383,8 +623,42 @@ function format(num, precision=2, small=false) {
|
|
|
383
623
|
return prefix + regularFormat(mant, sciPrecision) + "E-" + commaFormat(expPart)
|
|
384
624
|
}
|
|
385
625
|
}
|
|
386
|
-
// Tier 2: mag is large → E
|
|
387
|
-
|
|
626
|
+
// Tier 2: mag is large → "E-" + letter-law form of mag =
|
|
627
|
+
// log10 of the reciprocal base. Letter law (README): a chain
|
|
628
|
+
// of n same-level ops collapses to ONE next-level op with
|
|
629
|
+
// arg+n, so log10 shifts only the OUTERMOST letter:
|
|
630
|
+
// E-chain (EE1000): log10 = E1000 (one E stripped)
|
|
631
|
+
// F-chain (F500): log10 = F499 (arg −1, same letter)
|
|
632
|
+
// Γ ≥ G (G200): log10 = G200 (Γβ−1+1 = Γβ: the leading
|
|
633
|
+
// F(G(β−1)) collapses back to G(β))
|
|
634
|
+
// Roundtrip exact: "E-1.000G200" parses back to 1/G200,
|
|
635
|
+
// "E-1.000F499" to 1/F500 (test_mixed format block)
|
|
636
|
+
var recipStr = format(recipFmt, precision, small)
|
|
637
|
+
var magStr = recipStr
|
|
638
|
+
// leading run of E letters (e.g. "EE1.000E1,000"): strip one E
|
|
639
|
+
var eRun = recipStr.match(/^(E+)(.*)$/)
|
|
640
|
+
var rm = recipStr.match(/^(\d(?:\.\d+)?)([A-Z])((?:\d[\d,]*)?)$/)
|
|
641
|
+
if (eRun && !rm) {
|
|
642
|
+
var restStr = eRun[2]
|
|
643
|
+
if (eRun[1].length >= 2) {
|
|
644
|
+
// "EE…" → "E…" (log10 of an E-chain drops one E)
|
|
645
|
+
magStr = eRun[1].slice(0, -1) + restStr
|
|
646
|
+
} else if (eRun[1].length === 1) {
|
|
647
|
+
// single "EαEβ" / "Eβ": log10 is the β part
|
|
648
|
+
var betaPart = restStr.replace(/^\d(?:\.\d+)?(?=[A-Z])/, "")
|
|
649
|
+
if (/^[A-Z]/.test(betaPart) || /^[\d,]/.test(betaPart)) magStr = betaPart
|
|
650
|
+
}
|
|
651
|
+
} else if (rm) {
|
|
652
|
+
var rAlpha = rm[1], rLetter = rm[2], rArg = rm[3]
|
|
653
|
+
if (rLetter === "E" && rArg !== "") {
|
|
654
|
+
// E-chain with numeric arg (E200): log10 keeps E-arg
|
|
655
|
+
magStr = rAlpha + "E" + rArg
|
|
656
|
+
} else if (rLetter === "F" && rArg !== "") {
|
|
657
|
+
// F-chain: arg −1
|
|
658
|
+
magStr = rAlpha + "F" + commaFormat(Math.max(0, Number(rArg.replace(/,/g, "")) - 1))
|
|
659
|
+
}
|
|
660
|
+
// G and above: display unchanged
|
|
661
|
+
}
|
|
388
662
|
return prefix + "E-" + magStr
|
|
389
663
|
}
|
|
390
664
|
// Format as aE-b (e.g., 0.0000000001 → 1.000E-10)
|
|
@@ -465,8 +739,18 @@ function format(num, precision=2, small=false) {
|
|
|
465
739
|
if (r0[i] > 0) maxLevel = i
|
|
466
740
|
}
|
|
467
741
|
|
|
468
|
-
// ── Handle r0-only with maxLevel >= 23:
|
|
742
|
+
// ── Handle r0-only with maxLevel >= 23: Aa-diagonal (v2.0) ──
|
|
743
|
+
// The cascade collapses to αAaβ with (bottom, repeation, arrows) from
|
|
744
|
+
// polarizeFiniteChain (the structural climb), mantissa normalized into
|
|
745
|
+
// [1,10) carrying decades into β — identical to the finite-row display
|
|
746
|
+
// so format stays cross-config stable.
|
|
469
747
|
if (maxLevel >= 23) {
|
|
748
|
+
let pol = polarizeFiniteChain(r0.slice(0), null)
|
|
749
|
+
if (pol && isFinite(pol.bottom) && pol.bottom > 0 && isFinite(pol.arrows)) {
|
|
750
|
+
let pair = aaDiagonalPair(pol)
|
|
751
|
+
return regularFormat(pair.mant, multiPrecision) + "Aa" + commaFormat(pair.beta)
|
|
752
|
+
}
|
|
753
|
+
// fallback: legacy base-9 coefficient form
|
|
470
754
|
let coeff = r0[maxLevel]
|
|
471
755
|
let pow9 = 9
|
|
472
756
|
for (let i = maxLevel - 1; i >= 1; i--) {
|
|
@@ -537,17 +821,45 @@ function formatLayer(num, precision, precision2, precision3, precision4) {
|
|
|
537
821
|
return innerStr + "ε" + commaFormat(num.layer)
|
|
538
822
|
}
|
|
539
823
|
|
|
824
|
+
// Compact diagonal: the symbol row [1,0…0,1] with m=compactBase zeros is
|
|
825
|
+
// 10{ω^m}10 — a DIAGONAL letter (A + m a's) at argument 10. Diagonal
|
|
826
|
+
// letter combinations use the pure dlsdl α=2·5^f ∈ [2,10) convention:
|
|
827
|
+
// integer m anchors mantissa 2. For layers above ! the symbol of the
|
|
828
|
+
// layer below prefixes the same inner form:
|
|
829
|
+
// !Aa3 = 2.000Aaaa10, @Aa3 = !2.000Aaaa10.
|
|
830
|
+
// multiLetterLimit symbol carry: once the diagonal letter would be longer
|
|
831
|
+
// than the limit (m+1 letters) it switches to one more symbol and the Aa
|
|
832
|
+
// argument takes over the exponent — 10{ω^10}10 → !2.000Aa10, and layer 1
|
|
833
|
+
// on top stacks the symbols (no-symbol → ! → @ → # …):
|
|
834
|
+
// apix(3,10) = 10{ω^(ω^10)}10 → @2.000Aa10.
|
|
540
835
|
if (canCompact && compactBase >= 2) {
|
|
541
|
-
let
|
|
542
|
-
let
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
836
|
+
let mInt = Math.floor(compactBase)
|
|
837
|
+
let frac = compactBase - mInt
|
|
838
|
+
let mant = 2 * Math.pow(5, frac)
|
|
839
|
+
let limit = Math.max(2, FORMAT_OPTIONS.multiLetterLimit | 0)
|
|
840
|
+
if (mInt + 1 > limit) {
|
|
841
|
+
let total = num.layer + 1
|
|
842
|
+
let body = regularFormat(mant, precision4) + "Aa" + formatR0Arg(mInt, precision4)
|
|
843
|
+
if (total <= SYMBOLS.length) return SYMBOLS[total - 1] + body
|
|
844
|
+
return body + "ε" + commaFormat(total)
|
|
546
845
|
}
|
|
846
|
+
// Stored layer L (the parser de-layers small !Aa rows):
|
|
847
|
+
// L=0 (!Aa3) -> no prefix; L=1 (@Aa3) -> "!"; L=2 (#Aa3) -> "@!"
|
|
848
|
+
let prefixSyms = ""
|
|
849
|
+
for (let sl = num.layer; sl >= 1; sl--) prefixSyms += symbolName(sl)
|
|
850
|
+
return prefixSyms + regularFormat(mant, precision4) +
|
|
851
|
+
"A" + "a".repeat(mInt) + "10"
|
|
547
852
|
}
|
|
548
853
|
|
|
549
|
-
// For !, @, #, $, %, &, ~, <, >, ? symbols: prefix the symbol
|
|
854
|
+
// For !, @, #, $, %, &, ~, <, >, ? symbols: prefix the symbol. When the
|
|
855
|
+
// inner display itself had to carry (its letter exceeded multiLetterLimit)
|
|
856
|
+
// the symbols stack instead of doubling: layer 1 + inner "!…" → "@…".
|
|
550
857
|
let innerStr = format(inner, precision, false)
|
|
858
|
+
if (innerStr.length > 1 && SYMBOLS.indexOf(innerStr[0]) >= 0) {
|
|
859
|
+
let total = num.layer + 1
|
|
860
|
+
if (total <= SYMBOLS.length) return SYMBOLS[total - 1] + innerStr.slice(1)
|
|
861
|
+
return innerStr.slice(1) + "ε" + commaFormat(total)
|
|
862
|
+
}
|
|
551
863
|
return sym + innerStr
|
|
552
864
|
}
|
|
553
865
|
|
|
@@ -570,6 +882,69 @@ function formatR0Arg(value, precision) {
|
|
|
570
882
|
return regularFormat(m, precision) + "E" + commaFormat(e)
|
|
571
883
|
}
|
|
572
884
|
|
|
885
|
+
// ─── Gamma-Arg Recovery ─────────────────────────────────────────
|
|
886
|
+
// Recover the real argument x with 10{level}x = v (the Γ-canonical argument of
|
|
887
|
+
// v at letter `level`), by bisection on the engine's own smooth arrow curve.
|
|
888
|
+
// Returns null when x exceeds the double-precision bisect range. The doubling
|
|
889
|
+
// guard runs to 200: E-notation arguments (hardy(1120)'s F-arg ≈ 4.4e13) need
|
|
890
|
+
// ~45 doublings before the bracket closes.
|
|
891
|
+
function gammaArgOf(v, level, iters) {
|
|
892
|
+
if (iters === undefined) iters = 60
|
|
893
|
+
let G = function (x) { return MetaNum(10).arrow(level)(x) }
|
|
894
|
+
// v must sit inside [Γ(1), Γ(HUGE)]: find integer hi with Γ(hi) >= v
|
|
895
|
+
let lo = 1, hi = 2
|
|
896
|
+
let guard = 0
|
|
897
|
+
while (G(hi).lt(v)) {
|
|
898
|
+
lo = hi
|
|
899
|
+
hi *= 2
|
|
900
|
+
if (++guard > 200) return null
|
|
901
|
+
}
|
|
902
|
+
// bisect the fractional part
|
|
903
|
+
for (let i = 0; i < iters; i++) {
|
|
904
|
+
let mid = (lo + hi) / 2
|
|
905
|
+
if (G(mid).lt(v)) lo = mid; else hi = mid
|
|
906
|
+
if (hi - lo < 1e-12) break
|
|
907
|
+
}
|
|
908
|
+
return lo
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Format a recovered Γ-argument x as the binary form αΓβ:
|
|
912
|
+
// α = 10^frac(x) (in [1, 10)), β = floor(x) (may itself carry E notation).
|
|
913
|
+
// Once β ≥ 10^sciThreshold the α only perturbs β by log10(α) < 1, far below
|
|
914
|
+
// the displayed precision: write the letter bare and let the canonical β
|
|
915
|
+
// carry the single α (hardy(1120) → "F4.398E13").
|
|
916
|
+
function formatGammaBinary(x, level, precision) {
|
|
917
|
+
let sciBound = Math.pow(10, FORMAT_OPTIONS.sciThreshold)
|
|
918
|
+
let beta = Math.floor(x + 1e-12)
|
|
919
|
+
let frac = x - beta
|
|
920
|
+
if (frac < 0) { frac += 1; beta -= 1 }
|
|
921
|
+
if (frac >= 1) { frac -= 1; beta += 1 }
|
|
922
|
+
if (beta >= sciBound) {
|
|
923
|
+
let alpha = Math.pow(10, frac)
|
|
924
|
+
return letterName(level) + formatR0Arg(beta + Math.log10(alpha), precision)
|
|
925
|
+
}
|
|
926
|
+
if (frac < 1e-11 || frac > 1 - 1e-11) {
|
|
927
|
+
// exact integer argument: bare letter + β
|
|
928
|
+
return letterName(level) + formatR0Arg(beta, precision)
|
|
929
|
+
}
|
|
930
|
+
let alpha = Math.pow(10, frac)
|
|
931
|
+
return regularFormat(alpha, precision) + letterName(level) + formatR0Arg(beta, precision)
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// Distinct letter types in a display string: single-uppercase letters and
|
|
935
|
+
// Aa-style multi-letter tokens, ignoring an E inside a number ("7.626E12"
|
|
936
|
+
// sci notation, not an op) and !/@/# symbol prefixes.
|
|
937
|
+
function distinctLetterTypes(str) {
|
|
938
|
+
let stripped = String(str).replace(/(\d(?:\.\d+)?)E(\d[\d,]*)/g, "$1$2")
|
|
939
|
+
let tokens = stripped.match(/[A-Z][a-z]*/g) || []
|
|
940
|
+
let seen = {}
|
|
941
|
+
let out = []
|
|
942
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
943
|
+
if (!seen[tokens[i]]) { seen[tokens[i]] = true; out.push(tokens[i]) }
|
|
944
|
+
}
|
|
945
|
+
return out
|
|
946
|
+
}
|
|
947
|
+
|
|
573
948
|
// ─── Format r0 as Chain ────────────────────────────────────────
|
|
574
949
|
// Formats r0 = [base, e, f, g, ...] as a dlsdl letter-notation chain.
|
|
575
950
|
// Format pattern: outerLetters + α + lastLetter + β
|
|
@@ -591,8 +966,61 @@ function formatR0AsChain(r0, precision) {
|
|
|
591
966
|
let count = r0[maxLevel]
|
|
592
967
|
let effThreshold = Math.max(2, FORMAT_OPTIONS.repeatLetterThreshold)
|
|
593
968
|
|
|
594
|
-
|
|
595
|
-
|
|
969
|
+
// Detect a descending run of count-1 letters (e.g. the VUTS…G chain that
|
|
970
|
+
// the engine produces for arrow(3,19,3) = 3{19}3). Such a chain is the
|
|
971
|
+
// diagonalized expansion of the top letter applied twice, so it must be
|
|
972
|
+
// compressed to the ΓαΓβ binary form instead of being spelled out.
|
|
973
|
+
let runLen = 0
|
|
974
|
+
if (count === 1) {
|
|
975
|
+
runLen = 1
|
|
976
|
+
while (maxLevel - runLen >= 2 && r0[maxLevel - runLen] === 1) runLen++
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// ── ≤2-letter-types rule ──
|
|
980
|
+
// When the full structural spelling would use MORE than two letter
|
|
981
|
+
// types, spell only the top two levels' letters and compress everything
|
|
982
|
+
// below into the second letter's Binary-Canonical form
|
|
983
|
+
// "bottom Γ₂ repeation" — the polarize triple of the chain below the
|
|
984
|
+
// top letter (bisect fails there: the arg exceeds double range):
|
|
985
|
+
// 3{9}3 → "L2.376K2" (user: "L…K…")
|
|
986
|
+
// 3{10}3 → "M2.376L2" (user: "M(10^0.376)L2")
|
|
987
|
+
// 4{9}4 → "LLK3.550K3" (user: "LLK…K…")
|
|
988
|
+
// 5{9}5 → "LLLKK4.669K4" (user: "LLLKK…K…")
|
|
989
|
+
// A top count ≥ repeatLetterThreshold+1 carries to the next single
|
|
990
|
+
// letter instead (6{9}6 → "5.760M5" via the collapse paths below).
|
|
991
|
+
if (maxLevel >= 2 && maxLevel <= 22 && count <= effThreshold) {
|
|
992
|
+
let spellTypes = 0
|
|
993
|
+
for (let s = 2; s <= maxLevel; s++) {
|
|
994
|
+
if ((r0[s] || 0) > 0) spellTypes++
|
|
995
|
+
}
|
|
996
|
+
if (spellTypes > 2) {
|
|
997
|
+
let second = 0
|
|
998
|
+
for (let s = maxLevel - 1; s >= 2; s--) {
|
|
999
|
+
if (r0[s] > 0) { second = s; break }
|
|
1000
|
+
}
|
|
1001
|
+
if (second >= 2) {
|
|
1002
|
+
let c2 = r0[second]
|
|
1003
|
+
if (c2 <= effThreshold) {
|
|
1004
|
+
let restR0 = r0.slice(0)
|
|
1005
|
+
restR0[maxLevel] = 0
|
|
1006
|
+
let pol = polarizeFiniteChain(restR0, null)
|
|
1007
|
+
if (pol && pol.arrows === second && isFinite(pol.bottom) &&
|
|
1008
|
+
pol.bottom >= 1 && isFinite(pol.repeation)) {
|
|
1009
|
+
let prefix = letterName(maxLevel).repeat(count) +
|
|
1010
|
+
letterName(second).repeat(Math.max(c2 - 1, 0))
|
|
1011
|
+
return prefix + regularFormat(pol.bottom, precision) +
|
|
1012
|
+
letterName(second) + commaFormat(pol.repeation)
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (count <= effThreshold && runLen <= effThreshold) {
|
|
1020
|
+
// Non-collapse: peel off one letter at maxLevel, recurse on inner.
|
|
1021
|
+
// Long descending count-1 runs (runLen > threshold) fall through to the
|
|
1022
|
+
// Γ-canonical collapse below (2-letter rule: the diagonal
|
|
1023
|
+
// chain formats at its bisect-exact letter, not spelled out VUTS…).
|
|
596
1024
|
let innerR0 = r0.slice(0)
|
|
597
1025
|
innerR0[maxLevel] = count - 1
|
|
598
1026
|
if (innerR0[maxLevel] === 0) {
|
|
@@ -624,11 +1052,27 @@ function formatR0AsChain(r0, precision) {
|
|
|
624
1052
|
if ((r0[i] || 0) !== 8) all8 = false
|
|
625
1053
|
}
|
|
626
1054
|
|
|
1055
|
+
// sciThreshold boundary: once the top-level argument reaches 10^sciThreshold
|
|
1056
|
+
// the binary form αΓβ = Γ(β+log α) renders the argument in its own canonical
|
|
1057
|
+
// (αEβ) form and the outer letter is written bare — α must appear only once,
|
|
1058
|
+
// at the innermost value (2-letter rule: "G1.000E12", not "1.000G1.000E12")
|
|
1059
|
+
let sciBound = Math.pow(10, FORMAT_OPTIONS.sciThreshold)
|
|
1060
|
+
|
|
627
1061
|
if (all8) {
|
|
628
1062
|
// all-8s promotion: E^count(10^10) → F(count+2), F^count(F10) → G(count+2), etc.
|
|
629
1063
|
let arg = count + 2
|
|
630
1064
|
let level = maxLevel + 1
|
|
631
1065
|
let letter = letterName(level)
|
|
1066
|
+
if (level >= 23) {
|
|
1067
|
+
// Promotion into a diagonal letter: the all-8s r0 is the exact
|
|
1068
|
+
// diagonal 10{L+1}10 = Aa(L+1), whose dlsdl binary form anchors
|
|
1069
|
+
// mantissa 2 (α=2·5^0): 10{23}10 -> "2.000Aa23".
|
|
1070
|
+
return regularFormat(2.0, precision) + "Aa" + commaFormat(maxLevel + 1)
|
|
1071
|
+
}
|
|
1072
|
+
if (arg >= sciBound) {
|
|
1073
|
+
// Γ(β) with β ≥ 10^sciThreshold: bare letter + canonical argument
|
|
1074
|
+
return letter + formatR0Arg(arg, precision)
|
|
1075
|
+
}
|
|
632
1076
|
// Format: α + letter + β (always show α)
|
|
633
1077
|
let alpha = Math.pow(10, arg - Math.floor(arg))
|
|
634
1078
|
if (alpha < 1) alpha *= 10
|
|
@@ -637,22 +1081,150 @@ function formatR0AsChain(r0, precision) {
|
|
|
637
1081
|
return alphaStr + letter + betaStr
|
|
638
1082
|
}
|
|
639
1083
|
|
|
640
|
-
//
|
|
1084
|
+
// ── Γ-canonical recovery (2-letter rule) ──
|
|
1085
|
+
// Two rules, both driven by the engine's own smooth arrow curve:
|
|
1086
|
+
// (a) structural top count < threshold AND inner top < maxLevel: keep
|
|
1087
|
+
// the structural top letter Γᵀ bare and format the inner chain
|
|
1088
|
+
// recursively — at most 2 letter types appear (2-letter rule:
|
|
1089
|
+
// VαEβ → VαFβ → … → VαVβ, never a mixed "VFαEβ").
|
|
1090
|
+
// (b) otherwise: collapse to the single letter Γ = the HIGHEST level L
|
|
1091
|
+
// whose recovered argument x (bisect 10{L}x = v) is ≥ 2, in binary
|
|
1092
|
+
// form αΓβ (2-letter rule: hardy(4166) → "2.397G5", arrow(3,4.3,3) →
|
|
1093
|
+
// "1.285H8", arrow(3,4.1,3) → "G1.161G897").
|
|
1094
|
+
// Verified laws: n ops of level L on a Γ^{L+1}-structured value collapse
|
|
1095
|
+
// to ONE Γ^{L+1} with arg+n (F^4(G(1.3796)) = G(5.3796) — bisect-exact).
|
|
641
1096
|
let r0Num = new MetaNum({ sign: 1, layer: 0, array: [r0.slice(0)] })
|
|
1097
|
+
|
|
1098
|
+
if (count < effThreshold && maxLevel >= 2 && runLen <= effThreshold) {
|
|
1099
|
+
// (a) structural top letter: peel ONE op of level maxLevel and format
|
|
1100
|
+
// the inner chain recursively (exact even when the argument itself is
|
|
1101
|
+
// letter-structured, e.g. arrow(3,4.1,3) = G(F^895-chain) →
|
|
1102
|
+
// "G" + format(F^895-chain) = "G1.161G897")
|
|
1103
|
+
let innerR0 = r0.slice(0)
|
|
1104
|
+
innerR0[maxLevel] = count - 1
|
|
1105
|
+
if (innerR0[maxLevel] === 0) {
|
|
1106
|
+
innerR0 = innerR0.slice(0, maxLevel)
|
|
1107
|
+
}
|
|
1108
|
+
let innerStr = formatR0AsChain(innerR0, precision)
|
|
1109
|
+
let hasLetter = /[A-Z]/.test(innerStr)
|
|
1110
|
+
if (!hasLetter) {
|
|
1111
|
+
let base = r0[0]
|
|
1112
|
+
let alpha = Math.pow(10, base - Math.floor(base))
|
|
1113
|
+
if (alpha < 1) alpha *= 10
|
|
1114
|
+
return regularFormat(alpha, precision) + letterName(maxLevel) + innerStr
|
|
1115
|
+
}
|
|
1116
|
+
// inner carries letters: bare outer letter (2-letter rule) — the
|
|
1117
|
+
// inner top must be LOWER (descending chain); ascending or same-level
|
|
1118
|
+
// chains fall through to collapse
|
|
1119
|
+
let innerTop = 0
|
|
1120
|
+
for (let it = innerR0.length - 1; it >= 1; it--) {
|
|
1121
|
+
if (innerR0[it] > 0) { innerTop = it; break }
|
|
1122
|
+
}
|
|
1123
|
+
if (innerTop < maxLevel) {
|
|
1124
|
+
// ≤2 letter types rule: count the distinct letter
|
|
1125
|
+
// types the peeled prefix + innerStr would use. If the inner chain
|
|
1126
|
+
// spells a THIRD type, collapse the inner to the binary form at
|
|
1127
|
+
// maxLevel-1 instead — VαEβ → VαFβ → … → VαVβ, never a mixed
|
|
1128
|
+
// "VFαEβ" (e.g. arrow(3,9,3) → "L2.376K2": the MLKJIHG F-chain
|
|
1129
|
+
// spells only L and K, the M..J part collapses into K's arg).
|
|
1130
|
+
let topLetter = letterName(maxLevel)
|
|
1131
|
+
let innerTypes = distinctLetterTypes(innerStr)
|
|
1132
|
+
if (innerTypes.length >= 2
|
|
1133
|
+
&& innerTypes.indexOf(topLetter) < 0
|
|
1134
|
+
&& innerTypes.indexOf(letterName(innerTop)) < 0) {
|
|
1135
|
+
// inner already uses its own 2 types: cannot keep the structural
|
|
1136
|
+
// letter — collapse below
|
|
1137
|
+
} else {
|
|
1138
|
+
let wouldTypes = {}
|
|
1139
|
+
wouldTypes[topLetter] = true
|
|
1140
|
+
for (let wt = 0; wt < innerTypes.length; wt++) wouldTypes[innerTypes[wt]] = true
|
|
1141
|
+
if (Object.keys(wouldTypes).length <= 2) {
|
|
1142
|
+
return letterName(maxLevel) + innerStr
|
|
1143
|
+
}
|
|
1144
|
+
// 3rd type would appear: collapse the inner chain to the binary
|
|
1145
|
+
// form at innerTop — arg recovered on the engine curve when
|
|
1146
|
+
// possible (level < 20), else structurally via refPolarize
|
|
1147
|
+
let innerNum = new MetaNum({ sign: 1, layer: 0, array: [innerR0.slice(0)] })
|
|
1148
|
+
let bx = (innerTop <= 19) ? gammaArgOf(innerNum, innerTop, 40) : null
|
|
1149
|
+
if (bx !== null && isFinite(bx) && bx >= 2) {
|
|
1150
|
+
return letterName(maxLevel) + formatGammaBinary(bx, innerTop, precision)
|
|
1151
|
+
}
|
|
1152
|
+
let pol2 = polarizeFiniteChain(innerR0, null)
|
|
1153
|
+
if (pol2 && pol2.arrows >= 2 && pol2.arrows <= 22) {
|
|
1154
|
+
let aStr = regularFormat(Math.max(pol2.bottom, 1.0001), precision)
|
|
1155
|
+
return letterName(maxLevel) + aStr + letterName(pol2.arrows) + commaFormat(pol2.repeation)
|
|
1156
|
+
}
|
|
1157
|
+
// polarize reached the multi-letter region: Aa-diagonal form
|
|
1158
|
+
if (pol2 && pol2.arrows >= 23) {
|
|
1159
|
+
let aStr = regularFormat(Math.max(pol2.bottom, 1.0001), precision)
|
|
1160
|
+
return letterName(maxLevel) + aStr + "Aa" + commaFormat(pol2.arrows)
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// (b) collapse: the structural chain (n repeats of the top letter Γᵀ on
|
|
1167
|
+
// an inner value whose own canonical letter is Γᵀ⁺¹, e.g. F^895(G(2.06)))
|
|
1168
|
+
// compresses to ONE Γᵀ⁺¹ with arg+n — bisect at the STRUCTURAL next
|
|
1169
|
+
// letter first (maxLevel+1); if its arg falls below 2, search upward for
|
|
1170
|
+
// the highest level whose arg ≥ 2 (2-letter rule: hardy(4166) → "2.397G5",
|
|
1171
|
+
// arrow(3,4.3,3) → "1.285H8")
|
|
1172
|
+
let structuralNext = Math.min(maxLevel + 1, 22)
|
|
1173
|
+
let sx = gammaArgOf(r0Num, structuralNext, 40)
|
|
1174
|
+
if (sx !== null && isFinite(sx) && sx >= 2) {
|
|
1175
|
+
return formatGammaBinary(sx, structuralNext, precision)
|
|
1176
|
+
}
|
|
1177
|
+
for (let lv = 22; lv >= 2; lv--) {
|
|
1178
|
+
if (lv === structuralNext) continue
|
|
1179
|
+
let x = gammaArgOf(r0Num, lv, 40)
|
|
1180
|
+
if (x !== null && isFinite(x) && x >= 2) {
|
|
1181
|
+
return formatGammaBinary(x, lv, precision)
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Γ-arg not recoverable as a double (arg beyond bisect range): fall back
|
|
1186
|
+
// to metaPolarize for the polarized form
|
|
642
1187
|
let pol = metaPolarize(r0.slice(0), r0Num)
|
|
643
1188
|
let h = pol.height
|
|
644
1189
|
let rep = pol.repeat || 1
|
|
645
1190
|
let top = pol.top
|
|
646
1191
|
let bottom = pol.bottom
|
|
647
1192
|
|
|
1193
|
+
if (runLen > effThreshold) {
|
|
1194
|
+
// The descending count-1 run is the diagonalized expansion of the top
|
|
1195
|
+
// letter applied twice: force the ΓαΓβ form (issues.md: arrow(3,19,3)
|
|
1196
|
+
// → "VαVβ", not "VUTS…G…")
|
|
1197
|
+
rep = 2
|
|
1198
|
+
h = maxLevel
|
|
1199
|
+
}
|
|
1200
|
+
|
|
648
1201
|
// Convert repeated letters to next level when over threshold
|
|
649
1202
|
if (rep > 1 && rep >= effThreshold) {
|
|
650
1203
|
let newH = h + 1
|
|
651
1204
|
let newTop = rep + 1
|
|
652
1205
|
let newLetter = letterName(newH)
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1206
|
+
if (newTop >= sciBound) {
|
|
1207
|
+
// bare letter + canonical argument (single α at the innermost value)
|
|
1208
|
+
return newLetter + formatR0Arg(newTop + Math.log10(bottom < 1 ? Math.pow(10, bottom) : bottom), precision)
|
|
1209
|
+
}
|
|
1210
|
+
// Format: α + letter + β. Canonical all-8s arrays (built from the
|
|
1211
|
+
// 10^10 base: G600, ZZ10, ...) carry no recoverable mantissa below
|
|
1212
|
+
// 10^10, so α = 1.000 by construction. Every other value recovers
|
|
1213
|
+
// its real α by stripping newTop slog's — the residue lands in
|
|
1214
|
+
// [1,10) and is exactly the α of the binary form αΓβ = Γ(β+log α)
|
|
1215
|
+
// (issues.md: hardy(4150) → "2.300G5", not "1.000G5")
|
|
1216
|
+
let alpha = 1.0
|
|
1217
|
+
let all8s = (r0[0] === 10000000000)
|
|
1218
|
+
for (let ai = 2; ai < pol.height && all8s; ai++) {
|
|
1219
|
+
if ((r0[ai] || 0) !== 8) all8s = false
|
|
1220
|
+
}
|
|
1221
|
+
if (!all8s) {
|
|
1222
|
+
try {
|
|
1223
|
+
let av = r0Num.clone()
|
|
1224
|
+
for (let si = 0; si < newTop && av.gt(MetaNum(1e15)); si++) av = av.slog()
|
|
1225
|
+
if (av.gte(1) && av.lt(10)) alpha = av.toNumber()
|
|
1226
|
+
} catch (e) { /* keep α = 1.0 */ }
|
|
1227
|
+
}
|
|
656
1228
|
let alphaStr = regularFormat(alpha, precision)
|
|
657
1229
|
let betaStr = formatR0Arg(newTop, precision)
|
|
658
1230
|
return alphaStr + newLetter + betaStr
|
|
@@ -665,14 +1237,32 @@ function formatR0AsChain(r0, precision) {
|
|
|
665
1237
|
// Repeated letters: outerLetters + α + lastLetter + β
|
|
666
1238
|
let outerLetters = letter.repeat(rep - 1)
|
|
667
1239
|
let alpha = bottom
|
|
668
|
-
|
|
1240
|
+
let logAlpha = 0
|
|
1241
|
+
if (alpha < 1) {
|
|
1242
|
+
logAlpha = alpha
|
|
1243
|
+
alpha = Math.pow(10, alpha)
|
|
1244
|
+
} else {
|
|
1245
|
+
logAlpha = Math.log10(alpha)
|
|
1246
|
+
}
|
|
1247
|
+
let betaVal = top + logAlpha
|
|
1248
|
+
if (top >= sciBound) {
|
|
1249
|
+
// bare letters + canonical argument (single α at the innermost value)
|
|
1250
|
+
return outerLetters + letter + formatR0Arg(betaVal, precision)
|
|
1251
|
+
}
|
|
669
1252
|
let alphaStr = regularFormat(alpha, precision)
|
|
670
1253
|
let betaStr = formatR0Arg(top, precision)
|
|
671
1254
|
return outerLetters + alphaStr + letter + betaStr
|
|
672
1255
|
}
|
|
673
|
-
// Single letter: α + letter + β
|
|
1256
|
+
// Single letter: α + letter + β — but once β ≥ 10^sciThreshold the
|
|
1257
|
+
// α only perturbs β by log10(α) < 1, far below the displayed
|
|
1258
|
+
// precision: write the letter bare and let β carry the single α
|
|
1259
|
+
// (issues.md: hardy(1120) → "F4.398E13", not "4.295F4.398E13")
|
|
674
1260
|
let alpha = bottom
|
|
675
1261
|
if (alpha < 1) alpha = Math.pow(10, alpha)
|
|
1262
|
+
let betaVal = top + Math.log10(alpha)
|
|
1263
|
+
if (top >= sciBound) {
|
|
1264
|
+
return letter + formatR0Arg(betaVal, precision)
|
|
1265
|
+
}
|
|
676
1266
|
let alphaStr = regularFormat(alpha, precision)
|
|
677
1267
|
let betaStr = formatR0Arg(top, precision)
|
|
678
1268
|
return alphaStr + letter + betaStr
|
|
@@ -689,39 +1279,127 @@ function formatR0AsChain(r0, precision) {
|
|
|
689
1279
|
|
|
690
1280
|
// ─── Format with Ordinal Rows ────────────────────────────────────
|
|
691
1281
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1282
|
+
// finite-row chain display (≤2-letter-types rule):
|
|
1283
|
+
// r0 cols + [count, level] rows form one hyperoperation cascade. Display:
|
|
1284
|
+
// top level ≥ 23 (Aa region): the αAaβ diagonal — (bottom, repeation,
|
|
1285
|
+
// arrows) from polarizeFiniteChain, mantissa normalized into [1,10)
|
|
1286
|
+
// carrying decades into β: 3{24}3 → "2.376Aa23", 3{100}3 → "2.376Aa99",
|
|
1287
|
+
// 10{10000}10 → "1.000Aa10,000" (bottom 1, repeation 10 carries).
|
|
1288
|
+
// top level ≤ 22 (single letters): the structural two-letter form
|
|
1289
|
+
// Γ_top×c₁ + Γ_second×(c₂−1) + bottom + Γ_second + repeation:
|
|
1290
|
+
// 3{22}3 → "Y2.376X2", 3{23}3 → "Z2.376Y2" (user: "Y…X…", "Z…Y…").
|
|
1291
|
+
// c₁ ≥ repeatLetterThreshold+1 carries to the next single letter.
|
|
1292
|
+
function formatFiniteRowChain(r0, finiteRows, precision) {
|
|
1293
|
+
function levelCount(lv) {
|
|
1294
|
+
if (lv < r0.length && (r0[lv] || 0) > 0) return r0[lv]
|
|
1295
|
+
for (let i = 0; i < finiteRows.length; i++) {
|
|
1296
|
+
if (finiteRows[i][1] === lv) return finiteRows[i][0]
|
|
1297
|
+
}
|
|
1298
|
+
return 0
|
|
1299
|
+
}
|
|
1300
|
+
let topLv = 0, c1 = 0
|
|
1301
|
+
for (let i = 1; i < r0.length; i++) {
|
|
1302
|
+
if ((r0[i] || 0) > 0) { topLv = i; c1 = r0[i] }
|
|
1303
|
+
}
|
|
1304
|
+
for (let i = 0; i < finiteRows.length; i++) {
|
|
1305
|
+
if (finiteRows[i][1] > topLv) { topLv = finiteRows[i][1]; c1 = finiteRows[i][0] }
|
|
1306
|
+
}
|
|
1307
|
+
if (topLv === 0) return formatR0Arg(r0[0], precision)
|
|
1308
|
+
let effThreshold = Math.max(2, FORMAT_OPTIONS.repeatLetterThreshold)
|
|
695
1309
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
1310
|
+
if (topLv >= 23) {
|
|
1311
|
+
let pol = polarizeFiniteChain(r0, finiteRows)
|
|
1312
|
+
if (pol && isFinite(pol.bottom) && pol.bottom > 0 && isFinite(pol.arrows)) {
|
|
1313
|
+
let pair = aaDiagonalPair(pol)
|
|
1314
|
+
return regularFormat(pair.mant, precision) + "Aa" + commaFormat(pair.beta)
|
|
1315
|
+
}
|
|
1316
|
+
// fallback: legacy coefficient form on the top row
|
|
1317
|
+
let lastRow = finiteRows[finiteRows.length - 1]
|
|
1318
|
+
if (lastRow) return lastRow[0] + "Aa" + commaFormat(lastRow[1])
|
|
1319
|
+
return formatR0AsChain(r0.slice(0), precision)
|
|
700
1320
|
}
|
|
701
1321
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
let
|
|
1322
|
+
// carry: c₁ ≥ threshold+1 top-letter repeats advance to the next letter
|
|
1323
|
+
if (c1 >= effThreshold + 1) {
|
|
1324
|
+
let lv = topLv + 1
|
|
1325
|
+
if (lv <= 19) {
|
|
1326
|
+
let num = new MetaNum({ sign: 1, layer: 0, array: [r0.slice(0)].concat(finiteRows.map(r => r.slice(0))) })
|
|
1327
|
+
let bx = gammaArgOf(num, lv, 40)
|
|
1328
|
+
if (bx !== null && isFinite(bx) && bx >= 2) return formatGammaBinary(bx, lv, precision)
|
|
1329
|
+
}
|
|
1330
|
+
let pol = polarizeFiniteChain(r0, finiteRows)
|
|
1331
|
+
if (pol && isFinite(pol.bottom) && pol.bottom > 0 && isFinite(pol.arrows)) {
|
|
1332
|
+
if (lv >= 23) {
|
|
1333
|
+
// carry lands on the Aa diagonal: convert the polarize triple
|
|
1334
|
+
// via the same linear mantissa/β rule as the topLv>=23 path
|
|
1335
|
+
// (10{23}10 -> 1.000Aa23, not "1.000Aa10")
|
|
1336
|
+
let pair = aaDiagonalPair(pol)
|
|
1337
|
+
return regularFormat(pair.mant, precision) + "Aa" + commaFormat(pair.beta)
|
|
1338
|
+
}
|
|
1339
|
+
if (pol.bottom >= 1) {
|
|
1340
|
+
return regularFormat(pol.bottom, precision) + letterName(lv) + commaFormat(pol.repeation)
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
705
1344
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1345
|
+
// structural two-letter form (≤ 22: single-letter region)
|
|
1346
|
+
let secondLv = 0, c2 = 0
|
|
1347
|
+
for (let s = topLv - 1; s >= 1; s--) {
|
|
1348
|
+
let c = levelCount(s)
|
|
1349
|
+
if (c > 0) { secondLv = s; c2 = c; break }
|
|
1350
|
+
}
|
|
1351
|
+
if (secondLv >= 1 && c2 <= effThreshold) {
|
|
1352
|
+
let restR0 = r0.slice(0, Math.min(secondLv + 1, r0.length))
|
|
1353
|
+
let restRows = finiteRows.filter(r => r[1] <= secondLv)
|
|
1354
|
+
let pol = polarizeFiniteChain(restR0, restRows)
|
|
1355
|
+
if (pol && pol.arrows === secondLv && isFinite(pol.bottom) && pol.bottom >= 1) {
|
|
1356
|
+
let prefix = letterName(topLv).repeat(Math.min(c1, effThreshold)) +
|
|
1357
|
+
letterName(secondLv).repeat(Math.max(c2 - 1, 0))
|
|
1358
|
+
return prefix + regularFormat(pol.bottom, precision) + letterName(secondLv) + commaFormat(pol.repeation)
|
|
1359
|
+
}
|
|
1360
|
+
// low levels: engine-curve bisect at the second letter
|
|
1361
|
+
if (secondLv <= 19) {
|
|
1362
|
+
let num = new MetaNum({ sign: 1, layer: 0, array: [restR0].concat(restRows.map(r => r.slice(0))) })
|
|
1363
|
+
let bx = gammaArgOf(num, secondLv, 40)
|
|
1364
|
+
if (bx !== null && isFinite(bx) && bx >= 2) {
|
|
1365
|
+
let prefix = letterName(topLv).repeat(Math.min(c1, effThreshold))
|
|
1366
|
+
return prefix + formatGammaBinary(bx, secondLv, precision)
|
|
1367
|
+
}
|
|
710
1368
|
}
|
|
1369
|
+
}
|
|
711
1370
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1371
|
+
// fallback: single binary at the top letter
|
|
1372
|
+
let polF = polarizeFiniteChain(r0, finiteRows)
|
|
1373
|
+
if (polF && isFinite(polF.bottom) && polF.bottom >= 1) {
|
|
1374
|
+
return regularFormat(polF.bottom, precision) + letterName(topLv) + commaFormat(polF.repeation)
|
|
1375
|
+
}
|
|
1376
|
+
return formatR0AsChain(r0.slice(0), precision)
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function formatOrdinal(num, precision, precision4) {
|
|
1380
|
+
let r0 = num.array[0]
|
|
1381
|
+
let ordRows = num.array.slice(1)
|
|
1382
|
+
|
|
1383
|
+
// v2.1 classification: Format-B finite rows [count, level] form the
|
|
1384
|
+
// hyperoperation cascade; Format-A ordinal rows [count, v…, diag] are the
|
|
1385
|
+
// ω-diagonalization markers displayed as letter prefixes.
|
|
1386
|
+
let finiteRows = []
|
|
1387
|
+
let ordinalRows = []
|
|
1388
|
+
for (let i = 0; i < ordRows.length; i++) {
|
|
1389
|
+
if (ordRows[i].length === 2) finiteRows.push(ordRows[i])
|
|
1390
|
+
else ordinalRows.push(ordRows[i])
|
|
1391
|
+
}
|
|
1392
|
+
if (finiteRows.length > 0 && ordinalRows.length === 0) {
|
|
1393
|
+
return formatFiniteRowChain(r0, finiteRows, precision4)
|
|
1394
|
+
}
|
|
1395
|
+
if (finiteRows.length > 0 && ordinalRows.length > 0) {
|
|
1396
|
+
let prefixLetter = getOrdinalLetter(ordinalRows)
|
|
1397
|
+
if (prefixLetter) {
|
|
1398
|
+
return prefixLetter + formatFiniteRowChain(r0, finiteRows, precision4)
|
|
718
1399
|
}
|
|
719
|
-
|
|
720
|
-
let bottom = Math.pow(10, base - Math.floor(base))
|
|
721
|
-
let top = Math.floor(base)
|
|
722
|
-
let baseStr = regularFormat(bottom, precision4)
|
|
723
|
-
return tokens.join("") + baseStr
|
|
1400
|
+
// no letter recovered: fall through to the legacy machinery below
|
|
724
1401
|
}
|
|
1402
|
+
ordRows = ordinalRows
|
|
725
1403
|
|
|
726
1404
|
// Get the letter name from ordinal rows (3+ element format)
|
|
727
1405
|
// ── Check for ω-level rows (diag=1, all vals=0) → J-like format ──
|
|
@@ -788,9 +1466,31 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
788
1466
|
return regularFormat(mantissa, precision4) + finalLetter + commaFormat(finalParam)
|
|
789
1467
|
} else if (totalAaCount > 1) {
|
|
790
1468
|
// Repeated Aa below threshold: use AaAa... prefix
|
|
791
|
-
|
|
1469
|
+
let tokAa = letterTokenOf(baseHeight)
|
|
1470
|
+
if (tokAa.sym) {
|
|
1471
|
+
// multiLetterLimit symbol carry: the diagonal letter reads
|
|
1472
|
+
// as one more symbol with the chain as its argument
|
|
1473
|
+
return tokAa.sym + regularFormat(2, precision4) + tokAa.letter + argStr
|
|
1474
|
+
}
|
|
1475
|
+
// past the collapse threshold the repeats shift the argument and
|
|
1476
|
+
// advance the letter — a raw repeat would try to build a string
|
|
1477
|
+
// with `count` copies (Aaa repeated 1e9 times cannot be built)
|
|
1478
|
+
let repArgStr = argStr
|
|
1479
|
+
if (totalAaCount > 64) {
|
|
1480
|
+
let r0Bottom = Math.pow(10, r0[0] - Math.floor(r0[0]))
|
|
1481
|
+
let repParam = totalAaCount + 1
|
|
1482
|
+
let mantissa = Math.log10(Math.max(r0Bottom, 0.001)) +
|
|
1483
|
+
Math.log10(Math.max(repParam, 1)) * 0.4 + 1.6
|
|
1484
|
+
return regularFormat(mantissa, precision4) +
|
|
1485
|
+
letterName(baseHeight + 1) + commaFormat(repParam)
|
|
1486
|
+
}
|
|
1487
|
+
return letter.repeat(totalAaCount) + repArgStr
|
|
792
1488
|
} else {
|
|
793
1489
|
// Single Aa
|
|
1490
|
+
let tokAa = letterTokenOf(baseHeight)
|
|
1491
|
+
if (tokAa.sym) {
|
|
1492
|
+
return tokAa.sym + regularFormat(2, precision4) + tokAa.letter + argStr
|
|
1493
|
+
}
|
|
794
1494
|
return letter + argStr
|
|
795
1495
|
}
|
|
796
1496
|
} else {
|
|
@@ -806,18 +1506,39 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
806
1506
|
let collapseAt = effThreshold + 1
|
|
807
1507
|
|
|
808
1508
|
if (totalAaCount >= collapseAt) {
|
|
809
|
-
// Collapse to next letter (Aa→Ab, Ab→Ac, etc.)
|
|
810
|
-
|
|
1509
|
+
// Collapse to next letter (Aa→Ab, Ab→Ac, etc.): the repeats
|
|
1510
|
+
// shift the argument (arg = count+1) and the mantissa keeps
|
|
1511
|
+
// the [1,10) convention (2.398Ab99 — the same α the GRAHAMS
|
|
1512
|
+
// branch above produces), never the raw count as α
|
|
1513
|
+
let arg = totalAaCount + 1
|
|
1514
|
+
let mantVal = Math.log10(Math.max(alpha, 0.001)) +
|
|
1515
|
+
Math.log10(Math.max(arg, 1)) * 0.4 + 1.6
|
|
811
1516
|
let newHeight = baseHeight + 1
|
|
812
1517
|
let newLetter = letterName(newHeight)
|
|
813
|
-
return regularFormat(
|
|
1518
|
+
return regularFormat(mantVal, precision4) + newLetter + formatR0Arg(arg, precision4)
|
|
814
1519
|
} else if (totalAaCount > 1) {
|
|
815
1520
|
// Repeated letters: outerLetters + α + lastLetter + β
|
|
816
1521
|
let outerLetters = letter.repeat(totalAaCount - 1)
|
|
817
1522
|
return outerLetters + alphaStr + letter + betaStr
|
|
818
1523
|
} else {
|
|
819
|
-
// Single letter
|
|
820
|
-
|
|
1524
|
+
// Single DIAGONAL letter (the row ends in 'a': Aa, Aaa,
|
|
1525
|
+
// Ba, …): pure dlsdl convention α=2·5^f ∈ [2,10).
|
|
1526
|
+
// - symbol-de-layered row (base == #zeros, e.g. !Aa3): the
|
|
1527
|
+
// base is the LETTER INDEX, argument is fixed 10
|
|
1528
|
+
// (!Aa3 -> 2.000Aaaa10)
|
|
1529
|
+
// - ordinary letter application: base is the argument β
|
|
1530
|
+
// (Aa100 -> 2.000Aa100)
|
|
1531
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1532
|
+
let diagBeta = (base === aaZeroCount) ? 10 : top
|
|
1533
|
+
// multiLetterLimit symbol carry: A + aaZeroCount a's is
|
|
1534
|
+
// aaZeroCount+1 letters — past the limit the diagonal reads
|
|
1535
|
+
// as one more symbol with the Aa argument taking the exponent
|
|
1536
|
+
// (10{ω^10}10 → !2.000Aa10)
|
|
1537
|
+
if (aaZeroCount + 1 > Math.max(2, FORMAT_OPTIONS.multiLetterLimit | 0)) {
|
|
1538
|
+
return "!" + regularFormat(diagMant, precision4) +
|
|
1539
|
+
"Aa" + formatR0Arg(aaZeroCount, precision4)
|
|
1540
|
+
}
|
|
1541
|
+
return regularFormat(diagMant, precision4) + letter + commaFormat(diagBeta)
|
|
821
1542
|
}
|
|
822
1543
|
}
|
|
823
1544
|
}
|
|
@@ -839,8 +1560,12 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
839
1560
|
// Calculate height for this letter
|
|
840
1561
|
let height = 0
|
|
841
1562
|
if (nVals === 1) {
|
|
842
|
-
// 2-letter: Aa=23, Ab=24, ..., Az=48, Ba=49, ...
|
|
843
|
-
|
|
1563
|
+
// 2-letter: Aa=23, Ab=24, ..., Az=48, Ba=49, ... A coefficient
|
|
1564
|
+
// above 25 (ω+81, ω+99, … from a ω*2 fundamental sequence) has no
|
|
1565
|
+
// letter in the grid — clamp down to the largest letter below it
|
|
1566
|
+
// rather than wrapping into a higher, wrong letter.
|
|
1567
|
+
let v0 = vals[0] > 25 ? 25 : vals[0]
|
|
1568
|
+
height = 23 + (diag - 1) * 26 + v0
|
|
844
1569
|
} else {
|
|
845
1570
|
// 3+ letters: need to calculate offset
|
|
846
1571
|
// vals are stored in reverse letter order (e.g. "bc" -> [2,1]),
|
|
@@ -885,14 +1610,26 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
885
1610
|
// r0 has E/F/G levels: format as letter(s) + chain (e.g., Ab1.000E500)
|
|
886
1611
|
let r0Str = formatR0AsChain(r0, precision4)
|
|
887
1612
|
let letter = letterName(baseLetterHeight)
|
|
1613
|
+
let tokAdv = letterTokenOf(baseLetterHeight + 1)
|
|
1614
|
+
if (totalRepeat >= collapseAt && tokAdv.sym) {
|
|
1615
|
+
// Collapse to next letter — carried when it exceeds the limit;
|
|
1616
|
+
// the r0 chain stays as the β of the carried form
|
|
1617
|
+
return tokAdv.sym + regularFormat(tokAdv.mant, precision4) +
|
|
1618
|
+
tokAdv.letter + r0Str
|
|
1619
|
+
}
|
|
888
1620
|
if (totalRepeat >= collapseAt) {
|
|
889
|
-
// Collapse to next letter
|
|
1621
|
+
// Collapse to next letter — the repeat count shifts the
|
|
1622
|
+
// argument, so the mantissa keeps the [1,10) convention
|
|
890
1623
|
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
1624
|
+
let mantVal = Math.pow(10, mantissa - Math.floor(mantissa))
|
|
1625
|
+
if (mantVal < 1) mantVal *= 10
|
|
891
1626
|
let newLetter = letterName(baseLetterHeight + 1)
|
|
892
|
-
return regularFormat(
|
|
1627
|
+
return regularFormat(mantVal, precision4) + newLetter + r0Str
|
|
893
1628
|
} else if (totalRepeat > 1) {
|
|
894
|
-
// Repeated letters + chain
|
|
895
|
-
|
|
1629
|
+
// Repeated letters + chain: one token per application, the
|
|
1630
|
+
// innermost one in front of the chain (Ba(Ba(1e16)) → the
|
|
1631
|
+
// stacked "BaBa1.000E16" — apea(apea(a)) must stay visible)
|
|
1632
|
+
let outerLetters = letter.repeat(totalRepeat)
|
|
896
1633
|
return outerLetters + r0Str
|
|
897
1634
|
} else {
|
|
898
1635
|
// Single letter + chain
|
|
@@ -902,19 +1639,56 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
902
1639
|
|
|
903
1640
|
// Simple r0 (no E/F/G levels)
|
|
904
1641
|
if (totalRepeat >= collapseAt) {
|
|
905
|
-
// Advance to next letter with mantissa
|
|
1642
|
+
// Advance to next letter with mantissa — carried when the letter
|
|
1643
|
+
// exceeds multiLetterLimit (!αAaβ, repeats below display precision)
|
|
1644
|
+
let tokAdv = letterTokenOf(baseLetterHeight + 1)
|
|
1645
|
+
if (tokAdv.sym) {
|
|
1646
|
+
return emitLetterToken(tokAdv, regularFormat(tokAdv.mant, precision4), betaStr, precision4)
|
|
1647
|
+
}
|
|
906
1648
|
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
1649
|
+
// the repeat count shifts the argument, so the mantissa keeps the
|
|
1650
|
+
// [1,10) convention (the ω-level form recovers the base mantissa
|
|
1651
|
+
// the way the single-row Aa cascade does: 2.398Ab99)
|
|
1652
|
+
let mantVal = (baseLetterHeight === 23)
|
|
1653
|
+
? Math.log10(Math.max(alpha, 0.001)) +
|
|
1654
|
+
Math.log10(Math.max(totalRepeat + 1, 1)) * 0.4 + 1.6
|
|
1655
|
+
: Math.pow(10, mantissa - Math.floor(mantissa))
|
|
1656
|
+
if (mantVal < 1) mantVal *= 10
|
|
907
1657
|
let newHeight = baseLetterHeight + 1
|
|
908
1658
|
let letter = letterName(newHeight)
|
|
909
|
-
return regularFormat(
|
|
1659
|
+
return regularFormat(mantVal, precision4) + letter +
|
|
1660
|
+
formatR0Arg(totalRepeat + 1, precision4)
|
|
910
1661
|
} else if (totalRepeat > 1) {
|
|
911
1662
|
// Repeated letters: outerLetters + α + lastLetter + β
|
|
912
1663
|
let letter = letterName(baseLetterHeight)
|
|
1664
|
+
let tokRep = letterTokenOf(baseLetterHeight)
|
|
1665
|
+
if (tokRep.sym) {
|
|
1666
|
+
return emitLetterToken(tokRep, alphaStr, betaStr, precision4)
|
|
1667
|
+
}
|
|
913
1668
|
let outerLetters = letter.repeat(totalRepeat - 1)
|
|
914
1669
|
return outerLetters + alphaStr + letter + betaStr
|
|
915
1670
|
} else {
|
|
916
|
-
// Single letter
|
|
1671
|
+
// Single letter. DIAGONAL letters (token ends in 'a': Aa, Ba,
|
|
1672
|
+
// Ca, Aaa, …) use the dlsdl [2,10) mantissa 2·5^f with β = base
|
|
1673
|
+
// argument (Ba1000 -> 2.000Ba1,000); successor letters (Ab, Bb,
|
|
1674
|
+
// … keep the ordinary [1,10) α/β form).
|
|
917
1675
|
let letter = letterName(baseLetterHeight)
|
|
1676
|
+
// multiLetterLimit symbol carry for a single long letter: the
|
|
1677
|
+
// diagonal keeps its 2·5^f mantissa, the ordinary letters use the
|
|
1678
|
+
// definition digits (!αAaβ), and β becomes the ω-exponent
|
|
1679
|
+
let tokSingle = letterTokenOf(baseLetterHeight)
|
|
1680
|
+
if (tokSingle.sym) {
|
|
1681
|
+
if (letter.length >= 2 && letter[letter.length - 1] === "a") {
|
|
1682
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1683
|
+
return tokSingle.sym + regularFormat(diagMant, precision4) +
|
|
1684
|
+
tokSingle.letter + formatR0Arg(tokSingle.arg, precision4)
|
|
1685
|
+
}
|
|
1686
|
+
return emitLetterToken(tokSingle, alphaStr, betaStr, precision4)
|
|
1687
|
+
}
|
|
1688
|
+
if (letter.length >= 2 && letter[letter.length - 1] === "a") {
|
|
1689
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1690
|
+
return regularFormat(diagMant, precision4) + letter + betaStr
|
|
1691
|
+
}
|
|
918
1692
|
return alphaStr + letter + betaStr
|
|
919
1693
|
}
|
|
920
1694
|
}
|
|
@@ -939,12 +1713,22 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
939
1713
|
let vals = row.slice(1, row.length - 1)
|
|
940
1714
|
let nVals = vals.length
|
|
941
1715
|
if (nVals === 1) {
|
|
942
|
-
|
|
1716
|
+
// the letter grid holds ω·d+v with 0 ≤ v ≤ 25; a larger
|
|
1717
|
+
// coefficient (a ω*2 fundamental sequence produces ω+81,
|
|
1718
|
+
// ω+99, …) has no letter, so clamp it down to the largest
|
|
1719
|
+
// letter below the ordinal instead of wrapping into a
|
|
1720
|
+
// higher, wrong letter (ω+81 must not display as ω*4+3)
|
|
1721
|
+
let v0 = vals[0] > 25 ? 25 : vals[0]
|
|
1722
|
+
height = 23 + (diag - 1) * 26 + v0
|
|
943
1723
|
} else {
|
|
1724
|
+
// vals are the CNF coefficients a1, a2, …, a(x-1) of
|
|
1725
|
+
// ord = ω^(x-1)·ax + … + ω·a2 + a1 — little-endian, the same
|
|
1726
|
+
// order letterName()/getOrdinalLetter() decode the letter:
|
|
1727
|
+
// the LAST val is the first lowercase letter of the token.
|
|
944
1728
|
let offset = 0
|
|
945
1729
|
for (let k = 2; k <= nVals; k++) offset += Math.pow(26, k)
|
|
946
1730
|
let n = (diag - 1) * Math.pow(26, nVals)
|
|
947
|
-
for (let j = 0; j < nVals; j++) n += vals[j] * Math.pow(26,
|
|
1731
|
+
for (let j = 0; j < nVals; j++) n += vals[j] * Math.pow(26, j)
|
|
948
1732
|
height = 23 + offset + n
|
|
949
1733
|
}
|
|
950
1734
|
}
|
|
@@ -955,72 +1739,107 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
955
1739
|
letterCounts[height] = (letterCounts[height] || 0) + count
|
|
956
1740
|
}
|
|
957
1741
|
|
|
958
|
-
if (heights.length >=
|
|
1742
|
+
if (heights.length >= 1) {
|
|
959
1743
|
// Sort heights ascending
|
|
960
1744
|
heights.sort((a, b) => a - b)
|
|
961
1745
|
|
|
962
|
-
//
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
let
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (!(nextH in letterCounts) && heights.indexOf(nextH) < 0) heights.push(nextH)
|
|
972
|
-
letterCounts[nextH] = (letterCounts[nextH] || 0) + 1
|
|
973
|
-
collapseInfo[nextH] = count
|
|
974
|
-
delete letterCounts[h]
|
|
975
|
-
}
|
|
1746
|
+
// README: at most TWO finite letter types survive — a longer
|
|
1747
|
+
// cascade keeps only its top two levels, every lower row
|
|
1748
|
+
// compresses into the second letter's argument (they are below
|
|
1749
|
+
// display precision, and folding them into its repeat COUNT would
|
|
1750
|
+
// overstate the level: 8 ω's + 8 (ω+1)'s ≠ 16 more (ω+2)'s).
|
|
1751
|
+
if (heights.length > 2) {
|
|
1752
|
+
let keepLo = heights[heights.length - 2]
|
|
1753
|
+
for (let i = 0; i < heights.length - 2; i++) delete letterCounts[heights[i]]
|
|
1754
|
+
heights = Object.keys(letterCounts).map(Number).sort((a, b) => a - b)
|
|
976
1755
|
}
|
|
977
1756
|
|
|
978
|
-
// Re-sort after collapse
|
|
979
|
-
heights = Object.keys(letterCounts).map(Number).sort((a, b) => a - b)
|
|
980
|
-
|
|
981
1757
|
if (heights.length >= 1) {
|
|
982
|
-
//
|
|
1758
|
+
// ── Display rules (README "format-metanum rules (v2.0)") ──
|
|
1759
|
+
// * the rows are a COMPOSITION: the top row is the outermost
|
|
1760
|
+
// operation, so it leads the display and the row below it
|
|
1761
|
+
// supplies the single α and β (at most TWO letter types —
|
|
1762
|
+
// every lower row compresses into that inner argument);
|
|
1763
|
+
// * "+count collapse": count >= multiLetterRepeatThreshold+1
|
|
1764
|
+
// repeats of a letter collapse to ONE next letter with
|
|
1765
|
+
// arg count+1 (F⁴(G(1.3796)) = G(5.3796));
|
|
1766
|
+
// * stacking the same function therefore nests the letter:
|
|
1767
|
+
// poea(10,poea(10,100)) = Ad(Ad(99)) → "Ad1.000Ad99";
|
|
1768
|
+
// * a combination longer than multiLetterLimit carries to the
|
|
1769
|
+
// symbol notation !αAaβ.
|
|
1770
|
+
let effTh = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
1771
|
+
let collapseAt = effTh + 1
|
|
1772
|
+
|
|
983
1773
|
let base = r0[0]
|
|
984
1774
|
let r0Alpha = Math.pow(10, base - Math.floor(base))
|
|
985
1775
|
if (r0Alpha < 1) r0Alpha *= 10
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
let
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1776
|
+
let frac = base - Math.floor(base)
|
|
1777
|
+
if (frac < 0) frac += 1
|
|
1778
|
+
|
|
1779
|
+
// α of a "+count collapse" (the ω-level form recovers the base
|
|
1780
|
+
// mantissa the way the single-row Aa cascade does: 2.398Ab99)
|
|
1781
|
+
let collapseMant = function (h, count) {
|
|
1782
|
+
let arg = count + 1
|
|
1783
|
+
if (h === 23) {
|
|
1784
|
+
return regularFormat(Math.log10(Math.max(r0Alpha, 0.001)) +
|
|
1785
|
+
Math.log10(Math.max(arg, 1)) * 0.4 + 1.6, precision4)
|
|
1786
|
+
}
|
|
1787
|
+
return regularFormat(r0Alpha, precision4)
|
|
1788
|
+
}
|
|
1789
|
+
// one collapsed level: α + next letter + (count+1)
|
|
1790
|
+
let collapseStr = function (h, count) {
|
|
1791
|
+
let tok = letterTokenOf(h + 1)
|
|
1792
|
+
if (tok.sym) {
|
|
1793
|
+
return tok.sym + regularFormat(tok.mant, precision4) +
|
|
1794
|
+
tok.letter + formatR0Arg(count + 1, precision4)
|
|
1795
|
+
}
|
|
1796
|
+
return collapseMant(h, count) + tok.letter + formatR0Arg(count + 1, precision4)
|
|
1004
1797
|
}
|
|
1005
1798
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
let
|
|
1009
|
-
let
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1799
|
+
let hTop = heights[heights.length - 1]
|
|
1800
|
+
let cTop = letterCounts[hTop] || 1
|
|
1801
|
+
let hLow = heights.length >= 2 ? heights[heights.length - 2] : -1
|
|
1802
|
+
let cLow = hLow < 0 ? 0 : (letterCounts[hLow] || 1)
|
|
1803
|
+
|
|
1804
|
+
// (1) a single row level: collapse, or repeats with α + letter + β
|
|
1805
|
+
if (hLow < 0) {
|
|
1806
|
+
if (cTop >= collapseAt) return collapseStr(hTop, cTop)
|
|
1807
|
+
let tok = letterTokenOf(hTop)
|
|
1808
|
+
if (tok.sym) return emitLetterToken(tok, regularFormat(r0Alpha, precision4),
|
|
1809
|
+
formatR0Arg(Math.floor(base), precision4), precision4)
|
|
1810
|
+
let aStr = (tok.letter.length >= 2 && tok.letter[tok.letter.length - 1] === "a")
|
|
1811
|
+
? regularFormat(2 * Math.pow(5, frac), precision4)
|
|
1812
|
+
: regularFormat(r0Alpha, precision4)
|
|
1813
|
+
return tok.letter.repeat(Math.max(cTop - 1, 0)) + aStr + tok.letter +
|
|
1814
|
+
formatR0Arg(Math.floor(base), precision4)
|
|
1018
1815
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1816
|
+
|
|
1817
|
+
// (2) the inner (second) level carries the single α and β
|
|
1818
|
+
let lowTok = letterTokenOf(hLow)
|
|
1819
|
+
let innerStr
|
|
1820
|
+
if (cLow >= collapseAt) {
|
|
1821
|
+
innerStr = collapseStr(hLow, cLow)
|
|
1822
|
+
} else {
|
|
1823
|
+
let aStr = regularFormat(r0Alpha, precision4)
|
|
1824
|
+
innerStr = lowTok.letter.repeat(Math.max(cLow - 1, 0)) + aStr + lowTok.letter +
|
|
1825
|
+
formatR0Arg(Math.floor(base), precision4)
|
|
1021
1826
|
}
|
|
1022
1827
|
|
|
1023
|
-
|
|
1828
|
+
// (3) the top level leads: collapse when it repeats enough, else
|
|
1829
|
+
// one token per application (or the symbol-carried !αAaβ)
|
|
1830
|
+
if (cTop >= collapseAt) return collapseStr(hTop, cTop)
|
|
1831
|
+
let topTok = letterTokenOf(hTop)
|
|
1832
|
+
if (topTok.sym) {
|
|
1833
|
+
// multiLetterLimit carry: !αAaβ, β = the ω-exponent; the
|
|
1834
|
+
// inner level only survives when it collapses on its own
|
|
1835
|
+
if (cLow >= collapseAt) {
|
|
1836
|
+
return topTok.sym + regularFormat(topTok.mant, precision4) +
|
|
1837
|
+
topTok.letter + collapseStr(hLow, cLow)
|
|
1838
|
+
}
|
|
1839
|
+
return topTok.sym + regularFormat(topTok.mant, precision4) +
|
|
1840
|
+
topTok.letter + formatR0Arg(topTok.arg, precision4)
|
|
1841
|
+
}
|
|
1842
|
+
return topTok.letter.repeat(Math.min(cTop, effTh)) + innerStr
|
|
1024
1843
|
}
|
|
1025
1844
|
}
|
|
1026
1845
|
}
|
|
@@ -1089,5 +1908,5 @@ function formatSmall(num, precision=2) {
|
|
|
1089
1908
|
// ─── Exports ─────────────────────────────────────────────────────
|
|
1090
1909
|
|
|
1091
1910
|
if (typeof module !== 'undefined' && module.exports) {
|
|
1092
|
-
module.exports = { format, formatWhole, formatSmall, metaPolarize, letterName, symbolName, getOrdinalLetter, formatR0AsChain, FORMAT_OPTIONS }
|
|
1911
|
+
module.exports = { format, formatWhole, formatSmall, metaPolarize, letterName, symbolName, getOrdinalLetter, formatR0AsChain, FORMAT_OPTIONS, polarizeFiniteChain, gammaArgOf, formatGammaBinary }
|
|
1093
1912
|
}
|