221 lines
5.3 KiB
JavaScript
221 lines
5.3 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// attractiveNumbers :: () -> Gen [Int]
|
|
const attractiveNumbers = () =>
|
|
// An infinite series of attractive numbers.
|
|
filter(
|
|
compose(isPrime, length, primeFactors)
|
|
)(enumFrom(1));
|
|
|
|
|
|
// ----------------------- TEST -----------------------
|
|
// main :: IO ()
|
|
const main = () =>
|
|
showCols(10)(
|
|
takeWhile(ge(120))(
|
|
attractiveNumbers()
|
|
)
|
|
);
|
|
|
|
|
|
// ---------------------- PRIMES ----------------------
|
|
|
|
// isPrime :: Int -> Bool
|
|
const isPrime = n => {
|
|
// True if n is prime.
|
|
if (2 === n || 3 === n) {
|
|
return true
|
|
}
|
|
if (2 > n || 0 === n % 2) {
|
|
return false
|
|
}
|
|
if (9 > n) {
|
|
return true
|
|
}
|
|
if (0 === n % 3) {
|
|
return false
|
|
}
|
|
return !enumFromThenTo(5)(11)(
|
|
1 + Math.floor(Math.pow(n, 0.5))
|
|
).some(x => 0 === n % x || 0 === n % (2 + x));
|
|
};
|
|
|
|
|
|
// primeFactors :: Int -> [Int]
|
|
const primeFactors = n => {
|
|
// A list of the prime factors of n.
|
|
const
|
|
go = x => {
|
|
const
|
|
root = Math.floor(Math.sqrt(x)),
|
|
m = until(
|
|
([q, _]) => (root < q) || (0 === (x % q))
|
|
)(
|
|
([_, r]) => [step(r), 1 + r]
|
|
)([
|
|
0 === x % 2 ? (
|
|
2
|
|
) : 3,
|
|
1
|
|
])[0];
|
|
return m > root ? (
|
|
[x]
|
|
) : ([m].concat(go(Math.floor(x / m))));
|
|
},
|
|
step = x => 1 + (x << 2) - ((x >> 1) << 1);
|
|
return go(n);
|
|
};
|
|
|
|
|
|
// ---------------- GENERIC FUNCTIONS -----------------
|
|
|
|
// chunksOf :: Int -> [a] -> [[a]]
|
|
const chunksOf = n =>
|
|
xs => enumFromThenTo(0)(n)(
|
|
xs.length - 1
|
|
).reduce(
|
|
(a, i) => a.concat([xs.slice(i, (n + i))]),
|
|
[]
|
|
);
|
|
|
|
|
|
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
|
const compose = (...fs) =>
|
|
fs.reduce(
|
|
(f, g) => x => f(g(x)),
|
|
x => x
|
|
);
|
|
|
|
|
|
// enumFrom :: Enum a => a -> [a]
|
|
function* enumFrom(x) {
|
|
// A non-finite succession of enumerable
|
|
// values, starting with the value x.
|
|
let v = x;
|
|
while (true) {
|
|
yield v;
|
|
v = 1 + v;
|
|
}
|
|
}
|
|
|
|
|
|
// enumFromThenTo :: Int -> Int -> Int -> [Int]
|
|
const enumFromThenTo = x1 =>
|
|
x2 => y => {
|
|
const d = x2 - x1;
|
|
return Array.from({
|
|
length: Math.floor(y - x2) / d + 2
|
|
}, (_, i) => x1 + (d * i));
|
|
};
|
|
|
|
|
|
// filter :: (a -> Bool) -> Gen [a] -> [a]
|
|
const filter = p => xs => {
|
|
function* go() {
|
|
let x = xs.next();
|
|
while (!x.done) {
|
|
let v = x.value;
|
|
if (p(v)) {
|
|
yield v
|
|
}
|
|
x = xs.next();
|
|
}
|
|
}
|
|
return go(xs);
|
|
};
|
|
|
|
|
|
// ge :: Ord a => a -> a -> Bool
|
|
const ge = x =>
|
|
// True if x >= y
|
|
y => x >= y;
|
|
|
|
|
|
// justifyRight :: Int -> Char -> String -> String
|
|
const justifyRight = n =>
|
|
// The string s, preceded by enough padding (with
|
|
// the character c) to reach the string length n.
|
|
c => s => n > s.length ? (
|
|
s.padStart(n, c)
|
|
) : s;
|
|
|
|
|
|
// last :: [a] -> a
|
|
const last = xs =>
|
|
// The last item of a list.
|
|
0 < xs.length ? xs.slice(-1)[0] : undefined;
|
|
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs =>
|
|
// 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
|
|
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
|
xs.length
|
|
) : Infinity;
|
|
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = f =>
|
|
// The list obtained by applying f
|
|
// to each element of xs.
|
|
// (The image of xs under f).
|
|
xs => (
|
|
Array.isArray(xs) ? (
|
|
xs
|
|
) : xs.split('')
|
|
).map(f);
|
|
|
|
|
|
// showCols :: Int -> [a] -> String
|
|
const showCols = w => xs => {
|
|
const
|
|
ys = xs.map(str),
|
|
mx = last(ys).length;
|
|
return unlines(chunksOf(w)(ys).map(
|
|
row => row.map(justifyRight(mx)(' ')).join(' ')
|
|
))
|
|
};
|
|
|
|
|
|
// str :: a -> String
|
|
const str = x =>
|
|
x.toString();
|
|
|
|
|
|
// takeWhile :: (a -> Bool) -> Gen [a] -> [a]
|
|
const takeWhile = p => xs => {
|
|
const ys = [];
|
|
let
|
|
nxt = xs.next(),
|
|
v = nxt.value;
|
|
while (!nxt.done && p(v)) {
|
|
ys.push(v);
|
|
nxt = xs.next();
|
|
v = nxt.value
|
|
}
|
|
return ys;
|
|
};
|
|
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs =>
|
|
// A single string formed by the intercalation
|
|
// of a list of strings with the newline character.
|
|
xs.join('\n');
|
|
|
|
|
|
// until :: (a -> Bool) -> (a -> a) -> a -> a
|
|
const until = p => f => x => {
|
|
let v = x;
|
|
while (!p(v)) v = f(v);
|
|
return v;
|
|
};
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|