219 lines
6.0 KiB
JavaScript
219 lines
6.0 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
const main = () =>
|
|
unlines(
|
|
map(mayanFramed,
|
|
[4005, 8017, 326205, 886205, 1081439556, 1000000, 1000000000]
|
|
)
|
|
);
|
|
|
|
// MAYAN NUMBERS --------------------------------------
|
|
|
|
// mayanFramed :: Int -> String
|
|
const mayanFramed = n =>
|
|
'\nMayan ' + n.toString() + ':\n\n' +
|
|
wikiTable({
|
|
style: 'text-align:center; background-color:#F0EDDE; ' +
|
|
'color:#605B4B; border:2px solid silver',
|
|
colwidth: '3em'
|
|
})(
|
|
mayanGlyph(n)
|
|
);
|
|
|
|
// mayanGlyph :: Int -> [[String]]
|
|
const mayanGlyph = n =>
|
|
filter(any(compose(not, isNull)),
|
|
transpose(leftPadded(
|
|
showIntAtBase(20, mayanDigit, n, [])
|
|
))
|
|
);
|
|
|
|
// mayanDigit :: Int -> [String]
|
|
const mayanDigit = n =>
|
|
0 !== n ? cons(
|
|
replicateString(rem(n, 5), '●'),
|
|
replicate(quot(n, 5), '━━')
|
|
) : ['Θ'];
|
|
|
|
// FORMATTING -----------------------------------------
|
|
|
|
// wikiTable :: Dict -> [[a]] -> String
|
|
const wikiTable = opts => rows => {
|
|
const colWidth = () =>
|
|
'colwidth' in opts ? (
|
|
'|style="width:' + opts.colwidth + ';"'
|
|
) : '';
|
|
return 0 < rows.length ? (
|
|
'{| ' + ['class', 'style'].reduce(
|
|
(a, k) => k in opts ? (
|
|
a + k + '="' + opts[k] + '" '
|
|
) : a, ''
|
|
) + '\n' + rows.map(
|
|
(row, i) => row.map(
|
|
x => (0 === i ? (
|
|
colWidth() + '| '
|
|
) : '|') + (x.toString() || ' ')
|
|
).join('\n')
|
|
).join('\n|-\n') + '\n|}\n\n'
|
|
) : '';
|
|
};
|
|
|
|
// leftPadded :: [[String]] -> [[String]]
|
|
const leftPadded = xs => {
|
|
const w = maximum(map(length, xs));
|
|
return map(
|
|
x => replicate(w - x.length, '').concat(x),
|
|
xs
|
|
);
|
|
};
|
|
|
|
// GENERIC FUNCTIONS ----------------------------------
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = (a, b) => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// any :: (a -> Bool) -> [a] -> Bool
|
|
const any = p => xs => xs.some(p);
|
|
|
|
// comparing :: (a -> b) -> (a -> a -> Ordering)
|
|
const comparing = f =>
|
|
(x, y) => {
|
|
const
|
|
a = f(x),
|
|
b = f(y);
|
|
return a < b ? -1 : (a > b ? 1 : 0);
|
|
};
|
|
|
|
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
|
const compose = (f, g) => x => f(g(x));
|
|
|
|
// concatMap :: (a -> [b]) -> [a] -> [b]
|
|
const concatMap = (f, xs) =>
|
|
xs.reduce((a, x) => a.concat(f(x)), []);
|
|
|
|
// cons :: a -> [a] -> [a]
|
|
const cons = (x, xs) =>
|
|
Array.isArray(xs) ? (
|
|
[x].concat(xs)
|
|
) : 'GeneratorFunction' !== xs.constructor.constructor.name ? (
|
|
x + xs
|
|
) : ( // Existing generator wrapped with one additional element
|
|
function*() {
|
|
yield x;
|
|
let nxt = xs.next()
|
|
while (!nxt.done) {
|
|
yield nxt.value;
|
|
nxt = xs.next();
|
|
}
|
|
}
|
|
)();
|
|
|
|
// filter :: (a -> Bool) -> [a] -> [a]
|
|
const filter = (f, xs) => xs.filter(f);
|
|
|
|
// foldl1 :: (a -> a -> a) -> [a] -> a
|
|
const foldl1 = (f, xs) =>
|
|
1 < xs.length ? xs.slice(1)
|
|
.reduce(f, xs[0]) : xs[0];
|
|
|
|
// isNull :: [a] -> Bool
|
|
// isNull :: String -> Bool
|
|
const isNull = xs =>
|
|
Array.isArray(xs) || ('string' === typeof xs) ? (
|
|
1 > xs.length
|
|
) : undefined;
|
|
|
|
// Returns Infinity over objects without finite length.
|
|
// This enables zip and zipWith to choose the shorter
|
|
// argument when one is non-finite, like cycle, repeat etc
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs =>
|
|
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
|
xs.length
|
|
) : Infinity;
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) =>
|
|
(Array.isArray(xs) ? (
|
|
xs
|
|
) : xs.split('')).map(f);
|
|
|
|
// maximum :: Ord a => [a] -> a
|
|
const maximum = xs =>
|
|
0 < xs.length ? (
|
|
foldl1((a, x) => x > a ? x : a, xs)
|
|
) : undefined;
|
|
|
|
// Ordering: (LT|EQ|GT):
|
|
// GT: 1 (or other positive n)
|
|
// EQ: 0
|
|
// LT: -1 (or other negative n)
|
|
|
|
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
|
|
const maximumBy = (f, xs) =>
|
|
0 < xs.length ? (
|
|
xs.slice(1)
|
|
.reduce((a, x) => 0 < f(x, a) ? x : a, xs[0])
|
|
) : undefined;
|
|
|
|
// not :: Bool -> Bool
|
|
const not = b => !b;
|
|
|
|
// quot :: Int -> Int -> Int
|
|
const quot = (n, m) => Math.floor(n / m);
|
|
|
|
// quotRem :: Int -> Int -> (Int, Int)
|
|
const quotRem = (m, n) =>
|
|
Tuple(Math.floor(m / n), m % n);
|
|
|
|
// rem :: Int -> Int -> Int
|
|
const rem = (n, m) => n % m;
|
|
|
|
// replicate :: Int -> a -> [a]
|
|
const replicate = (n, x) =>
|
|
Array.from({
|
|
length: n
|
|
}, () => x);
|
|
|
|
// replicateString :: Int -> String -> String
|
|
const replicateString = (n, s) => s.repeat(n);
|
|
|
|
// showIntAtBase :: Int -> (Int -> [String])
|
|
// -> Int -> [[String]] -> [[String]]
|
|
const showIntAtBase = (base, toStr, n, rs) => {
|
|
const go = ([n, d], r) => {
|
|
const r_ = cons(toStr(d), r);
|
|
return 0 !== n ? (
|
|
go(Array.from(quotRem(n, base)), r_)
|
|
) : r_;
|
|
};
|
|
return go(Array.from(quotRem(n, base)), rs);
|
|
};
|
|
|
|
// transpose :: [[a]] -> [[a]]
|
|
const transpose = tbl => {
|
|
const
|
|
gaps = replicate(
|
|
length(maximumBy(comparing(length), tbl)), []
|
|
),
|
|
rows = map(xs => xs.concat(gaps.slice(xs.length)), tbl);
|
|
return map(
|
|
(_, col) => concatMap(row => [row[col]], rows),
|
|
rows[0]
|
|
);
|
|
};
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs => xs.join('\n');
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|