numeral-conversion/script.js

564 lines
15 KiB
JavaScript
Raw Permalink Normal View History

2025-04-14 11:58:47 +00:00
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
}
}
2025-04-14 11:58:47 +00:00
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) {
}
2025-04-14 11:58:47 +00:00
// Make a function to render a positional number system whose digits are drawn from the given string.
return Object.assign({
2025-04-14 11:58:47 +00:00
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 || {});
2025-04-14 11:58:47 +00:00
}
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: {
2025-04-14 11:58:47 +00:00
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: {
2025-04-14 11:58:47 +00:00
name: 'Greek',
fn: concatenative([
["Αʹ", "Βʹ", "Γʹ", "Δʹ", "Εʹ", "Ϛʹ", "Ζʹ", "Ηʹ", "Θʹ" ],
["Ιʹ", "Κʹ", "Λʹ", "Μʹ", "Νʹ", "Ξʹ", "Οʹ", "Πʹ", "Ϟʹ"],
[ "Ρʹ", "Σʹ", "Τʹ", "Υʹ", "Φʹ", "Χʹ", "Ψʹ", "Ωʹ", "Ϡʹ" ],
[ "͵Α", "͵Β", ",Γ", "͵Δ", "͵Ε", "͵Ϛ͵ΣΤ", "͵Ζ", "͵Η", "͵Θ" ],
[ "͵Ι", "͵Κ", "͵Λ", "͵Μ", "͵Ν", "͵Ξ", "͵Ο", "͵Π", "͵Ϟ" ],
[ "͵Ρ", "͵Σ", "͵Τ", "͵Υ", "͵Φ", "͵Χ", "͵Ψ", "͵Ω", "͵Ϡ" ]
])
},
hans: {
2025-04-14 11:58:47 +00:00
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'}),
2025-04-14 11:58:47 +00:00
hant: {
2025-04-14 11:58:47 +00:00
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: {
2025-04-14 11:58:47 +00:00
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: {
2025-04-14 11:58:47 +00:00
name: 'Armenian',
fn: concatenative([
['Ա','Բ','Գ','Դ','Ե','Զ','Է','Ը','Թ'],
['Ժ','Ի','Լ','Խ','Ծ','Կ','Հ','Ձ','Ղ'],
['Ճ', 'Մ', 'Յ', 'Ն', 'Շ', 'Ո', 'Չ', 'Պ', 'Ջ'],
['Ռ', 'Ս', 'Վ', 'Տ', 'Ր', 'Ց', 'Ւ', 'Փ', 'Ք'],
['Օ', 'Ֆ']
])
},
abjad: {
name: 'Arabic abjad',
fn: concatenative([
["ا","ب","جـ","د","هـ","و","ز","حـ","ط"],
["ى","ك","ل","مـ","ن","س","ع","ف","ص"],
["ق","ر","ش","ت","ث","خـ","ذ","ض","ظ"]
])
},
glagolitic: {
name: 'Glagolitic',
fn: concatenative([
span('Ⰰ',9),
span('Ⰹ',9),
span('Ⱃ',9),
span('Ⱍ',9),
])
},
cyrl: {
2025-04-14 11:58:47 +00:00
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",
2025-04-14 13:26:08 +00:00
"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]);
});
2025-04-14 11:58:47 +00:00
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]) => {
2025-04-14 11:58:47 +00:00
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);
2025-04-14 11:58:47 +00:00
if(orientation) {
bdi.style['writing-mode'] = orientation;
2025-04-14 11:58:47 +00:00
}
entry.dd = bdi;
2025-04-14 11:58:47 +00:00
})
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)