228 lines
6.4 KiB
JavaScript
228 lines
6.4 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
// GENERIC FUNCTIONS ----------------------------------------------------
|
|
|
|
// permutationsWithRepetition :: Int -> [a] -> [[a]]
|
|
var permutationsWithRepetition = function (n, as) {
|
|
return as.length > 0 ?
|
|
foldl1(curry(cartesianProduct)(as), replicate(n, as)) : [];
|
|
};
|
|
|
|
// cartesianProduct :: [a] -> [b] -> [[a, b]]
|
|
var cartesianProduct = function (xs, ys) {
|
|
return [].concat.apply([], xs.map(function (x) {
|
|
return [].concat.apply([], ys.map(function (y) {
|
|
return [
|
|
[x].concat(y)
|
|
];
|
|
}));
|
|
}));
|
|
};
|
|
|
|
// curry :: ((a, b) -> c) -> a -> b -> c
|
|
var curry = function (f) {
|
|
return function (a) {
|
|
return function (b) {
|
|
return f(a, b);
|
|
};
|
|
};
|
|
};
|
|
|
|
// flip :: (a -> b -> c) -> b -> a -> c
|
|
var flip = function (f) {
|
|
return function (a, b) {
|
|
return f.apply(null, [b, a]);
|
|
};
|
|
};
|
|
|
|
// foldl1 :: (a -> a -> a) -> [a] -> a
|
|
var foldl1 = function (f, xs) {
|
|
return xs.length > 0 ? xs.slice(1)
|
|
.reduce(f, xs[0]) : [];
|
|
};
|
|
|
|
// replicate :: Int -> a -> [a]
|
|
var replicate = function (n, a) {
|
|
var v = [a],
|
|
o = [];
|
|
if (n < 1) return o;
|
|
while (n > 1) {
|
|
if (n & 1) o = o.concat(v);
|
|
n >>= 1;
|
|
v = v.concat(v);
|
|
}
|
|
return o.concat(v);
|
|
};
|
|
|
|
// group :: Eq a => [a] -> [[a]]
|
|
var group = function (xs) {
|
|
return groupBy(function (a, b) {
|
|
return a === b;
|
|
}, xs);
|
|
};
|
|
|
|
// groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
|
|
var groupBy = function (f, xs) {
|
|
var dct = xs.slice(1)
|
|
.reduce(function (a, x) {
|
|
var h = a.active.length > 0 ? a.active[0] : undefined,
|
|
blnGroup = h !== undefined && f(h, x);
|
|
|
|
return {
|
|
active: blnGroup ? a.active.concat(x) : [x],
|
|
sofar: blnGroup ? a.sofar : a.sofar.concat([a.active])
|
|
};
|
|
}, {
|
|
active: xs.length > 0 ? [xs[0]] : [],
|
|
sofar: []
|
|
});
|
|
return dct.sofar.concat(dct.active.length > 0 ? [dct.active] : []);
|
|
};
|
|
|
|
// compare :: a -> a -> Ordering
|
|
var compare = function (a, b) {
|
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
};
|
|
|
|
// on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
|
|
var on = function (f, g) {
|
|
return function (a, b) {
|
|
return f(g(a), g(b));
|
|
};
|
|
};
|
|
|
|
// nub :: [a] -> [a]
|
|
var nub = function (xs) {
|
|
return nubBy(function (a, b) {
|
|
return a === b;
|
|
}, xs);
|
|
};
|
|
|
|
// nubBy :: (a -> a -> Bool) -> [a] -> [a]
|
|
var nubBy = function (p, xs) {
|
|
var x = xs.length ? xs[0] : undefined;
|
|
|
|
return x !== undefined ? [x].concat(nubBy(p, xs.slice(1)
|
|
.filter(function (y) {
|
|
return !p(x, y);
|
|
}))) : [];
|
|
};
|
|
|
|
// find :: (a -> Bool) -> [a] -> Maybe a
|
|
var find = function (f, xs) {
|
|
for (var i = 0, lng = xs.length; i < lng; i++) {
|
|
if (f(xs[i], i)) return xs[i];
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
// Int -> [a] -> [a]
|
|
var take = function (n, xs) {
|
|
return xs.slice(0, n);
|
|
};
|
|
|
|
// unlines :: [String] -> String
|
|
var unlines = function (xs) {
|
|
return xs.join('\n');
|
|
};
|
|
|
|
// show :: a -> String
|
|
var show = function (x) {
|
|
return JSON.stringify(x);
|
|
}; //, null, 2);
|
|
|
|
// head :: [a] -> a
|
|
var head = function (xs) {
|
|
return xs.length ? xs[0] : undefined;
|
|
};
|
|
|
|
// tail :: [a] -> [a]
|
|
var tail = function (xs) {
|
|
return xs.length ? xs.slice(1) : undefined;
|
|
};
|
|
|
|
// length :: [a] -> Int
|
|
var length = function (xs) {
|
|
return xs.length;
|
|
};
|
|
|
|
// SIGNED DIGIT SEQUENCES (mapped to sums and to strings)
|
|
|
|
// data Sign :: [ 0 | 1 | -1 ] = ( Unsigned | Plus | Minus )
|
|
// asSum :: [Sign] -> Int
|
|
var asSum = function (xs) {
|
|
var dct = xs.reduceRight(function (a, sign, i) {
|
|
var d = i + 1; // zero-based index to [1-9] positions
|
|
if (sign !== 0) {
|
|
// Sum increased, digits cleared
|
|
return {
|
|
digits: [],
|
|
n: a.n + sign * parseInt([d].concat(a.digits)
|
|
.join(''), 10)
|
|
};
|
|
} else return { // Digits extended, sum unchanged
|
|
digits: [d].concat(a.digits),
|
|
n: a.n
|
|
};
|
|
}, {
|
|
digits: [],
|
|
n: 0
|
|
});
|
|
return dct.n + (
|
|
dct.digits.length > 0 ? parseInt(dct.digits.join(''), 10) : 0
|
|
);
|
|
};
|
|
|
|
// data Sign :: [ 0 | 1 | -1 ] = ( Unsigned | Plus | Minus )
|
|
// asString :: [Sign] -> String
|
|
var asString = function (xs) {
|
|
var ns = xs.reduce(function (a, sign, i) {
|
|
var d = (i + 1)
|
|
.toString();
|
|
return sign === 0 ? a + d : a + (sign > 0 ? ' +' : ' -') + d;
|
|
}, '');
|
|
|
|
return ns[0] === '+' ? tail(ns) : ns;
|
|
};
|
|
|
|
// SUM T0 100 ------------------------------------------------------------
|
|
|
|
// universe :: [[Sign]]
|
|
var universe = permutationsWithRepetition(9, [0, 1, -1])
|
|
.filter(function (x) {
|
|
return x[0] !== 1;
|
|
});
|
|
|
|
// allNonNegativeSums :: [Int]
|
|
var allNonNegativeSums = universe.map(asSum)
|
|
.filter(function (x) {
|
|
return x >= 0;
|
|
})
|
|
.sort();
|
|
|
|
// uniqueNonNegativeSums :: [Int]
|
|
var uniqueNonNegativeSums = nub(allNonNegativeSums);
|
|
|
|
return ["Sums to 100:\n", unlines(universe.filter(function (x) {
|
|
return asSum(x) === 100;
|
|
})
|
|
.map(asString)),
|
|
|
|
"\n\n10 commonest sums (sum, followed by number of routes to it):\n",
|
|
show(take(10, group(allNonNegativeSums)
|
|
.sort(on(flip(compare), length))
|
|
.map(function (xs) {
|
|
return [xs[0], xs.length];
|
|
}))),
|
|
|
|
"\n\nFirst positive integer not expressible as a sum of this kind:\n",
|
|
show(find(function (x, i) {
|
|
return x !== i;
|
|
}, uniqueNonNegativeSums.sort(compare)) - 1), // zero-based index
|
|
|
|
"\n10 largest sums:\n",
|
|
show(take(10, uniqueNonNegativeSums.sort(flip(compare))))
|
|
].join('\n') + '\n';
|
|
})();
|