#
# Algorighms for Magic Squares taken from 
# Mathematical recreations and essays, 12th ed.,
# by W.W. Rouse Ball and H.S.M. Coxeter
#
# Author: Dominik Gruntz
#

magic := proc(n) local A, i, j, k, i1, j1, m, m1, m2, t;
#magic := proc(n:posint) local A, i, j, k, i1, j1, m, m1, m2, t;

   if not type(n,posint) then
      ERROR(`first argument must be a postive integer`) fi;

   if n=2 then ERROR(`magic square of size n=2 does not exist`) fi;

   A := linalg[matrix](n,n,0):

   if irem(n,4) = 0 then # double even order
      k := 1;
      for i to n do
         for j to n do
            if iquo(irem(i,4),2)=iquo(irem(j,4),2) then
               A[i,j] := n*n+1-k
            else
               A[i,j] := k
            fi;
            k := k+1;
         od;
      od;
      RETURN(eval(A))
   fi;

   if irem(n,2)=0 then m := n/2 else m := n fi;

   # odd order or upper corner of even order:

   i := 1;
   j := iquo(m+1,2);
   for k to m*m do 
      A[i,j] := k;
      if i>1 then i1 := i-1 else i1 := m fi;
      if j<m then j1 := j+1 else j1 := 1 fi;
      if A[i1,j1] <> 0 then i1 := i+1; j1 := j fi;
      i := i1; j := j1;
   od;
   
   if irem(n,2) <> 0 then RETURN(eval(A)) fi;
   
   # rest of even order

   t := m*m;
   for i to m do
      for j to m do
         A[i,   j+m] := A[i,j] + 2*t;
         A[i+m, j  ] := A[i,j] + 3*t;
         A[i+m, j+m] := A[i,j] +   t;
      od;
   od;
   m1 := iquo(m-1,2);
   
   for j to m1 do
      for i to m do
         t := A[i,j]; A[i,j] := A[m+i,j]; A[m+i,j] := t
      od
   od;
   m1 := iquo(m+1,2); m2 := m1+m;
   t := A[m1, 1]; A[m1, 1] := A[m2, 1]; A[m2, 1] := t;
   t := A[m1,m1]; A[m1,m1] := A[m2,m1]; A[m2,m1] := t;
   if n = 6 then RETURN(eval(A)) fi;
   for j from n+1-iquo(m-3,2) to n do
      for i to m do
         t := A[i,j]; A[i,j] := A[m+i,j]; A[m+i,j] := t;
      od
   od:
   RETURN(eval(A))
end:

ismagic := proc(A) local i, m, n, v;

   if not type(A,'matrix(integer,square)') then ERROR(`invalid arguments`) fi;
   n := linalg[rowdim](A);
   m := n*(n^2+1)/2;
   if convert(A,set) <> {seq(i, i=1..n*n)} then RETURN(false) fi;
   for v in [linalg[row](A,1..n),linalg[col](A,1..n)] do
      if convert(convert(v,list),`+`) <> m then RETURN(false) fi;
   od;
   sum('A[i,i]', 'i'=1..n) = m and sum('A[n-i+1, i]', 'i'=1..n) = m 
end:

`help/text/magic` := TEXT(
`FUNCTION: magic - create a magic square`,
`      `,
`CALLING SEQUENCE:`,
`   magic(n)`,
`      `,
`PARAMETERS:`,
`   n - a positive integer`,
`      `,
`SYNOPSIS:   `,
`- The procedure magic(n) constructs an n by n matrix which`,
`  is a magic square.  A magic square is a matrix containing`,
`  each of the first n^2 integers such that each row and`,
`  each column and each diagonal sum to n*(n^2+1)/2.`,
`   `,
`- The resulting matrices have many interesting properties e.g.`,
`  the eigenvalues turn out to be real.`,
`   `,
`- The utility routine ismagic can be used to test if a square`,
`  matrix of integers forms a magic square`,
`      `,
`EXAMPLE:   `,
`   `,
`> M4 := magic(4);`,
`   `,
`                                  [ 16   2   3  13 ]`,
`                                  [                ]`,
`                                  [  5  11  10   8 ]`,
`                            M4 := [                ]`,
`                                  [  9   7   6  12 ]`,
`                                  [                ]`,
`                                  [  4  14  15   1 ]`,
`   `,
`> ismagic(M4);`,
`   `,
`                                      true`,
`   `,
`> 4*(4^2+1)/2 = sum(M4[i,1],i=1..4);`,
`   `,
`                                    34 = 34`,
`   `,
`> linalg[eigenvals](M4);`,
`   `,
`                                      1/2       1/2`,
`                            0, 34, 4 5   , - 4 5`
):

#save `magic.m`;
#quit
