212 lines
5.8 KiB
JavaScript
212 lines
5.8 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
const main = () => {
|
|
|
|
// abundantTuple :: Int -> [(Int, Int)]
|
|
const abundantTuple = n => {
|
|
// Either a list containing the tuple of N
|
|
// and its divisor sum (if n is abundant),
|
|
// or otherwise an empty list.
|
|
const x = divisorSum(n);
|
|
return n < x ? ([
|
|
Tuple(n)(x)
|
|
]) : [];
|
|
};
|
|
|
|
// divisorSum :: Int -> Int
|
|
const divisorSum = n => {
|
|
// Sum of the divisors of n.
|
|
const
|
|
floatRoot = Math.sqrt(n),
|
|
intRoot = Math.floor(floatRoot),
|
|
lows = filter(x => 0 === n % x)(
|
|
enumFromTo(1)(intRoot)
|
|
);
|
|
return sum(lows.concat(map(quot(n))(
|
|
intRoot === floatRoot ? (
|
|
lows.slice(1, -1)
|
|
) : lows.slice(1)
|
|
)));
|
|
};
|
|
|
|
// TEST ---------------------------------------
|
|
console.log(
|
|
'First 25 abundant odd numbers, with their divisor sums:'
|
|
)
|
|
console.log(unlines(map(showTuple)(
|
|
take(25)(
|
|
concatMapGen(abundantTuple)(
|
|
enumFromThen(1)(3)
|
|
)
|
|
)
|
|
)));
|
|
console.log(
|
|
'\n\n1000th abundant odd number, with its divisor sum:'
|
|
)
|
|
console.log(showTuple(
|
|
take(1)(drop(999)(
|
|
concatMapGen(abundantTuple)(
|
|
enumFromThen(1)(3)
|
|
)
|
|
))[0]
|
|
))
|
|
console.log(
|
|
'\n\nFirst abundant odd number above 10^9, with divisor sum:'
|
|
)
|
|
const billion = Math.pow(10, 9);
|
|
console.log(showTuple(
|
|
take(1)(
|
|
concatMapGen(abundantTuple)(
|
|
enumFromThen(1 + billion)(3 + billion)
|
|
)
|
|
)[0]
|
|
))
|
|
};
|
|
|
|
|
|
// GENERAL REUSABLE FUNCTIONS -------------------------
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = a => b => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// concatMapGen :: (a -> [b]) -> Gen [a] -> Gen [b]
|
|
const concatMapGen = f =>
|
|
function*(xs) {
|
|
let
|
|
x = xs.next(),
|
|
v = undefined;
|
|
while (!x.done) {
|
|
v = f(x.value);
|
|
if (0 < v.length) {
|
|
yield v[0];
|
|
}
|
|
x = xs.next();
|
|
}
|
|
};
|
|
|
|
// drop :: Int -> [a] -> [a]
|
|
// drop :: Int -> Generator [a] -> Generator [a]
|
|
// drop :: Int -> String -> String
|
|
const drop = n => xs =>
|
|
Infinity > length(xs) ? (
|
|
xs.slice(n)
|
|
) : (take(n)(xs), xs);
|
|
|
|
// dropAround :: (a -> Bool) -> [a] -> [a]
|
|
// dropAround :: (Char -> Bool) -> String -> String
|
|
const dropAround = p => xs => dropWhile(p)(
|
|
dropWhileEnd(p)(xs)
|
|
);
|
|
|
|
// 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)
|
|
) : [];
|
|
};
|
|
|
|
// dropWhileEnd :: (a -> Bool) -> [a] -> [a]
|
|
// dropWhileEnd :: (Char -> Bool) -> String -> String
|
|
const dropWhileEnd = p => xs => {
|
|
let i = xs.length;
|
|
while (i-- && p(xs[i])) {}
|
|
return xs.slice(0, i + 1);
|
|
};
|
|
|
|
// enumFromThen :: Int -> Int -> Gen [Int]
|
|
const enumFromThen = x =>
|
|
// A non-finite stream of integers,
|
|
// starting with x and y, and continuing
|
|
// with the same interval.
|
|
function*(y) {
|
|
const d = y - x;
|
|
let v = y + d;
|
|
yield x;
|
|
yield y;
|
|
while (true) {
|
|
yield v;
|
|
v = d + v;
|
|
}
|
|
};
|
|
|
|
// enumFromTo :: Int -> Int -> [Int]
|
|
const enumFromTo = m => n =>
|
|
Array.from({
|
|
length: 1 + n - m
|
|
}, (_, i) => m + i);
|
|
|
|
// filter :: (a -> Bool) -> [a] -> [a]
|
|
const filter = f => xs => xs.filter(f);
|
|
|
|
// 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
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs =>
|
|
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
|
xs.length
|
|
) : Infinity;
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = f => xs =>
|
|
(Array.isArray(xs) ? (
|
|
xs
|
|
) : xs.split('')).map(f);
|
|
|
|
// quot :: Int -> Int -> Int
|
|
const quot = n => m => Math.floor(n / m);
|
|
|
|
// show :: a -> String
|
|
const show = JSON.stringify;
|
|
|
|
// showTuple :: Tuple -> String
|
|
const showTuple = tpl =>
|
|
'(' + enumFromTo(0)(tpl.length - 1)
|
|
.map(x => unQuoted(show(tpl[x])))
|
|
.join(',') + ')';
|
|
|
|
// 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];
|
|
}));
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs => 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;
|
|
};
|
|
|
|
// unQuoted :: String -> String
|
|
const unQuoted = s =>
|
|
dropAround(x => 34 === x.codePointAt(0))(
|
|
s
|
|
);
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|