numeral-conversion/script.js
Christian Lawson-Perfect 45faac5b14 first commit
2025-04-14 11:58:52 +00:00

536 lines
No EOL
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);