171 lines
4.1 KiB
JavaScript
171 lines
4.1 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// isSmith :: Int -> Bool
|
|
const isSmith = n => {
|
|
const pfs = primeFactors(n);
|
|
return (1 < pfs.length || n !== pfs[0]) && (
|
|
sumDigits(n) === pfs.reduce(
|
|
(a, x) => a + sumDigits(x),
|
|
0
|
|
)
|
|
);
|
|
};
|
|
|
|
// TEST -----------------------------------------------
|
|
|
|
// main :: IO ()
|
|
const main = () => {
|
|
|
|
// lowSmiths :: [Int]
|
|
const lowSmiths = enumFromTo(2)(9999)
|
|
.filter(isSmith);
|
|
|
|
// lowSmithCount :: Int
|
|
const lowSmithCount = lowSmiths.length;
|
|
return [
|
|
"Count of Smith Numbers below 10k:",
|
|
show(lowSmithCount),
|
|
"\nFirst 15 Smith Numbers:",
|
|
unwords(take(15)(lowSmiths)),
|
|
"\nLast 12 Smith Numbers below 10000:",
|
|
unwords(drop(lowSmithCount - 12)(lowSmiths))
|
|
].join('\n');
|
|
};
|
|
|
|
// SMITH ----------------------------------------------
|
|
|
|
// primeFactors :: Int -> [Int]
|
|
const primeFactors = x => {
|
|
const go = n => {
|
|
const fs = take(1)(
|
|
dropWhile(x => 0 != n % x)(
|
|
enumFromTo(2)(
|
|
floor(sqrt(n))
|
|
)
|
|
)
|
|
);
|
|
return 0 === fs.length ? [n] : fs.concat(
|
|
go(floor(n / fs[0]))
|
|
);
|
|
};
|
|
return go(x);
|
|
};
|
|
|
|
// sumDigits :: Int -> Int
|
|
const sumDigits = n =>
|
|
unfoldl(
|
|
x => 0 === x ? (
|
|
Nothing()
|
|
) : Just(quotRem(x)(10))
|
|
)(n).reduce((a, x) => a + x, 0);
|
|
|
|
|
|
// GENERIC --------------------------------------------
|
|
|
|
// Nothing :: Maybe a
|
|
const Nothing = () => ({
|
|
type: 'Maybe',
|
|
Nothing: true,
|
|
});
|
|
|
|
// Just :: a -> Maybe a
|
|
const Just = x => ({
|
|
type: 'Maybe',
|
|
Nothing: false,
|
|
Just: x
|
|
});
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = a => b => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// drop :: Int -> [a] -> [a]
|
|
// drop :: Int -> String -> String
|
|
const drop = n => xs =>
|
|
xs.slice(n)
|
|
|
|
|
|
// dropWhile :: (a -> Bool) -> [a] -> [a]
|
|
// dropWhile :: (Char -> Bool) -> String -> String
|
|
const dropWhile = p => xs => {
|
|
const lng = xs.length;
|
|
return 0 < lng ? xs.slice(
|
|
until(i => i === lng || !p(xs[i]))(
|
|
i => 1 + i
|
|
)(0)
|
|
) : [];
|
|
};
|
|
|
|
// enumFromTo :: Int -> Int -> [Int]
|
|
const enumFromTo = m => n =>
|
|
Array.from({
|
|
length: 1 + n - m
|
|
}, (_, i) => m + i);
|
|
|
|
// floor :: Num -> Int
|
|
const floor = Math.floor;
|
|
|
|
|
|
// quotRem :: Int -> Int -> (Int, Int)
|
|
const quotRem = m => n =>
|
|
Tuple(Math.floor(m / n))(
|
|
m % n
|
|
);
|
|
|
|
// show :: a -> String
|
|
const show = x => JSON.stringify(x, null, 2);
|
|
|
|
// sqrt :: Num -> Num
|
|
const sqrt = n =>
|
|
(0 <= n) ? Math.sqrt(n) : undefined;
|
|
|
|
// sum :: [Num] -> Num
|
|
const sum = xs => xs.reduce((a, x) => a + x, 0);
|
|
|
|
// take :: Int -> [a] -> [a]
|
|
// take :: Int -> String -> String
|
|
const take = n => xs =>
|
|
'GeneratorFunction' !== xs.constructor.constructor.name ? (
|
|
xs.slice(0, n)
|
|
) : [].concat.apply([], Array.from({
|
|
length: n
|
|
}, () => {
|
|
const x = xs.next();
|
|
return x.done ? [] : [x.value];
|
|
}));
|
|
|
|
|
|
// unfoldl :: (b -> Maybe (b, a)) -> b -> [a]
|
|
const unfoldl = f => v => {
|
|
let
|
|
xr = [v, v],
|
|
xs = [];
|
|
while (true) {
|
|
const mb = f(xr[0]);
|
|
if (mb.Nothing) {
|
|
return xs
|
|
} else {
|
|
xr = mb.Just;
|
|
xs = [xr[1]].concat(xs);
|
|
}
|
|
}
|
|
};
|
|
|
|
// until :: (a -> Bool) -> (a -> a) -> a -> a
|
|
const until = p => f => x => {
|
|
let v = x;
|
|
while (!p(v)) v = f(v);
|
|
return v;
|
|
};
|
|
|
|
// unwords :: [String] -> String
|
|
const unwords = xs => xs.join(' ');
|
|
|
|
return main();
|
|
})();
|