/* 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.