metanum 1.4.0 → 2.0.0
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 +380 -114
- package/format-metanum.js +848 -72
- package/metanum.js +1245 -310
- package/package.json +32 -23
- package/test_mixed.js +606 -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)
|
|
710
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
|
+
}
|
|
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,19 @@ 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
|
|
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
|
+
}
|
|
791
1475
|
return letter.repeat(totalAaCount) + argStr
|
|
792
1476
|
} else {
|
|
793
1477
|
// Single Aa
|
|
1478
|
+
let tokAa = letterTokenOf(baseHeight)
|
|
1479
|
+
if (tokAa.sym) {
|
|
1480
|
+
return tokAa.sym + regularFormat(2, precision4) + tokAa.letter + argStr
|
|
1481
|
+
}
|
|
794
1482
|
return letter + argStr
|
|
795
1483
|
}
|
|
796
1484
|
} else {
|
|
@@ -816,8 +1504,24 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
816
1504
|
let outerLetters = letter.repeat(totalAaCount - 1)
|
|
817
1505
|
return outerLetters + alphaStr + letter + betaStr
|
|
818
1506
|
} else {
|
|
819
|
-
// Single letter
|
|
820
|
-
|
|
1507
|
+
// Single DIAGONAL letter (the row ends in 'a': Aa, Aaa,
|
|
1508
|
+
// Ba, …): pure dlsdl convention α=2·5^f ∈ [2,10).
|
|
1509
|
+
// - symbol-de-layered row (base == #zeros, e.g. !Aa3): the
|
|
1510
|
+
// base is the LETTER INDEX, argument is fixed 10
|
|
1511
|
+
// (!Aa3 -> 2.000Aaaa10)
|
|
1512
|
+
// - ordinary letter application: base is the argument β
|
|
1513
|
+
// (Aa100 -> 2.000Aa100)
|
|
1514
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1515
|
+
let diagBeta = (base === aaZeroCount) ? 10 : top
|
|
1516
|
+
// multiLetterLimit symbol carry: A + aaZeroCount a's is
|
|
1517
|
+
// aaZeroCount+1 letters — past the limit the diagonal reads
|
|
1518
|
+
// as one more symbol with the Aa argument taking the exponent
|
|
1519
|
+
// (10{ω^10}10 → !2.000Aa10)
|
|
1520
|
+
if (aaZeroCount + 1 > Math.max(2, FORMAT_OPTIONS.multiLetterLimit | 0)) {
|
|
1521
|
+
return "!" + regularFormat(diagMant, precision4) +
|
|
1522
|
+
"Aa" + formatR0Arg(aaZeroCount, precision4)
|
|
1523
|
+
}
|
|
1524
|
+
return regularFormat(diagMant, precision4) + letter + commaFormat(diagBeta)
|
|
821
1525
|
}
|
|
822
1526
|
}
|
|
823
1527
|
}
|
|
@@ -885,6 +1589,13 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
885
1589
|
// r0 has E/F/G levels: format as letter(s) + chain (e.g., Ab1.000E500)
|
|
886
1590
|
let r0Str = formatR0AsChain(r0, precision4)
|
|
887
1591
|
let letter = letterName(baseLetterHeight)
|
|
1592
|
+
let tokAdv = letterTokenOf(baseLetterHeight + 1)
|
|
1593
|
+
if (totalRepeat >= collapseAt && tokAdv.sym) {
|
|
1594
|
+
// Collapse to next letter — carried when it exceeds the limit;
|
|
1595
|
+
// the r0 chain stays as the β of the carried form
|
|
1596
|
+
return tokAdv.sym + regularFormat(tokAdv.mant, precision4) +
|
|
1597
|
+
tokAdv.letter + r0Str
|
|
1598
|
+
}
|
|
888
1599
|
if (totalRepeat >= collapseAt) {
|
|
889
1600
|
// Collapse to next letter
|
|
890
1601
|
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
@@ -902,7 +1613,12 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
902
1613
|
|
|
903
1614
|
// Simple r0 (no E/F/G levels)
|
|
904
1615
|
if (totalRepeat >= collapseAt) {
|
|
905
|
-
// Advance to next letter with mantissa
|
|
1616
|
+
// Advance to next letter with mantissa — carried when the letter
|
|
1617
|
+
// exceeds multiLetterLimit (!αAaβ, repeats below display precision)
|
|
1618
|
+
let tokAdv = letterTokenOf(baseLetterHeight + 1)
|
|
1619
|
+
if (tokAdv.sym) {
|
|
1620
|
+
return emitLetterToken(tokAdv, regularFormat(tokAdv.mant, precision4), betaStr, precision4)
|
|
1621
|
+
}
|
|
906
1622
|
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
907
1623
|
let newHeight = baseLetterHeight + 1
|
|
908
1624
|
let letter = letterName(newHeight)
|
|
@@ -910,11 +1626,34 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
910
1626
|
} else if (totalRepeat > 1) {
|
|
911
1627
|
// Repeated letters: outerLetters + α + lastLetter + β
|
|
912
1628
|
let letter = letterName(baseLetterHeight)
|
|
1629
|
+
let tokRep = letterTokenOf(baseLetterHeight)
|
|
1630
|
+
if (tokRep.sym) {
|
|
1631
|
+
return emitLetterToken(tokRep, alphaStr, betaStr, precision4)
|
|
1632
|
+
}
|
|
913
1633
|
let outerLetters = letter.repeat(totalRepeat - 1)
|
|
914
1634
|
return outerLetters + alphaStr + letter + betaStr
|
|
915
1635
|
} else {
|
|
916
|
-
// Single letter
|
|
1636
|
+
// Single letter. DIAGONAL letters (token ends in 'a': Aa, Ba,
|
|
1637
|
+
// Ca, Aaa, …) use the dlsdl [2,10) mantissa 2·5^f with β = base
|
|
1638
|
+
// argument (Ba1000 -> 2.000Ba1,000); successor letters (Ab, Bb,
|
|
1639
|
+
// … keep the ordinary [1,10) α/β form).
|
|
917
1640
|
let letter = letterName(baseLetterHeight)
|
|
1641
|
+
// multiLetterLimit symbol carry for a single long letter: the
|
|
1642
|
+
// diagonal keeps its 2·5^f mantissa, the ordinary letters use the
|
|
1643
|
+
// definition digits (!αAaβ), and β becomes the ω-exponent
|
|
1644
|
+
let tokSingle = letterTokenOf(baseLetterHeight)
|
|
1645
|
+
if (tokSingle.sym) {
|
|
1646
|
+
if (letter.length >= 2 && letter[letter.length - 1] === "a") {
|
|
1647
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1648
|
+
return tokSingle.sym + regularFormat(diagMant, precision4) +
|
|
1649
|
+
tokSingle.letter + formatR0Arg(tokSingle.arg, precision4)
|
|
1650
|
+
}
|
|
1651
|
+
return emitLetterToken(tokSingle, alphaStr, betaStr, precision4)
|
|
1652
|
+
}
|
|
1653
|
+
if (letter.length >= 2 && letter[letter.length - 1] === "a") {
|
|
1654
|
+
let diagMant = 2 * Math.pow(5, base - Math.floor(base))
|
|
1655
|
+
return regularFormat(diagMant, precision4) + letter + betaStr
|
|
1656
|
+
}
|
|
918
1657
|
return alphaStr + letter + betaStr
|
|
919
1658
|
}
|
|
920
1659
|
}
|
|
@@ -959,6 +1698,18 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
959
1698
|
// Sort heights ascending
|
|
960
1699
|
heights.sort((a, b) => a - b)
|
|
961
1700
|
|
|
1701
|
+
// README: at most TWO finite letter types survive — a longer
|
|
1702
|
+
// cascade keeps only its top two levels and compresses the rest
|
|
1703
|
+
// into the second letter's binary form (its repeat count).
|
|
1704
|
+
if (heights.length > 2) {
|
|
1705
|
+
let keepLo = heights[heights.length - 2]
|
|
1706
|
+
for (let i = 0; i < heights.length - 2; i++) {
|
|
1707
|
+
letterCounts[keepLo] = (letterCounts[keepLo] || 0) + (letterCounts[heights[i]] || 0)
|
|
1708
|
+
delete letterCounts[heights[i]]
|
|
1709
|
+
}
|
|
1710
|
+
heights = Object.keys(letterCounts).map(Number).sort((a, b) => a - b)
|
|
1711
|
+
}
|
|
1712
|
+
|
|
962
1713
|
// Collapse from lowest to highest
|
|
963
1714
|
let effThreshold = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
964
1715
|
let collapseAt = effThreshold + 1
|
|
@@ -979,48 +1730,73 @@ function formatOrdinal(num, precision, precision4) {
|
|
|
979
1730
|
heights = Object.keys(letterCounts).map(Number).sort((a, b) => a - b)
|
|
980
1731
|
|
|
981
1732
|
if (heights.length >= 1) {
|
|
982
|
-
//
|
|
1733
|
+
// ── Display rules (README "format-metanum rules (v2.0)") ──
|
|
1734
|
+
// * at most TWO finite letter types survive (VαEβ … VαVβ):
|
|
1735
|
+
// a longer cascade keeps its top two levels and compresses
|
|
1736
|
+
// the rest into the second letter's binary form;
|
|
1737
|
+
// * exactly one α, attached to the innermost (lowest) letter;
|
|
1738
|
+
// * a combination longer than multiLetterLimit carries to the
|
|
1739
|
+
// symbol notation !αAaβ instead of growing another letter.
|
|
1740
|
+
let effTh = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
1741
|
+
|
|
1742
|
+
// (1) the single α / β pair
|
|
983
1743
|
let base = r0[0]
|
|
984
1744
|
let r0Alpha = Math.pow(10, base - Math.floor(base))
|
|
985
1745
|
if (r0Alpha < 1) r0Alpha *= 10
|
|
1746
|
+
let frac = base - Math.floor(base)
|
|
1747
|
+
if (frac < 0) frac += 1
|
|
986
1748
|
|
|
987
|
-
|
|
988
|
-
let
|
|
989
|
-
|
|
1749
|
+
let collapseCount = 0
|
|
1750
|
+
for (let ci = 0; ci < heights.length; ci++) {
|
|
1751
|
+
collapseCount += collapseInfo[heights[ci]] || 0
|
|
1752
|
+
}
|
|
990
1753
|
|
|
991
1754
|
let alphaStr, betaStr
|
|
992
1755
|
if (collapseCount > 0) {
|
|
993
|
-
//
|
|
994
|
-
// α = 10^(fractional part) ∈ [1, 10), β = floor(mantissa)
|
|
1756
|
+
// Collapsed repeats: α and β come from the mantissa
|
|
1757
|
+
// α = 10^(fractional part) ∈ [1, 10), β = floor(mantissa)
|
|
995
1758
|
let mantissa = Math.log10(Math.max(r0Alpha, 0.001)) + collapseCount
|
|
996
1759
|
let alphaVal = Math.pow(10, mantissa - Math.floor(mantissa))
|
|
997
1760
|
if (alphaVal < 1) alphaVal *= 10
|
|
998
1761
|
alphaStr = regularFormat(alphaVal, precision4)
|
|
999
1762
|
betaStr = commaFormat(Math.floor(mantissa))
|
|
1000
1763
|
} else {
|
|
1001
|
-
// No collapse at innermost: α and β come directly from r0[0]
|
|
1002
1764
|
alphaStr = regularFormat(r0Alpha, precision4)
|
|
1003
1765
|
betaStr = formatR0Arg(Math.floor(base), precision4)
|
|
1004
1766
|
}
|
|
1767
|
+
// Diagonal combinations (letters ending in 'a': Aa, Ba, Aaa, …)
|
|
1768
|
+
// use the pure dlsdl mantissa α = 2·5^f ∈ [2, 10) — but only
|
|
1769
|
+
// when the diagonal letter IS the value's canonical letter
|
|
1770
|
+
// (single-type display). Inside a cascade the α is the inner
|
|
1771
|
+
// value's own mantissa and keeps the ordinary [1,10) form
|
|
1772
|
+
// (AbAa600 → Ab1.000Aa600).
|
|
1773
|
+
let diagMant = function (tok) {
|
|
1774
|
+
if (collapseCount > 0) return alphaStr
|
|
1775
|
+
if (descHeights.length > 1) return alphaStr
|
|
1776
|
+
if (!(tok.letter.length >= 2 && tok.letter[tok.letter.length - 1] === "a")) return alphaStr
|
|
1777
|
+
return regularFormat(2 * Math.pow(5, frac), precision4)
|
|
1778
|
+
}
|
|
1005
1779
|
|
|
1006
|
-
//
|
|
1007
|
-
// Descending order (highest first = outermost)
|
|
1780
|
+
// (3) compose: descending order (highest first = outermost)
|
|
1008
1781
|
let descHeights = heights.slice().reverse()
|
|
1009
|
-
let
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
for (let i = 0; i < descHeights.length - 1; i++) {
|
|
1015
|
-
let h = descHeights[i]
|
|
1016
|
-
let cnt = letterCounts[h] || 1
|
|
1017
|
-
outerLetters += letterName(h).repeat(cnt)
|
|
1782
|
+
let topTok = letterTokenOf(descHeights[0])
|
|
1783
|
+
if (topTok.sym) {
|
|
1784
|
+
// the top level needs the symbol notation — it dwarfs every
|
|
1785
|
+
// lower level, so the display is the single !αAaβ form
|
|
1786
|
+
return emitLetterToken(topTok, alphaStr, betaStr, precision4)
|
|
1018
1787
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1788
|
+
let outerLetters = topTok.letter.repeat(
|
|
1789
|
+
Math.min(letterCounts[descHeights[0]] || 1, effTh))
|
|
1790
|
+
if (descHeights.length >= 2) {
|
|
1791
|
+
let loH = descHeights[1]
|
|
1792
|
+
let loTok = letterTokenOf(loH)
|
|
1793
|
+
let loCnt = letterCounts[loH] || 1
|
|
1794
|
+
if (loCnt > 1) {
|
|
1795
|
+
outerLetters += loTok.letter.repeat(Math.min(loCnt - 1, effTh))
|
|
1796
|
+
}
|
|
1797
|
+
return outerLetters + diagMant(loTok) + loTok.letter + betaStr
|
|
1021
1798
|
}
|
|
1022
|
-
|
|
1023
|
-
return outerLetters + alphaStr + lastLetter + betaStr
|
|
1799
|
+
return diagMant(topTok) + topTok.letter + betaStr
|
|
1024
1800
|
}
|
|
1025
1801
|
}
|
|
1026
1802
|
}
|
|
@@ -1089,5 +1865,5 @@ function formatSmall(num, precision=2) {
|
|
|
1089
1865
|
// ─── Exports ─────────────────────────────────────────────────────
|
|
1090
1866
|
|
|
1091
1867
|
if (typeof module !== 'undefined' && module.exports) {
|
|
1092
|
-
module.exports = { format, formatWhole, formatSmall, metaPolarize, letterName, symbolName, getOrdinalLetter, formatR0AsChain, FORMAT_OPTIONS }
|
|
1868
|
+
module.exports = { format, formatWhole, formatSmall, metaPolarize, letterName, symbolName, getOrdinalLetter, formatR0AsChain, FORMAT_OPTIONS, polarizeFiniteChain, gammaArgOf, formatGammaBinary }
|
|
1093
1869
|
}
|