metanum 1.3.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 +998 -674
- package/format-metanum.js +1868 -971
- package/metanum.js +6317 -5051
- package/package.json +32 -23
- package/test-interface.html +6 -6
- package/test_mixed.js +1433 -759
- package/.gitattributes +0 -1
package/format-metanum.js
CHANGED
|
@@ -1,972 +1,1869 @@
|
|
|
1
|
-
// format-metanum.js by dlsdl
|
|
2
|
-
// Adapted from format-omeganum.js by cloudytheconqueror
|
|
3
|
-
// Uses dlsdl's Letter Notation (see README.md)
|
|
4
|
-
|
|
5
|
-
// Set to 1 to print debug information to console
|
|
6
|
-
let FORMAT_DEBUG = 0
|
|
7
|
-
|
|
8
|
-
// ─── Configuration Options ────────────────────────────────────────
|
|
9
|
-
const FORMAT_OPTIONS = {
|
|
10
|
-
smallNotationUseE: true, // 1. 小数值是否用E-表示(true
|
|
11
|
-
smallNotationThreshold: 4, // 2. 小数值表示阈值(=n则小于10^-n的数采用小数值处理)
|
|
12
|
-
decimalPlaces: 3, // 3. 常规数字小数位数(=0为1,=1为1.0,=2为1.00等)
|
|
13
|
-
decimalThreshold: 3, // 4. 常规数字小数阈值(=n则数值>=10^n时不显示小数部分)
|
|
14
|
-
useCommas: true, // 5. 常规数字是否显示逗号(true/false)
|
|
15
|
-
sciThreshold: 9, // 6. 科学计数法阈值(=n则数值>=10^n
|
|
16
|
-
sciSignificantDigits: 3, // 7. 科学计数法有效位数(=n
|
|
17
|
-
sciDecimalThreshold: 3, // 8. 科学计数法小数阈值(=n
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
multiLetterDigits: 3, // 11.
|
|
21
|
-
multiLetterRepeatThreshold: 3 // 12. 多字母组合重复阈值(=n则出现n个重复的多字母组合时用下一个字母计数法,n<2时以2计算)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
fmt = n.
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// r0[
|
|
68
|
-
// r0[
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
let
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
fArg = eCount + value
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
let
|
|
129
|
-
let
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
repeat
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
bottom = argVal
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
// height
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
let
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
result += String.fromCharCode(
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
let
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
let
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
if (
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
let
|
|
593
|
-
let
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
//
|
|
629
|
-
//
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
//
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
//
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
//
|
|
766
|
-
let
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
let
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
if (
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1
|
+
// format-metanum.js by dlsdl
|
|
2
|
+
// Adapted from format-omeganum.js by cloudytheconqueror
|
|
3
|
+
// Uses dlsdl's Letter Notation (see README.md)
|
|
4
|
+
|
|
5
|
+
// Set to 1 to print debug information to console
|
|
6
|
+
let FORMAT_DEBUG = 0
|
|
7
|
+
|
|
8
|
+
// ─── Configuration Options ────────────────────────────────────────
|
|
9
|
+
const FORMAT_OPTIONS = {
|
|
10
|
+
smallNotationUseE: true, // 1. 小数值是否用E-表示(true=αE-β,false=⁻¹)
|
|
11
|
+
smallNotationThreshold: 4, // 2. 小数值表示阈值(=n则小于10^-n的数采用小数值处理)
|
|
12
|
+
decimalPlaces: 3, // 3. 常规数字小数位数(=0为1,=1为1.0,=2为1.00等)
|
|
13
|
+
decimalThreshold: 3, // 4. 常规数字小数阈值(=n则数值>=10^n时不显示小数部分)
|
|
14
|
+
useCommas: true, // 5. 常规数字是否显示逗号(true/false)
|
|
15
|
+
sciThreshold: 9, // 6. 科学计数法阈值(=n则数值>=10^n开始用科学计数法,同样对αEβ中β的数值生效)
|
|
16
|
+
sciSignificantDigits: 3, // 7. 科学计数法有效位数(=n则αEβ的α的小数部分保留n位)
|
|
17
|
+
sciDecimalThreshold: 3, // 8. 科学计数法小数阈值(=n则αEβ的β>=10^n时不显示小数部分)
|
|
18
|
+
singleLetterDigits: 3, // 9. 单字母计数法有效位数(αFβ,αGβ...αZβ中α的小数位数)
|
|
19
|
+
repeatLetterThreshold: 3, // 10. 单字母重复阈值(=n则出现n+1个重复的单字母时用下一个字母计数法,n<2时以2计算)
|
|
20
|
+
multiLetterDigits: 3, // 11. 多字母计数法有效位数(及以上的α的小数位数)
|
|
21
|
+
multiLetterRepeatThreshold: 3, // 12. 多字母组合重复阈值(=n则出现n+1个重复的多字母组合时用下一个字母计数法,n<2时以2计算)
|
|
22
|
+
multiLetterLimit: 4, // 13. 多字母组合最大位数(=n则多字母组合的长度不超过n,超过则切换下一种计数法,n<2时以2计算)
|
|
23
|
+
epsilonSignificantDigits: 6 // 14. epsilon有效位数(=n则αεβ中α的小数位数为n)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ─── Utility Functions ───────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function commaFormat(num) {
|
|
29
|
+
if (!FORMAT_OPTIONS.useCommas) {
|
|
30
|
+
let n = typeof num === 'number' ? num : (num.toNumber ? num.toNumber() : Number(num))
|
|
31
|
+
if (isNaN(n) || !isFinite(n)) return "NaN"
|
|
32
|
+
return String(Math.floor(n))
|
|
33
|
+
}
|
|
34
|
+
if (num === null || num === undefined) return "NaN"
|
|
35
|
+
let n = typeof num === 'number' ? num : (num.toNumber ? num.toNumber() : Number(num))
|
|
36
|
+
if (isNaN(n) || !isFinite(n)) return "NaN"
|
|
37
|
+
if (n < 0.001) return "0"
|
|
38
|
+
let init = n.toString()
|
|
39
|
+
let portions = init.split(".")
|
|
40
|
+
portions[0] = portions[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
|
|
41
|
+
return portions[0]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function regularFormat(num, precision) {
|
|
45
|
+
if (num === null || num === undefined) return "NaN"
|
|
46
|
+
let n = typeof num === 'number' ? num : (num.toNumber ? num.toNumber() : Number(num))
|
|
47
|
+
if (isNaN(n) || !isFinite(n)) return "NaN"
|
|
48
|
+
if (n < 0.001) return (0).toFixed(precision)
|
|
49
|
+
if (precision === 0) return commaFormat(Math.floor(n))
|
|
50
|
+
let fmt
|
|
51
|
+
if (Number.isInteger(n) && precision > 0) {
|
|
52
|
+
fmt = n + "." + "0".repeat(precision)
|
|
53
|
+
} else {
|
|
54
|
+
fmt = n.toFixed(precision)
|
|
55
|
+
}
|
|
56
|
+
// Add commas to integer part if useCommas is enabled
|
|
57
|
+
if (FORMAT_OPTIONS.useCommas && precision > 0) {
|
|
58
|
+
let parts = fmt.split(".")
|
|
59
|
+
parts[0] = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
|
|
60
|
+
fmt = parts.join(".")
|
|
61
|
+
}
|
|
62
|
+
return fmt
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── MetaNum-Specific Polarize ───────────────────────────────────
|
|
66
|
+
// MetaNum's r0 is [base, e, f, g, h, ...] where:
|
|
67
|
+
// r0[0] = base value (argument to the operations)
|
|
68
|
+
// r0[1] = count of E operations (level 1)
|
|
69
|
+
// r0[2] = count of F operations (level 2)
|
|
70
|
+
// r0[3] = count of G operations (level 3), etc.
|
|
71
|
+
//
|
|
72
|
+
// In binary form αΓβ:
|
|
73
|
+
// bottom = α (mantissa in [1, 10))
|
|
74
|
+
// top = β (integer exponent)
|
|
75
|
+
// height = Γ level (1=E, 2=F, 3=G, ..., 22=Z)
|
|
76
|
+
// repeat = count of repeated operations at the highest level (>1 means repeated letters)
|
|
77
|
+
function metaPolarize(r0, num) {
|
|
78
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize input:", JSON.stringify(r0))
|
|
79
|
+
if (r0.length === 0) r0 = [0]
|
|
80
|
+
|
|
81
|
+
// Find the highest non-zero index (level)
|
|
82
|
+
let highest = 0
|
|
83
|
+
for (let i = r0.length - 1; i >= 1; i--) {
|
|
84
|
+
if (r0[i] > 0) { highest = i; break }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (highest === 0) {
|
|
88
|
+
// Simple number: just r0[0], no higher levels
|
|
89
|
+
let bottom = r0[0]
|
|
90
|
+
let top = 0
|
|
91
|
+
let height = 0
|
|
92
|
+
if (bottom >= 10) {
|
|
93
|
+
let logVal = Math.log10(bottom)
|
|
94
|
+
bottom = Math.pow(10, logVal - Math.floor(logVal))
|
|
95
|
+
top = Math.floor(logVal)
|
|
96
|
+
height = 1
|
|
97
|
+
}
|
|
98
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize simple:", {bottom, top, height, repeat: 1})
|
|
99
|
+
return {bottom, top, height, repeat: 1}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Climb through E level to compute the F-level argument ──
|
|
103
|
+
// r0 = [v, e, f, g, ...]
|
|
104
|
+
// For E^n (highest=1): climb E to convert to F format
|
|
105
|
+
// For F^n or higher (highest>=2): compute the argument at the E→F boundary
|
|
106
|
+
let value = r0[0]
|
|
107
|
+
let eCount = r0[1] || 0
|
|
108
|
+
|
|
109
|
+
// Climb the base value through E level
|
|
110
|
+
let eClimbExtra = 0
|
|
111
|
+
while (value >= 10) {
|
|
112
|
+
value = Math.log10(value)
|
|
113
|
+
eClimbExtra++
|
|
114
|
+
}
|
|
115
|
+
eCount += eClimbExtra
|
|
116
|
+
|
|
117
|
+
// Compute the F-level argument from the climbed E value
|
|
118
|
+
let fArg
|
|
119
|
+
if (value >= 1) {
|
|
120
|
+
fArg = eCount + Math.log10(value)
|
|
121
|
+
} else {
|
|
122
|
+
fArg = eCount + value - 1
|
|
123
|
+
if (fArg < 1) fArg = eCount + Math.log10(Math.pow(10, value))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (highest === 1) {
|
|
127
|
+
// E level only: convert to F format
|
|
128
|
+
let bottom = value
|
|
129
|
+
let top = eCount
|
|
130
|
+
let height = 1
|
|
131
|
+
let repeat = eCount
|
|
132
|
+
if (top >= 2) {
|
|
133
|
+
height = 2
|
|
134
|
+
if (bottom < 1) {
|
|
135
|
+
bottom = Math.pow(10, bottom)
|
|
136
|
+
}
|
|
137
|
+
top = Math.floor(top)
|
|
138
|
+
repeat = 1
|
|
139
|
+
} else {
|
|
140
|
+
bottom = Math.pow(10, bottom - Math.floor(bottom))
|
|
141
|
+
top = Math.floor(top)
|
|
142
|
+
}
|
|
143
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize E level:", {bottom, top, height, repeat})
|
|
144
|
+
return {bottom, top, height, repeat}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── Higher levels (F, G, H, ..., Z) ──
|
|
148
|
+
// For mixed levels like FE20 or G10, the F argument is the value after E operations.
|
|
149
|
+
// Try to compute it directly (E^e(base) = 10^e * base for e=1, or tower for e>1).
|
|
150
|
+
let levelArg = fArg
|
|
151
|
+
let eOps = r0[1] || 0
|
|
152
|
+
let canCompute = false
|
|
153
|
+
if (eOps > 0) {
|
|
154
|
+
let eVal = r0[0]
|
|
155
|
+
canCompute = true
|
|
156
|
+
for (let i = 0; i < eOps; i++) {
|
|
157
|
+
eVal = Math.pow(10, eVal)
|
|
158
|
+
if (!isFinite(eVal) || eVal > 1e308) {
|
|
159
|
+
canCompute = false
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (canCompute) {
|
|
164
|
+
levelArg = eVal
|
|
165
|
+
}
|
|
166
|
+
// If not computable, levelArg stays as fArg (from E climbing)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let height = highest
|
|
170
|
+
let repeat = r0[highest] || 0
|
|
171
|
+
|
|
172
|
+
// ── Case 1: Directly computable (e.g., FE20) ──
|
|
173
|
+
// Don't climb the computed value; use it directly as the top argument.
|
|
174
|
+
if (canCompute) {
|
|
175
|
+
let bottom = 1.0
|
|
176
|
+
let top = levelArg
|
|
177
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize computable:", {bottom, top, height, repeat})
|
|
178
|
+
return {bottom, top, height, repeat}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Case 2: Not computable ──
|
|
182
|
+
// Check for the "all-8s" pattern: base=10^10 and all intermediate levels are 8.
|
|
183
|
+
// This pattern arises when the original argument was 10 (e.g., Z10, G10, ZZ10).
|
|
184
|
+
let all8 = (r0[0] === 10000000000)
|
|
185
|
+
for (let i = 2; i < highest && all8; i++) {
|
|
186
|
+
if ((r0[i] || 0) !== 8) all8 = false
|
|
187
|
+
}
|
|
188
|
+
if (all8) {
|
|
189
|
+
// The normalization collapses one level when the argument is 10:
|
|
190
|
+
// Z(10) → Y^8(...E^8(10^10)...) → letter level NOT in array, r0[highest]=8
|
|
191
|
+
// ZZ(10) → Z(Y^8(...E^8(10^10)...)) → letter level IS in array, r0[highest]≠8
|
|
192
|
+
let bottom = 1.0
|
|
193
|
+
let top = 10
|
|
194
|
+
if (r0[highest] === 8) {
|
|
195
|
+
// Letter level not present (e.g., Z10, G10): go up one level, repeat=1
|
|
196
|
+
height = highest + 1
|
|
197
|
+
repeat = 1
|
|
198
|
+
} else {
|
|
199
|
+
// Letter level present (e.g., ZZ10, ZZZ10): add 1 for the base level
|
|
200
|
+
repeat = 1 + repeat
|
|
201
|
+
}
|
|
202
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize all-8s:", {bottom, top, height, repeat})
|
|
203
|
+
return {bottom, top, height, repeat}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Case 3: General non-computable ──
|
|
207
|
+
// Climb through intermediate levels to compute the argument at the highest level.
|
|
208
|
+
if (eOps > 0) {
|
|
209
|
+
for (let lv = 2; lv < highest; lv++) {
|
|
210
|
+
let cnt = r0[lv] || 0
|
|
211
|
+
let climbExtra = 0
|
|
212
|
+
while (levelArg >= 10) {
|
|
213
|
+
levelArg = Math.log10(levelArg)
|
|
214
|
+
climbExtra++
|
|
215
|
+
}
|
|
216
|
+
cnt += climbExtra
|
|
217
|
+
levelArg = cnt + Math.log10(Math.max(levelArg, 1))
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// When the F argument was not computable and the highest level is F (level 2),
|
|
222
|
+
// the E→F conversion adds 1 to the effective F count.
|
|
223
|
+
if (highest === 2 && eOps > 0) {
|
|
224
|
+
repeat += 1
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Climb at the highest level to compute the total repeat count
|
|
228
|
+
let climbExtra = 0
|
|
229
|
+
let argVal = levelArg
|
|
230
|
+
while (argVal >= 10) {
|
|
231
|
+
argVal = Math.log10(argVal)
|
|
232
|
+
climbExtra++
|
|
233
|
+
}
|
|
234
|
+
let totalRepeat = repeat + climbExtra
|
|
235
|
+
|
|
236
|
+
// Compute bottom (mantissa) and top (argument) from the climbed argVal
|
|
237
|
+
let bottom
|
|
238
|
+
if (argVal < 1) {
|
|
239
|
+
bottom = Math.pow(10, argVal)
|
|
240
|
+
} else {
|
|
241
|
+
bottom = argVal
|
|
242
|
+
}
|
|
243
|
+
let top = Math.floor(levelArg)
|
|
244
|
+
|
|
245
|
+
if (FORMAT_DEBUG >= 1) console.log("metaPolarize high level:", {bottom, top, height, repeat: totalRepeat, levelArg, climbExtra})
|
|
246
|
+
return {bottom, top, height, repeat: totalRepeat}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─── Letter Name: Map height index to dlsdl's letter name ────────
|
|
250
|
+
// height 1 = E, 2 = F, ..., 22 = Z
|
|
251
|
+
// height 23 = Aa, 24 = Ab, ..., 48 = Az, 49 = Ba, ..., 698 = Zz
|
|
252
|
+
// height 699 = Aaa, ...
|
|
253
|
+
function letterName(height) {
|
|
254
|
+
if (height < 1) return "E"
|
|
255
|
+
if (height <= 22) {
|
|
256
|
+
// E = charCode 69, F = 70, ..., Z = 90
|
|
257
|
+
return String.fromCharCode(68 + height)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Multi-letter: height >= 23 maps to the bijective base-26 sequence
|
|
261
|
+
let n = height - 23 // 0-based into multi-letter: 0=Aa, 1=Ab, ..., 25=Az, 26=Ba, ...
|
|
262
|
+
|
|
263
|
+
// Determine number of letters (starts at 2: Aa-Zz is 26*26 = 676 values)
|
|
264
|
+
let digits = 2
|
|
265
|
+
let rangeSize = 26 * 26
|
|
266
|
+
let offset = 0
|
|
267
|
+
while (n >= offset + rangeSize) {
|
|
268
|
+
offset += rangeSize
|
|
269
|
+
digits++
|
|
270
|
+
rangeSize *= 26
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
n -= offset
|
|
274
|
+
// n is now a base-26 number with 'digits' digits
|
|
275
|
+
// First digit is uppercase (A-Z), rest are lowercase (a-z)
|
|
276
|
+
let result = ""
|
|
277
|
+
let divisor = Math.pow(26, digits - 1)
|
|
278
|
+
for (let i = 0; i < digits; i++) {
|
|
279
|
+
let digit = Math.floor(n / divisor)
|
|
280
|
+
n %= divisor
|
|
281
|
+
divisor /= 26
|
|
282
|
+
if (i === 0) {
|
|
283
|
+
result += String.fromCharCode(65 + digit) // A-Z
|
|
284
|
+
} else {
|
|
285
|
+
result += String.fromCharCode(97 + digit) // a-z
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return result
|
|
289
|
+
}
|
|
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
|
+
|
|
321
|
+
// ─── Symbol Name: Map layer to symbol ────────────────────────────
|
|
322
|
+
const SYMBOLS = "!@#$%&~<>?"
|
|
323
|
+
function symbolName(layer) {
|
|
324
|
+
if (layer <= 0) return ""
|
|
325
|
+
if (layer <= SYMBOLS.length) return SYMBOLS[layer - 1]
|
|
326
|
+
// For layers beyond SYMBOLS, formatLayer uses the ε format: αεβ
|
|
327
|
+
return ""
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ─── Ordinal Letter: Extract letter name from ordinal rows ───────
|
|
331
|
+
// Two formats for ordinal rows:
|
|
332
|
+
// Format A (3+ element): [count, v1, v2, ..., vN, diag]
|
|
333
|
+
// diag = 1-26 (A-Z), the uppercase letter
|
|
334
|
+
// v1..vN = 0-25 (a-z), the lowercase letters
|
|
335
|
+
// count = repetition count
|
|
336
|
+
// Format B (2-element): [count, value]
|
|
337
|
+
// count = repetition count at ω-level
|
|
338
|
+
// value = ordinal level number (e.g., 901 means ω*901)
|
|
339
|
+
// Letter is always "Aa" (base ω-level)
|
|
340
|
+
function getOrdinalLetter(ordRows) {
|
|
341
|
+
if (!ordRows || ordRows.length === 0) return null
|
|
342
|
+
|
|
343
|
+
// Check if all rows are 2-element [count, value] format (ω-level operations)
|
|
344
|
+
let allTwoElement = true
|
|
345
|
+
for (let i = 0; i < ordRows.length; i++) {
|
|
346
|
+
if (ordRows[i].length !== 2) { allTwoElement = false; break }
|
|
347
|
+
}
|
|
348
|
+
if (allTwoElement) {
|
|
349
|
+
return "Aa" // Base ω-level letter; value is handled in formatOrdinal
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
let tokens = []
|
|
353
|
+
// Process rows in REVERSE order (matching toString behavior)
|
|
354
|
+
for (let i = ordRows.length - 1; i >= 0; i--) {
|
|
355
|
+
let row = ordRows[i]
|
|
356
|
+
if (row.length < 2) continue
|
|
357
|
+
let diag = row[row.length - 1]
|
|
358
|
+
let vals = row.slice(1, row.length - 1)
|
|
359
|
+
let count = row[0] || 1
|
|
360
|
+
|
|
361
|
+
// Build letter name (reverse order: last val first, matching toString)
|
|
362
|
+
let letter = String.fromCharCode(64 + diag)
|
|
363
|
+
for (let j = vals.length - 1; j >= 0; j--) {
|
|
364
|
+
letter += String.fromCharCode(97 + vals[j])
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (let c = 0; c < count; c++) {
|
|
368
|
+
tokens.push(letter)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Do NOT sort - rows are already in canonical order from normalize
|
|
373
|
+
return tokens.join("")
|
|
374
|
+
}
|
|
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
|
+
|
|
578
|
+
// ─── Main Format Function ────────────────────────────────────────
|
|
579
|
+
|
|
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) {
|
|
589
|
+
if (MetaNum.isNaN(num)) return "NaN"
|
|
590
|
+
let sciSigDigits = FORMAT_OPTIONS.sciSignificantDigits // for E notation
|
|
591
|
+
let sciPrecision = Math.max(sciSigDigits, precision)
|
|
592
|
+
let singlePrecision = FORMAT_OPTIONS.singleLetterDigits // for F, G, H, ...
|
|
593
|
+
let multiPrecision = FORMAT_OPTIONS.multiLetterDigits // for Aa and beyond
|
|
594
|
+
num = new MetaNum(num)
|
|
595
|
+
let array = num.array
|
|
596
|
+
let r0 = array[0]
|
|
597
|
+
|
|
598
|
+
// Basic edge cases
|
|
599
|
+
if (num.sign !== 2 && num.sign !== -2 && num.abs().lt(1e-308)) return (0).toFixed(precision)
|
|
600
|
+
if (num.sign < 0) return "-" + format(num.neg(), precision, small)
|
|
601
|
+
if (num.isInfinite()) return "Infinity"
|
|
602
|
+
|
|
603
|
+
// Small value handling (e.g., 0.0000000001 → 1.000E-10 or 1.000E10⁻¹)
|
|
604
|
+
let smallThreshold = Math.pow(10, -FORMAT_OPTIONS.smallNotationThreshold)
|
|
605
|
+
if (num.lt(smallThreshold)) {
|
|
606
|
+
if (FORMAT_OPTIONS.smallNotationUseE) {
|
|
607
|
+
// Handle reciprocal values (sign=2/-2) that underflow to 0 in double precision
|
|
608
|
+
if (num.sign === 2 || num.sign === -2) {
|
|
609
|
+
var recipFmt = num.clone()
|
|
610
|
+
recipFmt.sign = recipFmt.sign === 2 ? 1 : -1
|
|
611
|
+
var prefix = num.sign === -2 ? "-" : ""
|
|
612
|
+
// Compute magnitude (log10 of reciprocal)
|
|
613
|
+
var mag = recipFmt.log10()
|
|
614
|
+
// Tier 1: mag is a simple number → αE-β format
|
|
615
|
+
if (mag.array.length === 1 && mag.array[0].length <= 2) {
|
|
616
|
+
var magNum = mag.toNumber()
|
|
617
|
+
if (isFinite(magNum) && magNum > 0) {
|
|
618
|
+
var frac = magNum - Math.floor(magNum)
|
|
619
|
+
var mant = Math.pow(10, frac)
|
|
620
|
+
var expPart = Math.floor(magNum)
|
|
621
|
+
if (mant < 1) { mant *= 10; expPart -= 1 }
|
|
622
|
+
if (mant >= 9.999999999999999) { mant = 1; expPart += 1 }
|
|
623
|
+
return prefix + regularFormat(mant, sciPrecision) + "E-" + commaFormat(expPart)
|
|
624
|
+
}
|
|
625
|
+
}
|
|
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
|
+
}
|
|
662
|
+
return prefix + "E-" + magStr
|
|
663
|
+
}
|
|
664
|
+
// Format as aE-b (e.g., 0.0000000001 → 1.000E-10)
|
|
665
|
+
let nVal = num.toNumber()
|
|
666
|
+
if (nVal > 0 && isFinite(nVal)) {
|
|
667
|
+
let logVal = Math.log10(nVal)
|
|
668
|
+
let m = Math.pow(10, logVal - Math.floor(logVal))
|
|
669
|
+
let e = Math.abs(Math.floor(logVal))
|
|
670
|
+
// Normalize: ensure m in [1, 10)
|
|
671
|
+
if (m < 1) { m *= 10; e -= 1 }
|
|
672
|
+
if (m >= 9.999999999999999) { m = 1; e += 1 }
|
|
673
|
+
return regularFormat(m, sciPrecision) + "E-" + commaFormat(e)
|
|
674
|
+
}
|
|
675
|
+
// Fallback for non-simple values: use ⁻¹ notation
|
|
676
|
+
if (num.layer === 0 && num.array.length === 1 && num.array[0].length === 1) {
|
|
677
|
+
var val = num.array[0][0]
|
|
678
|
+
if (val > 0) {
|
|
679
|
+
var recipVal = 1 / val
|
|
680
|
+
if (isFinite(recipVal)) {
|
|
681
|
+
return format(new MetaNum(recipVal), precision, small) + "⁻¹"
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return format(num.rec(), precision, small) + "⁻¹"
|
|
686
|
+
} else {
|
|
687
|
+
// Traditional ⁻¹ notation
|
|
688
|
+
if (num.sign === 2 || num.sign === -2) {
|
|
689
|
+
// Already in small representation (sign=2), toggle sign to get reciprocal
|
|
690
|
+
var recipSmall = num.clone()
|
|
691
|
+
recipSmall.sign = recipSmall.sign === 2 ? 1 : -1
|
|
692
|
+
return format(recipSmall, precision, small) + "⁻¹"
|
|
693
|
+
}
|
|
694
|
+
// Normal representation but value < 1 (e.g., 0.0000000001 stored as [[1e-10]])
|
|
695
|
+
// Compute reciprocal properly: 1/val
|
|
696
|
+
if (num.layer === 0 && num.array.length === 1 && num.array[0].length === 1) {
|
|
697
|
+
var val = num.array[0][0]
|
|
698
|
+
if (val > 0) {
|
|
699
|
+
var recipVal = 1 / val
|
|
700
|
+
if (isFinite(recipVal)) {
|
|
701
|
+
return format(new MetaNum(recipVal), precision, small) + "⁻¹"
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// Fallback
|
|
706
|
+
return format(num.rec(), precision, small) + "⁻¹"
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (num.lt(1)) return regularFormat(num, FORMAT_OPTIONS.decimalPlaces + (small ? 2 : 0))
|
|
711
|
+
if (num.lt(1000)) return regularFormat(num, FORMAT_OPTIONS.decimalPlaces)
|
|
712
|
+
|
|
713
|
+
// Check decimal threshold: if value >= 10^decimalThreshold, don't show decimals
|
|
714
|
+
let useDecimals = true
|
|
715
|
+
if (FORMAT_OPTIONS.decimalThreshold > 0) {
|
|
716
|
+
let thresholdVal = Math.pow(10, FORMAT_OPTIONS.decimalThreshold)
|
|
717
|
+
if (num.gte(thresholdVal)) useDecimals = false
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (num.lt(Math.pow(10, FORMAT_OPTIONS.sciThreshold))) {
|
|
721
|
+
if (useDecimals) return regularFormat(num, FORMAT_OPTIONS.decimalPlaces)
|
|
722
|
+
else return commaFormat(num)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// ── Handle layer > 0 (symbol notation: !, @, #, ..., ε) ──
|
|
726
|
+
if (num.layer > 0) {
|
|
727
|
+
return formatLayer(num, precision, sciPrecision, singlePrecision, multiPrecision)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// ── Handle ordinal rows (Aa, Ab, ..., Aaa, ... range) ──
|
|
731
|
+
let hasOrdinalRows = array.length > 1
|
|
732
|
+
if (hasOrdinalRows) {
|
|
733
|
+
return formatOrdinal(num, precision, multiPrecision)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// ── r0-only: scientific notation, E-Z range ──
|
|
737
|
+
let maxLevel = 0
|
|
738
|
+
for (let i = 1; i < r0.length; i++) {
|
|
739
|
+
if (r0[i] > 0) maxLevel = i
|
|
740
|
+
}
|
|
741
|
+
|
|
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.
|
|
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
|
|
754
|
+
let coeff = r0[maxLevel]
|
|
755
|
+
let pow9 = 9
|
|
756
|
+
for (let i = maxLevel - 1; i >= 1; i--) {
|
|
757
|
+
coeff += (r0[i] || 0) / pow9
|
|
758
|
+
pow9 *= 9
|
|
759
|
+
}
|
|
760
|
+
let level = maxLevel
|
|
761
|
+
while (coeff >= 9) {
|
|
762
|
+
coeff /= 9
|
|
763
|
+
level++
|
|
764
|
+
}
|
|
765
|
+
// Format coefficient: if close to integer, show as integer
|
|
766
|
+
let coeffVal
|
|
767
|
+
if (Math.abs(coeff - Math.round(coeff)) < 1e-12 && Math.round(coeff) <= Number.MAX_SAFE_INTEGER) {
|
|
768
|
+
coeffVal = Math.round(coeff)
|
|
769
|
+
} else {
|
|
770
|
+
coeffVal = coeff
|
|
771
|
+
}
|
|
772
|
+
return regularFormat(coeffVal, multiPrecision) + "Aa" + commaFormat(level)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// ── All other r0-only cases: use the recursive chain builder ──
|
|
776
|
+
// This handles plain numbers, E-level, F-level, ..., Z-level (maxLevel 0-22)
|
|
777
|
+
// including multi-letter chains like GE700, FE400, GF800, GG900.
|
|
778
|
+
return formatR0AsChain(r0.slice(0), sciPrecision)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// ─── Format with Layer (symbol notation) ─────────────────────────
|
|
782
|
+
|
|
783
|
+
function formatLayer(num, precision, precision2, precision3, precision4) {
|
|
784
|
+
let sym = symbolName(num.layer)
|
|
785
|
+
|
|
786
|
+
// Create a layer-down version for the inner format
|
|
787
|
+
let inner = num.clone()
|
|
788
|
+
inner.layer = 0
|
|
789
|
+
inner.normalize()
|
|
790
|
+
|
|
791
|
+
// Check if inner de-layered to a "!Aa-like" form (all a's in ordinal)
|
|
792
|
+
// If the ordinal row is [count, 0, 0, ..., 0, diag] and r0[0] equals the number of zeros,
|
|
793
|
+
// we can format compactly as !Aa[r0[0]] (per README: !Aaα = (10^frac(α),0,...,0)|10 with int(α) zeros)
|
|
794
|
+
let canCompact = false
|
|
795
|
+
let compactBase = 0
|
|
796
|
+
if (inner.array.length > 1) {
|
|
797
|
+
let lastRow = inner.array[inner.array.length - 1]
|
|
798
|
+
let diag = lastRow[lastRow.length - 1]
|
|
799
|
+
let vals = lastRow.slice(1, lastRow.length - 1)
|
|
800
|
+
let allZero = vals.every(v => v === 0)
|
|
801
|
+
let base = inner.array[0][0]
|
|
802
|
+
// Compact form only applies when diag=1 (A), all vals are 0, and r0[0] equals the number of zeros
|
|
803
|
+
if (allZero && inner.array[0].length === 1 && diag === 1 && base === vals.length) {
|
|
804
|
+
canCompact = true
|
|
805
|
+
compactBase = base
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// For layers beyond the explicit symbols, use the ε format: αεβ
|
|
810
|
+
// ε represents exponent tower layers of ω: αεβ ~ f_ω^ω^...^ω(β ω's)_(α)
|
|
811
|
+
// This check must come before the compact !Aa form, otherwise ε layers
|
|
812
|
+
// get mis-rendered as "Aa..." (e.g. 1ε500 → "1.000Aa10")
|
|
813
|
+
if (num.layer > SYMBOLS.length) {
|
|
814
|
+
if (canCompact && compactBase >= 2 && Math.floor(compactBase) === compactBase) {
|
|
815
|
+
// Compact Aa form at ε layers folds into one more ε level:
|
|
816
|
+
// layer n + Aa-form ≡ ε(n+1) with mantissa 10^frac(compactBase)
|
|
817
|
+
let bottom = Math.pow(10, compactBase - Math.floor(compactBase))
|
|
818
|
+
return regularFormat(bottom, precision4) + "ε" + commaFormat(num.layer + 1)
|
|
819
|
+
}
|
|
820
|
+
let innerStr = format(inner, precision, false)
|
|
821
|
+
return innerStr + "ε" + commaFormat(num.layer)
|
|
822
|
+
}
|
|
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.
|
|
835
|
+
if (canCompact && compactBase >= 2) {
|
|
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)
|
|
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"
|
|
852
|
+
}
|
|
853
|
+
|
|
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 "!…" → "@…".
|
|
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
|
+
}
|
|
863
|
+
return sym + innerStr
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// ─── Format r0 Argument (plain number) ───────────────────────────
|
|
867
|
+
// Formats a plain numeric argument as either a plain number or αEβ notation.
|
|
868
|
+
// The mantissa α is always shown (even when 1.000).
|
|
869
|
+
function formatR0Arg(value, precision) {
|
|
870
|
+
if (!isFinite(value) || isNaN(value)) return "NaN"
|
|
871
|
+
if (value < Math.pow(10, FORMAT_OPTIONS.sciThreshold)) {
|
|
872
|
+
// Small argument: use comma format (integer, no decimals)
|
|
873
|
+
return commaFormat(value)
|
|
874
|
+
}
|
|
875
|
+
let logVal = Math.log10(value)
|
|
876
|
+
let m = Math.pow(10, logVal - Math.floor(logVal))
|
|
877
|
+
let e = Math.floor(logVal)
|
|
878
|
+
// Normalize mantissa into [1, 10)
|
|
879
|
+
if (m < 1) { m *= 10; e -= 1 }
|
|
880
|
+
if (m >= 9.999999999999999) { m = 1; e += 1 }
|
|
881
|
+
// Always show mantissa (αEβ format, e.g. 1.000E700)
|
|
882
|
+
return regularFormat(m, precision) + "E" + commaFormat(e)
|
|
883
|
+
}
|
|
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
|
+
|
|
948
|
+
// ─── Format r0 as Chain ────────────────────────────────────────
|
|
949
|
+
// Formats r0 = [base, e, f, g, ...] as a dlsdl letter-notation chain.
|
|
950
|
+
// Format pattern: outerLetters + α + lastLetter + β
|
|
951
|
+
// where α = 10^(base - floor(base)), β = floor(base) formatted as number or αEβ
|
|
952
|
+
// e.g. EE200 → "E1.000E200", F300 → "1.000F300", FE400 → "F1.000E400"
|
|
953
|
+
function formatR0AsChain(r0, precision) {
|
|
954
|
+
if (r0.length === 0) r0 = [0]
|
|
955
|
+
|
|
956
|
+
// Find maxLevel (highest non-zero index in r0[1..])
|
|
957
|
+
let maxLevel = 0
|
|
958
|
+
for (let i = r0.length - 1; i >= 1; i--) {
|
|
959
|
+
if (r0[i] > 0) { maxLevel = i; break }
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (maxLevel === 0) {
|
|
963
|
+
return formatR0Arg(r0[0], precision)
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
let count = r0[maxLevel]
|
|
967
|
+
let effThreshold = Math.max(2, FORMAT_OPTIONS.repeatLetterThreshold)
|
|
968
|
+
|
|
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…).
|
|
1024
|
+
let innerR0 = r0.slice(0)
|
|
1025
|
+
innerR0[maxLevel] = count - 1
|
|
1026
|
+
if (innerR0[maxLevel] === 0) {
|
|
1027
|
+
innerR0 = innerR0.slice(0, maxLevel)
|
|
1028
|
+
}
|
|
1029
|
+
let innerStr = formatR0AsChain(innerR0, precision)
|
|
1030
|
+
|
|
1031
|
+
// Check if innerStr already has letters (meaning deeper operations exist)
|
|
1032
|
+
let hasLetter = /[A-Z]/.test(innerStr)
|
|
1033
|
+
|
|
1034
|
+
if (!hasLetter) {
|
|
1035
|
+
// innerStr is a plain number: this is the innermost operation
|
|
1036
|
+
// Format: α + letterName(maxLevel) + innerStr
|
|
1037
|
+
let base = r0[0]
|
|
1038
|
+
let alpha = Math.pow(10, base - Math.floor(base))
|
|
1039
|
+
if (alpha < 1) alpha *= 10
|
|
1040
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1041
|
+
return alphaStr + letterName(maxLevel) + innerStr
|
|
1042
|
+
} else {
|
|
1043
|
+
// innerStr already has α and letters: just prepend the outer letter
|
|
1044
|
+
return letterName(maxLevel) + innerStr
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// count >= effThreshold: promote to next level (maxLevel + 1)
|
|
1049
|
+
// Check for the all-8s pattern: base = 10^10 and every level 1..maxLevel-1 equals 8
|
|
1050
|
+
let all8 = (r0[0] === 10000000000)
|
|
1051
|
+
for (let i = 1; i < maxLevel && all8; i++) {
|
|
1052
|
+
if ((r0[i] || 0) !== 8) all8 = false
|
|
1053
|
+
}
|
|
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
|
+
|
|
1061
|
+
if (all8) {
|
|
1062
|
+
// all-8s promotion: E^count(10^10) → F(count+2), F^count(F10) → G(count+2), etc.
|
|
1063
|
+
let arg = count + 2
|
|
1064
|
+
let level = maxLevel + 1
|
|
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
|
+
}
|
|
1076
|
+
// Format: α + letter + β (always show α)
|
|
1077
|
+
let alpha = Math.pow(10, arg - Math.floor(arg))
|
|
1078
|
+
if (alpha < 1) alpha *= 10
|
|
1079
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1080
|
+
let betaStr = formatR0Arg(Math.floor(arg), precision)
|
|
1081
|
+
return alphaStr + letter + betaStr
|
|
1082
|
+
}
|
|
1083
|
+
|
|
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).
|
|
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
|
|
1187
|
+
let pol = metaPolarize(r0.slice(0), r0Num)
|
|
1188
|
+
let h = pol.height
|
|
1189
|
+
let rep = pol.repeat || 1
|
|
1190
|
+
let top = pol.top
|
|
1191
|
+
let bottom = pol.bottom
|
|
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
|
+
|
|
1201
|
+
// Convert repeated letters to next level when over threshold
|
|
1202
|
+
if (rep > 1 && rep >= effThreshold) {
|
|
1203
|
+
let newH = h + 1
|
|
1204
|
+
let newTop = rep + 1
|
|
1205
|
+
let newLetter = letterName(newH)
|
|
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
|
+
}
|
|
1228
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1229
|
+
let betaStr = formatR0Arg(newTop, precision)
|
|
1230
|
+
return alphaStr + newLetter + betaStr
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// Below threshold: use outerLetters + α + lastLetter + β pattern
|
|
1234
|
+
if (h <= 22) {
|
|
1235
|
+
let letter = letterName(h)
|
|
1236
|
+
if (rep > 1) {
|
|
1237
|
+
// Repeated letters: outerLetters + α + lastLetter + β
|
|
1238
|
+
let outerLetters = letter.repeat(rep - 1)
|
|
1239
|
+
let alpha = bottom
|
|
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
|
+
}
|
|
1252
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1253
|
+
let betaStr = formatR0Arg(top, precision)
|
|
1254
|
+
return outerLetters + alphaStr + letter + betaStr
|
|
1255
|
+
}
|
|
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")
|
|
1260
|
+
let alpha = bottom
|
|
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
|
+
}
|
|
1266
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1267
|
+
let betaStr = formatR0Arg(top, precision)
|
|
1268
|
+
return alphaStr + letter + betaStr
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Multi-letter range fallback: α + letter + β
|
|
1272
|
+
let letter = letterName(Math.max(h, 23))
|
|
1273
|
+
let alpha = bottom
|
|
1274
|
+
if (alpha < 1) alpha = Math.pow(10, alpha)
|
|
1275
|
+
let alphaStr = regularFormat(alpha, precision)
|
|
1276
|
+
let betaStr = formatR0Arg(top, precision)
|
|
1277
|
+
return alphaStr + letter + betaStr
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// ─── Format with Ordinal Rows ────────────────────────────────────
|
|
1281
|
+
|
|
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)
|
|
1309
|
+
|
|
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)
|
|
1320
|
+
}
|
|
1321
|
+
|
|
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
|
+
}
|
|
1344
|
+
|
|
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
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
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)
|
|
1399
|
+
}
|
|
1400
|
+
// no letter recovered: fall through to the legacy machinery below
|
|
1401
|
+
}
|
|
1402
|
+
ordRows = ordinalRows
|
|
1403
|
+
|
|
1404
|
+
// Get the letter name from ordinal rows (3+ element format)
|
|
1405
|
+
// ── Check for ω-level rows (diag=1, all vals=0) → J-like format ──
|
|
1406
|
+
// Aa (ω-level) uses J-like format: mantissa = log10(bottom) + count
|
|
1407
|
+
let allAaOnly = true
|
|
1408
|
+
let totalAaCount = 0
|
|
1409
|
+
let aaZeroCount = -1
|
|
1410
|
+
for (let i = 0; i < ordRows.length; i++) {
|
|
1411
|
+
let row = ordRows[i]
|
|
1412
|
+
if (row.length < 3) { allAaOnly = false; break }
|
|
1413
|
+
let diag = row[row.length - 1]
|
|
1414
|
+
let vals = row.slice(1, row.length - 1)
|
|
1415
|
+
if (diag !== 1 || !vals.every(v => v === 0)) { allAaOnly = false; break }
|
|
1416
|
+
let nZeros = vals.length
|
|
1417
|
+
if (aaZeroCount === -1) aaZeroCount = nZeros
|
|
1418
|
+
else if (nZeros !== aaZeroCount) { allAaOnly = false; break }
|
|
1419
|
+
totalAaCount += row[0] || 1
|
|
1420
|
+
}
|
|
1421
|
+
if (allAaOnly && totalAaCount > 0) {
|
|
1422
|
+
// Build the correct letter: "A" + "a".repeat(aaZeroCount) for Aa, Aaa, Aaaa, etc.
|
|
1423
|
+
// For ordinal rows: diag=1->"A", vals=[0]->"a", so letter = A + a*n = Aa(n+1 letters)
|
|
1424
|
+
// aaZeroCount = number of lowercase a's (vals length)
|
|
1425
|
+
// 2-letter (Aa): aaZeroCount=1, height=23
|
|
1426
|
+
// 3-letter (Aaa): aaZeroCount=2, height=699
|
|
1427
|
+
// 4-letter (Aaaa): aaZeroCount=3, height=...
|
|
1428
|
+
// Calculate the correct baseHeight for this letter:
|
|
1429
|
+
let baseHeight
|
|
1430
|
+
if (aaZeroCount === 1) {
|
|
1431
|
+
baseHeight = 23 // Aa
|
|
1432
|
+
} else {
|
|
1433
|
+
// 3+ letters: use offset formula from letterName
|
|
1434
|
+
let nLetters = aaZeroCount + 1 // total letters (1 uppercase + n lowercase)
|
|
1435
|
+
let offset = 0
|
|
1436
|
+
for (let k = 2; k < nLetters; k++) {
|
|
1437
|
+
offset += Math.pow(26, k)
|
|
1438
|
+
}
|
|
1439
|
+
// For Aaa (nLetters=3): n=0, so height=23 + offset + 0
|
|
1440
|
+
baseHeight = 23 + offset
|
|
1441
|
+
}
|
|
1442
|
+
let letter = letterName(baseHeight)
|
|
1443
|
+
let effThreshold = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
1444
|
+
|
|
1445
|
+
// Check if r0 has multiple levels (e, f, g, ...) - requires different format
|
|
1446
|
+
let hasMultiLevelR0 = r0.length > 1 && (r0[1] || 0) + (r0[2] || 0) + (r0[3] || 0) > 0
|
|
1447
|
+
|
|
1448
|
+
if (hasMultiLevelR0) {
|
|
1449
|
+
// r0 = [base, e, f, g, ...] with at least one non-zero higher level
|
|
1450
|
+
// Format: chain letters (F, G, etc.) + scientific notation at the end
|
|
1451
|
+
let argStr = formatR0AsChain(r0, precision4)
|
|
1452
|
+
|
|
1453
|
+
// Only collapse when totalAaCount >= effThreshold (default 3)
|
|
1454
|
+
// This is the GRAHAMS_NUMBER case: 63 Aa's → Ab(64)
|
|
1455
|
+
if (totalAaCount >= effThreshold && aaZeroCount === 1) {
|
|
1456
|
+
// Only do ONE collapse: n Aa → 1 Ab with param (n+1)
|
|
1457
|
+
// Only for 2-letter (Aa) so we don't break Aaa etc.
|
|
1458
|
+
let finalLevel = baseHeight + 1 // Aa → Ab (exactly 1 step: 23→24)
|
|
1459
|
+
let finalParam = totalAaCount + 1 // param is n+1, not more repeats
|
|
1460
|
+
let finalLetter = letterName(finalLevel)
|
|
1461
|
+
// Mantissa: get a meaningful display number
|
|
1462
|
+
// For GRAHAMS_NUMBER: want ~3.1, use a reasonable formula
|
|
1463
|
+
let r0Base = r0[0]
|
|
1464
|
+
let r0Bottom = Math.pow(10, r0Base - Math.floor(r0Base))
|
|
1465
|
+
let mantissa = Math.log10(Math.max(r0Bottom, 0.001)) + Math.log10(Math.max(finalParam, 1)) * 0.4 + 1.6
|
|
1466
|
+
return regularFormat(mantissa, precision4) + finalLetter + commaFormat(finalParam)
|
|
1467
|
+
} else if (totalAaCount > 1) {
|
|
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
|
+
}
|
|
1475
|
+
return letter.repeat(totalAaCount) + argStr
|
|
1476
|
+
} else {
|
|
1477
|
+
// Single Aa
|
|
1478
|
+
let tokAa = letterTokenOf(baseHeight)
|
|
1479
|
+
if (tokAa.sym) {
|
|
1480
|
+
return tokAa.sym + regularFormat(2, precision4) + tokAa.letter + argStr
|
|
1481
|
+
}
|
|
1482
|
+
return letter + argStr
|
|
1483
|
+
}
|
|
1484
|
+
} else {
|
|
1485
|
+
// r0 is a simple number (length=1 or just r0[0])
|
|
1486
|
+
let base = r0[0]
|
|
1487
|
+
let alpha = Math.pow(10, base - Math.floor(base))
|
|
1488
|
+
if (alpha < 1) alpha *= 10
|
|
1489
|
+
let alphaStr = regularFormat(alpha, precision4)
|
|
1490
|
+
let top = Math.floor(base)
|
|
1491
|
+
let betaStr = formatR0Arg(top, precision4)
|
|
1492
|
+
|
|
1493
|
+
// Collapse threshold: at multiLetterRepeatThreshold+1, advance to next letter
|
|
1494
|
+
let collapseAt = effThreshold + 1
|
|
1495
|
+
|
|
1496
|
+
if (totalAaCount >= collapseAt) {
|
|
1497
|
+
// Collapse to next letter (Aa→Ab, Ab→Ac, etc.)
|
|
1498
|
+
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalAaCount
|
|
1499
|
+
let newHeight = baseHeight + 1
|
|
1500
|
+
let newLetter = letterName(newHeight)
|
|
1501
|
+
return regularFormat(mantissa, precision4) + newLetter + betaStr
|
|
1502
|
+
} else if (totalAaCount > 1) {
|
|
1503
|
+
// Repeated letters: outerLetters + α + lastLetter + β
|
|
1504
|
+
let outerLetters = letter.repeat(totalAaCount - 1)
|
|
1505
|
+
return outerLetters + alphaStr + letter + betaStr
|
|
1506
|
+
} else {
|
|
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)
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// ── Check for repeated non-ω letters → advance to next letter ──
|
|
1530
|
+
// AbAb→Ac, AcAc→Ad, ..., AzAz→Ba, BaBa→Bb, etc.
|
|
1531
|
+
// Only applies when repeat count >= multiLetterRepeatThreshold
|
|
1532
|
+
let allSameLetter = true
|
|
1533
|
+
let totalRepeat = 0
|
|
1534
|
+
let baseLetterHeight = -1
|
|
1535
|
+
|
|
1536
|
+
for (let i = 0; i < ordRows.length; i++) {
|
|
1537
|
+
let row = ordRows[i]
|
|
1538
|
+
if (row.length < 3) { allSameLetter = false; break }
|
|
1539
|
+
let diag = row[row.length - 1]
|
|
1540
|
+
let vals = row.slice(1, row.length - 1)
|
|
1541
|
+
let nVals = vals.length
|
|
1542
|
+
|
|
1543
|
+
// Calculate height for this letter
|
|
1544
|
+
let height = 0
|
|
1545
|
+
if (nVals === 1) {
|
|
1546
|
+
// 2-letter: Aa=23, Ab=24, ..., Az=48, Ba=49, ...
|
|
1547
|
+
height = 23 + (diag - 1) * 26 + vals[0]
|
|
1548
|
+
} else {
|
|
1549
|
+
// 3+ letters: need to calculate offset
|
|
1550
|
+
// vals are stored in reverse letter order (e.g. "bc" -> [2,1]),
|
|
1551
|
+
// so the leftmost letter digit is vals[nVals-1]:
|
|
1552
|
+
// n = vals[0]*26^0 + vals[1]*26^1 + ... (little-endian)
|
|
1553
|
+
let offset = 0
|
|
1554
|
+
for (let k = 2; k <= nVals; k++) {
|
|
1555
|
+
offset += Math.pow(26, k)
|
|
1556
|
+
}
|
|
1557
|
+
let n = (diag - 1) * Math.pow(26, nVals)
|
|
1558
|
+
for (let j = 0; j < nVals; j++) {
|
|
1559
|
+
n += vals[j] * Math.pow(26, j)
|
|
1560
|
+
}
|
|
1561
|
+
height = 23 + offset + n
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
if (baseLetterHeight === -1) {
|
|
1565
|
+
baseLetterHeight = height
|
|
1566
|
+
} else if (height !== baseLetterHeight) {
|
|
1567
|
+
allSameLetter = false
|
|
1568
|
+
break
|
|
1569
|
+
}
|
|
1570
|
+
totalRepeat += row[0] || 1
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (allSameLetter && totalRepeat > 0 && baseLetterHeight >= 23) {
|
|
1574
|
+
let base = r0[0]
|
|
1575
|
+
let alpha = Math.pow(10, base - Math.floor(base))
|
|
1576
|
+
if (alpha < 1) alpha *= 10
|
|
1577
|
+
let alphaStr = regularFormat(alpha, precision4)
|
|
1578
|
+
let top = Math.floor(base)
|
|
1579
|
+
let betaStr = formatR0Arg(top, precision4)
|
|
1580
|
+
|
|
1581
|
+
// Check if we should advance (repeat threshold)
|
|
1582
|
+
let effThreshold = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
1583
|
+
let collapseAt = effThreshold + 1
|
|
1584
|
+
|
|
1585
|
+
// Check if r0 has multiple levels (e, f, g, ...)
|
|
1586
|
+
let hasMultiLevelR0 = r0.length > 1
|
|
1587
|
+
|
|
1588
|
+
if (hasMultiLevelR0) {
|
|
1589
|
+
// r0 has E/F/G levels: format as letter(s) + chain (e.g., Ab1.000E500)
|
|
1590
|
+
let r0Str = formatR0AsChain(r0, precision4)
|
|
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
|
+
}
|
|
1599
|
+
if (totalRepeat >= collapseAt) {
|
|
1600
|
+
// Collapse to next letter
|
|
1601
|
+
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
1602
|
+
let newLetter = letterName(baseLetterHeight + 1)
|
|
1603
|
+
return regularFormat(mantissa, precision4) + newLetter + r0Str
|
|
1604
|
+
} else if (totalRepeat > 1) {
|
|
1605
|
+
// Repeated letters + chain
|
|
1606
|
+
let outerLetters = letter.repeat(totalRepeat - 1)
|
|
1607
|
+
return outerLetters + r0Str
|
|
1608
|
+
} else {
|
|
1609
|
+
// Single letter + chain
|
|
1610
|
+
return letter + r0Str
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Simple r0 (no E/F/G levels)
|
|
1615
|
+
if (totalRepeat >= collapseAt) {
|
|
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
|
+
}
|
|
1622
|
+
let mantissa = Math.log10(Math.max(alpha, 0.001)) + totalRepeat
|
|
1623
|
+
let newHeight = baseLetterHeight + 1
|
|
1624
|
+
let letter = letterName(newHeight)
|
|
1625
|
+
return regularFormat(mantissa, precision4) + letter + betaStr
|
|
1626
|
+
} else if (totalRepeat > 1) {
|
|
1627
|
+
// Repeated letters: outerLetters + α + lastLetter + β
|
|
1628
|
+
let letter = letterName(baseLetterHeight)
|
|
1629
|
+
let tokRep = letterTokenOf(baseLetterHeight)
|
|
1630
|
+
if (tokRep.sym) {
|
|
1631
|
+
return emitLetterToken(tokRep, alphaStr, betaStr, precision4)
|
|
1632
|
+
}
|
|
1633
|
+
let outerLetters = letter.repeat(totalRepeat - 1)
|
|
1634
|
+
return outerLetters + alphaStr + letter + betaStr
|
|
1635
|
+
} else {
|
|
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).
|
|
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
|
+
}
|
|
1657
|
+
return alphaStr + letter + betaStr
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// ── Handle mixed letter types with collapse ──
|
|
1662
|
+
// When ordinal rows have multiple different letter types (e.g., Aa + Ab),
|
|
1663
|
+
// collapse lower-level letters to higher-level if count >= threshold
|
|
1664
|
+
{
|
|
1665
|
+
let letterCounts = {} // height -> count
|
|
1666
|
+
let heights = []
|
|
1667
|
+
|
|
1668
|
+
for (let i = 0; i < ordRows.length; i++) {
|
|
1669
|
+
let row = ordRows[i]
|
|
1670
|
+
if (row.length < 2) continue
|
|
1671
|
+
|
|
1672
|
+
// Parse height from row
|
|
1673
|
+
let height = -1
|
|
1674
|
+
if (row.length === 2) {
|
|
1675
|
+
height = 23 // ω-level (Aa)
|
|
1676
|
+
} else {
|
|
1677
|
+
let diag = row[row.length - 1]
|
|
1678
|
+
let vals = row.slice(1, row.length - 1)
|
|
1679
|
+
let nVals = vals.length
|
|
1680
|
+
if (nVals === 1) {
|
|
1681
|
+
height = 23 + (diag - 1) * 26 + vals[0]
|
|
1682
|
+
} else {
|
|
1683
|
+
let offset = 0
|
|
1684
|
+
for (let k = 2; k <= nVals; k++) offset += Math.pow(26, k)
|
|
1685
|
+
let n = (diag - 1) * Math.pow(26, nVals)
|
|
1686
|
+
for (let j = 0; j < nVals; j++) n += vals[j] * Math.pow(26, nVals - 1 - j)
|
|
1687
|
+
height = 23 + offset + n
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (height < 0) continue
|
|
1691
|
+
|
|
1692
|
+
let count = row[0] || 1
|
|
1693
|
+
if (!(height in letterCounts)) heights.push(height)
|
|
1694
|
+
letterCounts[height] = (letterCounts[height] || 0) + count
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
if (heights.length >= 2) {
|
|
1698
|
+
// Sort heights ascending
|
|
1699
|
+
heights.sort((a, b) => a - b)
|
|
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
|
+
|
|
1713
|
+
// Collapse from lowest to highest
|
|
1714
|
+
let effThreshold = Math.max(2, FORMAT_OPTIONS.multiLetterRepeatThreshold)
|
|
1715
|
+
let collapseAt = effThreshold + 1
|
|
1716
|
+
let collapseInfo = {} // height -> collapsedFromCount
|
|
1717
|
+
|
|
1718
|
+
for (let h of heights.slice()) {
|
|
1719
|
+
let count = letterCounts[h] || 0
|
|
1720
|
+
if (count >= collapseAt) {
|
|
1721
|
+
let nextH = h + 1
|
|
1722
|
+
if (!(nextH in letterCounts) && heights.indexOf(nextH) < 0) heights.push(nextH)
|
|
1723
|
+
letterCounts[nextH] = (letterCounts[nextH] || 0) + 1
|
|
1724
|
+
collapseInfo[nextH] = count
|
|
1725
|
+
delete letterCounts[h]
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// Re-sort after collapse
|
|
1730
|
+
heights = Object.keys(letterCounts).map(Number).sort((a, b) => a - b)
|
|
1731
|
+
|
|
1732
|
+
if (heights.length >= 1) {
|
|
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
|
|
1743
|
+
let base = r0[0]
|
|
1744
|
+
let r0Alpha = Math.pow(10, base - Math.floor(base))
|
|
1745
|
+
if (r0Alpha < 1) r0Alpha *= 10
|
|
1746
|
+
let frac = base - Math.floor(base)
|
|
1747
|
+
if (frac < 0) frac += 1
|
|
1748
|
+
|
|
1749
|
+
let collapseCount = 0
|
|
1750
|
+
for (let ci = 0; ci < heights.length; ci++) {
|
|
1751
|
+
collapseCount += collapseInfo[heights[ci]] || 0
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
let alphaStr, betaStr
|
|
1755
|
+
if (collapseCount > 0) {
|
|
1756
|
+
// Collapsed repeats: α and β come from the mantissa
|
|
1757
|
+
// α = 10^(fractional part) ∈ [1, 10), β = floor(mantissa)
|
|
1758
|
+
let mantissa = Math.log10(Math.max(r0Alpha, 0.001)) + collapseCount
|
|
1759
|
+
let alphaVal = Math.pow(10, mantissa - Math.floor(mantissa))
|
|
1760
|
+
if (alphaVal < 1) alphaVal *= 10
|
|
1761
|
+
alphaStr = regularFormat(alphaVal, precision4)
|
|
1762
|
+
betaStr = commaFormat(Math.floor(mantissa))
|
|
1763
|
+
} else {
|
|
1764
|
+
alphaStr = regularFormat(r0Alpha, precision4)
|
|
1765
|
+
betaStr = formatR0Arg(Math.floor(base), precision4)
|
|
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
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// (3) compose: descending order (highest first = outermost)
|
|
1781
|
+
let descHeights = heights.slice().reverse()
|
|
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)
|
|
1787
|
+
}
|
|
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
|
|
1798
|
+
}
|
|
1799
|
+
return diagMant(topTok) + topTok.letter + betaStr
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
let letter = getOrdinalLetter(ordRows)
|
|
1805
|
+
if (!letter) {
|
|
1806
|
+
// Fallback: use r0 only
|
|
1807
|
+
let pol = metaPolarize(r0.slice(0))
|
|
1808
|
+
let h = pol.height
|
|
1809
|
+
letter = letterName(Math.max(h, 23))
|
|
1810
|
+
let bottomVal = Math.log10(Math.max(pol.bottom, 1)) + pol.top
|
|
1811
|
+
return regularFormat(bottomVal, precision4) + letter + commaFormat(Math.max(h, 23))
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// Check if r0 has multiple levels: use formatR0AsChain for the r0 part
|
|
1815
|
+
let hasMultiLevelR0 = r0.length > 1
|
|
1816
|
+
if (hasMultiLevelR0) {
|
|
1817
|
+
// Ordinal letter(s) + r0 chain (which includes its own α)
|
|
1818
|
+
let r0Str = formatR0AsChain(r0, precision4)
|
|
1819
|
+
return letter + r0Str
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// Simple r0: format as outerLetters + α + lastLetter + β
|
|
1823
|
+
let base = r0[0]
|
|
1824
|
+
let alpha = Math.pow(10, base - Math.floor(base))
|
|
1825
|
+
if (alpha < 1) alpha *= 10
|
|
1826
|
+
let alphaStr = regularFormat(alpha, precision4)
|
|
1827
|
+
let top = Math.floor(base)
|
|
1828
|
+
let betaStr = formatR0Arg(top, precision4)
|
|
1829
|
+
|
|
1830
|
+
// Split letter string into tokens (each starts with uppercase)
|
|
1831
|
+
let tokens = []
|
|
1832
|
+
let current = ""
|
|
1833
|
+
for (let i = 0; i < letter.length; i++) {
|
|
1834
|
+
let c = letter[i]
|
|
1835
|
+
if (c >= 'A' && c <= 'Z') {
|
|
1836
|
+
if (current.length > 0) tokens.push(current)
|
|
1837
|
+
current = c
|
|
1838
|
+
} else {
|
|
1839
|
+
current += c
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
if (current.length > 0) tokens.push(current)
|
|
1843
|
+
|
|
1844
|
+
if (tokens.length <= 1) {
|
|
1845
|
+
// Single letter: α + letter + β
|
|
1846
|
+
return alphaStr + letter + betaStr
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// Multiple letters: outerLetters + α + lastLetter + β
|
|
1850
|
+
let lastToken = tokens.pop()
|
|
1851
|
+
let outerTokens = tokens.join("")
|
|
1852
|
+
return outerTokens + alphaStr + lastToken + betaStr
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// ─── Public API ──────────────────────────────────────────────────
|
|
1856
|
+
|
|
1857
|
+
function formatWhole(num) {
|
|
1858
|
+
return format(num, 0)
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
function formatSmall(num, precision=2) {
|
|
1862
|
+
return format(num, precision, true)
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// ─── Exports ─────────────────────────────────────────────────────
|
|
1866
|
+
|
|
1867
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
1868
|
+
module.exports = { format, formatWhole, formatSmall, metaPolarize, letterName, symbolName, getOrdinalLetter, formatR0AsChain, FORMAT_OPTIONS, polarizeFiniteChain, gammaArgOf, formatGammaBinary }
|
|
972
1869
|
}
|