564 lines
No EOL
15 KiB
JavaScript
564 lines
No EOL
15 KiB
JavaScript
function span(first, n=10) {
|
||
return [...new Array(n)].map((_,i) => first.slice(0,first.length-1)+String.fromCharCode(first.charCodeAt(first.length-1) + i));
|
||
}
|
||
|
||
function intl_formatter(system, name, locale='nu', extra) {
|
||
const formatter = new Intl.NumberFormat(locale, Object.assign({numberingSystem:system},extra));
|
||
return {
|
||
name,
|
||
fn: formatter.format
|
||
}
|
||
}
|
||
|
||
function positional_unicode_span(name, zero, base=10n, extra) {
|
||
// Make a function to render a positional number system whose digits are in a contiguous span of unicode code points.
|
||
return Object.assign({
|
||
name,
|
||
fn: (n) => {
|
||
let o = '';
|
||
if(n==0n) {
|
||
return zero;
|
||
}
|
||
while(n) {
|
||
const d = n % base;
|
||
n = (n - d)/base;
|
||
const c = zero.slice(0,zero.length-1) + String.fromCharCode(zero.charCodeAt(zero.length-1) + Number(d));
|
||
o = c + o;
|
||
}
|
||
return o;
|
||
}
|
||
}, extra || {})
|
||
}
|
||
|
||
function positional_symbols(name, symbols, extra) {
|
||
if(extra) {
|
||
}
|
||
// Make a function to render a positional number system whose digits are drawn from the given string.
|
||
return Object.assign({
|
||
name,
|
||
fn: (n) => {
|
||
let o = '';
|
||
const base = BigInt(symbols.length);
|
||
if(n == 0n) {
|
||
return symbols[0];
|
||
}
|
||
|
||
while(n) {
|
||
const d = n % base;
|
||
n = (n - d)/base;
|
||
o = symbols[d] + o;
|
||
}
|
||
return o;
|
||
}
|
||
}, extra || {});
|
||
}
|
||
|
||
function concatenative(power_lists, base=10n) {
|
||
return function(n) {
|
||
if(n==0n) {
|
||
return '';
|
||
}
|
||
|
||
let o = '';
|
||
for(let symbols of power_lists) {
|
||
|
||
const r = n % 10n;
|
||
if(r > symbols.length) {
|
||
return '???';
|
||
|
||
}
|
||
if(r > 0n) {
|
||
o = symbols[Number(r)-1] + o;
|
||
}
|
||
n = (n-r) / 10n;
|
||
}
|
||
if(n) {
|
||
return '???';
|
||
}
|
||
return o;
|
||
}
|
||
}
|
||
|
||
function chinese_system(zero,units, powers_of_ten, powers_of_myriad) {
|
||
return (n => {
|
||
if(n==0n) {
|
||
return zero;
|
||
}
|
||
|
||
let o = '';
|
||
for(let m of powers_of_myriad) {
|
||
if(n % 10000n == 0n) {
|
||
n = n / 10000n;
|
||
continue;
|
||
}
|
||
o = m + o;
|
||
const u = n % 10n;
|
||
n = (n-u) / 10n;
|
||
o = (u > 1 || m=='' ? units[u] : '') + o;
|
||
for(let pow of powers_of_ten) {
|
||
if(n==0n) {
|
||
break;
|
||
}
|
||
const d = n % 10n;
|
||
n = (n-d) / 10n;
|
||
if(d > 0n) {
|
||
o = (d>1 ? units[d] : '') + pow + o;
|
||
}
|
||
}
|
||
}
|
||
if(n) {
|
||
return '???';
|
||
}
|
||
return o;
|
||
})
|
||
}
|
||
|
||
const systems = {
|
||
cuneiform: {
|
||
name: 'Cuneiform',
|
||
fn: (n) => {
|
||
const tens = [ "", "𒌋", "𒎙", "𒌍", "𒐏", "𒐐" ];
|
||
const ones = [ "", "𒐕", "𒐖", "𒐗", "𒐘", "𒐙", "𒐚", "𒐛", "𒐜", "𒐝" ];
|
||
|
||
let bits = [];
|
||
while(n) {
|
||
const u = n%10n;
|
||
n = (n-u)/10n;
|
||
const t = n % 6n;
|
||
n = (n-t)/6n;
|
||
bits.splice(0,0,tens[t] + ones[u]);
|
||
}
|
||
return bits.join('\u{3000}');
|
||
}
|
||
},
|
||
|
||
roman: {
|
||
name: 'Roman',
|
||
fn: concatenative([
|
||
['Ⅰ', 'Ⅱ', 'Ⅲ', 'Ⅳ', 'Ⅴ', 'Ⅵ', 'Ⅶ', 'Ⅷ', 'Ⅸ'],
|
||
['Ⅹ', 'ⅩⅩ', 'ⅩⅩⅩ', 'ⅩⅬ', 'Ⅼ', 'ⅬⅩ', 'ⅬⅩⅩ', 'ⅬⅩⅩⅩ', 'ⅩⅭ'],
|
||
['Ⅽ', 'ⅭⅭ', 'ⅭⅭⅭ', 'ⅭⅮ', 'Ⅾ', 'ⅮⅭ', 'ⅮⅭⅭ', 'ⅮⅭⅭⅭ', 'ⅭⅯ' ],
|
||
['Ⅿ', 'ⅯⅯ', 'ⅯⅯⅯ']
|
||
])
|
||
},
|
||
|
||
hieratic: {
|
||
name: 'Egyptian hieroglyphs',
|
||
fn: concatenative([
|
||
['', '𓏺', '𓏻', '𓏼', '𓏽', '𓏾', '𓏿', '𓐀', '𓐁', '𓐂'],
|
||
span('𓎆'),
|
||
span('𓍢'),
|
||
span('𓆼'),
|
||
span('𓂭'),
|
||
['𓆐', '𓆐𓆐', '𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐𓆐𓆐𓆐𓆐', '𓆐𓆐𓆐𓆐𓆐𓆐𓆐𓆐𓆐'],
|
||
['𓁨', '𓁨𓁨', '𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨𓁨𓁨𓁨𓁨', '𓁨𓁨𓁨𓁨𓁨𓁨𓁨𓁨𓁨']
|
||
])
|
||
},
|
||
|
||
aegean: {
|
||
name: 'Aegean',
|
||
fn: concatenative([
|
||
span('𐄇'),
|
||
span('𐄐'),
|
||
span('𐄙'),
|
||
span('𐄢'),
|
||
span('𐄫'),
|
||
|
||
])
|
||
},
|
||
|
||
hebr: {
|
||
name: 'Hebrew',
|
||
fn: (n) => {
|
||
if(n >= 10000n) {
|
||
return '???';
|
||
}
|
||
const ones = [ "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט" ];
|
||
const tens = [ "י", "ך", "ל", "ם", "ן", "ס", "ע", "ף", "ץ" ];
|
||
const hundreds = [ "ק", "ר", "ש", "ת", "תק", "תר", "תש", "תתק", "תתר" ];
|
||
const thousands = [`א׳`,`ב׳`,`אב׳`,`בב׳`,`ה׳`,`אה׳`,`בה׳`,`אה׳`,`בבה׳`];
|
||
if(n==0n) {
|
||
return '';
|
||
}
|
||
|
||
let o = '';
|
||
for(let symbols of [ones,tens,hundreds,thousands]) {
|
||
if(n==0n) {
|
||
break;
|
||
}
|
||
const d = n % 10n;
|
||
n = (n-d) / 10n;
|
||
if(d > 0n) {
|
||
o = symbols[Number(d)-1] + o;
|
||
}
|
||
}
|
||
return o;
|
||
}
|
||
},
|
||
|
||
|
||
indian: intl_formatter('latn','Indian','en-in'),
|
||
|
||
latn_commas: intl_formatter('latn','Latin with commas','en-gb'),
|
||
|
||
latn_spaces: intl_formatter('latn','Latin with spaces','fi-fi'),
|
||
|
||
latn_dots: intl_formatter('latn','European','eu'),
|
||
|
||
latn_swiss: intl_formatter('latn','Swiss','de-ch'),
|
||
|
||
scientific: intl_formatter('latn','Engineering','en',{notation:'scientific'}),
|
||
|
||
engineering: intl_formatter('latn','Engineering','en',{notation:'engineering'}),
|
||
|
||
compact: intl_formatter('latn','Compact','en',{notation:'compact'}),
|
||
|
||
grek: {
|
||
name: 'Greek',
|
||
fn: concatenative([
|
||
["Αʹ", "Βʹ", "Γʹ", "Δʹ", "Εʹ", "Ϛʹ", "Ζʹ", "Ηʹ", "Θʹ" ],
|
||
["Ιʹ", "Κʹ", "Λʹ", "Μʹ", "Νʹ", "Ξʹ", "Οʹ", "Πʹ", "Ϟʹ"],
|
||
[ "Ρʹ", "Σʹ", "Τʹ", "Υʹ", "Φʹ", "Χʹ", "Ψʹ", "Ωʹ", "Ϡʹ" ],
|
||
[ "͵Α", "͵Β", ",Γ", "͵Δ", "͵Ε", "͵Ϛ͵ΣΤ", "͵Ζ", "͵Η", "͵Θ" ],
|
||
[ "͵Ι", "͵Κ", "͵Λ", "͵Μ", "͵Ν", "͵Ξ", "͵Ο", "͵Π", "͵Ϟ" ],
|
||
[ "͵Ρ", "͵Σ", "͵Τ", "͵Υ", "͵Φ", "͵Χ", "͵Ψ", "͵Ω", "͵Ϡ" ]
|
||
])
|
||
},
|
||
|
||
hans: {
|
||
name: 'Chinese',
|
||
fn: chinese_system(
|
||
'〇',
|
||
[ '', '一', '二', '三', '四', '五', '六', '七', '八', '九' ],
|
||
[ '十', '百', '千' ],
|
||
[ '', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载' ]
|
||
)
|
||
},
|
||
|
||
counting_rod_horizontal: positional_symbols('Counting rod (horizontal)', ['〇'].concat(span('𝍠',9))),
|
||
|
||
counting_rod_vertical: positional_symbols('Counting rod (vertical)', ['〇'].concat(span('𝍩',9)), {orientation: 'vertical-lr'}),
|
||
|
||
hant: {
|
||
name: 'Suzhou',
|
||
fn: (n) => {
|
||
const zero = '〇';
|
||
|
||
if(n==0n) {
|
||
return zero;
|
||
}
|
||
|
||
let o = '';
|
||
while(n) {
|
||
const d = n % 10n;
|
||
n = (n - d) / 10n;
|
||
o = (d==0n ? zero : String.fromCharCode(0x3020 + Number(d))) + o;
|
||
}
|
||
|
||
return o;
|
||
}
|
||
},
|
||
|
||
ethi: {
|
||
name: 'Ethiopic',
|
||
fn: concatenative([
|
||
span('፩',9),
|
||
span('፲',9),
|
||
span('፩',9).map(x => x +'፻'),
|
||
span('፲',9).map(x => x +'፻'),
|
||
span('፩',9).map(x => x +'፼'),
|
||
span('፲',9).map(x => x +'፼'),
|
||
])
|
||
},
|
||
|
||
kharosthi: {
|
||
name: 'Kharosthi',
|
||
fn: (n) => {
|
||
const ones = [ "𐩀", "𐩁", "𐩂", "𐩃", "𐩃𐩀", "𐩃𐩁", "𐩃𐩂", "𐩃𐩃", "𐩃𐩃𐩀", "𐩃𐩃𐩁" ];
|
||
const tens = ["𐩄","𐩅","𐩅𐩄","𐩅𐩅","𐩅𐩅𐩄","𐩅𐩅𐩅","𐩅𐩅𐩅𐩄","𐩅𐩅𐩅𐩅","𐩅𐩅𐩅𐩅𐩄"];
|
||
if(n == 0) {
|
||
return '';
|
||
}
|
||
let o = '';
|
||
const u = n % 10n;
|
||
n = (n - u) / 10n;
|
||
o = (u > 0 ? ones[u - 1n] : '') + o;
|
||
const t = n % 10n;
|
||
n = (n - t) / 10n;
|
||
o = (t > 0 ? tens[t - 1n] : '') + o;
|
||
const h = n % 10n;
|
||
n = (n - h) / 10n;
|
||
o = (h > 0 ? (h > 1 ? ones[h - 1n] : '') + "𐩆" : '') + o;
|
||
const th = n % 10n;
|
||
n = (n - th) / 10n;
|
||
o = (th > 0 ? (th > 1 ? ones[th - 1n] : '') + "𐩇" : '') + o;
|
||
|
||
return o;
|
||
}
|
||
},
|
||
|
||
phoenician: {
|
||
name: 'Phoenician',
|
||
fn: concatenative([
|
||
["𐤖","𐤚","𐤛","𐤛𐤖","𐤛𐤚","𐤛𐤛","𐤛𐤛𐤖","𐤛𐤛𐤚","𐤛𐤛𐤛",],
|
||
["𐤗","𐤘","𐤘𐤗","𐤘𐤘","𐤘𐤘𐤗","𐤘𐤘𐤘","𐤘𐤘𐤘𐤗","𐤘𐤘𐤘𐤘","𐤘𐤘𐤘𐤘𐤗"],
|
||
["𐤙","𐤚𐤙","𐤛𐤙","𐤛𐤖𐤙","𐤛𐤚𐤙","𐤛𐤛𐤙","𐤛𐤛𐤖𐤙","𐤛𐤛𐤚𐤙","𐤛𐤛𐤛𐤙"],
|
||
["𐤙","𐤘𐤙","𐤘𐤗𐤙","𐤘𐤘𐤙","𐤘𐤘𐤗𐤙","𐤘𐤘𐤘𐤙","𐤘𐤘𐤘𐤗𐤙","𐤘𐤘𐤘𐤘𐤙","𐤘𐤘𐤘𐤘𐤗𐤙"]
|
||
])
|
||
},
|
||
|
||
armn: {
|
||
name: 'Armenian',
|
||
fn: concatenative([
|
||
['Ա','Բ','Գ','Դ','Ե','Զ','Է','Ը','Թ'],
|
||
['Ժ','Ի','Լ','Խ','Ծ','Կ','Հ','Ձ','Ղ'],
|
||
['Ճ', 'Մ', 'Յ', 'Ն', 'Շ', 'Ո', 'Չ', 'Պ', 'Ջ'],
|
||
['Ռ', 'Ս', 'Վ', 'Տ', 'Ր', 'Ց', 'Ւ', 'Փ', 'Ք'],
|
||
['Օ', 'Ֆ']
|
||
])
|
||
},
|
||
|
||
abjad: {
|
||
name: 'Arabic abjad',
|
||
fn: concatenative([
|
||
["ا","ب","جـ","د","هـ","و","ز","حـ","ط"],
|
||
["ى","ك","ل","مـ","ن","س","ع","ف","ص"],
|
||
["ق","ر","ش","ت","ث","خـ","ذ","ض","ظ"]
|
||
])
|
||
},
|
||
|
||
glagolitic: {
|
||
name: 'Glagolitic',
|
||
fn: concatenative([
|
||
span('Ⰰ',9),
|
||
span('Ⰹ',9),
|
||
span('Ⱃ',9),
|
||
span('Ⱍ',9),
|
||
])
|
||
},
|
||
|
||
cyrl: {
|
||
name: 'Cyrillic',
|
||
fn: (() => {
|
||
const units = ["А","В","Г","Д","Є","Ѕ","З","И","Ѳ"];
|
||
const tens = ["І","К","Л","М","Н","Ѯ","Ѻ","П","Ч"];
|
||
const hundreds = ["Р","С","Т","У","Ф","Х","Ѱ","Ѡ","Ц"];
|
||
return concatenative([
|
||
units,
|
||
tens,
|
||
hundreds,
|
||
units.map(x => x+'҂'),
|
||
units.map(x => x+"\u200d\u20dd "),
|
||
units.map(x => x+'\u200d\u0488 '),
|
||
units.map(x => x+'\u200d\u0489 '),
|
||
units.map(x => x+'\u200d\ua670 '),
|
||
units.map(x => x+'\u200d\ua671 '),
|
||
units.map(x => x+'\u200d\ua672 '),
|
||
])
|
||
})()
|
||
},
|
||
|
||
tangut: {
|
||
name: 'Tangut',
|
||
fn: chinese_system(
|
||
'',
|
||
['',"𘈩","𗍫","𘕕","𗥃","𗏁","𗤁","𗒹","𘉋","𗢭"],
|
||
['𗰗', '𘊝', '𗡞'],
|
||
['', '𗕑', '𗦲']
|
||
)
|
||
},
|
||
|
||
hangul: {
|
||
name: 'Hangul',
|
||
fn: chinese_system(
|
||
'영',
|
||
['',"일","이","삼","사","오","육, 륙","칠","팔","구"],
|
||
['십','백','천'],
|
||
['','만','억','조','경','해','자','양','구','간','정','재','극','항하사','아승기','나유타','불가사의','무량대수']
|
||
)
|
||
},
|
||
|
||
// TODO: Cistercian (needs SVG?)
|
||
|
||
// TODO: Pentadic numerals (needs SVG?)
|
||
|
||
rumi: positional_unicode_span('Rumi', '𐹠'),
|
||
|
||
mayan: positional_unicode_span('Mayan', '𝋠', 20n, {orientation: 'vertical-lr'}),
|
||
|
||
prachalit: positional_unicode_span('Prachalit', '𑑐'),
|
||
|
||
medefaidrin: positional_unicode_span('Medefaidrin', '𖺀', 20n),
|
||
|
||
kaktovik: positional_unicode_span('Kaktovik', '𝋀', 20n),
|
||
|
||
// garay: positional_unicode_span('Garay', ''), //(not in noto??)
|
||
|
||
binary: {
|
||
name: 'Binary',
|
||
fn: (n) => n.toString(2),
|
||
},
|
||
|
||
octal: {
|
||
name: 'Octal',
|
||
fn: (n) => n.toString(8),
|
||
},
|
||
|
||
hexadecimal: {
|
||
name: 'Hexadecimal',
|
||
fn: (n) => n.toString(16),
|
||
},
|
||
|
||
balanced_ternary: {
|
||
name: 'Balanced ternary',
|
||
fn: (n) => {
|
||
let o = '';
|
||
while(n) {
|
||
const r = n % 3n;
|
||
n = (n - r) / 3n;
|
||
if(r == 2) {
|
||
n += 1n;
|
||
}
|
||
o = ['0','+','-'][r] + o;
|
||
}
|
||
return o;
|
||
}
|
||
}
|
||
}
|
||
|
||
const intl_system_names = {
|
||
"adlm": "Adlam",
|
||
"ahom": "Ahom",
|
||
"arab": "Arabic-Indic",
|
||
"arabext": "Farsi/Urdu",
|
||
"armn": "Armenian upper case",
|
||
"armnlow": "Armenian lower case",
|
||
"bali": "Balinese",
|
||
"beng": "Bengali",
|
||
"bhks": "Bhaiksuki",
|
||
"brah": "Brahmi",
|
||
"cakm": "Chakma",
|
||
"cham": "Cham",
|
||
"cyrl": "Cyrillic",
|
||
"deva": "Devanagari",
|
||
"diak": "Dives Akuru",
|
||
"ethi": "Ethiopic",
|
||
"fullwide": "Full width",
|
||
"gara": "Garay",
|
||
"geor": "Georgian",
|
||
"gong": "Gunjala Gondi",
|
||
"gonm": "Masaram Gondi",
|
||
"grek": "Greek upper case",
|
||
"greklow": "Greek lower case",
|
||
"gujr": "Gujarati",
|
||
"gukh": "Gurung Khema",
|
||
"guru": "Gurmukhi",
|
||
"hanidays": "Han calendar",
|
||
"hanidec": "Chinese (positional)",
|
||
"hans": "Simplified Chinese",
|
||
"hansfin": "Simplified Chinese financial",
|
||
"hant": "Traditional Chinese",
|
||
"hantfin": "Traditional Chinese financial",
|
||
"hebr": "Hebrew",
|
||
"hmng": "Pahawh Hmong",
|
||
"hmnp": "Nyiakeng Puachue Hmong",
|
||
"java": "Javanese",
|
||
"jpan": "Japanese",
|
||
"jpanfin": "Japanese financial",
|
||
"jpanyear": "Japanese calendar",
|
||
"kali": "Kayah Li",
|
||
"kawi": "Kawi",
|
||
"khmr": "Khmer",
|
||
"knda": "Kannada",
|
||
"krai": "Kirat Rai",
|
||
"lana": "Tai Tham Hora",
|
||
"lanatham": "Tai Tham",
|
||
"laoo": "Lao",
|
||
"latn": "Latin",
|
||
"lepc": "Lepcha",
|
||
"limb": "Limbu",
|
||
"mathbold": "Mathematical bold",
|
||
"mathdbl": "Mathematical double-struck",
|
||
"mathmono": "Mathematical monospace",
|
||
"mathsanb": "Mathematical sans-serif bold",
|
||
"mathsans": "Mathematical sans-serif",
|
||
"mlym": "Malayalam",
|
||
"modi": "Modi",
|
||
"mong": "Mongolian",
|
||
"mroo": "Mro",
|
||
"mtei": "Meetei Mayek",
|
||
"mymr": "Myanmar",
|
||
"mymrepka": "Myanmar Eastern Pwo Karen",
|
||
"mymrpao": "Myanmar Pao",
|
||
"mymrshan": "Myanmar Shan",
|
||
"mymrtlng": "Myanmar Tai Laing",
|
||
"nagm": "Nag Mundari",
|
||
"newa": "Newa",
|
||
"nkoo": "N'Ko",
|
||
"olck": "Ol Chiki",
|
||
"onao": "Ol Onal",
|
||
"orya": "Odia",
|
||
"osma": "Osmanya",
|
||
"outlined": "Outlined digits",
|
||
"rohg": "Hanifi Rohingya",
|
||
"roman": "Roman upper case",
|
||
"romanlow": "Roman lowercase",
|
||
"saur": "Saurashtra",
|
||
"segment": "7-segment display",
|
||
"shrd": "Sharada",
|
||
"sind": "Khudawadi",
|
||
"sinh": "Sinhala Lith",
|
||
"sora": "Sora_Sompeng",
|
||
"sund": "Sundanese",
|
||
"sunu": "Sunuwar",
|
||
"takr": "Takri",
|
||
"talu": "New Tai Lue",
|
||
"taml": "Tamil",
|
||
"tamldec": "Modern Tamil decimal",
|
||
"telu": "Telugu",
|
||
"thai": "Thai",
|
||
"tibt": "Tibetan",
|
||
"tirh": "Tirhuta",
|
||
"tnsa": "Tangsa",
|
||
"vaii": "Vai",
|
||
"wara": "Warang Citi",
|
||
"wcho": "Wancho"
|
||
}
|
||
|
||
Intl.supportedValuesOf('numberingSystem').forEach(system => {
|
||
systems[system] = intl_formatter(system, intl_system_names[system]);
|
||
});
|
||
|
||
const dl = document.getElementById('systems');
|
||
|
||
function sort_by_name([_,{name: a}], [__,{name: b}]) {
|
||
return a < b ? -1 : a>b ? 1 : 0;
|
||
}
|
||
|
||
Object.entries(systems).toSorted(sort_by_name).forEach(([id, entry]) => {
|
||
const {name, orientation} = entry;
|
||
const dt = document.createElement('dt');
|
||
dt.textContent = name;
|
||
dl.append(dt);
|
||
const dd = document.createElement('dd');
|
||
const bdi = document.createElement('bdi');
|
||
dd.append(bdi);
|
||
dl.append(dd);
|
||
if(orientation) {
|
||
bdi.style['writing-mode'] = orientation;
|
||
}
|
||
entry.dd = bdi;
|
||
})
|
||
|
||
function update() {
|
||
const n = BigInt(decimal_input.value || '0');
|
||
Object.values(systems).forEach(({name,fn,dd}) => {
|
||
dd.textContent = fn(n);
|
||
});
|
||
}
|
||
|
||
const decimal_input = document.getElementById('decimal');
|
||
|
||
update();
|
||
|
||
decimal_input.addEventListener('input', update) |