RosettaCodeData/Task/Proper-divisors/JavaScript/proper-divisors-2.js

111 lines
3.1 KiB
JavaScript

(() => {
'use strict';
// properDivisors :: Int -> [Int]
const properDivisors = n => {
// The integer divisors of n, excluding n itself.
const
rRoot = Math.sqrt(n),
intRoot = Math.floor(rRoot),
blnPerfectSquare = rRoot === intRoot,
lows = enumFromTo(1)(intRoot)
.filter(x => 0 === (n % x));
// For perfect squares, we can drop
// the head of the 'highs' list
return lows.concat(lows
.map(x => n / x)
.reverse()
.slice(blnPerfectSquare | 0)
)
.slice(0, -1); // except n itself
};
// ------------------------TESTS-----------------------
// main :: IO ()
const main = () =>
console.log([
fTable('Proper divisors of [1..10]:')(str)(
JSON.stringify
)(properDivisors)(enumFromTo(1)(10)),
'',
'Example of maximum divisor count in the range [1..20000]:',
' ' + maximumBy(comparing(snd))(
enumFromTo(1)(20000).map(
n => [n, properDivisors(n).length]
)
).join(' has ') + ' proper divisors.'
].join('\n'));
// -----------------GENERIC FUNCTIONS------------------
// 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);
};
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = m => n =>
Array.from({
length: 1 + n - m
}, (_, i) => m + i);
// fTable :: String -> (a -> String) -> (b -> String)
// -> (a -> b) -> [a] -> String
const fTable = s => xShow => fxShow => f => xs => {
// Heading -> x display function ->
// fx display function ->
// f -> values -> tabular string
const
ys = xs.map(xShow),
w = Math.max(...ys.map(x => x.length));
return s + '\n' + zipWith(
a => b => a.padStart(w, ' ') + ' -> ' + b
)(ys)(
xs.map(x => fxShow(f(x)))
).join('\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;
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
// str :: a -> String
const str = x => x.toString();
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = p => f => x => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f => xs => ys => {
const
lng = Math.min(xs.length, xs.length),
as = xs.slice(0, lng),
bs = ys.slice(0, lng);
return Array.from({
length: lng
}, (_, i) => f(as[i])(
bs[i]
));
};
// MAIN ---
return main();
})();