numeral-conversion/script.js
Christian Lawson-Perfect 10c9328358 Oriya -> Odia
2025-04-14 13:26:13 +00:00

564 lines
No EOL
15 KiB
JavaScript
Raw Permalink 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 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": "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]);
});
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)