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 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) { // Make a function to render a positional number system whose digits are drawn from the given string. return { 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; } }; } 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}'); } }, arabic: positional_unicode_span('Eastern Arabic', 'ู '), 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('๐„ซ'), ]) }, hebrew: { 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]) { console.log(symbols, n); if(n==0n) { break; } const d = n % 10n; n = (n-d) / 10n; if(d > 0n) { o = symbols[Number(d)-1] + o; } } return o; } }, greek: { name: 'Greek', fn: concatenative([ ["ฮ‘สน", "ฮ’สน", "ฮ“สน", "ฮ”สน", "ฮ•สน", "ฯšสน", "ฮ–สน", "ฮ—สน", "ฮ˜สน" ], ["ฮ™สน", "ฮšสน", "ฮ›สน", "ฮœสน", "ฮสน", "ฮžสน", "ฮŸสน", "ฮ สน", "ฯžสน"], [ "ฮกสน", "ฮฃสน", "ฮคสน", "ฮฅสน", "ฮฆสน", "ฮงสน", "ฮจสน", "ฮฉสน", "ฯ สน" ], [ "อตฮ‘", "อตฮ’", ",ฮ“", "อตฮ”", "อตฮ•", "อตฯšอตฮฃฮค", "อตฮ–", "อตฮ—", "อตฮ˜" ], [ "อตฮ™", "อตฮš", "อตฮ›", "อตฮœ", "อตฮ", "อตฮž", "อตฮŸ", "อตฮ ", "อตฯž" ], [ "อตฮก", "อตฮฃ", "อตฮค", "อตฮฅ", "อตฮฆ", "อตฮง", "อตฮจ", "อตฮฉ", "อตฯ " ] ]) }, chinese: { 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)), 10n, {orientation: 'vertical-lr'}), suzhou: { 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; } }, ethiopic: { 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([ ["๐ค–","๐คš","๐ค›","๐ค›๐ค–","๐ค›๐คš","๐ค›๐ค›","๐ค›๐ค›๐ค–","๐ค›๐ค›๐คš","๐ค›๐ค›๐ค›",], ["๐ค—","๐ค˜","๐ค˜๐ค—","๐ค˜๐ค˜","๐ค˜๐ค˜๐ค—","๐ค˜๐ค˜๐ค˜","๐ค˜๐ค˜๐ค˜๐ค—","๐ค˜๐ค˜๐ค˜๐ค˜","๐ค˜๐ค˜๐ค˜๐ค˜๐ค—"], ["๐ค™","๐คš๐ค™","๐ค›๐ค™","๐ค›๐ค–๐ค™","๐ค›๐คš๐ค™","๐ค›๐ค›๐ค™","๐ค›๐ค›๐ค–๐ค™","๐ค›๐ค›๐คš๐ค™","๐ค›๐ค›๐ค›๐ค™"], ["๐ค™","๐ค˜๐ค™","๐ค˜๐ค—๐ค™","๐ค˜๐ค˜๐ค™","๐ค˜๐ค˜๐ค—๐ค™","๐ค˜๐ค˜๐ค˜๐ค™","๐ค˜๐ค˜๐ค˜๐ค—๐ค™","๐ค˜๐ค˜๐ค˜๐ค˜๐ค™","๐ค˜๐ค˜๐ค˜๐ค˜๐ค—๐ค™"] ]) }, armenian: { name: 'Armenian', fn: concatenative([ ['ิฑ','ิฒ','ิณ','ิด','ิต','ิถ','ิท','ิธ','ิน'], ['ิบ','ิป','ิผ','ิฝ','ิพ','ิฟ','ี€','ี','ี‚'], ['ีƒ', 'ี„', 'ี…', 'ี†', 'ี‡', 'ีˆ', 'ี‰', 'ีŠ', 'ี‹'], ['ีŒ', 'ี', 'ีŽ', 'ี', 'ี', 'ี‘', 'ี’', 'ี“', 'ี”'], ['ี•', 'ี–'] ]) }, abjad: { name: 'Arabic abjad', fn: concatenative([ ["ุง","ุจ","ุฌู€","ุฏ","ู‡ู€","ูˆ","ุฒ","ุญู€","ุท"], ["ู‰","ูƒ","ู„","ู…ู€","ู†","ุณ","ุน","ู","ุต"], ["ู‚","ุฑ","ุด","ุช","ุซ","ุฎู€","ุฐ","ุถ","ุธ"] ]) }, glagolitic: { name: 'Glagolitic', fn: concatenative([ span('โฐ€',9), span('โฐ‰',9), span('โฐ“',9), span('โฐ',9), ]) }, cyrillic: { 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'}), nko: positional_unicode_span('N\'Ko', '฿€'), devanagari: positional_unicode_span('Devanagari', 'เฅฆ'), bengali: positional_unicode_span('Bengali', 'เงฆ'), gurmukhi: positional_unicode_span('Gurmukhi','เฉฆ'), gujarati: positional_unicode_span('Gujarati', 'เซฆ'), odia: positional_unicode_span('Odia', 'เญฆ'), tamil: positional_unicode_span('Tamil', 'เฏฆ'), telugu: positional_unicode_span('Telugu', 'เฑฆ'), kannada: positional_unicode_span('Kannada', 'เณฆ'), malayalam: positional_unicode_span('Malayalam', 'เตฆ'), thai: positional_unicode_span('Thai', 'เน'), lao: positional_unicode_span('Lao', 'เป'), tibetan: positional_unicode_span('Tibetan', 'เผ '), burmese: positional_unicode_span('Burmese', 'แ€'), khmer: positional_unicode_span('Khmer', 'แŸ '), mongolian: positional_unicode_span('Mongolian', 'แ '), limbu: positional_unicode_span('Limbu', 'แฅ†'), new_tai_lue: positional_unicode_span('New Tai Lue', 'แง'), lek_hora: positional_unicode_span('Lek Hora','แช€'), lek_nai_tham: positional_unicode_span('Lek Nai Tham', 'แช'), balinese: positional_unicode_span('Balinese', 'แญ'), sundanese: positional_unicode_span('Sundanese', 'แฎฐ'), lepcha: positional_unicode_span('Lepcha', 'แฑ€'), ol_chiki: positional_unicode_span('Ol Chiki', 'แฑ'), vai: positional_unicode_span('Vai', '๊˜ '), saurashtra: positional_unicode_span('Saurashtra', '๊ฃ'), kayah_li: positional_unicode_span('Kayah Li', '๊ค€'), javanese: positional_unicode_span('Javanese', '๊ง'), cham: positional_unicode_span('Cham', '๊ฉ'), meitei: positional_unicode_span('meitei', '๊ฏฐ'), osmanya: positional_unicode_span('Osmanya', '๐’ '), brahmi: positional_unicode_span('Brahmi', '๐‘ฆ'), sorang_sompeng: positional_unicode_span('Sorang Sompeng', '๐‘ƒฐ'), chakma: positional_unicode_span('Chakma', '๐‘„ถ'), sharada: positional_unicode_span('Sharada', '๐‘‡'), takri: positional_unicode_span('Takri', '๐‘›€'), sinhala: positional_unicode_span('Sinhala', 'เทฆ'), tai_laing: positional_unicode_span('Tai Laing', '๊งฐ'), khudabadi: positional_unicode_span('Khudabadi', '๐‘‹ฐ'), tirhuta: positional_unicode_span('Tirhuta', '๐‘“'), modi: positional_unicode_span('Modi', '๐‘™'), warang_citi: positional_unicode_span('Warang Citi', '๐‘ฃ '), mro: positional_unicode_span('Mro', '๐–ฉ '), pahawh_hmong: positional_unicode_span('Pahawh Hmong', '๐–ญ'), ahom: positional_unicode_span('Tai Ahom', '๐‘œฐ'), prachalit: positional_unicode_span('Prachalit', '๐‘‘'), hanifi_rohingya: positional_unicode_span('Hanifi Rohingya', '๐ดฐ'), bhaiksuki: positional_unicode_span('Bhaiksuki', '๐‘ฑ'), masaram_gondi: positional_unicode_span('Masaram Gondi', '๐‘ต'), gunjala_gondi: positional_unicode_span('Gunjala Gondi', '๐‘ถ '), medefaidrin: positional_unicode_span('Medefaidrin', '๐–บ€', 20n), nyiakeng_puachue_hmong: positional_unicode_span('Nyiakeng Puachue Hmong', '๐ž…€'), wancho: positional_unicode_span('Wancho', '๐ž‹ฐ'), adlam: positional_unicode_span('Adlam', '๐žฅ'), 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 dl = document.getElementById('systems'); Object.entries(systems).forEach(([id, entry]) => { const {name, orientation} = entry; const dt = document.createElement('dt'); dt.textContent = name; dl.append(dt); const dd = document.createElement('dd'); if(orientation) { dd.style['writing-mode'] = orientation; } dl.append(dd); entry.dd = dd; }) function update() { const n = BigInt(decimal_input.value || '0'); Object.values(systems).forEach(({name,fn,dd}) => { console.log(name); dd.textContent = fn(n); }); } const decimal_input = document.getElementById('decimal'); update(); decimal_input.addEventListener('input', update);