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": "Oriya", "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)