Magic squares of doubly even order: Difference between revisions

Content deleted Content added
Hout (talk | contribs)
→‎JS ES6: Updated primitives, tidied.
Line 1,278: Line 1,278:
'use strict';
'use strict';


// doubleEvenMagicSquare :: Int -> [[Int]]
// doublyEvenMagicSquare :: Int -> [[Int]]
const doubleEvenMagicSquare = n => {
const doublyEvenMagicSquare = n =>
if (n % 4 > 0) return undefined;
0 === n % 4 ? (() => {
const
sqr = n * n,
power = Math.log2(sqr),
scale = replicate(n / 4);
return chunksOf(n)(
map((x, i) => x ? 1 + i : sqr - i)(
isInt(power) ? truthSeries(power) : (
compose(
flatten,
scale,
map(scale),
chunksOf(4)
)(truthSeries(4))
)
)
);
})() : undefined;


// truthSeries :: Int -> [Int]
// truthSeries :: Int -> [Bool]
const truthSeries = n => {
const truthSeries = n =>
if (n <= 0) return [true];
0 >= n ? (
[true]
) : (() => {
const xs = truthSeries(n - 1);
const xs = truthSeries(n - 1);
return xs.concat(xs.map(x => !x));
return xs.concat(xs.map(x => !x));
};
})();


// TEST -----------------------------------------------
const sqr = n * n,
scale = curry(replicate)(n / 4),
const main = () =>
power = Math.log2(sqr),
// Magic squares of orders 4, 8 and 12.
map(n => {
sequence = isInt(power) ? truthSeries(power) : (
flatten(
const
scale(
lines = doublyEvenMagicSquare(n),
splitEvery(4, truthSeries(4))
sums = lines.concat(
.map(scale)
transpose(lines)
.concat(diagonals(lines))
)
.map(xs => xs.reduce((a, b) => a + b, 0)),
sum = sums[0];
return [
"Order: " + n.toString(),
"Summing to: " + sum.toString(),
"Row, column and diagonal sums checked: " +
all(eq(sum))(sums)
.toString() + '\n',
lines.map(
xs => xs.map(
compose(justifyRight(3)(' '), str)
)
)
)
.join(' '))
);
.join('\n')
].join('\n')
})([4, 8, 12])
.join('\n\n');


return splitEvery(n, sequence
.map((x, i) => x ? i + 1 : sqr - i));
};


// GENERIC FUNCTIONS ----------------------------------


// all :: (a -> Bool) -> [a] -> Bool
// GENERIC FUNCTIONS ----------------------------------------------------
const all = p =>
// True if p(x) holds for every x in xs.
xs => xs.every(p);


// flatten :: Tree a -> [a]
// chunksOf :: Int -> [a] -> [[a]]
const flatten = t => (t instanceof Array ? concatMap(flatten, t) : [t]);
const chunksOf = n => xs =>
enumFromThenTo(0)(n)(
xs.length - 1
).reduce(
(a, i) => a.concat([xs.slice(i, (n + i))]),
[]
);


// concatMap :: (a -> [b]) -> [a] -> [b]
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const concatMap = (f, xs) => [].concat.apply([], xs.map(f));
const compose = (...fs) =>
x => fs.reduceRight((a, f) => f(a), x);


// splitEvery :: Int -> [a] -> [][a]]
// diagonals :: [[a]] -> ([a], [a])
const splitEvery = (n, xs) => {
const diagonals = xs => {
const
if (xs.length <= n) return [xs];
const [h, t] = [xs.slice(0, n), xs.slice(n)];
nRows = xs.length,
nCols = 0 < nRows ? (
return [h].concat(splitEvery(n, t));
xs[0].length
}
) : 0;
return nRows !== nCols ? [
[],
[]
] : (() => {
const
ns = enumFromTo(0)(nCols - 1),
f = zipWith(x => y => xs[y][x])(ns);
return [f(ns), f(reverse(ns))];
})();
};


