RosettaCodeData/Task/Smith-numbers/JavaScript/smith-numbers.js

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();
})();