/*
Magic squares of odd orders (Rosetta code) in Picat.
http://rosettacode.org/wiki/Magic_squares_of_odd_order
"""
A magic square is an N x N square matrix whose numbers (usually integers)
consist of consecutive numbers arranged so that the sum of each row and column,
and both long (main) diagonals are equal to the same sum (which is called the
magic number or magic constant).
The numbers are usually (but not always) the 1st N**2 positive
integers.
A magic square whose rows and columns add up to a magic number but whose main
diagonals do not, is known as a semimagic square.
8 1 6
3 5 7
4 9 2
Task
For any odd N, generate a magic square with the integers [1, \ldots, N2] and show the results.
Optionally, show the magic number.
You should demonstrate the generator by showing at least a magic square for N = 5.
"""
This solution was inspired by the J (APL) solution.
See http://www.jsoftware.com/papers/eem/magicsq.htm
This Picat model was created by Hakan Kjellerstrand, hakank@gmail.com
See also my Picat page: http://www.hakank.org/picat/
*/
import util.
% import cp.
main => go.
go =>
N = 11,
M = magic_square(N),
print_matrix(M),
check(M),
nl.
go2 =>
N = 111,
M = magic_square(N),
% print_matrix(M),
check(M),
nl.
go3 =>
foreach(N in 1..2..211)
println(n=N),
M = magic_square(N),
% print_matrix(M),
check(M),
nl
end,
nl.
%
% See http://www.jsoftware.com/papers/eem/magicsq.htm
% for a discussion of the working of this.
% (Yes, APL/J/K is neater for these kind of operations.
% Though I like that Picat can chain functions...)
magic_square(N) = MS =>
if N mod 2 = 0 then
printf("N (%d) is not odd!\n", N),
halt
end,
R = make_rotate_list(N), % the rotate indices
MS = make_square(N).transpose().rotate_matrix(R).transpose().rotate_matrix(R).
%
% make a square matrix of size N (containing the numbers 1..N*N)
%
make_square(N) = [[I*N+J : J in 1..N]: I in 0..N-1].
%
% rotate list:
% rotate_list(11) = [-5,-4,-3,-2,-1,0,1,2,3,4,5]
%
make_rotate_list(N) = [I - ceiling(N / 2) : I in 1..N].
%
% rotate the matrix M according to rotate list R
%
rotate_matrix(M, R) = [rotate_n(Row,N) : {Row,N} in zip(M,R)].
%
% Rotate the list L N steps (either positive or negative N)
% rotate(1..10,3) -> [4,5,6,7,8,9,10,1,2,3]
% rotate(1..10,-3) -> [8,9,10,1,2,3,4,5,6,7]
%
rotate_n(L,N) = Rot =>
Len = L.length,
R = cond(N < 0, Len + N, N),
Rot = [L[I] : I in (R+1..Len) ++ 1..R].
%
% Check if M is a magic square
%
check(M) =>
N = M.length,
Sum = N*(N*N+1) // 2,
println(sum=Sum),
Rows = [sum(Row) : Row in M],
Cols = [sum(Col) : Col in M.transpose()],
Diag1 = sum([M[I,I] : I in 1..N]),
Diag2 = sum([M[I,N-I+1] : I in 1..N]),
All = Rows ++ Cols ++ [Diag1, Diag2],
% println(all=All),
OK = true,
foreach(X in All)
if X != Sum then
printf("%d != %d\n", X, Sum),
OK := false
end
end,
if OK then
println(ok)
else
println(not_ok),
halt
end,
nl.
print_matrix(M) =>
Format = to_fstring("%%%dd",max(flatten(M)).to_string().length+1),
foreach(Row in M)
foreach(X in Row)
printf(Format, X)
end,
nl
end,
nl.