Magic squares of doubly even order: Difference between revisions
Content deleted Content added
→JS ES6: Updated primitives, tidied. |
|||
Line 1,278: | Line 1,278: | ||
'use strict'; |
'use strict'; |
||
// |
// doublyEvenMagicSquare :: Int -> [[Int]] |
||
const |
const doublyEvenMagicSquare = n => |
||
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 -> [Bool] |
|||
const truthSeries = n => |
|||
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, |
|||
const main = () => |
|||
// Magic squares of orders 4, 8 and 12. |
|||
map(n => { |
|||
sequence = isInt(power) ? truthSeries(power) : ( |
|||
const |
|||
lines = doublyEvenMagicSquare(n), |
|||
sums = lines.concat( |
|||
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); |
|||
// |
// chunksOf :: Int -> [a] -> [[a]] |
||
const |
const chunksOf = n => xs => |
||
enumFromThenTo(0)(n)( |
|||
xs.length - 1 |
|||
).reduce( |
|||
(a, i) => a.concat([xs.slice(i, (n + i))]), |
|||
[] |
|||
); |
|||
// |
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c |
||
const |
const compose = (...fs) => |
||
x => fs.reduceRight((a, f) => f(a), x); |
|||
// |
// diagonals :: [[a]] -> ([a], [a]) |
||
const |
const diagonals = xs => { |
||
const |
|||
if (xs.length <= n) return [xs]; |
|||
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))]; |
|||
})(); |
|||
}; |
|||
// |
// enumFromThenTo :: Int -> Int -> Int -> [Int] |
||
const |
const enumFromThenTo = x1 => x2 => y => { |
||
const d = x2 - x1; |
|||
return Array.from({ |
|||
length: Math.floor(y - x2) / d + 2 |
|||
const replicate = (n, 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); |
|||
}; |
}; |
||
// |
// enumFromTo :: Int -> Int -> [Int] |
||
const |
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); |
|||
// |
// isInt :: Int -> Bool |
||
const |
const isInt = x => x === Math.floor(x); |
||
xs[0].map((_, iCol) => xs.map((row) => row[iCol])); |
|||
// |
// justifyRight :: Int -> Char -> String -> String |
||
const |
const justifyRight = n => cFiller => s => |
||
n > s.length ? ( |
|||
s.padStart(n, cFiller) |
|||
) : s; |
|||
// map :: (a -> b) -> [a] -> [b] |
|||
const map = f => xs => |
|||
(Array.isArray(xs) ? ( |
|||
return [zipWith(cell, ns, ns), zipWith(cell, ns, reverse(ns))]; |
|||
xs |
|||
) : xs.split('')).map(f); |
|||
[] |
|||
]; |
|||
}; |
|||
// |
// replicate :: Int -> a -> [a] |
||
const |
const replicate = n => x => |
||
Array.from({ |
|||
length: n |
|||
}, () => x); |
|||
} |
|||
// reverse :: [a] -> [a] |
// reverse :: [a] -> [a] |
||
const reverse = |
const reverse = xs => |
||
'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); |
||
// |
// str :: a -> String |
||
const |
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] |
|||
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 |