<main>
<div>
<h2>Decimal to balanced-elevenary-in-Ogham</h2>
<input type="number" id="decimal-in" value="17.38" step="any">
<span id="digits-out"></span>
<span id="roman-out"></span>
<span id="ogham-out" class="ogham"></span>
</div>
<div>
<h2>Balanced-elevenary-in-Ogham to decimal</h2>
<input type="text" id="ogham-in" class="ogham" value="ᚆᚋᚂᚈ">
<input type="text" id="roman-in">
<span id="digits-in"></span>
<span id="decimal-out"></span>
</div>
</main>
main {
display: flex;
flex-flow: row;
gap: 1em;
}
main > div {
display: flex;
flex-flow: column;
flex: auto;
width: 50%;
}
.ogham {
font-size: 200%;
}
const decimalInEl = document.getElementById("decimal-in");
const digitsOutEl = document.getElementById("digits-out");
const romanOutEl = document.getElementById("roman-out");
const oghamOutEl = document.getElementById("ogham-out");
const oghamInEl = document.getElementById("ogham-in");
const romanInEl = document.getElementById("roman-in");
const digitsInEl = document.getElementById("digits-in");
const decimalOutEl = document.getElementById("decimal-out");
// "bit" and "trit" are cool and all but elevenary doesn't have anything
const FRAC_DIGITS = 5;
function intToBalancedElevenary(value: number) {
const result = [];
while (value != 0) {
let lastDigit = value % 11;
if (lastDigit > 5) {
value += 11;
lastDigit = lastDigit - 11;
} else if (lastDigit < -5) {
value -= 11;
lastDigit = lastDigit + 11;
}
result.push(lastDigit);
value = Math.trunc(value / 11);
}
result.reverse();
if (result.length === 0) {
return [0];
}
return result;
}
function fracToBalancedElevenary(value: number) {
// This happens to be lazy as all hell.
const offset = Math.pow(11, FRAC_DIGITS);
const fracPart = Math.round(value * offset);
let fracPartDigits = intToBalancedElevenary(fracPart);
while (fracPartDigits.length < FRAC_DIGITS) {
fracPartDigits = [0].concat(fracPartDigits);
}
while (fracPartDigits[fracPartDigits.length - 1] === 0) {
fracPartDigits.pop();
}
return fracPartDigits;
}
const digitSpellings = {
// M H D T C Q
// ᚋ ᚆ ᚇ ᚈ ᚉ ᚊ
"-5": { roman: "N", ogham: "ᚅ" },
"-4": { roman: "S", ogham: "ᚄ" },
"-3": { roman: "F", ogham: "ᚃ" },
"-2": { roman: "L", ogham: "ᚂ" },
"-1": { roman: "B", ogham: "ᚁ" },
0: { roman: "M", ogham: "ᚋ" },
1: { roman: "H", ogham: "ᚆ" },
2: { roman: "D", ogham: "ᚇ" },
3: { roman: "T", ogham: "ᚈ" },
4: { roman: "C", ogham: "ᚉ" },
5: { roman: "Q", ogham: "ᚊ" }
};
type DigitsResult = { intDigits: number[]; fracDigits: number[] };
function toBalancedElevenary(value: number): DigitsResult {
const intDigits = intToBalancedElevenary(Math.trunc(value));
const fracDigits = fracToBalancedElevenary(value - Math.trunc(value));
return { intDigits, fracDigits };
}
function encodeRaw({ intDigits, fracDigits }: DigitsResult) {
if (fracDigits.length > 0) {
return intDigits.join(" : ") + " . " + fracDigits.join(" : ");
} else {
return intDigits.join(" : ");
}
}
function encode(
{ intDigits, fracDigits }: DigitsResult,
mode: "ogham" | "roman"
): string {
const radix = mode === "ogham" ? "ᚖ" : ".";
const intString = intDigits
.map((digit) => digitSpellings[digit][mode])
.join("");
const fracString = fracDigits
.map((digit) => digitSpellings[digit][mode])
.join("");
if (fracDigits.length > 0) {
return intString + radix + fracString;
} else {
return intString;
}
}
function renderDecimalInOghamOut() {
const numericValue = parseFloat(decimalInEl.value);
if (Number.isNaN(numericValue)) {
digitsOutEl.innerText = romanOutEl.innerText = oghamOutEl.innerText =
"error!";
return;
}
const result = toBalancedElevenary(numericValue);
digitsOutEl.innerText = encodeRaw(result);
romanOutEl.innerText = encode(result, "roman");
oghamOutEl.innerText = encode(result, "ogham");
}
renderDecimalInOghamOut();
decimalInEl.oninput = renderDecimalInOghamOut;
function decodeDigit(digit: string, mode: "ogham" | "roman"): number {
for (let value = -5; value <= 5; value++) {
if (digitSpellings[value][mode] === digit) {
return value;
}
}
}
function decode(stringValue: string, mode: "ogham" | "roman"): DigitsResult {
const radix = mode === "ogham" ? "ᚖ" : ".";
const radixSplit = stringValue.split(radix);
const intDigits = radixSplit[0]
.split("")
.map((digit) => decodeDigit(digit, mode));
const fracDigits =
radixSplit[1]?.split("").map((digit) => decodeDigit(digit, mode)) || [];
return { intDigits, fracDigits };
}
function sum(data: number[]): number {
return data.reduce((a, b) => a + b, 0);
}
function parse({ intDigits, fracDigits }: DigitsResult): number {
return (
sum(
intDigits.map(
(digitValue, digitIndex) =>
digitValue * Math.pow(11, intDigits.length - digitIndex - 1)
)
) +
sum(
fracDigits.map(
(digitValue, digitIndex) => digitValue * Math.pow(11, -digitIndex - 1)
)
)
);
}
function renderOghamInDecimalOut() {
const decoded = decode(oghamInEl.value, "ogham");
romanInEl.value = encode(decoded, "roman");
digitsInEl.innerText = encodeRaw(decoded);
decimalOutEl.innerText = parse(decoded);
}
function renderRomanInDecimalOut() {
const decoded = decode(romanInEl.value, "roman");
oghamInEl.value = encode(decoded, "ogham");
digitsInEl.innerText = encodeRaw(decoded);
decimalOutEl.innerText = parse(decoded);
}
renderOghamInDecimalOut();
oghamInEl.oninput = renderOghamInDecimalOut;
romanInEl.oninput = renderRomanInDecimalOut;
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.