123 lines
3.3 KiB
JavaScript
123 lines
3.3 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// shuffleCycleLength :: Int -> Int
|
|
const shuffleCycleLength = deckSize =>
|
|
firstCycle(shuffle, range(1, deckSize))
|
|
.all.length;
|
|
|
|
// shuffle :: [a] -> [a]
|
|
const shuffle = xs =>
|
|
concat(zip.apply(null, splitAt(div(length(xs), 2), xs)));
|
|
|
|
// firstycle :: Eq a => (a -> a) -> a -> [a]
|
|
const firstCycle = (f, x) =>
|
|
until(
|
|
m => EqArray(x, m.current),
|
|
m => {
|
|
const fx = f(m.current);
|
|
return {
|
|
current: fx,
|
|
all: m.all.concat([fx])
|
|
};
|
|
}, {
|
|
current: f(x),
|
|
all: [x]
|
|
}
|
|
);
|
|
|
|
// Two arrays equal ?
|
|
// EqArray :: [a] -> [b] -> Bool
|
|
const EqArray = (xs, ys) => {
|
|
const [nx, ny] = [xs.length, ys.length];
|
|
return nx === ny ? (
|
|
nx > 0 ? (
|
|
xs[0] === ys[0] && EqArray(xs.slice(1), ys.slice(1))
|
|
) : true
|
|
) : false;
|
|
};
|
|
|
|
// GENERIC FUNCTIONS
|
|
|
|
// zip :: [a] -> [b] -> [(a,b)]
|
|
const zip = (xs, ys) =>
|
|
xs.slice(0, Math.min(xs.length, ys.length))
|
|
.map((x, i) => [x, ys[i]]);
|
|
|
|
// concat :: [[a]] -> [a]
|
|
const concat = xs => [].concat.apply([], xs);
|
|
|
|
// splitAt :: Int -> [a] -> ([a],[a])
|
|
const splitAt = (n, xs) => [xs.slice(0, n), xs.slice(n)];
|
|
|
|
// div :: Num -> Num -> Int
|
|
const div = (x, y) => Math.floor(x / y);
|
|
|
|
// until :: (a -> Bool) -> (a -> a) -> a -> a
|
|
const until = (p, f, x) => {
|
|
const go = x => p(x) ? x : go(f(x));
|
|
return go(x);
|
|
}
|
|
|
|
// range :: Int -> Int -> [Int]
|
|
const range = (m, n) =>
|
|
Array.from({
|
|
length: Math.floor(n - m) + 1
|
|
}, (_, i) => m + i);
|
|
|
|
// length :: [a] -> Int
|
|
// length :: Text -> Int
|
|
const length = xs => xs.length;
|
|
|
|
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
|
|
const maximumBy = (f, xs) =>
|
|
xs.reduce((a, x) => a === undefined ? x : (
|
|
f(x, a) > 0 ? x : a
|
|
), undefined);
|
|
|
|
// transpose :: [[a]] -> [[a]]
|
|
const transpose = xs =>
|
|
xs[0].map((_, iCol) => xs.map((row) => row[iCol]));
|
|
|
|
// show :: a -> String
|
|
const show = x => JSON.stringify(x, null, 2);
|
|
|
|
// replicateS :: Int -> String -> String
|
|
const replicateS = (n, s) => {
|
|
let v = s,
|
|
o = '';
|
|
if (n < 1) return o;
|
|
while (n > 1) {
|
|
if (n & 1) o = o.concat(v);
|
|
n >>= 1;
|
|
v = v.concat(v);
|
|
}
|
|
return o.concat(v);
|
|
};
|
|
|
|
// justifyRight :: Int -> Char -> Text -> Text
|
|
const justifyRight = (n, cFiller, strText) =>
|
|
n > strText.length ? (
|
|
(replicateS(n, cFiller) + strText)
|
|
.slice(-n)
|
|
) : strText;
|
|
|
|
// TEST
|
|
return transpose(transpose([
|
|
['Deck', 'Shuffles']
|
|
].concat(
|
|
[8, 24, 52, 100, 1020, 1024, 10000]
|
|
.map(n => [n.toString(), shuffleCycleLength(n)
|
|
.toString()
|
|
])))
|
|
.map(col => { // Right-justified number columns
|
|
const width = length(
|
|
maximumBy((a, b) => length(a) - length(b), col)
|
|
) + 2;
|
|
|
|
return col.map(x => justifyRight(width, ' ', x));
|
|
}))
|
|
.map(row => row.join(''))
|
|
.join('\n');
|
|
})();
|