numeral-conversion/script.js

536 lines
14 KiB
JavaScript
Raw 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 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);