// curry :: ((a, b) -> c) -> a -> b -> c
// enumFromThenTo :: Int -> Int -> Int -> [Int]
const curry = f => a => b => f(a, b);
const enumFromThenTo = x1 => x2 => y => {
const d = x2 - x1;

// replicate :: Int -> a -> [a]
return Array.from({
length: Math.floor(y - x2) / d + 2
const replicate = (n, a) => {
let v = [a],
}, (_, i) => x1 + (d * i));
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);
};
};


// isInt :: Int -> Bool
// enumFromTo :: Int -> Int -> [Int]
const isInt = x => x === Math.floor(x);
const enumFromTo = m => n =>
Array.from({
length: 1 + n - m
}, (_, i) => m + i);


// eq (==) :: Eq a => a -> a -> Bool
const eq = a => b => a === b;


// flatten :: NestedList a -> [a]
// TEST AND DISPLAY FUNCTIONS -------------------------------------------
const flatten = nest => nest.flat(Infinity);


// transpose :: [[a]] -> [[a]]
// isInt :: Int -> Bool
const transpose = xs =>
const isInt = x => x === Math.floor(x);
xs[0].map((_, iCol) => xs.map((row) => row[iCol]));


// diagonals :: [[a]] -> ([a], [a])
// justifyRight :: Int -> Char -> String -> String
const diagonals = xs => {
const justifyRight = n => cFiller => s =>
const nRows = xs.length,
n > s.length ? (
nCols = (nRows > 0 ? xs[0].length : 0);
s.padStart(n, cFiller)
const cell = (x, y) => xs[y][x];
) : s;


if (nRows === nCols) {
// map :: (a -> b) -> [a] -> [b]
const ns = range(0, nCols - 1);
const map = f => xs =>
(Array.isArray(xs) ? (
return [zipWith(cell, ns, ns), zipWith(cell, ns, reverse(ns))];
} else return [
xs
[],
) : xs.split('')).map(f);
[]
];
};


// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
// replicate :: Int -> a -> [a]
const zipWith = (f, xs, ys) => {
const replicate = n => x =>
const ny = ys.length;
Array.from({
return (xs.length <= ny ? xs : xs.slice(0, ny))
length: n
.map((x, i) => f(x, ys[i]));
}, () => x);
}


// reverse :: [a] -> [a]
// reverse :: [a] -> [a]
const reverse = (xs) => xs.slice(0)
const reverse = xs =>
.reverse()
'string' !== typeof xs ? (
xs.slice(0).reverse()

) : xs.split('').reverse().join('');
// range :: Int -> Int -> [Int]
const range = (m, n) =>
Array.from({
length: Math.floor(n - m) + 1
}, (_, i) => m + i);

// all :: (a -> Bool) -> [a] -> Bool
const all = (f, xs) => xs.every(f);


// show :: a -> String
// show :: a -> String
const show = x => JSON.stringify(x);
const show = x => JSON.stringify(x);


// justifyRight :: Int -> Char -> Text -> Text
// str :: a -> String
const justifyRight = (n, cFiller, strText) =>
const str = x => x.toString();
n > strText.length ? (
(cFiller.repeat(n) + strText)
.slice(-n)
) : strText;


// transpose :: [[a]] -> [[a]]
// TEST -----------------------------------------------------------------
const transpose = xs =>
xs[0].map((_, iCol) => xs.map((row) => row[iCol]));


// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
//return doubleEvenMagicSquare(8)
const zipWith = f => xs => ys =>
xs.slice(
0, Math.min(xs.length, ys.length)
).map((x, i) => f(x)(ys[i]));


// MAIN -----------------------------------------------
return [4, 8, 12]
.map(n => {
return main();
const lines = doubleEvenMagicSquare(n);
const sums = lines.concat(
transpose(lines)
.concat(diagonals(lines))
)
.map(xs => xs.reduce((a, b) => a + b, 0));
const sum = sums[0];
return [
"Order: " + n.toString(),
"Summing to: " + sum.toString(),
"Row, column and diagonal sums checked: " +
all(x => x === sum, sums)
.toString() + '\n',
lines.map(
xs => xs.map(
x => justifyRight(3, ' ', x.toString())
)
.join(' '))
.join('\n')
].join('\n')
})
.join('\n\n');
})();</lang>
})();</lang>

{{Out}}
{{Out}}
<pre>Order: 4
<pre>Order: 4