/* The Computer Language Shootout
   http://shootout.alioth.debian.org/

   benchmark implementation (not optimized)
   JRE 1.5 only
   contributed by Josh Goldfoot */

import java.io.*;
import java.lang.*;
import java.util.*;

public class magicsquares {

    static int n;
    static int mn;

    public static class FfmResult {
        public int x;
        public int y;
        public int[] moves;
        public FfmResult(int ix, int iy, int[] imoves) {
            x = ix;
            y = iy;
            moves = (int[]) imoves.clone();
        }
    }

    public static class PQNode implements Comparable {
        public int [] grid;
        boolean priorityCalculated;
        private int priority;
        private FfmResult ffm;

        public PQNode() {
            grid = new int [n * n];
            int i;
            for (i = 0; i < n * n; i++)
                grid[i] = 0;
            priorityCalculated = false;
            ffm = null;
        }
        public PQNode(PQNode other) {
            grid = (int[]) other.grid.clone();
            priorityCalculated = false;
        }

        public int[] gridRow(int y) {
            int[] r = new int[n];
            int x;
            for (x = 0; x < n; x++)
                r[x] = grid[x + y * n];
            return r;
        }

        public int[] gridCol(int x) {
            int[] r = new int[n];
            int y;
            for (y = 0; y < n; y++)
                r[y] = grid[x + y * n];
            return r;
        }

        public int[] diagonal(int q) {
            int[] r = new int[n];
            int i;
            for (i = 0; i < n; i++) {
                if (q == 1)
                    r[i] = grid[i + i * n];
                else if (q == 2) {
                    r[i] = grid[i + (n - 1 - i) * n];
                }
            }
            return r;
        }

        public int[] possibleMoves(int x, int y) {
            int zerocount, sum, highest, j, i;

            if (grid[x + y * n] != 0)
                return ( new int [] { });
            ArrayList<int[]> cellGroups = new ArrayList<int[]>();
            cellGroups.add(gridCol(x));
            cellGroups.add(gridRow(y));
            if (x == y)
                cellGroups.add(diagonal(1));
            if (x + y == n - 1)
                cellGroups.add(diagonal(2));
            HashSet<Integer> usedNumbers = new HashSet<Integer>();
            for (i = 0; i < grid.length; i++)
                usedNumbers.add(new Integer(grid[i]));
            HashSet<Integer> onePossible = new HashSet<Integer>();
            highest = n * n;
            for (int[] group: cellGroups) {
                zerocount = 0;
                sum = 0;
                for (int num: group) {
                    if (num == 0)
                        zerocount++;
                    sum += num;
                }
                if (zerocount == 1)
                    onePossible.add(new Integer(mn - sum));
                if (mn - sum < highest)
                    highest = mn - sum;
            }
            if (onePossible.size() == 1) {
                Integer onlyPossibleMove = (Integer) onePossible.iterator().next();
                int opmint = onlyPossibleMove.intValue();
                if (opmint >= 1 && 
                        opmint <= n * n && 
                        ! usedNumbers.contains(onlyPossibleMove))
                    return (new int[] { opmint });
                else
                    return ( new int [] { });
            } else if (onePossible.size() > 1)
                return ( new int [] { });
            ArrayList<Integer> moves = new ArrayList<Integer>();
            for (i = 1; i <= highest; i++) {
                Integer ii = new Integer(i);
                if (! usedNumbers.contains(ii))
                    moves.add(ii);
            }
            int[] r = new int[moves.size()];
            i = 0;
            for (Integer move: moves) {
                r[i++] = move.intValue();
            }
            return r;
        }

        public FfmResult findFewestMoves() {
            if (ffm != null)
                return ffm;
            int x, y, mx, my, ind;
            int[] minmoves = (new int[] { });
            int[] moves;
            mx = my = -1;
            for (y = 0; y < n; y++)
                for (x = 0; x < n; x++) {
                    ind = x + y * n;
                    if (grid[ind] == 0) {
                        moves = possibleMoves(x,y);
                        if (mx == -1 || moves.length < minmoves.length) {
                            mx = x;
                            my = y;
                            minmoves = (int[]) moves.clone();
                        }
                    }
                }
            ffm = new FfmResult(mx, my, minmoves);
            return ffm;
        }

        public void calculatePriority()
        {
            int i, zerocount;
            zerocount = 0;
            for (int cell: grid)
                if (cell == 0)
                    zerocount++;
            priority = zerocount + findFewestMoves().moves.length;
            priorityCalculated = true;
        }

        public int getPriority()
        {
            if (priorityCalculated)
                return priority;
            else {
                calculatePriority();
                return priority;
            }
        }

        public int compareTo(Object anotherMSquare) throws ClassCastException {
            if (!(anotherMSquare instanceof PQNode))
                throw new ClassCastException();
            PQNode other = (PQNode) anotherMSquare;
            int c = getPriority() - other.getPriority();
            if (c == 0) {
                int i = 0;
                while (c == 0 && i < n * n) {
                    c = grid[i] - other.grid[i];
                    i++;
                }
            }
            return c;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder();
            int y, x;
            for (y = 0; y < n; y++) {
                for (x = 0; x < n; x++) {
                    sb.append(grid[x + y * n]);
                    if (x < n-1)
                        sb.append(' ');
                }
                if (y < n-1)
                    sb.append('\n');
            }
            return sb.toString();
        }


    }

    public magicsquares() { }

    public static void main(String[] args) {
        n = 3;
        if (args.length > 0) 
            n = Integer.parseInt(args[0]);
        mn = n * (1 + n * n) / 2;
        PriorityQueue<magicsquares.PQNode> pq = new PriorityQueue<magicsquares.PQNode>(); 
        pq.offer(new magicsquares.PQNode() );
        while (! pq.isEmpty()) {
            PQNode topSquare = pq.poll();
            if (topSquare.getPriority() == 0) {
                System.out.println(topSquare);
                break;
            }
            magicsquares.FfmResult result = topSquare.findFewestMoves();

            int ind = result.x + result.y * n;
            for (int move: result.moves) {
                magicsquares.PQNode newSquare = new magicsquares.PQNode(topSquare);
                newSquare.grid[ind] = move;
                pq.offer(newSquare);
            }
        }

    }
}
