111 lines
3.1 KiB
JavaScript
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();
|
|
})();
|